@fragno-dev/core 0.1.7 → 0.1.9

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.
Files changed (183) hide show
  1. package/.turbo/turbo-build.log +131 -64
  2. package/CHANGELOG.md +19 -0
  3. package/dist/api/api.d.ts +38 -2
  4. package/dist/api/api.d.ts.map +1 -0
  5. package/dist/api/api.js +9 -2
  6. package/dist/api/api.js.map +1 -0
  7. package/dist/api/bind-services.d.ts +6 -0
  8. package/dist/api/bind-services.d.ts.map +1 -0
  9. package/dist/api/bind-services.js +20 -0
  10. package/dist/api/bind-services.js.map +1 -0
  11. package/dist/api/error.d.ts +26 -0
  12. package/dist/api/error.d.ts.map +1 -0
  13. package/dist/{api-DngJDcmO.js → api/error.js} +2 -8
  14. package/dist/api/error.js.map +1 -0
  15. package/dist/api/fragment-definition-builder.d.ts +313 -0
  16. package/dist/api/fragment-definition-builder.d.ts.map +1 -0
  17. package/dist/api/fragment-definition-builder.js +326 -0
  18. package/dist/api/fragment-definition-builder.js.map +1 -0
  19. package/dist/api/fragment-instantiator.d.ts +216 -0
  20. package/dist/api/fragment-instantiator.d.ts.map +1 -0
  21. package/dist/api/fragment-instantiator.js +487 -0
  22. package/dist/api/fragment-instantiator.js.map +1 -0
  23. package/dist/api/fragno-response.d.ts +30 -0
  24. package/dist/api/fragno-response.d.ts.map +1 -0
  25. package/dist/api/fragno-response.js +73 -0
  26. package/dist/api/fragno-response.js.map +1 -0
  27. package/dist/api/internal/path.d.ts +50 -0
  28. package/dist/api/internal/path.d.ts.map +1 -0
  29. package/dist/api/internal/path.js +76 -0
  30. package/dist/api/internal/path.js.map +1 -0
  31. package/dist/api/internal/response-stream.d.ts +43 -0
  32. package/dist/api/internal/response-stream.d.ts.map +1 -0
  33. package/dist/api/internal/response-stream.js +81 -0
  34. package/dist/api/internal/response-stream.js.map +1 -0
  35. package/dist/api/internal/route.js +10 -0
  36. package/dist/api/internal/route.js.map +1 -0
  37. package/dist/api/mutable-request-state.d.ts +82 -0
  38. package/dist/api/mutable-request-state.d.ts.map +1 -0
  39. package/dist/api/mutable-request-state.js +97 -0
  40. package/dist/api/mutable-request-state.js.map +1 -0
  41. package/dist/api/request-context-storage.d.ts +42 -0
  42. package/dist/api/request-context-storage.d.ts.map +1 -0
  43. package/dist/api/request-context-storage.js +43 -0
  44. package/dist/api/request-context-storage.js.map +1 -0
  45. package/dist/api/request-input-context.d.ts +89 -0
  46. package/dist/api/request-input-context.d.ts.map +1 -0
  47. package/dist/api/request-input-context.js +118 -0
  48. package/dist/api/request-input-context.js.map +1 -0
  49. package/dist/api/request-middleware.d.ts +50 -0
  50. package/dist/api/request-middleware.d.ts.map +1 -0
  51. package/dist/api/request-middleware.js +83 -0
  52. package/dist/api/request-middleware.js.map +1 -0
  53. package/dist/api/request-output-context.d.ts +41 -0
  54. package/dist/api/request-output-context.d.ts.map +1 -0
  55. package/dist/api/request-output-context.js +119 -0
  56. package/dist/api/request-output-context.js.map +1 -0
  57. package/dist/api/route-handler-input-options.d.ts +21 -0
  58. package/dist/api/route-handler-input-options.d.ts.map +1 -0
  59. package/dist/api/route.d.ts +54 -3
  60. package/dist/api/route.d.ts.map +1 -0
  61. package/dist/api/route.js +29 -2
  62. package/dist/api/route.js.map +1 -0
  63. package/dist/api/shared-types.d.ts +47 -0
  64. package/dist/api/shared-types.d.ts.map +1 -0
  65. package/dist/api/shared-types.js +1 -0
  66. package/dist/client/client-error.d.ts +60 -0
  67. package/dist/client/client-error.d.ts.map +1 -0
  68. package/dist/client/client-error.js +92 -0
  69. package/dist/client/client-error.js.map +1 -0
  70. package/dist/client/client.d.ts +210 -4
  71. package/dist/client/client.d.ts.map +1 -0
  72. package/dist/client/client.js +397 -6
  73. package/dist/client/client.js.map +1 -0
  74. package/dist/client/client.svelte.d.ts +5 -3
  75. package/dist/client/client.svelte.d.ts.map +1 -1
  76. package/dist/client/client.svelte.js +1 -5
  77. package/dist/client/client.svelte.js.map +1 -1
  78. package/dist/client/internal/fetcher-merge.js +36 -0
  79. package/dist/client/internal/fetcher-merge.js.map +1 -0
  80. package/dist/client/internal/ndjson-streaming.js +139 -0
  81. package/dist/client/internal/ndjson-streaming.js.map +1 -0
  82. package/dist/client/react.d.ts +5 -3
  83. package/dist/client/react.d.ts.map +1 -1
  84. package/dist/client/react.js +3 -5
  85. package/dist/client/react.js.map +1 -1
  86. package/dist/client/solid.d.ts +5 -3
  87. package/dist/client/solid.d.ts.map +1 -1
  88. package/dist/client/solid.js +2 -5
  89. package/dist/client/solid.js.map +1 -1
  90. package/dist/client/vanilla.d.ts +5 -3
  91. package/dist/client/vanilla.d.ts.map +1 -1
  92. package/dist/client/vanilla.js +2 -43
  93. package/dist/client/vanilla.js.map +1 -1
  94. package/dist/client/vue.d.ts +5 -3
  95. package/dist/client/vue.d.ts.map +1 -1
  96. package/dist/client/vue.js +1 -5
  97. package/dist/client/vue.js.map +1 -1
  98. package/dist/http/http-status.d.ts +26 -0
  99. package/dist/http/http-status.d.ts.map +1 -0
  100. package/dist/integrations/react-ssr.js +1 -1
  101. package/dist/internal/symbols.d.ts +9 -0
  102. package/dist/internal/symbols.d.ts.map +1 -0
  103. package/dist/internal/symbols.js +10 -0
  104. package/dist/internal/symbols.js.map +1 -0
  105. package/dist/mod-client.d.ts +36 -0
  106. package/dist/mod-client.d.ts.map +1 -0
  107. package/dist/mod-client.js +21 -0
  108. package/dist/mod-client.js.map +1 -0
  109. package/dist/mod.d.ts +7 -4
  110. package/dist/mod.js +4 -6
  111. package/dist/request/request.d.ts +4 -0
  112. package/dist/request/request.js +5 -0
  113. package/dist/test/test.d.ts +62 -35
  114. package/dist/test/test.d.ts.map +1 -1
  115. package/dist/test/test.js +75 -40
  116. package/dist/test/test.js.map +1 -1
  117. package/dist/util/async.js +40 -0
  118. package/dist/util/async.js.map +1 -0
  119. package/dist/util/content-type.js +49 -0
  120. package/dist/util/content-type.js.map +1 -0
  121. package/dist/util/nanostores.js +31 -0
  122. package/dist/util/nanostores.js.map +1 -0
  123. package/dist/{ssr-BByDVfFD.js → util/ssr.js} +2 -2
  124. package/dist/util/ssr.js.map +1 -0
  125. package/dist/util/types-util.d.ts +8 -0
  126. package/dist/util/types-util.d.ts.map +1 -0
  127. package/package.json +19 -12
  128. package/src/api/api.ts +41 -6
  129. package/src/api/bind-services.ts +42 -0
  130. package/src/api/fragment-definition-builder.extend.test.ts +810 -0
  131. package/src/api/fragment-definition-builder.test.ts +499 -0
  132. package/src/api/fragment-definition-builder.ts +1088 -0
  133. package/src/api/fragment-instantiator.test.ts +1488 -0
  134. package/src/api/fragment-instantiator.ts +1053 -0
  135. package/src/api/fragment-services.test.ts +727 -0
  136. package/src/api/request-context-storage.ts +64 -0
  137. package/src/api/request-middleware.test.ts +301 -225
  138. package/src/api/route.test.ts +87 -1
  139. package/src/api/route.ts +345 -24
  140. package/src/api/shared-types.ts +43 -0
  141. package/src/client/client-builder.test.ts +23 -23
  142. package/src/client/client.ssr.test.ts +3 -3
  143. package/src/client/client.svelte.test.ts +15 -15
  144. package/src/client/client.test.ts +22 -22
  145. package/src/client/client.ts +72 -12
  146. package/src/client/internal/fetcher-merge.ts +1 -1
  147. package/src/client/react.test.ts +2 -2
  148. package/src/client/solid.test.ts +2 -2
  149. package/src/client/vanilla.test.ts +2 -2
  150. package/src/client/vue.test.ts +2 -2
  151. package/src/internal/symbols.ts +5 -0
  152. package/src/mod-client.ts +59 -0
  153. package/src/mod.ts +26 -9
  154. package/src/request/request.ts +8 -0
  155. package/src/test/test.test.ts +200 -381
  156. package/src/test/test.ts +190 -117
  157. package/tsdown.config.ts +8 -5
  158. package/dist/api/fragment-builder.d.ts +0 -4
  159. package/dist/api/fragment-builder.js +0 -3
  160. package/dist/api/fragment-instantiation.d.ts +0 -4
  161. package/dist/api/fragment-instantiation.js +0 -6
  162. package/dist/api-BWN97TOr.d.ts +0 -377
  163. package/dist/api-BWN97TOr.d.ts.map +0 -1
  164. package/dist/api-DngJDcmO.js.map +0 -1
  165. package/dist/client-C5LsYHEI.js +0 -782
  166. package/dist/client-C5LsYHEI.js.map +0 -1
  167. package/dist/fragment-builder-DOnCVBqc.js +0 -47
  168. package/dist/fragment-builder-DOnCVBqc.js.map +0 -1
  169. package/dist/fragment-builder-MGr68GNb.d.ts +0 -409
  170. package/dist/fragment-builder-MGr68GNb.d.ts.map +0 -1
  171. package/dist/fragment-instantiation-C4wvwl6V.js +0 -446
  172. package/dist/fragment-instantiation-C4wvwl6V.js.map +0 -1
  173. package/dist/request-output-context-CdIjwmEN.js +0 -320
  174. package/dist/request-output-context-CdIjwmEN.js.map +0 -1
  175. package/dist/route-Bl9Zr1Yv.d.ts +0 -26
  176. package/dist/route-Bl9Zr1Yv.d.ts.map +0 -1
  177. package/dist/route-C5Uryylh.js +0 -21
  178. package/dist/route-C5Uryylh.js.map +0 -1
  179. package/dist/ssr-BByDVfFD.js.map +0 -1
  180. package/src/api/fragment-builder.ts +0 -80
  181. package/src/api/fragment-instantiation.test.ts +0 -460
  182. package/src/api/fragment-instantiation.ts +0 -499
  183. package/src/api/fragment.test.ts +0 -537
