@fragno-dev/core 0.1.7 → 0.1.8
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 +45 -53
- package/CHANGELOG.md +6 -0
- package/dist/api/api.d.ts +2 -2
- package/dist/api/api.js +3 -2
- package/dist/api/fragment-builder.d.ts +2 -4
- package/dist/api/fragment-builder.js +1 -1
- package/dist/api/fragment-instantiation.d.ts +2 -4
- package/dist/api/fragment-instantiation.js +3 -5
- package/dist/api/route.d.ts +2 -3
- package/dist/api/route.js +1 -1
- package/dist/api-BFrUCIsF.d.ts +963 -0
- package/dist/api-BFrUCIsF.d.ts.map +1 -0
- package/dist/client/client.d.ts +1 -3
- package/dist/client/client.js +4 -5
- package/dist/client/client.svelte.d.ts +2 -3
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +4 -5
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/react.d.ts +2 -3
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +4 -5
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +2 -3
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +4 -5
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +2 -3
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +4 -5
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +2 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +4 -5
- package/dist/client/vue.js.map +1 -1
- package/dist/{client-C5LsYHEI.js → client-DAFHcKqA.js} +4 -4
- package/dist/{client-C5LsYHEI.js.map → client-DAFHcKqA.js.map} +1 -1
- package/dist/fragment-builder-Boh2vNHq.js +108 -0
- package/dist/fragment-builder-Boh2vNHq.js.map +1 -0
- package/dist/fragment-instantiation-DUT-HLl1.js +898 -0
- package/dist/fragment-instantiation-DUT-HLl1.js.map +1 -0
- package/dist/integrations/react-ssr.js +1 -1
- package/dist/mod.d.ts +2 -4
- package/dist/mod.js +4 -6
- package/dist/{route-C5Uryylh.js → route-C4CyNHkC.js} +8 -3
- package/dist/route-C4CyNHkC.js.map +1 -0
- package/dist/{ssr-BByDVfFD.js → ssr-kyKI7pqH.js} +1 -1
- package/dist/{ssr-BByDVfFD.js.map → ssr-kyKI7pqH.js.map} +1 -1
- package/dist/test/test.d.ts +6 -7
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js +9 -7
- package/dist/test/test.js.map +1 -1
- package/package.json +1 -1
- package/src/api/api.ts +45 -6
- package/src/api/fragment-builder.ts +463 -25
- package/src/api/fragment-instantiation.test.ts +249 -7
- package/src/api/fragment-instantiation.ts +283 -16
- package/src/api/fragment-services.test.ts +462 -0
- package/src/api/fragment.test.ts +65 -17
- package/src/api/request-middleware.test.ts +6 -3
- package/src/api/route.test.ts +111 -1
- package/src/api/route.ts +323 -14
- package/src/mod.ts +11 -1
- package/src/test/test.test.ts +20 -15
- package/src/test/test.ts +48 -9
- package/dist/api-BWN97TOr.d.ts +0 -377
- package/dist/api-BWN97TOr.d.ts.map +0 -1
- package/dist/api-DngJDcmO.js +0 -54
- package/dist/api-DngJDcmO.js.map +0 -1
- package/dist/fragment-builder-DOnCVBqc.js +0 -47
- package/dist/fragment-builder-DOnCVBqc.js.map +0 -1
- package/dist/fragment-builder-MGr68GNb.d.ts +0 -409
- package/dist/fragment-builder-MGr68GNb.d.ts.map +0 -1
- package/dist/fragment-instantiation-C4wvwl6V.js +0 -446
- package/dist/fragment-instantiation-C4wvwl6V.js.map +0 -1
- package/dist/request-output-context-CdIjwmEN.js +0 -320
- package/dist/request-output-context-CdIjwmEN.js.map +0 -1
- package/dist/route-Bl9Zr1Yv.d.ts +0 -26
- package/dist/route-Bl9Zr1Yv.d.ts.map +0 -1
- package/dist/route-C5Uryylh.js.map +0 -1
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import { r as resolveRouteFactories } from "./route-C4CyNHkC.js";
|
|
2
|
+
import { addRoute, createRouter, findRoute } from "rou3";
|
|
3
|
+
|
|
4
|
+
//#region src/api/error.ts
|
|
5
|
+
var FragnoApiError = class extends Error {
|
|
6
|
+
#status;
|
|
7
|
+
#code;
|
|
8
|
+
constructor({ message, code }, status) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "FragnoApiError";
|
|
11
|
+
this.#status = status;
|
|
12
|
+
this.#code = code;
|
|
13
|
+
}
|
|
14
|
+
get status() {
|
|
15
|
+
return this.#status;
|
|
16
|
+
}
|
|
17
|
+
get code() {
|
|
18
|
+
return this.#code;
|
|
19
|
+
}
|
|
20
|
+
toResponse() {
|
|
21
|
+
return Response.json({
|
|
22
|
+
message: this.message,
|
|
23
|
+
code: this.code
|
|
24
|
+
}, { status: this.status });
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var FragnoApiValidationError = class extends FragnoApiError {
|
|
28
|
+
#issues;
|
|
29
|
+
constructor(message, issues) {
|
|
30
|
+
super({
|
|
31
|
+
message,
|
|
32
|
+
code: "FRAGNO_VALIDATION_ERROR"
|
|
33
|
+
}, 400);
|
|
34
|
+
this.name = "FragnoApiValidationError";
|
|
35
|
+
this.#issues = issues;
|
|
36
|
+
}
|
|
37
|
+
get issues() {
|
|
38
|
+
return this.#issues;
|
|
39
|
+
}
|
|
40
|
+
toResponse() {
|
|
41
|
+
return Response.json({
|
|
42
|
+
message: this.message,
|
|
43
|
+
issues: this.#issues,
|
|
44
|
+
code: this.code
|
|
45
|
+
}, { status: this.status });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/api/api.ts
|
|
51
|
+
function addRoute$1(route) {
|
|
52
|
+
return route;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/api/internal/route.ts
|
|
57
|
+
function getMountRoute(opts) {
|
|
58
|
+
const mountRoute = opts.mountRoute ?? `/api/${opts.name}`;
|
|
59
|
+
if (mountRoute.endsWith("/")) return mountRoute.slice(0, -1);
|
|
60
|
+
return mountRoute;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/api/request-input-context.ts
|
|
65
|
+
var RequestInputContext = class RequestInputContext {
|
|
66
|
+
#path;
|
|
67
|
+
#method;
|
|
68
|
+
#pathParams;
|
|
69
|
+
#searchParams;
|
|
70
|
+
#headers;
|
|
71
|
+
#body;
|
|
72
|
+
#parsedBody;
|
|
73
|
+
#inputSchema;
|
|
74
|
+
#shouldValidateInput;
|
|
75
|
+
constructor(config) {
|
|
76
|
+
this.#path = config.path;
|
|
77
|
+
this.#method = config.method;
|
|
78
|
+
this.#pathParams = config.pathParams;
|
|
79
|
+
this.#searchParams = config.searchParams;
|
|
80
|
+
this.#headers = config.headers;
|
|
81
|
+
this.#body = config.rawBody;
|
|
82
|
+
this.#parsedBody = config.parsedBody;
|
|
83
|
+
this.#inputSchema = config.inputSchema;
|
|
84
|
+
this.#shouldValidateInput = config.shouldValidateInput ?? true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Create a RequestContext from a Request object for server-side handling
|
|
88
|
+
*/
|
|
89
|
+
static async fromRequest(config) {
|
|
90
|
+
return new RequestInputContext({
|
|
91
|
+
method: config.method,
|
|
92
|
+
path: config.path,
|
|
93
|
+
pathParams: config.state.pathParams,
|
|
94
|
+
searchParams: config.state.searchParams,
|
|
95
|
+
headers: config.state.headers,
|
|
96
|
+
parsedBody: config.state.body,
|
|
97
|
+
rawBody: config.rawBody,
|
|
98
|
+
inputSchema: config.inputSchema,
|
|
99
|
+
shouldValidateInput: config.shouldValidateInput
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a RequestContext for server-side rendering contexts (no Request object)
|
|
104
|
+
*/
|
|
105
|
+
static fromSSRContext(config) {
|
|
106
|
+
return new RequestInputContext({
|
|
107
|
+
method: config.method,
|
|
108
|
+
path: config.path,
|
|
109
|
+
pathParams: config.pathParams,
|
|
110
|
+
searchParams: config.searchParams ?? new URLSearchParams(),
|
|
111
|
+
headers: config.headers ?? new Headers(),
|
|
112
|
+
parsedBody: "body" in config ? config.body : void 0,
|
|
113
|
+
inputSchema: "inputSchema" in config ? config.inputSchema : void 0,
|
|
114
|
+
shouldValidateInput: false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The HTTP method as string (e.g., `GET`, `POST`)
|
|
119
|
+
*/
|
|
120
|
+
get method() {
|
|
121
|
+
return this.#method;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The matched route path (e.g., `/users/:id`)
|
|
125
|
+
* @remarks `string`
|
|
126
|
+
*/
|
|
127
|
+
get path() {
|
|
128
|
+
return this.#path;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extracted path parameters as object (e.g., `{ id: '123' }`)
|
|
132
|
+
* @remarks `Record<string, string>`
|
|
133
|
+
*/
|
|
134
|
+
get pathParams() {
|
|
135
|
+
return this.#pathParams;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object for query parameters
|
|
139
|
+
* @remarks `URLSearchParams`
|
|
140
|
+
*/
|
|
141
|
+
get query() {
|
|
142
|
+
return this.#searchParams;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object for request headers
|
|
146
|
+
* @remarks `Headers`
|
|
147
|
+
*/
|
|
148
|
+
get headers() {
|
|
149
|
+
return this.#headers;
|
|
150
|
+
}
|
|
151
|
+
get rawBody() {
|
|
152
|
+
return this.#body;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Input validation context (only if inputSchema is defined)
|
|
156
|
+
* @remarks `InputContext`
|
|
157
|
+
*/
|
|
158
|
+
get input() {
|
|
159
|
+
if (!this.#inputSchema) return;
|
|
160
|
+
return {
|
|
161
|
+
schema: this.#inputSchema,
|
|
162
|
+
valid: async () => {
|
|
163
|
+
if (!this.#shouldValidateInput) return this.#parsedBody;
|
|
164
|
+
return this.#validateInput();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async #validateInput() {
|
|
169
|
+
if (!this.#inputSchema) throw new Error("No input schema defined for this route");
|
|
170
|
+
if (this.#parsedBody instanceof FormData || this.#parsedBody instanceof Blob) throw new Error("Schema validation is only supported for JSON data, not FormData or Blob");
|
|
171
|
+
const result = await this.#inputSchema["~standard"].validate(this.#parsedBody);
|
|
172
|
+
if (result.issues) throw new FragnoApiValidationError("Validation failed", result.issues);
|
|
173
|
+
return result.value;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/api/internal/response-stream.ts
|
|
179
|
+
var ResponseStream = class {
|
|
180
|
+
#writer;
|
|
181
|
+
#encoder;
|
|
182
|
+
#abortSubscribers = [];
|
|
183
|
+
#responseReadable;
|
|
184
|
+
#aborted = false;
|
|
185
|
+
#closed = false;
|
|
186
|
+
/**
|
|
187
|
+
* Whether the stream has been aborted.
|
|
188
|
+
*/
|
|
189
|
+
get aborted() {
|
|
190
|
+
return this.#aborted;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether the stream has been closed normally.
|
|
194
|
+
*/
|
|
195
|
+
get closed() {
|
|
196
|
+
return this.#closed;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The readable stream that the response is piped to.
|
|
200
|
+
*/
|
|
201
|
+
get responseReadable() {
|
|
202
|
+
return this.#responseReadable;
|
|
203
|
+
}
|
|
204
|
+
constructor(writable, readable) {
|
|
205
|
+
this.#writer = writable.getWriter();
|
|
206
|
+
this.#encoder = new TextEncoder();
|
|
207
|
+
const reader = readable.getReader();
|
|
208
|
+
this.#abortSubscribers.push(async () => {
|
|
209
|
+
await reader.cancel();
|
|
210
|
+
});
|
|
211
|
+
this.#responseReadable = new ReadableStream({
|
|
212
|
+
async pull(controller) {
|
|
213
|
+
const { done, value } = await reader.read();
|
|
214
|
+
if (done) controller.close();
|
|
215
|
+
else controller.enqueue(value);
|
|
216
|
+
},
|
|
217
|
+
cancel: () => {
|
|
218
|
+
this.abort();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async writeRaw(input) {
|
|
223
|
+
try {
|
|
224
|
+
if (typeof input === "string") input = this.#encoder.encode(input);
|
|
225
|
+
await this.#writer.write(input);
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
write(input) {
|
|
229
|
+
return this.writeRaw(JSON.stringify(input) + "\n");
|
|
230
|
+
}
|
|
231
|
+
sleep(ms) {
|
|
232
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
233
|
+
}
|
|
234
|
+
async close() {
|
|
235
|
+
try {
|
|
236
|
+
await this.#writer.close();
|
|
237
|
+
} catch {} finally {
|
|
238
|
+
this.#closed = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
onAbort(listener) {
|
|
242
|
+
this.#abortSubscribers.push(listener);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Abort the stream.
|
|
246
|
+
* You can call this method when stream is aborted by external event.
|
|
247
|
+
*/
|
|
248
|
+
abort() {
|
|
249
|
+
if (!this.aborted) {
|
|
250
|
+
this.#aborted = true;
|
|
251
|
+
this.#abortSubscribers.forEach((subscriber) => subscriber());
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/api/request-output-context.ts
|
|
258
|
+
/**
|
|
259
|
+
* Utility function to merge headers from multiple sources.
|
|
260
|
+
* Later headers override earlier ones.
|
|
261
|
+
*/
|
|
262
|
+
function mergeHeaders(...headerSources) {
|
|
263
|
+
const mergedHeaders = new Headers();
|
|
264
|
+
for (const headerSource of headerSources) {
|
|
265
|
+
if (!headerSource) continue;
|
|
266
|
+
if (headerSource instanceof Headers) for (const [key, value] of headerSource.entries()) mergedHeaders.set(key, value);
|
|
267
|
+
else if (Array.isArray(headerSource)) for (const [key, value] of headerSource) mergedHeaders.set(key, value);
|
|
268
|
+
else for (const [key, value] of Object.entries(headerSource)) mergedHeaders.set(key, value);
|
|
269
|
+
}
|
|
270
|
+
return mergedHeaders;
|
|
271
|
+
}
|
|
272
|
+
var OutputContext = class {
|
|
273
|
+
/**
|
|
274
|
+
* Creates an error response.
|
|
275
|
+
*
|
|
276
|
+
* Shortcut for `throw new FragnoApiError(...)`
|
|
277
|
+
*/
|
|
278
|
+
error = ({ message, code }, initOrStatus, headers) => {
|
|
279
|
+
if (typeof initOrStatus === "undefined") return Response.json({
|
|
280
|
+
message,
|
|
281
|
+
code
|
|
282
|
+
}, {
|
|
283
|
+
status: 500,
|
|
284
|
+
headers
|
|
285
|
+
});
|
|
286
|
+
if (typeof initOrStatus === "number") return Response.json({
|
|
287
|
+
message,
|
|
288
|
+
code
|
|
289
|
+
}, {
|
|
290
|
+
status: initOrStatus,
|
|
291
|
+
headers
|
|
292
|
+
});
|
|
293
|
+
const mergedHeaders = mergeHeaders(initOrStatus.headers, headers);
|
|
294
|
+
return Response.json({
|
|
295
|
+
message,
|
|
296
|
+
code
|
|
297
|
+
}, {
|
|
298
|
+
status: initOrStatus.status,
|
|
299
|
+
headers: mergedHeaders
|
|
300
|
+
});
|
|
301
|
+
};
|
|
302
|
+
empty = (initOrStatus, headers) => {
|
|
303
|
+
const defaultHeaders = {};
|
|
304
|
+
if (typeof initOrStatus === "undefined") {
|
|
305
|
+
const mergedHeaders$1 = mergeHeaders(defaultHeaders, headers);
|
|
306
|
+
return new Response(null, {
|
|
307
|
+
status: 201,
|
|
308
|
+
headers: mergedHeaders$1
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (typeof initOrStatus === "number") {
|
|
312
|
+
const mergedHeaders$1 = mergeHeaders(defaultHeaders, headers);
|
|
313
|
+
return new Response(null, {
|
|
314
|
+
status: initOrStatus,
|
|
315
|
+
headers: mergedHeaders$1
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const mergedHeaders = mergeHeaders(defaultHeaders, initOrStatus.headers, headers);
|
|
319
|
+
return new Response(null, {
|
|
320
|
+
status: initOrStatus.status,
|
|
321
|
+
headers: mergedHeaders
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
json = (object, initOrStatus, headers) => {
|
|
325
|
+
if (typeof initOrStatus === "undefined") return Response.json(object, {
|
|
326
|
+
status: 200,
|
|
327
|
+
headers
|
|
328
|
+
});
|
|
329
|
+
if (typeof initOrStatus === "number") return Response.json(object, {
|
|
330
|
+
status: initOrStatus,
|
|
331
|
+
headers
|
|
332
|
+
});
|
|
333
|
+
const mergedHeaders = mergeHeaders(initOrStatus.headers, headers);
|
|
334
|
+
return Response.json(object, {
|
|
335
|
+
status: initOrStatus.status,
|
|
336
|
+
headers: mergedHeaders
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
jsonStream = (cb, { onError, headers } = {}) => {
|
|
340
|
+
const defaultHeaders = {
|
|
341
|
+
"content-type": "application/x-ndjson; charset=utf-8",
|
|
342
|
+
"transfer-encoding": "chunked",
|
|
343
|
+
"cache-control": "no-cache"
|
|
344
|
+
};
|
|
345
|
+
const { readable, writable } = new TransformStream();
|
|
346
|
+
const stream = new ResponseStream(writable, readable);
|
|
347
|
+
(async () => {
|
|
348
|
+
try {
|
|
349
|
+
await cb(stream);
|
|
350
|
+
} catch (e) {
|
|
351
|
+
if (e === void 0) {} else if (e instanceof Error && onError) await onError(e, stream);
|
|
352
|
+
else console.error(e);
|
|
353
|
+
} finally {
|
|
354
|
+
stream.close();
|
|
355
|
+
}
|
|
356
|
+
})();
|
|
357
|
+
return new Response(stream.responseReadable, {
|
|
358
|
+
status: 200,
|
|
359
|
+
headers: mergeHeaders(defaultHeaders, headers)
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
var RequestOutputContext = class extends OutputContext {
|
|
364
|
+
#outputSchema;
|
|
365
|
+
constructor(outputSchema) {
|
|
366
|
+
super();
|
|
367
|
+
this.#outputSchema = outputSchema;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/api/mutable-request-state.ts
|
|
373
|
+
/**
|
|
374
|
+
* Holds mutable request state that can be modified by middleware and consumed by handlers.
|
|
375
|
+
*
|
|
376
|
+
* This class provides a structural way for middleware to modify request data:
|
|
377
|
+
* - Path parameters can be modified
|
|
378
|
+
* - Query/search parameters can be modified
|
|
379
|
+
* - Request body can be overridden
|
|
380
|
+
* - Request headers can be modified
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* // In middleware
|
|
385
|
+
* const state = new MutableRequestState({
|
|
386
|
+
* pathParams: { id: "123" },
|
|
387
|
+
* searchParams: new URLSearchParams("?role=user"),
|
|
388
|
+
* body: { name: "John" },
|
|
389
|
+
* headers: new Headers()
|
|
390
|
+
* });
|
|
391
|
+
*
|
|
392
|
+
* // Modify query parameters
|
|
393
|
+
* state.searchParams.set("role", "admin");
|
|
394
|
+
*
|
|
395
|
+
* // Override body
|
|
396
|
+
* state.setBody({ name: "Jane" });
|
|
397
|
+
*
|
|
398
|
+
* // Modify headers
|
|
399
|
+
* state.headers.set("X-Custom", "value");
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
var MutableRequestState = class {
|
|
403
|
+
#pathParams;
|
|
404
|
+
#searchParams;
|
|
405
|
+
#headers;
|
|
406
|
+
#initialBody;
|
|
407
|
+
#bodyOverride;
|
|
408
|
+
constructor(config) {
|
|
409
|
+
this.#pathParams = config.pathParams;
|
|
410
|
+
this.#searchParams = config.searchParams;
|
|
411
|
+
this.#headers = config.headers;
|
|
412
|
+
this.#initialBody = config.body;
|
|
413
|
+
this.#bodyOverride = void 0;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Path parameters extracted from the route.
|
|
417
|
+
* Can be modified directly (e.g., `state.pathParams.id = "456"`).
|
|
418
|
+
*/
|
|
419
|
+
get pathParams() {
|
|
420
|
+
return this.#pathParams;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* URLSearchParams for query parameters.
|
|
424
|
+
* Can be modified using URLSearchParams API (e.g., `state.searchParams.set("key", "value")`).
|
|
425
|
+
*/
|
|
426
|
+
get searchParams() {
|
|
427
|
+
return this.#searchParams;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Request headers.
|
|
431
|
+
* Can be modified using Headers API (e.g., `state.headers.set("X-Custom", "value")`).
|
|
432
|
+
*/
|
|
433
|
+
get headers() {
|
|
434
|
+
return this.#headers;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Get the current body value.
|
|
438
|
+
* Returns the override if set, otherwise the initial body.
|
|
439
|
+
*/
|
|
440
|
+
get body() {
|
|
441
|
+
return this.#bodyOverride !== void 0 ? this.#bodyOverride : this.#initialBody;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Override the request body.
|
|
445
|
+
* This allows middleware to replace the body that will be seen by the handler.
|
|
446
|
+
*
|
|
447
|
+
* @param body - The new body value
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```typescript
|
|
451
|
+
* // In middleware
|
|
452
|
+
* state.setBody({ modifiedField: "new value" });
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
setBody(body) {
|
|
456
|
+
this.#bodyOverride = body;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Check if the body has been overridden by middleware.
|
|
460
|
+
*/
|
|
461
|
+
get hasBodyOverride() {
|
|
462
|
+
return this.#bodyOverride !== void 0;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/api/request-middleware.ts
|
|
468
|
+
var RequestMiddlewareOutputContext = class extends OutputContext {
|
|
469
|
+
#deps;
|
|
470
|
+
#services;
|
|
471
|
+
constructor(deps, services) {
|
|
472
|
+
super();
|
|
473
|
+
this.#deps = deps;
|
|
474
|
+
this.#services = services;
|
|
475
|
+
}
|
|
476
|
+
get deps() {
|
|
477
|
+
return this.#deps;
|
|
478
|
+
}
|
|
479
|
+
get services() {
|
|
480
|
+
return this.#services;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
var RequestMiddlewareInputContext = class {
|
|
484
|
+
#options;
|
|
485
|
+
#route;
|
|
486
|
+
#state;
|
|
487
|
+
constructor(routes, options) {
|
|
488
|
+
this.#options = options;
|
|
489
|
+
this.#state = options.state;
|
|
490
|
+
const route = routes.find((route$1) => route$1.path === options.path && route$1.method === options.method);
|
|
491
|
+
if (!route) throw new Error(`Route not found: ${options.path} ${options.method}`);
|
|
492
|
+
this.#route = route;
|
|
493
|
+
}
|
|
494
|
+
get path() {
|
|
495
|
+
return this.#options.path;
|
|
496
|
+
}
|
|
497
|
+
get method() {
|
|
498
|
+
return this.#options.method;
|
|
499
|
+
}
|
|
500
|
+
get pathParams() {
|
|
501
|
+
return this.#state.pathParams;
|
|
502
|
+
}
|
|
503
|
+
get queryParams() {
|
|
504
|
+
return this.#state.searchParams;
|
|
505
|
+
}
|
|
506
|
+
get headers() {
|
|
507
|
+
return this.#state.headers;
|
|
508
|
+
}
|
|
509
|
+
get inputSchema() {
|
|
510
|
+
return this.#route.inputSchema;
|
|
511
|
+
}
|
|
512
|
+
get outputSchema() {
|
|
513
|
+
return this.#route.outputSchema;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Access to the mutable request state.
|
|
517
|
+
* Use this to modify query parameters, path parameters, or request body.
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```typescript
|
|
521
|
+
* // Modify body
|
|
522
|
+
* requestState.setBody({ modified: true });
|
|
523
|
+
*
|
|
524
|
+
* // Query params are already accessible via queryParams getter
|
|
525
|
+
* // Path params are already accessible via pathParams getter
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
get requestState() {
|
|
529
|
+
return this.#state;
|
|
530
|
+
}
|
|
531
|
+
ifMatchesRoute = async (method, path, handler) => {
|
|
532
|
+
if (this.path !== path || this.method !== method) return;
|
|
533
|
+
return await handler(await RequestInputContext.fromRequest({
|
|
534
|
+
request: this.#options.request,
|
|
535
|
+
method: this.#options.method,
|
|
536
|
+
path,
|
|
537
|
+
pathParams: this.pathParams,
|
|
538
|
+
inputSchema: this.#route.inputSchema,
|
|
539
|
+
state: this.#state
|
|
540
|
+
}), new RequestOutputContext(this.#route.outputSchema));
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/api/fragno-response.ts
|
|
546
|
+
/**
|
|
547
|
+
* Parse a Response object into a FragnoResponse discriminated union
|
|
548
|
+
*/
|
|
549
|
+
async function parseFragnoResponse(response) {
|
|
550
|
+
const status = response.status;
|
|
551
|
+
const headers = response.headers;
|
|
552
|
+
if ((headers.get("content-type") || "").includes("application/x-ndjson")) return {
|
|
553
|
+
type: "jsonStream",
|
|
554
|
+
status,
|
|
555
|
+
headers,
|
|
556
|
+
stream: parseNDJSONStream(response)
|
|
557
|
+
};
|
|
558
|
+
const text = await response.text();
|
|
559
|
+
if (!text || text === "null") return {
|
|
560
|
+
type: "empty",
|
|
561
|
+
status,
|
|
562
|
+
headers
|
|
563
|
+
};
|
|
564
|
+
const data = JSON.parse(text);
|
|
565
|
+
if (data && typeof data === "object" && "code" in data) {
|
|
566
|
+
if ("message" in data) return {
|
|
567
|
+
type: "error",
|
|
568
|
+
status,
|
|
569
|
+
headers,
|
|
570
|
+
error: {
|
|
571
|
+
message: data.message,
|
|
572
|
+
code: data.code
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
if ("error" in data) return {
|
|
576
|
+
type: "error",
|
|
577
|
+
status,
|
|
578
|
+
headers,
|
|
579
|
+
error: {
|
|
580
|
+
message: data.error,
|
|
581
|
+
code: data.code
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
type: "json",
|
|
587
|
+
status,
|
|
588
|
+
headers,
|
|
589
|
+
data
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Parse an NDJSON stream into an async generator
|
|
594
|
+
*/
|
|
595
|
+
async function* parseNDJSONStream(response) {
|
|
596
|
+
if (!response.body) return;
|
|
597
|
+
const reader = response.body.getReader();
|
|
598
|
+
const decoder = new TextDecoder();
|
|
599
|
+
let buffer = "";
|
|
600
|
+
try {
|
|
601
|
+
while (true) {
|
|
602
|
+
const { done, value } = await reader.read();
|
|
603
|
+
if (done) break;
|
|
604
|
+
buffer += decoder.decode(value, { stream: true });
|
|
605
|
+
const lines = buffer.split("\n");
|
|
606
|
+
buffer = lines.pop() || "";
|
|
607
|
+
for (const line of lines) if (line.trim()) yield JSON.parse(line);
|
|
608
|
+
}
|
|
609
|
+
if (buffer.trim()) yield JSON.parse(buffer);
|
|
610
|
+
} finally {
|
|
611
|
+
reader.releaseLock();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/api/fragment-instantiation.ts
|
|
617
|
+
const instantiatedFragmentFakeSymbol = "$fragno-instantiated-fragment";
|
|
618
|
+
function createFragment(fragmentBuilder, config, routesOrFactories, options, interfaceImplementations) {
|
|
619
|
+
const definition = fragmentBuilder.definition;
|
|
620
|
+
if (definition.usedServices) for (const [serviceName, serviceMeta] of Object.entries(definition.usedServices)) {
|
|
621
|
+
const implementation = interfaceImplementations?.[serviceName];
|
|
622
|
+
if (serviceMeta.required && !implementation) throw new Error(`Fragment '${definition.name}' requires service '${serviceMeta.name}' but it was not provided`);
|
|
623
|
+
}
|
|
624
|
+
const depsWithInterfaces = {
|
|
625
|
+
...definition.dependencies?.(config, options) ?? {},
|
|
626
|
+
...interfaceImplementations
|
|
627
|
+
};
|
|
628
|
+
const servicesFromWithServices = definition.services?.(config, options, depsWithInterfaces) ?? {};
|
|
629
|
+
let providedServicesResolved;
|
|
630
|
+
if (typeof definition.providedServices === "function") providedServicesResolved = definition.providedServices(config, options, depsWithInterfaces);
|
|
631
|
+
else if (definition.providedServices && typeof definition.providedServices === "object") {
|
|
632
|
+
providedServicesResolved = {};
|
|
633
|
+
for (const [serviceName, serviceOrFactory] of Object.entries(definition.providedServices)) if (typeof serviceOrFactory === "function") providedServicesResolved[serviceName] = serviceOrFactory(config, options, depsWithInterfaces);
|
|
634
|
+
else providedServicesResolved[serviceName] = serviceOrFactory;
|
|
635
|
+
}
|
|
636
|
+
const services = {
|
|
637
|
+
...servicesFromWithServices,
|
|
638
|
+
...providedServicesResolved,
|
|
639
|
+
...interfaceImplementations
|
|
640
|
+
};
|
|
641
|
+
const routes = resolveRouteFactories({
|
|
642
|
+
config,
|
|
643
|
+
deps: depsWithInterfaces,
|
|
644
|
+
services
|
|
645
|
+
}, routesOrFactories);
|
|
646
|
+
const mountRoute = getMountRoute({
|
|
647
|
+
name: definition.name,
|
|
648
|
+
mountRoute: options.mountRoute
|
|
649
|
+
});
|
|
650
|
+
const router = createRouter();
|
|
651
|
+
let middlewareHandler;
|
|
652
|
+
const handlerWrapper = definition.createHandlerWrapper?.(options);
|
|
653
|
+
for (const routeConfig of routes) addRoute(router, routeConfig.method.toUpperCase(), routeConfig.path, routeConfig);
|
|
654
|
+
const fragment = {
|
|
655
|
+
[instantiatedFragmentFakeSymbol]: instantiatedFragmentFakeSymbol,
|
|
656
|
+
mountRoute,
|
|
657
|
+
config: {
|
|
658
|
+
name: definition.name,
|
|
659
|
+
routes
|
|
660
|
+
},
|
|
661
|
+
services,
|
|
662
|
+
deps: depsWithInterfaces,
|
|
663
|
+
additionalContext: {
|
|
664
|
+
...definition.additionalContext,
|
|
665
|
+
...options
|
|
666
|
+
},
|
|
667
|
+
withMiddleware: (handler) => {
|
|
668
|
+
if (middlewareHandler) throw new Error("Middleware already set");
|
|
669
|
+
middlewareHandler = handler;
|
|
670
|
+
return fragment;
|
|
671
|
+
},
|
|
672
|
+
callRoute: async (method, path, inputOptions) => {
|
|
673
|
+
return parseFragnoResponse(await fragment.callRouteRaw(method, path, inputOptions));
|
|
674
|
+
},
|
|
675
|
+
callRouteRaw: async (method, path, inputOptions) => {
|
|
676
|
+
const route = routes.find((r) => r.method === method && r.path === path);
|
|
677
|
+
if (!route) return Response.json({
|
|
678
|
+
error: `Route ${method} ${path} not found`,
|
|
679
|
+
code: "ROUTE_NOT_FOUND"
|
|
680
|
+
}, { status: 404 });
|
|
681
|
+
const { pathParams = {}, body, query, headers } = inputOptions || {};
|
|
682
|
+
const searchParams = query instanceof URLSearchParams ? query : query ? new URLSearchParams(query) : new URLSearchParams();
|
|
683
|
+
const requestHeaders = headers instanceof Headers ? headers : headers ? new Headers(headers) : new Headers();
|
|
684
|
+
const inputContext = new RequestInputContext({
|
|
685
|
+
path: route.path,
|
|
686
|
+
method: route.method,
|
|
687
|
+
pathParams,
|
|
688
|
+
searchParams,
|
|
689
|
+
headers: requestHeaders,
|
|
690
|
+
parsedBody: body,
|
|
691
|
+
inputSchema: route.inputSchema,
|
|
692
|
+
shouldValidateInput: true
|
|
693
|
+
});
|
|
694
|
+
const outputContext = new RequestOutputContext(route.outputSchema);
|
|
695
|
+
try {
|
|
696
|
+
let response;
|
|
697
|
+
const thisContext = {};
|
|
698
|
+
if (handlerWrapper) response = await handlerWrapper(route.handler).call(thisContext, inputContext, outputContext);
|
|
699
|
+
else response = await route.handler.call(thisContext, inputContext, outputContext);
|
|
700
|
+
return response;
|
|
701
|
+
} catch (error) {
|
|
702
|
+
console.error("Error in callRoute handler", error);
|
|
703
|
+
if (error instanceof FragnoApiError) return error.toResponse();
|
|
704
|
+
return Response.json({
|
|
705
|
+
error: "Internal server error",
|
|
706
|
+
code: "INTERNAL_SERVER_ERROR"
|
|
707
|
+
}, { status: 500 });
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
handlersFor: (framework) => {
|
|
711
|
+
const handler = fragment.handler;
|
|
712
|
+
if (framework === "h3" || framework === "nuxt") throw new Error(`To get handlers for h3, use the 'fromWebHandler' utility function:
|
|
713
|
+
import { fromWebHandler } from "h3";
|
|
714
|
+
export default fromWebHandler(myFragment().handler);`);
|
|
715
|
+
return {
|
|
716
|
+
astro: { ALL: handler },
|
|
717
|
+
"react-router": {
|
|
718
|
+
loader: ({ request }) => handler(request),
|
|
719
|
+
action: ({ request }) => handler(request)
|
|
720
|
+
},
|
|
721
|
+
"next-js": {
|
|
722
|
+
GET: handler,
|
|
723
|
+
POST: handler,
|
|
724
|
+
PUT: handler,
|
|
725
|
+
DELETE: handler,
|
|
726
|
+
PATCH: handler,
|
|
727
|
+
HEAD: handler,
|
|
728
|
+
OPTIONS: handler
|
|
729
|
+
},
|
|
730
|
+
"svelte-kit": {
|
|
731
|
+
GET: handler,
|
|
732
|
+
POST: handler,
|
|
733
|
+
PUT: handler,
|
|
734
|
+
DELETE: handler,
|
|
735
|
+
PATCH: handler,
|
|
736
|
+
HEAD: handler,
|
|
737
|
+
OPTIONS: handler
|
|
738
|
+
},
|
|
739
|
+
"solid-start": {
|
|
740
|
+
GET: ({ request }) => handler(request),
|
|
741
|
+
POST: ({ request }) => handler(request),
|
|
742
|
+
PUT: ({ request }) => handler(request),
|
|
743
|
+
DELETE: ({ request }) => handler(request),
|
|
744
|
+
PATCH: ({ request }) => handler(request),
|
|
745
|
+
HEAD: ({ request }) => handler(request),
|
|
746
|
+
OPTIONS: ({ request }) => handler(request)
|
|
747
|
+
},
|
|
748
|
+
"tanstack-start": {
|
|
749
|
+
GET: ({ request }) => handler(request),
|
|
750
|
+
POST: ({ request }) => handler(request),
|
|
751
|
+
PUT: ({ request }) => handler(request),
|
|
752
|
+
DELETE: ({ request }) => handler(request),
|
|
753
|
+
PATCH: ({ request }) => handler(request),
|
|
754
|
+
HEAD: ({ request }) => handler(request),
|
|
755
|
+
OPTIONS: ({ request }) => handler(request)
|
|
756
|
+
}
|
|
757
|
+
}[framework];
|
|
758
|
+
},
|
|
759
|
+
handler: async (req) => {
|
|
760
|
+
const url = new URL(req.url);
|
|
761
|
+
const pathname = url.pathname;
|
|
762
|
+
const matchRoute = pathname.startsWith(mountRoute) ? pathname.slice(mountRoute.length) : null;
|
|
763
|
+
if (matchRoute === null) return Response.json({
|
|
764
|
+
error: `Fragno: Route for '${definition.name}' not found. Is the fragment mounted on the right route? Expecting: '${mountRoute}'.`,
|
|
765
|
+
code: "ROUTE_NOT_FOUND"
|
|
766
|
+
}, { status: 404 });
|
|
767
|
+
const route = findRoute(router, req.method, matchRoute);
|
|
768
|
+
if (!route) return Response.json({
|
|
769
|
+
error: `Fragno: Route for '${definition.name}' not found`,
|
|
770
|
+
code: "ROUTE_NOT_FOUND"
|
|
771
|
+
}, { status: 404 });
|
|
772
|
+
const { handler, inputSchema, outputSchema, path } = route.data;
|
|
773
|
+
const outputContext = new RequestOutputContext(outputSchema);
|
|
774
|
+
let requestBody = void 0;
|
|
775
|
+
let rawBody = void 0;
|
|
776
|
+
if (req.body instanceof ReadableStream) {
|
|
777
|
+
rawBody = await req.clone().text();
|
|
778
|
+
if (rawBody) try {
|
|
779
|
+
requestBody = JSON.parse(rawBody);
|
|
780
|
+
} catch {
|
|
781
|
+
requestBody = void 0;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
const requestState = new MutableRequestState({
|
|
785
|
+
pathParams: route.params ?? {},
|
|
786
|
+
searchParams: url.searchParams,
|
|
787
|
+
body: requestBody,
|
|
788
|
+
headers: new Headers(req.headers)
|
|
789
|
+
});
|
|
790
|
+
if (middlewareHandler) {
|
|
791
|
+
const middlewareInputContext = new RequestMiddlewareInputContext(routes, {
|
|
792
|
+
method: req.method,
|
|
793
|
+
path,
|
|
794
|
+
request: req,
|
|
795
|
+
state: requestState
|
|
796
|
+
});
|
|
797
|
+
const middlewareOutputContext = new RequestMiddlewareOutputContext(depsWithInterfaces, services);
|
|
798
|
+
try {
|
|
799
|
+
const middlewareResult = await middlewareHandler(middlewareInputContext, middlewareOutputContext);
|
|
800
|
+
if (middlewareResult !== void 0) return middlewareResult;
|
|
801
|
+
} catch (error) {
|
|
802
|
+
console.error("Error in middleware", error);
|
|
803
|
+
if (error instanceof FragnoApiError) return error.toResponse();
|
|
804
|
+
return Response.json({
|
|
805
|
+
error: "Internal server error",
|
|
806
|
+
code: "INTERNAL_SERVER_ERROR"
|
|
807
|
+
}, { status: 500 });
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const inputContext = await RequestInputContext.fromRequest({
|
|
811
|
+
request: req,
|
|
812
|
+
method: req.method,
|
|
813
|
+
path,
|
|
814
|
+
pathParams: route.params ?? {},
|
|
815
|
+
inputSchema,
|
|
816
|
+
state: requestState,
|
|
817
|
+
rawBody
|
|
818
|
+
});
|
|
819
|
+
try {
|
|
820
|
+
return await (handlerWrapper ? handlerWrapper(handler) : handler).call({}, inputContext, outputContext);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
console.error("Error in handler", error);
|
|
823
|
+
if (error instanceof FragnoApiError) return error.toResponse();
|
|
824
|
+
return Response.json({
|
|
825
|
+
error: "Internal server error",
|
|
826
|
+
code: "INTERNAL_SERVER_ERROR"
|
|
827
|
+
}, { status: 500 });
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
return fragment;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Builder class for fluent fragment instantiation API
|
|
835
|
+
*/
|
|
836
|
+
var FragmentInstantiationBuilder = class {
|
|
837
|
+
#fragmentBuilder;
|
|
838
|
+
#config;
|
|
839
|
+
#routes;
|
|
840
|
+
#options;
|
|
841
|
+
#services;
|
|
842
|
+
constructor(fragmentBuilder) {
|
|
843
|
+
this.#fragmentBuilder = fragmentBuilder;
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Set the configuration for the fragment
|
|
847
|
+
*/
|
|
848
|
+
withConfig(config) {
|
|
849
|
+
this.#config = config;
|
|
850
|
+
return this;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Set the routes for the fragment
|
|
854
|
+
*/
|
|
855
|
+
withRoutes(routes) {
|
|
856
|
+
this.#routes = routes;
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Set the options for the fragment (e.g., mountRoute, databaseAdapter)
|
|
861
|
+
*/
|
|
862
|
+
withOptions(options) {
|
|
863
|
+
this.#options = options;
|
|
864
|
+
return this;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Provide implementations for services that this fragment uses
|
|
868
|
+
*/
|
|
869
|
+
withServices(services) {
|
|
870
|
+
this.#services = services;
|
|
871
|
+
return this;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Build and return the instantiated fragment
|
|
875
|
+
*/
|
|
876
|
+
build() {
|
|
877
|
+
return createFragment(this.#fragmentBuilder, this.#config ?? {}, this.#routes ?? [], this.#options ?? {}, this.#services);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
/**
|
|
881
|
+
* Create a fluent builder for instantiating a fragment
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```ts
|
|
885
|
+
* const fragment = instantiateFragment(myFragmentBuilder)
|
|
886
|
+
* .withConfig({ apiKey: "key" })
|
|
887
|
+
* .withRoutes([route1, route2])
|
|
888
|
+
* .withOptions({ mountRoute: "/api" })
|
|
889
|
+
* .build();
|
|
890
|
+
* ```
|
|
891
|
+
*/
|
|
892
|
+
function instantiateFragment(fragmentBuilder) {
|
|
893
|
+
return new FragmentInstantiationBuilder(fragmentBuilder);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
//#endregion
|
|
897
|
+
export { RequestOutputContext as a, addRoute$1 as c, instantiatedFragmentFakeSymbol as i, FragnoApiError as l, createFragment as n, RequestInputContext as o, instantiateFragment as r, getMountRoute as s, FragmentInstantiationBuilder as t, FragnoApiValidationError as u };
|
|
898
|
+
//# sourceMappingURL=fragment-instantiation-DUT-HLl1.js.map
|