a tool for shared writing and social publishing
1import { ZodObject, ZodRawShape, ZodUnion, z } from "zod"; 2 3type Route< 4 Cmd extends string, 5 Input extends ZodObject<ZodRawShape> | ZodUnion<any>, 6 Result extends object | null, 7 Env extends {}, 8> = { 9 route: Cmd; 10 input: Input; 11 handler: (msg: z.infer<Input>, env: Env) => Promise<Result>; 12}; 13 14type Routes<Env extends {}> = Route<string, any, any, Env>[]; 15 16export function makeAPIClient<R extends Routes<any>>(basePath: string) { 17 return async <T extends R[number]["route"]>( 18 route: T, 19 data: z.infer<Extract<R[number], { route: T }>["input"]>, 20 ) => { 21 let result = await fetch(`${basePath}/${route}`, { 22 body: JSON.stringify(data), 23 method: "POST", 24 headers: { "Content-type": "application/json" }, 25 }); 26 return result.json() as Promise< 27 Awaited<ReturnType<Extract<R[number], { route: T }>["handler"]>> 28 >; 29 }; 30} 31 32export const makeRouter = <Env extends {}>(routes: Routes<Env>) => { 33 return async (route: string, request: Request, env: Env) => { 34 let status = 200; 35 let result; 36 switch (request.method) { 37 case "POST": { 38 let handler = routes.find((f) => f.route === route); 39 if (!handler) { 40 status = 404; 41 result = { error: `route ${route} not Found` }; 42 break; 43 } 44 45 let body; 46 if (handler.input) 47 try { 48 body = await request.json(); 49 } catch (e) { 50 result = { error: "Request body must be valid JSON" }; 51 status = 400; 52 break; 53 } 54 55 let msg = handler.input.safeParse(body); 56 if (!msg.success) { 57 status = 400; 58 result = msg.error; 59 break; 60 } 61 try { 62 result = (await handler.handler(msg.data as any, env)) as object; 63 break; 64 } catch (e) { 65 console.log(e); 66 status = 500; 67 result = { 68 error: "An error occured while handling this request", 69 errorText: (e as Error).toString(), 70 }; 71 break; 72 } 73 } 74 default: 75 status = 404; 76 result = { error: "Only POST Supported" }; 77 } 78 79 let res = new Response(JSON.stringify(result), { 80 status, 81 headers: { 82 "Access-Control-Allow-Credentials": "true", 83 "Content-type": "application/json;charset=UTF-8", 84 "Access-Control-Allow-Origin": "*", 85 "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS", 86 }, 87 }); 88 //result.headers?.forEach((h) => res.headers.append(h[0], h[1])); 89 return res; 90 }; 91}; 92 93export function makeRoute< 94 Cmd extends string, 95 Input extends ZodObject<ZodRawShape> | ZodUnion<any>, 96 Result extends object | null, 97 Env extends {}, 98>(d: Route<Cmd, Input, Result, Env>) { 99 return d; 100}