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}