sveltekit-zero-api
sveltekit-zero-api copied to clipboard
bug: endpointPipe does not throw type-error when requirement for pipe-function is not met
The pull request https://github.com/Refzlund/sveltekit-zero-api/pull/20#issuecomment-1656705600 was flawed, as it fixes one thing but breaks another.
Now this "other" issue is less critical, so I prefer this as the current state of endpointPipeline.
Requirements for endpointPIpe, e.g. defining the event as KitEvent & { locals: { requirement: boolean } } does not throw a type-error.
In the process of resolving this issue I will rewrite how the pipe-function works. I think it may or may not work out. We'll see.
Here's my general idea of how they will be written:
Endpoint pipe function
import { pipeFn, endpoint, endpointPipe } from 'sveltekit-zero-api'
const authGuard = pipeFn(event => {
return <T>(...permissions: [...T]) => {
return BadRequest()
}
})
authGuard: PipeFunction<
{}, {},
Fn<<T>(...permissions: [...T]): BadRequest)>
>
// ✅ Use Pipe-function
const result = endpointPipe<Get>(event)(
authGuard('admin')
)
export const GET = endpoint<Get>(
authGuard('admin')
)
// ❌ BUT NOT
const result = authGuard('admin')
// ^? `result: (event) => ...`
The reason we structure it this way, is so that the types that is ex. returned from a pipe-function can make use of the event-type/locals that is passed into the function.
For instance, a querySpread pipe function would need to know the event contents:
import {
type InferKitEvent,
pipeFn,
querySpread as querySpreadFn
} from 'sveltekit-zero-api'
type UnknownRecord = Record<string | number | symbol, unknown>
interface Options<T extends UnknownRecord> {
required: (keyof T['query'])[]
}
const querySpread = pipeFn(event => {
type EventContent = InferKitEvent<typeof event>
return (options?: Options<EventContent>) => {
const result = querySpreadFn(event)
const queryErrors: ... = {}
for (const key of options?.required ?? {}) {
if(!!result[key])
queryErrors[key] = 'Required'
}
if(Object.keys(queryErrors).length)
return BadRequst({...})
}
})
Small example
interface Get {
query: {
seasonId: string
}
}
const GET = endpoint<Get>(
authGuard('admin'),
querySpread({ required: ['seasonId'] }),
pipeFn(async event => {
...
})
)
Input and output
import { type Settings, SettingsService } from '...'
import { pipeFn } from 'sveltekit-zero-api'
type LocalsOutput = {
settings: Settings
}
const getSettings = pipeFn<{}, LocalsOutput>(async event => {
if(event.locals.settings)
return
const result = await SettingsService.findOne(...)
if(!result)
return BadRequest()
event.locals.settings = result
})
// ... Somewhere far far away.ts
// Sets requirements for this function
type In = {
settings: Settings
}
// Sets output of this function
type Out = {
theme: 'dark' | 'light'
}
// (parameter) `event: KitEvent<EventContent>`
type EventContent = {
query: {...}
body: {...}
}
const getTheme = pipeFn<In, Out, EventContent>(async (event) => {
const settings = event.locals.settings
})