@fragno-dev/core 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +49 -45
- package/CHANGELOG.md +53 -0
- package/dist/api/api.d.ts +2 -2
- package/dist/api/fragment-builder.d.ts +3 -2
- package/dist/api/fragment-instantiation.d.ts +4 -3
- package/dist/api/fragment-instantiation.js +3 -3
- package/dist/api/route.d.ts +3 -0
- package/dist/api/route.js +3 -0
- package/dist/{api-BX90b4-D.d.ts → api-CoCkNi6h.d.ts} +20 -7
- package/dist/api-CoCkNi6h.d.ts.map +1 -0
- package/dist/api-DngJDcmO.js.map +1 -1
- package/dist/client/client.d.ts +4 -3
- package/dist/client/client.js +3 -3
- package/dist/client/client.svelte.d.ts +3 -3
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +3 -3
- package/dist/client/react.d.ts +3 -3
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +3 -3
- package/dist/client/solid.d.ts +3 -3
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +3 -3
- package/dist/client/vanilla.d.ts +3 -3
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +3 -3
- package/dist/client/vue.d.ts +3 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +3 -3
- package/dist/{client-C6LChM0Y.js → client-DJfCJiHK.js} +81 -7
- package/dist/client-DJfCJiHK.js.map +1 -0
- package/dist/{fragment-builder-BZr2JkuW.d.ts → fragment-builder-8-tiECi5.d.ts} +75 -38
- package/dist/fragment-builder-8-tiECi5.d.ts.map +1 -0
- package/dist/{fragment-instantiation-D74OQjbn.js → fragment-instantiation-C4wvwl6V.js} +129 -6
- package/dist/fragment-instantiation-C4wvwl6V.js.map +1 -0
- package/dist/mod.d.ts +3 -2
- package/dist/mod.js +3 -3
- package/dist/{route-D1MZR6JL.js → request-output-context-CdIjwmEN.js} +22 -33
- package/dist/request-output-context-CdIjwmEN.js.map +1 -0
- package/dist/route-C5Uryylh.js +21 -0
- package/dist/route-C5Uryylh.js.map +1 -0
- package/dist/route-mGLYSUvD.d.ts +26 -0
- package/dist/route-mGLYSUvD.d.ts.map +1 -0
- package/dist/test/test.d.ts +24 -70
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js +27 -115
- package/dist/test/test.js.map +1 -1
- package/package.json +6 -1
- package/src/api/api.ts +1 -0
- package/src/api/fragment-instantiation.test.ts +460 -0
- package/src/api/fragment-instantiation.ts +157 -5
- package/src/api/fragno-response.ts +132 -0
- package/src/api/request-input-context.test.ts +37 -29
- package/src/api/request-input-context.ts +16 -14
- package/src/api/request-output-context.test.ts +10 -10
- package/src/api/request-output-context.ts +3 -3
- package/src/api/route-handler-input-options.ts +15 -0
- package/src/client/client.test.ts +264 -0
- package/src/client/client.ts +65 -3
- package/src/client/internal/fetcher-merge.ts +59 -0
- package/src/test/test.test.ts +110 -165
- package/src/test/test.ts +56 -266
- package/tsdown.config.ts +1 -0
- package/dist/api-BX90b4-D.d.ts.map +0 -1
- package/dist/client-C6LChM0Y.js.map +0 -1
- package/dist/fragment-builder-BZr2JkuW.d.ts.map +0 -1
- package/dist/fragment-instantiation-D74OQjbn.js.map +0 -1
- package/dist/route-CTxjMtGZ.js +0 -10
- package/dist/route-CTxjMtGZ.js.map +0 -1
- package/dist/route-D1MZR6JL.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"development": "./src/mod.ts",
|
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
"types": "./dist/api/fragment-instantiation.d.ts",
|
|
28
28
|
"default": "./dist/api/fragment-instantiation.js"
|
|
29
29
|
},
|
|
30
|
+
"./api/route": {
|
|
31
|
+
"development": "./src/api/route.ts",
|
|
32
|
+
"types": "./dist/api/route.d.ts",
|
|
33
|
+
"default": "./dist/api/route.js"
|
|
34
|
+
},
|
|
30
35
|
"./client": {
|
|
31
36
|
"development": "./src/client/client.ts",
|
|
32
37
|
"types": "./dist/client/client.d.ts",
|
package/src/api/api.ts
CHANGED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { test, expect, describe } from "vitest";
|
|
2
|
+
import { defineFragment } from "./fragment-builder";
|
|
3
|
+
import { createFragment } from "./fragment-instantiation";
|
|
4
|
+
import { defineRoute, defineRoutes } from "./route";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
describe("callRoute", () => {
|
|
8
|
+
test("calls route handler with body", async () => {
|
|
9
|
+
const config = { greeting: "Hello" };
|
|
10
|
+
|
|
11
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
12
|
+
|
|
13
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
14
|
+
return [
|
|
15
|
+
defineRoute({
|
|
16
|
+
method: "POST",
|
|
17
|
+
path: "/greet",
|
|
18
|
+
inputSchema: z.object({ name: z.string() }),
|
|
19
|
+
outputSchema: z.object({ message: z.string() }),
|
|
20
|
+
handler: async ({ input }, { json }) => {
|
|
21
|
+
const { name } = await input.valid();
|
|
22
|
+
return json({ message: `Hello, ${name}!` });
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
];
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
29
|
+
|
|
30
|
+
const response = await instance.callRoute("POST", "/greet", {
|
|
31
|
+
body: { name: "World" },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(response.type).toBe("json");
|
|
35
|
+
if (response.type === "json") {
|
|
36
|
+
expect(response.status).toBe(200);
|
|
37
|
+
expect(response.data).toEqual({ message: "Hello, World!" });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("calls route handler with path params", async () => {
|
|
42
|
+
const config = {};
|
|
43
|
+
|
|
44
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
45
|
+
|
|
46
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
47
|
+
return [
|
|
48
|
+
defineRoute({
|
|
49
|
+
method: "GET",
|
|
50
|
+
path: "/users/:id",
|
|
51
|
+
outputSchema: z.object({ userId: z.string() }),
|
|
52
|
+
handler: async ({ pathParams }, { json }) => {
|
|
53
|
+
return json({ userId: pathParams.id });
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
];
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
60
|
+
|
|
61
|
+
const response = await instance.callRoute("GET", "/users/:id", {
|
|
62
|
+
pathParams: { id: "123" },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(response.type).toBe("json");
|
|
66
|
+
if (response.type === "json") {
|
|
67
|
+
expect(response.status).toBe(200);
|
|
68
|
+
expect(response.data).toEqual({ userId: "123" });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("calls route handler with query parameters", async () => {
|
|
73
|
+
const config = {};
|
|
74
|
+
|
|
75
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
76
|
+
|
|
77
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
78
|
+
return [
|
|
79
|
+
defineRoute({
|
|
80
|
+
method: "GET",
|
|
81
|
+
path: "/search",
|
|
82
|
+
queryParameters: ["q", "limit"],
|
|
83
|
+
outputSchema: z.object({ query: z.string(), limit: z.string().nullable() }),
|
|
84
|
+
handler: async ({ query }, { json }) => {
|
|
85
|
+
return json({
|
|
86
|
+
query: query.get("q") || "",
|
|
87
|
+
limit: query.get("limit"),
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
];
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
95
|
+
|
|
96
|
+
const response = await instance.callRoute("GET", "/search", {
|
|
97
|
+
query: { q: "test", limit: "10" },
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(response.type).toBe("json");
|
|
101
|
+
if (response.type === "json") {
|
|
102
|
+
expect(response.status).toBe(200);
|
|
103
|
+
expect(response.data).toEqual({ query: "test", limit: "10" });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("calls route handler with URLSearchParams query", async () => {
|
|
108
|
+
const config = {};
|
|
109
|
+
|
|
110
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
111
|
+
|
|
112
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
113
|
+
return [
|
|
114
|
+
defineRoute({
|
|
115
|
+
method: "GET",
|
|
116
|
+
path: "/search",
|
|
117
|
+
queryParameters: ["q"],
|
|
118
|
+
outputSchema: z.object({ query: z.string() }),
|
|
119
|
+
handler: async ({ query }, { json }) => {
|
|
120
|
+
return json({ query: query.get("q") || "" });
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
];
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
127
|
+
|
|
128
|
+
const searchParams = new URLSearchParams({ q: "test-query" });
|
|
129
|
+
const response = await instance.callRoute("GET", "/search", {
|
|
130
|
+
query: searchParams,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(response.type).toBe("json");
|
|
134
|
+
if (response.type === "json") {
|
|
135
|
+
expect(response.status).toBe(200);
|
|
136
|
+
expect(response.data).toEqual({ query: "test-query" });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("calls route handler with headers", async () => {
|
|
141
|
+
const config = {};
|
|
142
|
+
|
|
143
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
144
|
+
|
|
145
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
146
|
+
return [
|
|
147
|
+
defineRoute({
|
|
148
|
+
method: "GET",
|
|
149
|
+
path: "/headers",
|
|
150
|
+
outputSchema: z.object({ auth: z.string().nullable() }),
|
|
151
|
+
handler: async ({ headers }, { json }) => {
|
|
152
|
+
return json({ auth: headers.get("authorization") });
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
];
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
159
|
+
|
|
160
|
+
const response = await instance.callRoute("GET", "/headers", {
|
|
161
|
+
headers: { authorization: "Bearer token123" },
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(response.type).toBe("json");
|
|
165
|
+
if (response.type === "json") {
|
|
166
|
+
expect(response.status).toBe(200);
|
|
167
|
+
expect(response.data).toEqual({ auth: "Bearer token123" });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("calls route handler with Headers object", async () => {
|
|
172
|
+
const config = {};
|
|
173
|
+
|
|
174
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
175
|
+
|
|
176
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
177
|
+
return [
|
|
178
|
+
defineRoute({
|
|
179
|
+
method: "GET",
|
|
180
|
+
path: "/headers",
|
|
181
|
+
outputSchema: z.object({ auth: z.string().nullable() }),
|
|
182
|
+
handler: async ({ headers }, { json }) => {
|
|
183
|
+
return json({ auth: headers.get("authorization") });
|
|
184
|
+
},
|
|
185
|
+
}),
|
|
186
|
+
];
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
190
|
+
|
|
191
|
+
const requestHeaders = new Headers({ authorization: "Bearer token456" });
|
|
192
|
+
const response = await instance.callRoute("GET", "/headers", {
|
|
193
|
+
headers: requestHeaders,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(response.type).toBe("json");
|
|
197
|
+
if (response.type === "json") {
|
|
198
|
+
expect(response.status).toBe(200);
|
|
199
|
+
expect(response.data).toEqual({ auth: "Bearer token456" });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("preserves response headers including Set-Cookie", async () => {
|
|
204
|
+
const config = {};
|
|
205
|
+
|
|
206
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
207
|
+
|
|
208
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
209
|
+
return [
|
|
210
|
+
defineRoute({
|
|
211
|
+
method: "POST",
|
|
212
|
+
path: "/login",
|
|
213
|
+
inputSchema: z.object({ username: z.string() }),
|
|
214
|
+
outputSchema: z.object({ success: z.boolean() }),
|
|
215
|
+
handler: async ({ input }, { json }) => {
|
|
216
|
+
const { username } = await input.valid();
|
|
217
|
+
const response = json({ success: true });
|
|
218
|
+
response.headers.set("Set-Cookie", `session=${username}; HttpOnly; Path=/`);
|
|
219
|
+
response.headers.set("X-Custom-Header", "custom-value");
|
|
220
|
+
return response;
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
223
|
+
];
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
227
|
+
|
|
228
|
+
const response = await instance.callRoute("POST", "/login", {
|
|
229
|
+
body: { username: "testuser" },
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(response.type).toBe("json");
|
|
233
|
+
if (response.type === "json") {
|
|
234
|
+
expect(response.status).toBe(200);
|
|
235
|
+
expect(response.headers.get("Set-Cookie")).toBe("session=testuser; HttpOnly; Path=/");
|
|
236
|
+
expect(response.headers.get("X-Custom-Header")).toBe("custom-value");
|
|
237
|
+
expect(response.data).toEqual({ success: true });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("validates input and returns error for invalid data", async () => {
|
|
242
|
+
const config = {};
|
|
243
|
+
|
|
244
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
245
|
+
|
|
246
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
247
|
+
return [
|
|
248
|
+
defineRoute({
|
|
249
|
+
method: "POST",
|
|
250
|
+
path: "/validate",
|
|
251
|
+
inputSchema: z.object({ age: z.number().min(18) }),
|
|
252
|
+
outputSchema: z.object({ valid: z.boolean() }),
|
|
253
|
+
handler: async ({ input }, { json }) => {
|
|
254
|
+
const { age } = await input.valid();
|
|
255
|
+
return json({ valid: age >= 18 });
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
];
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
262
|
+
|
|
263
|
+
const response = await instance.callRoute("POST", "/validate", {
|
|
264
|
+
body: { age: 15 },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(response.type).toBe("error");
|
|
268
|
+
if (response.type === "error") {
|
|
269
|
+
expect(response.status).toBe(400);
|
|
270
|
+
expect(response.error.code).toBe("FRAGNO_VALIDATION_ERROR");
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("handles errors thrown in route handler", async () => {
|
|
275
|
+
const config = {};
|
|
276
|
+
|
|
277
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
278
|
+
|
|
279
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
280
|
+
return [
|
|
281
|
+
defineRoute({
|
|
282
|
+
method: "GET",
|
|
283
|
+
path: "/error",
|
|
284
|
+
outputSchema: z.object({ result: z.string() }),
|
|
285
|
+
handler: async () => {
|
|
286
|
+
throw new Error("Unexpected error");
|
|
287
|
+
},
|
|
288
|
+
}),
|
|
289
|
+
];
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
293
|
+
|
|
294
|
+
const response = await instance.callRoute("GET", "/error");
|
|
295
|
+
|
|
296
|
+
expect(response.type).toBe("error");
|
|
297
|
+
if (response.type === "error") {
|
|
298
|
+
expect(response.status).toBe(500);
|
|
299
|
+
expect(response.error).toEqual({
|
|
300
|
+
message: "Internal server error",
|
|
301
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("calls route handler with all parameters combined", async () => {
|
|
307
|
+
const config = {};
|
|
308
|
+
|
|
309
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
310
|
+
|
|
311
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
312
|
+
return [
|
|
313
|
+
defineRoute({
|
|
314
|
+
method: "POST",
|
|
315
|
+
path: "/users/:id/update",
|
|
316
|
+
inputSchema: z.object({ name: z.string() }),
|
|
317
|
+
queryParameters: ["reason"],
|
|
318
|
+
outputSchema: z.object({
|
|
319
|
+
id: z.string(),
|
|
320
|
+
name: z.string(),
|
|
321
|
+
reason: z.string().nullable(),
|
|
322
|
+
auth: z.string().nullable(),
|
|
323
|
+
}),
|
|
324
|
+
handler: async ({ pathParams, input, query, headers }, { json }) => {
|
|
325
|
+
const { name } = await input.valid();
|
|
326
|
+
return json({
|
|
327
|
+
id: pathParams.id,
|
|
328
|
+
name,
|
|
329
|
+
reason: query.get("reason"),
|
|
330
|
+
auth: headers.get("authorization"),
|
|
331
|
+
});
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
];
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
338
|
+
|
|
339
|
+
const response = await instance.callRoute("POST", "/users/:id/update", {
|
|
340
|
+
pathParams: { id: "user123" },
|
|
341
|
+
body: { name: "John Doe" },
|
|
342
|
+
query: { reason: "profile-update" },
|
|
343
|
+
headers: { authorization: "Bearer xyz" },
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
expect(response.type).toBe("json");
|
|
347
|
+
if (response.type === "json") {
|
|
348
|
+
expect(response.status).toBe(200);
|
|
349
|
+
expect(response.data).toEqual({
|
|
350
|
+
id: "user123",
|
|
351
|
+
name: "John Doe",
|
|
352
|
+
reason: "profile-update",
|
|
353
|
+
auth: "Bearer xyz",
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("calls route handler with no input options", async () => {
|
|
359
|
+
const config = {};
|
|
360
|
+
|
|
361
|
+
const fragment = defineFragment<typeof config>("test-fragment");
|
|
362
|
+
|
|
363
|
+
const routesFactory = defineRoutes<typeof config>().create(() => {
|
|
364
|
+
return [
|
|
365
|
+
defineRoute({
|
|
366
|
+
method: "GET",
|
|
367
|
+
path: "/ping",
|
|
368
|
+
outputSchema: z.object({ status: z.string() }),
|
|
369
|
+
handler: async (_, { json }) => {
|
|
370
|
+
return json({ status: "ok" });
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
];
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
377
|
+
|
|
378
|
+
const response = await instance.callRoute("GET", "/ping");
|
|
379
|
+
|
|
380
|
+
expect(response.type).toBe("json");
|
|
381
|
+
if (response.type === "json") {
|
|
382
|
+
expect(response.status).toBe(200);
|
|
383
|
+
expect(response.data).toEqual({ status: "ok" });
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("uses services in route handler called via callRoute", async () => {
|
|
388
|
+
const config = {};
|
|
389
|
+
|
|
390
|
+
type Services = {
|
|
391
|
+
getUserName: () => string;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const fragment = defineFragment<typeof config>("test-fragment").withServices(() => {
|
|
395
|
+
return {
|
|
396
|
+
getUserName: () => "Test User",
|
|
397
|
+
};
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const routesFactory = defineRoutes<typeof config, {}, Services>().create(({ services }) => {
|
|
401
|
+
return [
|
|
402
|
+
defineRoute({
|
|
403
|
+
method: "GET",
|
|
404
|
+
path: "/me",
|
|
405
|
+
outputSchema: z.object({ name: z.string() }),
|
|
406
|
+
handler: async (_, { json }) => {
|
|
407
|
+
return json({ name: services.getUserName() });
|
|
408
|
+
},
|
|
409
|
+
}),
|
|
410
|
+
];
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
414
|
+
|
|
415
|
+
const response = await instance.callRoute("GET", "/me");
|
|
416
|
+
|
|
417
|
+
expect(response.type).toBe("json");
|
|
418
|
+
if (response.type === "json") {
|
|
419
|
+
expect(response.status).toBe(200);
|
|
420
|
+
expect(response.data).toEqual({ name: "Test User" });
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("uses deps in route handler called via callRoute", async () => {
|
|
425
|
+
const config = {};
|
|
426
|
+
|
|
427
|
+
type Deps = {
|
|
428
|
+
database: { query: () => string };
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const fragment = defineFragment<typeof config>("test-fragment").withDependencies(() => {
|
|
432
|
+
return {
|
|
433
|
+
database: { query: () => "database-result" },
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const routesFactory = defineRoutes<typeof config, Deps>().create(({ deps }) => {
|
|
438
|
+
return [
|
|
439
|
+
defineRoute({
|
|
440
|
+
method: "GET",
|
|
441
|
+
path: "/data",
|
|
442
|
+
outputSchema: z.object({ result: z.string() }),
|
|
443
|
+
handler: async (_, { json }) => {
|
|
444
|
+
return json({ result: deps.database.query() });
|
|
445
|
+
},
|
|
446
|
+
}),
|
|
447
|
+
];
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const instance = createFragment(fragment, config, [routesFactory], {});
|
|
451
|
+
|
|
452
|
+
const response = await instance.callRoute("GET", "/data");
|
|
453
|
+
|
|
454
|
+
expect(response.type).toBe("json");
|
|
455
|
+
if (response.type === "json") {
|
|
456
|
+
expect(response.status).toBe(200);
|
|
457
|
+
expect(response.data).toEqual({ result: "database-result" });
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
});
|