@@ -0,0 +1,1053 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { type FragnoRouteConfig, type HTTPMethod, type RequestThisContext } from "./api";
3
+ import { FragnoApiError } from "./error";
4
+ import { getMountRoute } from "./internal/route";
5
+ import { addRoute, createRouter, findRoute } from "rou3";
6
+ import { RequestInputContext, type RequestBodyType } from "./request-input-context";
7
+ import type { ExtractPathParams } from "./internal/path";
8
+ import { RequestOutputContext } from "./request-output-context";
9
+ import {
10
+ type AnyFragnoRouteConfig,
11
+ type AnyRouteOrFactory,
12
+ type FlattenRouteFactories,
13
+ resolveRouteFactories,
14
+ } from "./route";
15
+ import {
16
+ RequestMiddlewareInputContext,
17
+ RequestMiddlewareOutputContext,
18
+ type FragnoMiddlewareCallback,
19
+ } from "./request-middleware";
20
+ import { MutableRequestState } from "./mutable-request-state";
21
+ import type { RouteHandlerInputOptions } from "./route-handler-input-options";
22
+ import type { ExtractRouteByPath, ExtractRoutePath } from "../client/client";
23
+ import { type FragnoResponse, parseFragnoResponse } from "./fragno-response";
24
+ import type { InferOrUnknown } from "../util/types-util";
25
+ import type { FragmentDefinition } from "./fragment-definition-builder";
26
+ import type { FragnoPublicConfig } from "./shared-types";
27
+ import { RequestContextStorage } from "./request-context-storage";
28
+ import { bindServicesToContext, type BoundServices } from "./bind-services";
29
+ import { instantiatedFragmentFakeSymbol } from "../internal/symbols";
30
+
31
+ // Re-export types needed by consumers
32
+ export type { BoundServices };
33
+
34
+ type AstroHandlers = {
35
+ ALL: (req: Request) => Promise<Response>;
36
+ };
37
+
38
+ type ReactRouterHandlers = {
39
+ loader: (args: { request: Request }) => Promise<Response>;
40
+ action: (args: { request: Request }) => Promise<Response>;
41
+ };
42
+
43
+ type SolidStartHandlers = {
44
+ GET: (args: { request: Request }) => Promise<Response>;
45
+ POST: (args: { request: Request }) => Promise<Response>;
46
+ PUT: (args: { request: Request }) => Promise<Response>;
47
+ DELETE: (args: { request: Request }) => Promise<Response>;
48
+ PATCH: (args: { request: Request }) => Promise<Response>;
49
+ HEAD: (args: { request: Request }) => Promise<Response>;
50
+ OPTIONS: (args: { request: Request }) => Promise<Response>;
51
+ };
52
+
53
+ type TanStackStartHandlers = SolidStartHandlers;
54
+
55
+ type StandardHandlers = {
56
+ GET: (req: Request) => Promise<Response>;
57
+ POST: (req: Request) => Promise<Response>;
58
+ PUT: (req: Request) => Promise<Response>;
59
+ DELETE: (req: Request) => Promise<Response>;
60
+ PATCH: (req: Request) => Promise<Response>;
61
+ HEAD: (req: Request) => Promise<Response>;
62
+ OPTIONS: (req: Request) => Promise<Response>;
63
+ };
64
+
65
+ type HandlersByFramework = {
66
+ astro: AstroHandlers;
67
+ "react-router": ReactRouterHandlers;
68
+ "next-js": StandardHandlers;
69
+ "svelte-kit": StandardHandlers;
70
+ "solid-start": SolidStartHandlers;
71
+ "tanstack-start": TanStackStartHandlers;
72
+ };
73
+
74
+ type FullstackFrameworks = keyof HandlersByFramework;
75
+
76
+ // Safe: This is a catch-all type for any instantiated fragment
77
+ type AnyFragnoInstantiatedFragment = FragnoInstantiatedFragment<
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ any,
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ any,
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ any,
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ any,
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ any,
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ any,
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ any,
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ any
94
+ >;
95
+
96
+ export type { AnyFragnoInstantiatedFragment };
97
+
98
+ export interface FragnoFragmentSharedConfig<
99
+ TRoutes extends readonly FragnoRouteConfig<
100
+ HTTPMethod,
101
+ string,
102
+ StandardSchemaV1 | undefined,
103
+ StandardSchemaV1 | undefined,
104
+ string,
105
+ string
106
+ >[],
107
+ > {
108
+ name: string;
109
+ routes: TRoutes;
110
+ }
111
+
112
+ /**
113
+ * Instantiated fragment class with encapsulated state.
114
+ * Provides the same public API as the old FragnoInstantiatedFragment but with better encapsulation.
115
+ */
116
+ export class FragnoInstantiatedFragment<
117
+ TRoutes extends readonly AnyFragnoRouteConfig[],
118
+ TDeps,
119
+ TServices extends Record<string, unknown>,
120
+ TServiceThisContext extends RequestThisContext,
121
+ THandlerThisContext extends RequestThisContext,
122
+ TRequestStorage = {},
123
+ TOptions extends FragnoPublicConfig = FragnoPublicConfig,
124
+ TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},
125
+ > {
126
+ readonly [instantiatedFragmentFakeSymbol] = instantiatedFragmentFakeSymbol;
127
+
128
+ // Private fields
129
+ #name: string;
130
+ #routes: TRoutes;
131
+ #deps: TDeps;
132
+ #services: TServices;
133
+ #mountRoute: string;
134
+ #router: ReturnType<typeof createRouter>;
135
+ #middlewareHandler?: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>;
136
+ #serviceThisContext?: TServiceThisContext; // Context for services (may have restricted capabilities)
137
+ #handlerThisContext?: THandlerThisContext; // Context for handlers (full capabilities)
138
+ #contextStorage: RequestContextStorage<TRequestStorage>;
139
+ #createRequestStorage?: () => TRequestStorage;
140
+ #options: TOptions;
141
+ #linkedFragments: TLinkedFragments;
142
+
143
+ constructor(params: {
144
+ name: string;
145
+ routes: TRoutes;
146
+ deps: TDeps;
147
+ services: TServices;
148
+ mountRoute: string;
149
+ serviceThisContext?: TServiceThisContext;
150
+ handlerThisContext?: THandlerThisContext;
151
+ storage: RequestContextStorage<TRequestStorage>;
152
+ createRequestStorage?: () => TRequestStorage;
153
+ options: TOptions;
154
+ linkedFragments?: TLinkedFragments;
155
+ }) {
156
+ this.#name = params.name;
157
+ this.#routes = params.routes;
158
+ this.#deps = params.deps;
159
+ this.#services = params.services;
160
+ this.#mountRoute = params.mountRoute;
161
+ this.#serviceThisContext = params.serviceThisContext;
162
+ this.#handlerThisContext = params.handlerThisContext;
163
+ this.#contextStorage = params.storage;
164
+ this.#createRequestStorage = params.createRequestStorage;
165
+ this.#options = params.options;
166
+ this.#linkedFragments = params.linkedFragments ?? ({} as TLinkedFragments);
167
+
168
+ // Build router
169
+ this.#router =
170
+ createRouter<
171
+ FragnoRouteConfig<
172
+ HTTPMethod,
173
+ string,
174
+ StandardSchemaV1 | undefined,
175
+ StandardSchemaV1 | undefined,
176
+ string,
177
+ string,
178
+ RequestThisContext
179
+ >
180
+ >();
181
+
182
+ for (const routeConfig of this.#routes) {
183
+ addRoute(this.#router, routeConfig.method.toUpperCase(), routeConfig.path, routeConfig);
184
+ }
185
+
186
+ // Bind handler method to maintain 'this' context
187
+ this.handler = this.handler.bind(this);
188
+ }
189
+
190
+ // Public getters
191
+ get name(): string {
192
+ return this.#name;
193
+ }
194
+
195
+ get routes(): TRoutes {
196
+ return this.#routes;
197
+ }
198
+
199
+ get services(): TServices {
200
+ return this.#services;
201
+ }
202
+
203
+ get mountRoute(): string {
204
+ return this.#mountRoute;
205
+ }
206
+
207
+ /**
208
+ * @internal
209
+ */
210
+ get $internal() {
211
+ return {
212
+ deps: this.#deps,
213
+ options: this.#options,
214
+ linkedFragments: this.#linkedFragments,
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Add middleware to this fragment.
220
+ * Middleware can inspect and modify requests before they reach handlers.
221
+ */
222
+ withMiddleware(handler: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>): this {
223
+ if (this.#middlewareHandler) {
224
+ throw new Error("Middleware already set");
225
+ }
226
+ this.#middlewareHandler = handler;
227
+ return this;
228
+ }
229
+
230
+ /**
231
+ * Run a callback within the fragment's request context with initialized storage.
232
+ * This is a shared helper used by inContext(), handler(), and callRouteRaw().
233
+ * @private
234
+ */
235
+ #withRequestStorage<T>(callback: () => T): T;
236
+ #withRequestStorage<T>(callback: () => Promise<T>): Promise<T>;
237
+ #withRequestStorage<T>(callback: () => T | Promise<T>): T | Promise<T> {
238
+ if (!this.#serviceThisContext && !this.#handlerThisContext) {
239
+ // No request context configured - just run callback directly
240
+ return callback();
241
+ }
242
+
243
+ // Initialize storage with fresh data for this request
244
+ const storageData = this.#createRequestStorage
245
+ ? this.#createRequestStorage()
246
+ : ({} as TRequestStorage);
247
+ return this.#contextStorage.run(storageData, callback);
248
+ }
249
+
250
+ /**
251
+ * Execute a callback within a request context.
252
+ * Establishes an async context for the duration of the callback, allowing services
253
+ * to access the `this` context. The callback's `this` will be bound to the fragment's
254
+ * handler context (with full capabilities including execute methods).
255
+ * Useful for calling services outside of route handlers (e.g., in tests, background jobs).
256
+ *
257
+ * @param callback - The callback to run within the context
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const result = await fragment.inContext(function () {
262
+ * // `this` is bound to the handler context (can call execute methods)
263
+ * await this.getUnitOfWork().executeRetrieve();
264
+ * return this.someContextMethod();
265
+ * });
266
+ * ```
267
+ */
268
+ inContext<T>(callback: (this: THandlerThisContext) => T): T;
269
+ inContext<T>(callback: (this: THandlerThisContext) => Promise<T>): Promise<T>;
270
+ inContext<T>(callback: (this: THandlerThisContext) => T | Promise<T>): T | Promise<T> {
271
+ // Always use handler context for inContext - it has full capabilities
272
+ if (this.#handlerThisContext) {
273
+ const boundCallback = callback.bind(this.#handlerThisContext);
274
+ return this.#withRequestStorage(boundCallback);
275
+ }
276
+ return this.#withRequestStorage(callback);
277
+ }
278
+
279
+ /**
280
+ * Get framework-specific handlers for this fragment.
281
+ * Use this to integrate the fragment with different fullstack frameworks.
282
+ */
283
+ handlersFor<T extends FullstackFrameworks>(framework: T): HandlersByFramework[T] {
284
+ const handler = this.handler.bind(this);
285
+
286
+ // LLMs hallucinate these values sometimes, solution isn't obvious so we throw this error
287
+ // @ts-expect-error TS2367
288
+ if (framework === "h3" || framework === "nuxt") {
289
+ throw new Error(`To get handlers for h3, use the 'fromWebHandler' utility function:
290
+ import { fromWebHandler } from "h3";
291
+ export default fromWebHandler(myFragment().handler);`);
292
+ }
293
+
294
+ const allHandlers = {
295
+ astro: { ALL: handler },
296
+ "react-router": {
297
+ loader: ({ request }: { request: Request }) => handler(request),
298
+ action: ({ request }: { request: Request }) => handler(request),
299
+ },
300
+ "next-js": {
301
+ GET: handler,
302
+ POST: handler,
303
+ PUT: handler,
304
+ DELETE: handler,
305
+ PATCH: handler,
306
+ HEAD: handler,
307
+ OPTIONS: handler,
308
+ },
309
+ "svelte-kit": {
310
+ GET: handler,
311
+ POST: handler,
312
+ PUT: handler,
313
+ DELETE: handler,
314
+ PATCH: handler,
315
+ HEAD: handler,
316
+ OPTIONS: handler,
317
+ },
318
+ "solid-start": {
319
+ GET: ({ request }: { request: Request }) => handler(request),
320
+ POST: ({ request }: { request: Request }) => handler(request),
321
+ PUT: ({ request }: { request: Request }) => handler(request),
322
+ DELETE: ({ request }: { request: Request }) => handler(request),
323
+ PATCH: ({ request }: { request: Request }) => handler(request),
324
+ HEAD: ({ request }: { request: Request }) => handler(request),
325
+ OPTIONS: ({ request }: { request: Request }) => handler(request),
326
+ },
327
+ "tanstack-start": {
328
+ GET: ({ request }: { request: Request }) => handler(request),
329
+ POST: ({ request }: { request: Request }) => handler(request),
330
+ PUT: ({ request }: { request: Request }) => handler(request),
331
+ DELETE: ({ request }: { request: Request }) => handler(request),
332
+ PATCH: ({ request }: { request: Request }) => handler(request),
333
+ HEAD: ({ request }: { request: Request }) => handler(request),
334
+ OPTIONS: ({ request }: { request: Request }) => handler(request),
335
+ },
336
+ } satisfies HandlersByFramework;
337
+
338
+ return allHandlers[framework];
339
+ }
340
+
341
+ /**
342
+ * Main request handler for this fragment.
343
+ * Handles routing, middleware, and error handling.
344
+ */
345
+ async handler(req: Request): Promise<Response> {
346
+ const url = new URL(req.url);
347
+ const pathname = url.pathname;
348
+
349
+ // Match route
350
+ const matchRoute = pathname.startsWith(this.#mountRoute)
351
+ ? pathname.slice(this.#mountRoute.length)
352
+ : null;
353
+
354
+ if (matchRoute === null) {
355
+ return Response.json(
356
+ {
357
+ error:
358
+ `Fragno: Route for '${this.#name}' not found. Is the fragment mounted on the right route? ` +
359
+ `Expecting: '${this.#mountRoute}'.`,
360
+ code: "ROUTE_NOT_FOUND",
361
+ },
362
+ { status: 404 },
363
+ );
364
+ }
365
+
366
+ const route = findRoute(this.#router, req.method, matchRoute);
367
+
368
+ if (!route) {
369
+ return Response.json(
370
+ { error: `Fragno: Route for '${this.#name}' not found`, code: "ROUTE_NOT_FOUND" },
371
+ { status: 404 },
372
+ );
373
+ }
374
+
375
+ // Parse request body
376
+ let requestBody: RequestBodyType = undefined;
377
+ let rawBody: string | undefined = undefined;
378
+
379
+ if (req.body instanceof ReadableStream) {
380
+ // Clone request to make sure we don't consume body stream
381
+ const clonedReq = req.clone();
382
+
383
+ // Get raw text
384
+ rawBody = await clonedReq.text();
385
+
386
+ // Parse JSON if body is not empty
387
+ if (rawBody) {
388
+ try {
389
+ requestBody = JSON.parse(rawBody);
390
+ } catch {
391
+ // If JSON parsing fails, keep body as undefined
392
+ // This handles cases where body is not JSON
393
+ requestBody = undefined;
394
+ }
395
+ }
396
+ }
397
+
398
+ const requestState = new MutableRequestState({
399
+ pathParams: route.params ?? {},
400
+ searchParams: url.searchParams,
401
+ body: requestBody,
402
+ headers: new Headers(req.headers),
403
+ });
404
+
405
+ // Execute middleware and handler
406
+ const executeRequest = async (): Promise<Response> => {
407
+ // Middleware execution (if present)
408
+ if (this.#middlewareHandler) {
409
+ const middlewareResult = await this.#executeMiddleware(req, route, requestState);
410
+ if (middlewareResult !== undefined) {
411
+ return middlewareResult;
412
+ }
413
+ }
414
+
415
+ // Handler execution
416
+ return this.#executeHandler(req, route, requestState, rawBody);
417
+ };
418
+
419
+ // Wrap with request storage context if provided
420
+ return this.#withRequestStorage(executeRequest);
421
+ }
422
+
423
+ /**
424
+ * Call a route directly with typed inputs and outputs.
425
+ * Useful for testing and server-side route calls.
426
+ */
427
+ async callRoute<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(
428
+ method: TMethod,
429
+ path: TPath,
430
+ inputOptions?: RouteHandlerInputOptions<
431
+ TPath,
432
+ ExtractRouteByPath<TRoutes, TPath, TMethod>["inputSchema"]
433
+ >,
434
+ ): Promise<
435
+ FragnoResponse<
436
+ InferOrUnknown<NonNullable<ExtractRouteByPath<TRoutes, TPath, TMethod>["outputSchema"]>>
437
+ >
438
+ > {
439
+ const response = await this.callRouteRaw(method, path, inputOptions);
440
+ return parseFragnoResponse(response);
441
+ }
442
+
443
+ /**
444
+ * Call a route directly and get the raw Response object.
445
+ * Useful for testing and server-side route calls.
446
+ */
447
+ async callRouteRaw<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(
448
+ method: TMethod,
449
+ path: TPath,
450
+ inputOptions?: RouteHandlerInputOptions<
451
+ TPath,
452
+ ExtractRouteByPath<TRoutes, TPath, TMethod>["inputSchema"]
453
+ >,
454
+ ): Promise<Response> {
455
+ // Find route in this.#routes
456
+ const route = this.#routes.find((r) => r.method === method && r.path === path);
457
+
458
+ if (!route) {
459
+ return Response.json(
460
+ {
461
+ error: `Route ${method} ${path} not found`,
462
+ code: "ROUTE_NOT_FOUND",
463
+ },
464
+ { status: 404 },
465
+ );
466
+ }
467
+
468
+ const { pathParams = {}, body, query, headers } = inputOptions || {};
469
+
470
+ // Convert query to URLSearchParams if needed
471
+ const searchParams =
472
+ query instanceof URLSearchParams
473
+ ? query
474
+ : query
475
+ ? new URLSearchParams(query)
476
+ : new URLSearchParams();
477
+
478
+ // Convert headers to Headers if needed
479
+ const requestHeaders =
480
+ headers instanceof Headers ? headers : headers ? new Headers(headers) : new Headers();
481
+
482
+ // Construct RequestInputContext
483
+ const inputContext = new RequestInputContext({
484
+ path: route.path,
485
+ method: route.method,
486
+ pathParams: pathParams as ExtractPathParams<typeof route.path>,
487
+ searchParams,
488
+ headers: requestHeaders,
489
+ parsedBody: body,
490
+ inputSchema: route.inputSchema,
491
+ shouldValidateInput: true, // Enable validation for production use
492
+ });
493
+
494
+ // Construct RequestOutputContext
495
+ const outputContext = new RequestOutputContext(route.outputSchema);
496
+
497
+ // Execute handler
498
+ const executeHandler = async (): Promise<Response> => {
499
+ try {
500
+ // Use handler context (full capabilities)
501
+ const thisContext = this.#handlerThisContext ?? ({} as RequestThisContext);
502
+ return await route.handler.call(thisContext, inputContext, outputContext);
503
+ } catch (error) {
504
+ console.error("Error in callRoute handler", error);
505
+
506
+ if (error instanceof FragnoApiError) {
507
+ return error.toResponse();
508
+ }
509
+
510
+ return Response.json(
511
+ { error: "Internal server error", code: "INTERNAL_SERVER_ERROR" },
512
+ { status: 500 },
513
+ );
514
+ }
515
+ };
516
+
517
+ // Wrap with request storage context if provided
518
+ return this.#withRequestStorage(executeHandler);
519
+ }
520
+
521
+ /**
522
+ * Execute middleware for a request.
523
+ * Returns undefined if middleware allows the request to continue to the handler.
524
+ */
525
+ async #executeMiddleware(
526
+ req: Request,
527
+ route: ReturnType<typeof findRoute>,
528
+ requestState: MutableRequestState,
529
+ ): Promise<Response | undefined> {
530
+ if (!this.#middlewareHandler || !route) {
531
+ return undefined;
532
+ }
533
+
534
+ const { path } = route.data as AnyFragnoRouteConfig;
535
+
536
+ const middlewareInputContext = new RequestMiddlewareInputContext(this.#routes, {
537
+ method: req.method as HTTPMethod,
538
+ path,
539
+ request: req,
540
+ state: requestState,
541
+ });
542
+
543
+ const middlewareOutputContext = new RequestMiddlewareOutputContext(this.#deps, this.#services);
544
+
545
+ try {
546
+ const middlewareResult = await this.#middlewareHandler(
547
+ middlewareInputContext,
548
+ middlewareOutputContext,
549
+ );
550
+ if (middlewareResult !== undefined) {
551
+ return middlewareResult;
552
+ }
553
+ } catch (error) {
554
+ console.error("Error in middleware", error);
555
+
556
+ if (error instanceof FragnoApiError) {
557
+ return error.toResponse();
558
+ }
559
+
560
+ return Response.json(
561
+ { error: "Internal server error", code: "INTERNAL_SERVER_ERROR" },
562
+ { status: 500 },
563
+ );
564
+ }
565
+
566
+ return undefined;
567
+ }
568
+
569
+ /**
570
+ * Execute a route handler with proper error handling.
571
+ */
572
+ async #executeHandler(
573
+ req: Request,
574
+ route: ReturnType<typeof findRoute>,
575
+ requestState: MutableRequestState,
576
+ rawBody?: string,
577
+ ): Promise<Response> {
578
+ if (!route) {
579
+ return Response.json({ error: "Route not found", code: "ROUTE_NOT_FOUND" }, { status: 404 });
580
+ }
581
+
582
+ const { handler, inputSchema, outputSchema, path } = route.data as AnyFragnoRouteConfig;
583
+
584
+ const inputContext = await RequestInputContext.fromRequest({
585
+ request: req,
586
+ method: req.method,
587
+ path,
588
+ pathParams: (route.params ?? {}) as ExtractPathParams<typeof path>,
589
+ inputSchema,
590
+ state: requestState,
591
+ rawBody,
592
+ });
593
+
594
+ const outputContext = new RequestOutputContext(outputSchema);
595
+
596
+ try {
597
+ // Note: We don't call .run() here because the storage should already be initialized
598
+ // by the handler() method or inContext() method before this point
599
+ // Use handler context (full capabilities)
600
+ const contextForHandler = this.#handlerThisContext ?? ({} as RequestThisContext);
601
+ const result = await handler.call(contextForHandler, inputContext, outputContext);
602
+ return result;
603
+ } catch (error) {
604
+ console.error("Error in handler", error);
605
+
606
+ if (error instanceof FragnoApiError) {
607
+ return error.toResponse();
608
+ }
609
+
610
+ return Response.json(
611
+ { error: "Internal server error", code: "INTERNAL_SERVER_ERROR" },
612
+ { status: 500 },
613
+ );
614
+ }
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Core instantiation function that creates a fragment instance from a definition.
620
+ * This function validates dependencies, calls all callbacks, and wires everything together.
621
+ */
622
+ export function instantiateFragment<
623
+ const TConfig,
624
+ const TOptions extends FragnoPublicConfig,
625
+ const TDeps,
626
+ const TBaseServices extends Record<string, unknown>,
627
+ const TServices extends Record<string, unknown>,
628
+ const TServiceDependencies,
629
+ const TPrivateServices extends Record<string, unknown>,
630
+ const TServiceThisContext extends RequestThisContext,
631
+ const THandlerThisContext extends RequestThisContext,
632
+ const TRequestStorage,
633
+ const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
634
+ const TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,
635
+ >(
636
+ definition: FragmentDefinition<
637
+ TConfig,
638
+ TOptions,
639
+ TDeps,
640
+ TBaseServices,
641
+ TServices,
642
+ TServiceDependencies,
643
+ TPrivateServices,
644
+ TServiceThisContext,
645
+ THandlerThisContext,
646
+ TRequestStorage,
647
+ TLinkedFragments
648
+ >,
649
+ config: TConfig,
650
+ routesOrFactories: TRoutesOrFactories,
651
+ options: TOptions,
652
+ serviceImplementations?: TServiceDependencies,
653
+ ): FragnoInstantiatedFragment<
654
+ FlattenRouteFactories<TRoutesOrFactories>,
655
+ TDeps,
656
+ BoundServices<TBaseServices & TServices>,
657
+ TServiceThisContext,
658
+ THandlerThisContext,
659
+ TRequestStorage,
660
+ TOptions,
661
+ TLinkedFragments
662
+ > {
663
+ // 1. Validate service dependencies
664
+ const serviceDependencies = definition.serviceDependencies;
665
+ if (serviceDependencies) {
666
+ for (const [serviceName, meta] of Object.entries(serviceDependencies)) {
667
+ const metadata = meta as { name: string; required: boolean };
668
+ const implementation = serviceImplementations?.[serviceName as keyof TServiceDependencies];
669
+ if (metadata.required && !implementation) {
670
+ throw new Error(
671
+ `Fragment '${definition.name}' requires service '${metadata.name}' but it was not provided`,
672
+ );
673
+ }
674
+ }
675
+ }
676
+
677
+ // 2. Call dependencies callback
678
+ const deps = definition.dependencies?.({ config, options }) ?? ({} as TDeps);
679
+
680
+ // 3. Instantiate linked fragments FIRST (before any services)
681
+ // Their services will be merged into private services
682
+ const linkedFragmentInstances = {} as TLinkedFragments;
683
+ const linkedFragmentServices: Record<string, unknown> = {};
684
+
685
+ if (definition.linkedFragments) {
686
+ for (const [name, callback] of Object.entries(definition.linkedFragments)) {
687
+ const linkedFragment = callback({
688
+ config,
689
+ options,
690
+ serviceDependencies: serviceImplementations,
691
+ });
692
+ (linkedFragmentInstances as Record<string, AnyFragnoInstantiatedFragment>)[name] =
693
+ linkedFragment;
694
+
695
+ // Merge all services from linked fragment into private services directly by their service name
696
+ const services = linkedFragment.services as Record<string, unknown>;
697
+ for (const [serviceName, service] of Object.entries(services)) {
698
+ linkedFragmentServices[serviceName] = service;
699
+ }
700
+ }
701
+ }
702
+
703
+ // Identity function for service definition (used to set 'this' context)
704
+ const defineService = <T>(services: T & ThisType<TServiceThisContext>): T => services;
705
+
706
+ // 4. Call privateServices factories
707
+ // Private services are instantiated in order, so earlier ones are available to later ones
708
+ // Start with linked fragment services, then add explicitly defined private services
709
+ const privateServices = { ...linkedFragmentServices } as TPrivateServices;
710
+ if (definition.privateServices) {
711
+ for (const [serviceName, factory] of Object.entries(definition.privateServices)) {
712
+ const serviceFactory = factory as (context: {
713
+ config: TConfig;
714
+ options: TOptions;
715
+ deps: TDeps;
716
+ serviceDeps: TServiceDependencies;
717
+ privateServices: TPrivateServices;
718
+ defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;
719
+ }) => unknown;
720
+ (privateServices as Record<string, unknown>)[serviceName] = serviceFactory({
721
+ config,
722
+ options,
723
+ deps,
724
+ serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,
725
+ privateServices, // Pass the current state of private services (earlier ones are available)
726
+ defineService,
727
+ });
728
+ }
729
+ }
730
+
731
+ // 5. Call baseServices callback (with access to private services including linked fragment services)
732
+ const baseServices =
733
+ definition.baseServices?.({
734
+ config,
735
+ options,
736
+ deps,
737
+ serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,
738
+ privateServices,
739
+ defineService,
740
+ }) ?? ({} as TBaseServices);
741
+
742
+ // 6. Call namedServices factories (with access to private services including linked fragment services)
743
+ const namedServices = {} as TServices;
744
+ if (definition.namedServices) {
745
+ for (const [serviceName, factory] of Object.entries(definition.namedServices)) {
746
+ const serviceFactory = factory as (context: {
747
+ config: TConfig;
748
+ options: TOptions;
749
+ deps: TDeps;
750
+ serviceDeps: TServiceDependencies;
751
+ privateServices: TPrivateServices;
752
+ defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;
753
+ }) => unknown;
754
+ (namedServices as Record<string, unknown>)[serviceName] = serviceFactory({
755
+ config,
756
+ options,
757
+ deps,
758
+ serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,
759
+ privateServices,
760
+ defineService,
761
+ });
762
+ }
763
+ }
764
+
765
+ // 7. Merge public services (NOT including private services)
766
+ const services = {
767
+ ...baseServices,
768
+ ...namedServices,
769
+ };
770
+
771
+ // 8. Create request context storage and both service & handler contexts
772
+ // Use external storage if provided, otherwise create new storage
773
+ const storage = definition.getExternalStorage
774
+ ? definition.getExternalStorage({ config, options, deps })
775
+ : new RequestContextStorage<TRequestStorage>();
776
+
777
+ // Create both contexts using createThisContext (returns { serviceContext, handlerContext })
778
+ const contexts = definition.createThisContext?.({
779
+ config,
780
+ options,
781
+ deps,
782
+ storage,
783
+ });
784
+
785
+ const serviceContext = contexts?.serviceContext;
786
+ const handlerContext = contexts?.handlerContext;
787
+
788
+ // 9. Bind services to serviceContext (restricted)
789
+ // Services get the restricted context (for database fragments, this excludes execute methods)
790
+ const boundServices = serviceContext ? bindServicesToContext(services, serviceContext) : services;
791
+
792
+ // 10. Resolve routes with bound services
793
+ const context = {
794
+ config,
795
+ deps,
796
+ services: boundServices,
797
+ serviceDeps: serviceImplementations ?? ({} as TServiceDependencies),
798
+ };
799
+ const routes = resolveRouteFactories(context, routesOrFactories);
800
+
801
+ // 11. Calculate mount route
802
+ const mountRoute = getMountRoute({
803
+ name: definition.name,
804
+ mountRoute: options.mountRoute,
805
+ });
806
+
807
+ // 12. Wrap createRequestStorage to capture context
808
+ const createRequestStorageWithContext = definition.createRequestStorage
809
+ ? () => definition.createRequestStorage!({ config, options, deps })
810
+ : undefined;
811
+
812
+ // 13. Create and return fragment instance
813
+ // Pass bound services so they have access to serviceContext via 'this'
814
+ // Handlers get handlerContext which may have more capabilities than serviceContext
815
+ return new FragnoInstantiatedFragment({
816
+ name: definition.name,
817
+ routes,
818
+ deps,
819
+ services: boundServices as BoundServices<TBaseServices & TServices>,
820
+ mountRoute,
821
+ serviceThisContext: serviceContext,
822
+ handlerThisContext: handlerContext,
823
+ storage,
824
+ createRequestStorage: createRequestStorageWithContext,
825
+ options,
826
+ linkedFragments: linkedFragmentInstances,
827
+ });
828
+ }
829
+
830
+ /**
831
+ * Fluent builder for instantiating fragments.
832
+ * Provides a type-safe API for configuring and building fragment instances.
833
+ */
834
+ export class FragmentInstantiationBuilder<
835
+ TConfig,
836
+ TOptions extends FragnoPublicConfig,
837
+ TDeps,
838
+ TBaseServices extends Record<string, unknown>,
839
+ TServices extends Record<string, unknown>,
840
+ TServiceDependencies,
841
+ TPrivateServices extends Record<string, unknown>,
842
+ TServiceThisContext extends RequestThisContext,
843
+ THandlerThisContext extends RequestThisContext,
844
+ TRequestStorage,
845
+ TRoutesOrFactories extends readonly AnyRouteOrFactory[],
846
+ TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,
847
+ > {
848
+ #definition: FragmentDefinition<
849
+ TConfig,
850
+ TOptions,
851
+ TDeps,
852
+ TBaseServices,
853
+ TServices,
854
+ TServiceDependencies,
855
+ TPrivateServices,
856
+ TServiceThisContext,
857
+ THandlerThisContext,
858
+ TRequestStorage,
859
+ TLinkedFragments
860
+ >;
861
+ #config?: TConfig;
862
+ #routes?: TRoutesOrFactories;
863
+ #options?: TOptions;
864
+ #services?: TServiceDependencies;
865
+
866
+ constructor(
867
+ definition: FragmentDefinition<
868
+ TConfig,
869
+ TOptions,
870
+ TDeps,
871
+ TBaseServices,
872
+ TServices,
873
+ TServiceDependencies,
874
+ TPrivateServices,
875
+ TServiceThisContext,
876
+ THandlerThisContext,
877
+ TRequestStorage,
878
+ TLinkedFragments
879
+ >,
880
+ routes?: TRoutesOrFactories,
881
+ ) {
882
+ this.#definition = definition;
883
+ this.#routes = routes;
884
+ }
885
+
886
+ /**
887
+ * Get the fragment definition
888
+ */
889
+ get definition(): FragmentDefinition<
890
+ TConfig,
891
+ TOptions,
892
+ TDeps,
893
+ TBaseServices,
894
+ TServices,
895
+ TServiceDependencies,
896
+ TPrivateServices,
897
+ TServiceThisContext,
898
+ THandlerThisContext,
899
+ TRequestStorage,
900
+ TLinkedFragments
901
+ > {
902
+ return this.#definition;
903
+ }
904
+
905
+ /**
906
+ * Get the configured routes
907
+ */
908
+ get routes(): TRoutesOrFactories {
909
+ return this.#routes ?? ([] as const as unknown as TRoutesOrFactories);
910
+ }
911
+
912
+ /**
913
+ * Get the configuration
914
+ */
915
+ get config(): TConfig | undefined {
916
+ return this.#config;
917
+ }
918
+
919
+ /**
920
+ * Get the options
921
+ */
922
+ get options(): TOptions | undefined {
923
+ return this.#options;
924
+ }
925
+
926
+ /**
927
+ * Set the configuration for the fragment
928
+ */
929
+ withConfig(config: TConfig): this {
930
+ this.#config = config;
931
+ return this;
932
+ }
933
+
934
+ /**
935
+ * Set the routes for the fragment
936
+ */
937
+ withRoutes<const TNewRoutes extends readonly AnyRouteOrFactory[]>(
938
+ routes: TNewRoutes,
939
+ ): FragmentInstantiationBuilder<
940
+ TConfig,
941
+ TOptions,
942
+ TDeps,
943
+ TBaseServices,
944
+ TServices,
945
+ TServiceDependencies,
946
+ TPrivateServices,
947
+ TServiceThisContext,
948
+ THandlerThisContext,
949
+ TRequestStorage,
950
+ TNewRoutes,
951
+ TLinkedFragments
952
+ > {
953
+ const newBuilder = new FragmentInstantiationBuilder(this.#definition, routes);
954
+ // Preserve config, options, and services from the current instance
955
+ newBuilder.#config = this.#config;
956
+ newBuilder.#options = this.#options;
957
+ newBuilder.#services = this.#services;
958
+ return newBuilder;
959
+ }
960
+
961
+ /**
962
+ * Set the options for the fragment (e.g., mountRoute, databaseAdapter)
963
+ */
964
+ withOptions(options: TOptions): this {
965
+ this.#options = options;
966
+ return this;
967
+ }
968
+
969
+ /**
970
+ * Provide implementations for services that this fragment uses
971
+ */
972
+ withServices(services: TServiceDependencies): this {
973
+ this.#services = services;
974
+ return this;
975
+ }
976
+
977
+ /**
978
+ * Build and return the instantiated fragment
979
+ */
980
+ build(): FragnoInstantiatedFragment<
981
+ FlattenRouteFactories<TRoutesOrFactories>,
982
+ TDeps,
983
+ BoundServices<TBaseServices & TServices>,
984
+ TServiceThisContext,
985
+ THandlerThisContext,
986
+ TRequestStorage,
987
+ TOptions,
988
+ TLinkedFragments
989
+ > {
990
+ return instantiateFragment(
991
+ this.#definition,
992
+ this.#config ?? ({} as TConfig),
993
+ this.#routes ?? ([] as const as unknown as TRoutesOrFactories),
994
+ this.#options ?? ({} as TOptions),
995
+ this.#services,
996
+ );
997
+ }
998
+ }
999
+
1000
+ /**
1001
+ * Create a fluent builder for instantiating a fragment.
1002
+ *
1003
+ * @example
1004
+ * ```ts
1005
+ * const fragment = instantiate(myFragmentDefinition)
1006
+ * .withConfig({ apiKey: "key" })
1007
+ * .withRoutes([route1, route2])
1008
+ * .withOptions({ mountRoute: "/api" })
1009
+ * .build();
1010
+ * ```
1011
+ */
1012
+ export function instantiate<
1013
+ TConfig,
1014
+ TOptions extends FragnoPublicConfig,
1015
+ TDeps,
1016
+ TBaseServices extends Record<string, unknown>,
1017
+ TServices extends Record<string, unknown>,
1018
+ TServiceDependencies,
1019
+ TPrivateServices extends Record<string, unknown>,
1020
+ TServiceThisContext extends RequestThisContext,
1021
+ THandlerThisContext extends RequestThisContext,
1022
+ TRequestStorage,
1023
+ TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,
1024
+ >(
1025
+ definition: FragmentDefinition<
1026
+ TConfig,
1027
+ TOptions,
1028
+ TDeps,
1029
+ TBaseServices,
1030
+ TServices,
1031
+ TServiceDependencies,
1032
+ TPrivateServices,
1033
+ TServiceThisContext,
1034
+ THandlerThisContext,
1035
+ TRequestStorage,
1036
+ TLinkedFragments
1037
+ >,
1038
+ ): FragmentInstantiationBuilder<
1039
+ TConfig,
1040
+ TOptions,
1041
+ TDeps,
1042
+ TBaseServices,
1043
+ TServices,
1044
+ TServiceDependencies,
1045
+ TPrivateServices,
1046
+ TServiceThisContext,
1047
+ THandlerThisContext,
1048
+ TRequestStorage,
1049
+ readonly [],
1050
+ TLinkedFragments
1051
+ > {
1052
+ return new FragmentInstantiationBuilder(definition);
1053
+ }