sveltekit-zero-api icon indicating copy to clipboard operation
sveltekit-zero-api copied to clipboard

bug: endpointPipe does not throw type-error when requirement for pipe-function is not met

Open Refzlund opened this issue 2 years ago • 1 comments

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.

Refzlund avatar Jul 29 '23 11:07 Refzlund

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
	
})

Refzlund avatar Jul 29 '23 22:07 Refzlund