import { ZodType, z } from 'zod'; export type InferInput> = { [K in keyof T]: z.infer; }; export type SchemaFn< I extends Record, O extends ZodType, C, > = (input: InferInput & C) => Promise>; // actual defs export class FnBuilder< I extends Record, O extends ZodType, C, M = InferInput & C, > { constructor( public inputSchema: I, public outputSchema: O, ) {} // I've given up trying to type this middlewares: ((x: any) => Promise)[] = []; use(fn: (input: M) => Promise): FnBuilder { this.middlewares.push(fn); return this as any; } do(fn: (input: M) => Promise>) { return new HyperRPCFn( this.inputSchema, this.outputSchema, fn, this.middlewares, ); } } export class HyperRPCFn< I extends Record, O extends ZodType, C = {}, > { private inputValidator: ZodType>; constructor( public input: I, public output: O, public fn: SchemaFn, public middlewares: ((x: any) => Promise)[] = [], ) { this.inputValidator = z.object(input); } async call(ctx: C, args: unknown): Promise> { const parsedArgs = this.inputValidator.parse(args); const res = await this.middlewares.reduce( async (acc: any, fn) => await fn(await acc), Object.assign(parsedArgs, ctx), ); return this.output.parse(await this.fn(res)); } }