Skip to content

Commit c0cf91c

Browse files
committed
chore: some playing with routing/grouping, ignore
Signed-off-by: tunnckoCore <5038030+tunnckoCore@users.noreply.github.com>
1 parent 30d2430 commit c0cf91c

File tree

3 files changed

+276
-5
lines changed

3 files changed

+276
-5
lines changed

src/index.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ export class Zagora<
131131
return wrapper(rawArgs, inputResult.data);
132132
}
133133

134-
// const processedInput =
135-
// processed === "$__MAGIC_VALUE_"
136-
// ? []
137-
// : processed ? [];
138-
139134
try {
140135
const errs = errorsSchema
141136
? createErrorHelpers(errorsSchema, isAsync)

src/routing/__router.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { z } from "zod";
2+
import { zagora } from "./src/index";
3+
4+
export type NewUser = z.infer<typeof NewUserSchema>;
5+
export type User = z.infer<typeof UserSchema>;
6+
7+
export const NewUserSchema = z.object({
8+
name: z.string(),
9+
email: z.email(),
10+
password: z.string(),
11+
});
12+
13+
export const UserSchema = z.object({
14+
id: z.string(),
15+
name: z.string(),
16+
email: z.email(),
17+
});
18+
19+
export type NewPlanet = z.infer<typeof NewPlanetSchema>;
20+
export type UpdatePlanet = z.infer<typeof UpdatePlanetSchema>;
21+
export type Planet = z.infer<typeof PlanetSchema>;
22+
23+
export const NewPlanetSchema = z.object({
24+
name: z.string(),
25+
description: z.string().optional(),
26+
});
27+
28+
export const UpdatePlanetSchema = z.object({
29+
id: z.number().int().min(1),
30+
name: z.string(),
31+
description: z.string().optional(),
32+
});
33+
34+
export const PlanetSchema = z.object({
35+
id: z.number().int().min(1),
36+
name: z.string(),
37+
description: z.string().optional(),
38+
imageUrl: z.url().optional(),
39+
creator: UserSchema,
40+
});
41+
42+
/////////
43+
///////// EXAMPLE PROCEDURES (contract-based)
44+
/////////
45+
46+
export const listPlanets = zagora()
47+
.input(
48+
z.object({
49+
limit: z.number().int().min(1).max(100).default(10),
50+
cursor: z.number().int().min(0).default(0),
51+
}),
52+
)
53+
.output(z.array(PlanetSchema));
54+
// .handler(async (input) => {
55+
// return `context.db.planets.list(${input.limit}, ${input.cursor});`;
56+
// });
57+
58+
export const createPlanet = zagora()
59+
.input(NewPlanetSchema)
60+
.output(PlanetSchema);
61+
// .handler(async (input) => {
62+
// return `${input.name}-desc>>${input.description ?? "nope"}<<`;
63+
// });
64+
65+
const procedures = {
66+
listPlanets,
67+
createPlanet,
68+
};
69+
70+
const middlewares = [fn1, fn2];
71+
72+
const planetsGroup = group(middlewares, procedures);
73+
74+
// router.listPlanets({});
75+
76+
function group<TContext, TProcedures>(
77+
middlewares: TMiddleware<TContext>[],
78+
handlers: TProcedures,
79+
) {}

src/routing/router.ts

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { z } from "zod";
2+
import { zagora } from "./src/index";
3+
4+
export type NewUser = z.infer<typeof NewUserSchema>;
5+
export type User = z.infer<typeof UserSchema>;
6+
7+
export const NewUserSchema = z.object({
8+
name: z.string(),
9+
email: z.email(),
10+
password: z.string(),
11+
});
12+
13+
export const UserSchema = z.object({
14+
id: z.string(),
15+
name: z.string(),
16+
email: z.email(),
17+
});
18+
19+
export type NewPlanet = z.infer<typeof NewPlanetSchema>;
20+
export type UpdatePlanet = z.infer<typeof UpdatePlanetSchema>;
21+
export type Planet = z.infer<typeof PlanetSchema>;
22+
23+
export const NewPlanetSchema = z.object({
24+
name: z.string(),
25+
description: z.string().optional(),
26+
});
27+
28+
export const UpdatePlanetSchema = z.object({
29+
id: z.number().int().min(1),
30+
name: z.string(),
31+
description: z.string().optional(),
32+
});
33+
34+
export const PlanetSchema = z.object({
35+
id: z.number().int().min(1),
36+
name: z.string(),
37+
description: z.string().optional(),
38+
imageUrl: z.url().optional(),
39+
creator: UserSchema,
40+
});
41+
42+
export const listPlanets = zagora()
43+
.input(
44+
z.object({
45+
limit: z.number().int().min(1).max(100).default(10),
46+
cursor: z.number().int().min(0).default(0),
47+
}),
48+
)
49+
.output(z.array(PlanetSchema));
50+
51+
export const createPlanet = zagora()
52+
.input(NewPlanetSchema)
53+
.output(PlanetSchema);
54+
55+
// ============================================
56+
// MIDDLEWARE & CONTEXT ROUTING
57+
// ============================================
58+
59+
type Middleware<TContext = {}> = (args: {
60+
context: TContext;
61+
next: <TNewContext extends Record<string, any>>(payload: {
62+
context: TNewContext;
63+
}) => Promise<any>;
64+
}) => Promise<any>;
65+
66+
class RouterBuilder<TContext = {}> {
67+
private middlewares: Middleware<any>[] = [];
68+
69+
use<TNewContext extends Record<string, any>>(
70+
middleware: Middleware<TContext> &
71+
((args: {
72+
context: TContext;
73+
next: (payload: { context: TNewContext }) => Promise<any>;
74+
}) => Promise<any>),
75+
): RouterBuilder<TContext & TNewContext> {
76+
(this.middlewares as any[]).push(middleware);
77+
return this as any;
78+
}
79+
80+
group<TProcedures extends Record<string, any>>(
81+
procedures: TProcedures,
82+
): TProcedures {
83+
return Object.fromEntries(
84+
Object.entries(procedures).map(([key, proc]) => [
85+
key,
86+
this.redefineWithContext(proc),
87+
]),
88+
) as TProcedures;
89+
}
90+
91+
private redefineWithContext = (procedure: any): any => {
92+
const def = procedure["~zagora"];
93+
94+
const inputSchema = def.inputSchema;
95+
const outputSchema = def.outputSchema;
96+
const errorsSchema = def.errorsSchema;
97+
98+
// Create wrapper input: { input: TInput, context: TContext }
99+
const wrappedInputSchema = z.object({
100+
input: inputSchema || z.any(),
101+
context: z.record(z.any(), z.any()).default({}),
102+
});
103+
104+
let builder = zagora() as any;
105+
if (wrappedInputSchema) builder = builder.input(wrappedInputSchema);
106+
if (outputSchema) builder = builder.output(outputSchema);
107+
if (errorsSchema) builder = builder.errors(errorsSchema);
108+
109+
return builder.handler(async ({ input, context }: any, errors: any) => {
110+
try {
111+
const finalContext = await this.executeMiddlewareChain(context);
112+
113+
return def.handler({ input, context: finalContext, errors });
114+
} catch (error) {
115+
// If middleware throws, it will be caught by Zagora's error handling
116+
throw error;
117+
}
118+
});
119+
};
120+
121+
private executeMiddlewareChain = async (
122+
initialContext: TContext,
123+
): Promise<TContext> => {
124+
const executeChain = async (
125+
middlewareIndex: number,
126+
currentContext: any,
127+
): Promise<any> => {
128+
if (middlewareIndex >= this.middlewares.length) {
129+
return currentContext;
130+
}
131+
132+
const middleware = this.middlewares[middlewareIndex];
133+
134+
try {
135+
return await middleware({
136+
context: currentContext,
137+
next: async (payload: { context: any }) => {
138+
return executeChain(middlewareIndex + 1, {
139+
...currentContext,
140+
...payload.context,
141+
});
142+
},
143+
});
144+
} catch (error) {
145+
// Middleware errors are thrown and will be caught by Zagora's error handling
146+
throw error;
147+
}
148+
};
149+
150+
return executeChain(0, initialContext);
151+
};
152+
}
153+
154+
function createRouter<TInitialContext = {}>(): RouterBuilder<TInitialContext> {
155+
return new RouterBuilder<TInitialContext>();
156+
}
157+
158+
// ============================================
159+
// EXAMPLE USAGE
160+
// ============================================
161+
162+
const authed = createRouter()
163+
.use(async ({ context, next }) => {
164+
return next({
165+
context: {
166+
userId: "user-123",
167+
permissions: ["read", "write"],
168+
},
169+
});
170+
})
171+
.use(async ({ context, next }) => {
172+
if (!context.userId) {
173+
throw new Error("UNAUTHORIZED");
174+
}
175+
176+
return next({
177+
context: {
178+
...context,
179+
requestId: crypto.randomUUID(),
180+
timestamp: Date.now(),
181+
},
182+
});
183+
})
184+
.group({
185+
listPlanets: listPlanets.handler(async ({ input, context, errors }) => {
186+
return `context.db.planets.list(${input.limit}, ${input.cursor});`;
187+
}),
188+
createPlanet: createPlanet.handler(async ({ input, context, errors }) => {
189+
return `${input.name}-desc>>${input.description ?? "nope"}<<`;
190+
}),
191+
});
192+
193+
// Usage
194+
// const result = await authed.listPlanets({
195+
// input: { limit: 10, cursor: 0 },
196+
// context: {},
197+
// });

0 commit comments

Comments
 (0)