@fragno-dev/core 0.1.10 → 0.2.0
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 +139 -131
- package/CHANGELOG.md +63 -0
- package/dist/api/api.d.ts +23 -5
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js.map +1 -1
- package/dist/api/fragment-definition-builder.d.ts +17 -7
- package/dist/api/fragment-definition-builder.d.ts.map +1 -1
- package/dist/api/fragment-definition-builder.js +3 -2
- package/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/api/fragment-instantiator.d.ts +129 -32
- package/dist/api/fragment-instantiator.d.ts.map +1 -1
- package/dist/api/fragment-instantiator.js +232 -50
- package/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/api/request-input-context.d.ts +57 -1
- package/dist/api/request-input-context.d.ts.map +1 -1
- package/dist/api/request-input-context.js +67 -0
- package/dist/api/request-input-context.js.map +1 -1
- package/dist/api/request-middleware.d.ts +1 -1
- package/dist/api/request-middleware.d.ts.map +1 -1
- package/dist/api/request-middleware.js.map +1 -1
- package/dist/api/route.d.ts +7 -7
- package/dist/api/route.d.ts.map +1 -1
- package/dist/api/route.js.map +1 -1
- package/dist/client/client.d.ts +4 -3
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +103 -7
- package/dist/client/client.js.map +1 -1
- package/dist/client/vue.d.ts +7 -3
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +16 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/internal/trace-context.d.ts +23 -0
- package/dist/internal/trace-context.d.ts.map +1 -0
- package/dist/internal/trace-context.js +14 -0
- package/dist/internal/trace-context.js.map +1 -0
- package/dist/mod-client.d.ts +5 -27
- package/dist/mod-client.d.ts.map +1 -1
- package/dist/mod-client.js +50 -13
- package/dist/mod-client.js.map +1 -1
- package/dist/mod.d.ts +4 -3
- package/dist/mod.js +2 -1
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +33 -0
- package/dist/runtime.js.map +1 -0
- package/dist/test/test.d.ts +2 -2
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js.map +1 -1
- package/package.json +31 -18
- package/src/api/api.ts +24 -0
- package/src/api/fragment-definition-builder.ts +36 -17
- package/src/api/fragment-instantiator.test.ts +429 -1
- package/src/api/fragment-instantiator.ts +572 -58
- package/src/api/internal/path-runtime.test.ts +7 -0
- package/src/api/request-input-context.test.ts +152 -0
- package/src/api/request-input-context.ts +85 -0
- package/src/api/request-middleware.test.ts +47 -1
- package/src/api/request-middleware.ts +1 -1
- package/src/api/route.ts +7 -2
- package/src/client/client.test.ts +195 -0
- package/src/client/client.ts +185 -10
- package/src/client/vue.test.ts +253 -3
- package/src/client/vue.ts +44 -1
- package/src/internal/trace-context.ts +35 -0
- package/src/mod-client.ts +89 -9
- package/src/mod.ts +7 -1
- package/src/runtime.ts +48 -0
- package/src/test/test.ts +13 -4
- package/tsdown.config.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment-instantiator.js","names":["#name","#routes","#deps","#services","#mountRoute","#serviceThisContext","#handlerThisContext","#contextStorage","#createRequestStorage","#options","#linkedFragments","#router","#middlewareHandler","#withRequestStorage","requestBody: RequestBodyType","rawBody: string | undefined","#executeMiddleware","#executeHandler","linkedFragmentServices: Record<string, unknown>","services","#definition","#config"],"sources":["../../src/api/fragment-instantiator.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { type FragnoRouteConfig, type HTTPMethod, type RequestThisContext } from \"./api\";\nimport { FragnoApiError } from \"./error\";\nimport { getMountRoute } from \"./internal/route\";\nimport { addRoute, createRouter, findRoute } from \"rou3\";\nimport { RequestInputContext, type RequestBodyType } from \"./request-input-context\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport { RequestOutputContext } from \"./request-output-context\";\nimport {\n type AnyFragnoRouteConfig,\n type AnyRouteOrFactory,\n type FlattenRouteFactories,\n resolveRouteFactories,\n} from \"./route\";\nimport {\n RequestMiddlewareInputContext,\n RequestMiddlewareOutputContext,\n type FragnoMiddlewareCallback,\n} from \"./request-middleware\";\nimport { MutableRequestState } from \"./mutable-request-state\";\nimport type { RouteHandlerInputOptions } from \"./route-handler-input-options\";\nimport type { ExtractRouteByPath, ExtractRoutePath } from \"../client/client\";\nimport { type FragnoResponse, parseFragnoResponse } from \"./fragno-response\";\nimport type { InferOrUnknown } from \"../util/types-util\";\nimport type { FragmentDefinition } from \"./fragment-definition-builder\";\nimport type { FragnoPublicConfig } from \"./shared-types\";\nimport { RequestContextStorage } from \"./request-context-storage\";\nimport { bindServicesToContext, type BoundServices } from \"./bind-services\";\nimport { instantiatedFragmentFakeSymbol } from \"../internal/symbols\";\n\n// Re-export types needed by consumers\nexport type { BoundServices };\n\ntype AstroHandlers = {\n ALL: (req: Request) => Promise<Response>;\n};\n\ntype ReactRouterHandlers = {\n loader: (args: { request: Request }) => Promise<Response>;\n action: (args: { request: Request }) => Promise<Response>;\n};\n\ntype SolidStartHandlers = {\n GET: (args: { request: Request }) => Promise<Response>;\n POST: (args: { request: Request }) => Promise<Response>;\n PUT: (args: { request: Request }) => Promise<Response>;\n DELETE: (args: { request: Request }) => Promise<Response>;\n PATCH: (args: { request: Request }) => Promise<Response>;\n HEAD: (args: { request: Request }) => Promise<Response>;\n OPTIONS: (args: { request: Request }) => Promise<Response>;\n};\n\ntype TanStackStartHandlers = SolidStartHandlers;\n\ntype StandardHandlers = {\n GET: (req: Request) => Promise<Response>;\n POST: (req: Request) => Promise<Response>;\n PUT: (req: Request) => Promise<Response>;\n DELETE: (req: Request) => Promise<Response>;\n PATCH: (req: Request) => Promise<Response>;\n HEAD: (req: Request) => Promise<Response>;\n OPTIONS: (req: Request) => Promise<Response>;\n};\n\ntype HandlersByFramework = {\n astro: AstroHandlers;\n \"react-router\": ReactRouterHandlers;\n \"next-js\": StandardHandlers;\n \"svelte-kit\": StandardHandlers;\n \"solid-start\": SolidStartHandlers;\n \"tanstack-start\": TanStackStartHandlers;\n};\n\ntype FullstackFrameworks = keyof HandlersByFramework;\n\n// Safe: This is a catch-all type for any instantiated fragment\ntype AnyFragnoInstantiatedFragment = FragnoInstantiatedFragment<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n>;\n\nexport type { AnyFragnoInstantiatedFragment };\n\nexport interface FragnoFragmentSharedConfig<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> {\n name: string;\n routes: TRoutes;\n}\n\n/**\n * Instantiated fragment class with encapsulated state.\n * Provides the same public API as the old FragnoInstantiatedFragment but with better encapsulation.\n */\nexport class FragnoInstantiatedFragment<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TDeps,\n TServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage = {},\n TOptions extends FragnoPublicConfig = FragnoPublicConfig,\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},\n> {\n readonly [instantiatedFragmentFakeSymbol] = instantiatedFragmentFakeSymbol;\n\n // Private fields\n #name: string;\n #routes: TRoutes;\n #deps: TDeps;\n #services: TServices;\n #mountRoute: string;\n #router: ReturnType<typeof createRouter>;\n #middlewareHandler?: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>;\n #serviceThisContext?: TServiceThisContext; // Context for services (may have restricted capabilities)\n #handlerThisContext?: THandlerThisContext; // Context for handlers (full capabilities)\n #contextStorage: RequestContextStorage<TRequestStorage>;\n #createRequestStorage?: () => TRequestStorage;\n #options: TOptions;\n #linkedFragments: TLinkedFragments;\n\n constructor(params: {\n name: string;\n routes: TRoutes;\n deps: TDeps;\n services: TServices;\n mountRoute: string;\n serviceThisContext?: TServiceThisContext;\n handlerThisContext?: THandlerThisContext;\n storage: RequestContextStorage<TRequestStorage>;\n createRequestStorage?: () => TRequestStorage;\n options: TOptions;\n linkedFragments?: TLinkedFragments;\n }) {\n this.#name = params.name;\n this.#routes = params.routes;\n this.#deps = params.deps;\n this.#services = params.services;\n this.#mountRoute = params.mountRoute;\n this.#serviceThisContext = params.serviceThisContext;\n this.#handlerThisContext = params.handlerThisContext;\n this.#contextStorage = params.storage;\n this.#createRequestStorage = params.createRequestStorage;\n this.#options = params.options;\n this.#linkedFragments = params.linkedFragments ?? ({} as TLinkedFragments);\n\n // Build router\n this.#router =\n createRouter<\n FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string,\n RequestThisContext\n >\n >();\n\n for (const routeConfig of this.#routes) {\n addRoute(this.#router, routeConfig.method.toUpperCase(), routeConfig.path, routeConfig);\n }\n\n // Bind handler method to maintain 'this' context\n this.handler = this.handler.bind(this);\n }\n\n // Public getters\n get name(): string {\n return this.#name;\n }\n\n get routes(): TRoutes {\n return this.#routes;\n }\n\n get services(): TServices {\n return this.#services;\n }\n\n get mountRoute(): string {\n return this.#mountRoute;\n }\n\n /**\n * @internal\n */\n get $internal() {\n return {\n deps: this.#deps,\n options: this.#options,\n linkedFragments: this.#linkedFragments,\n };\n }\n\n /**\n * Add middleware to this fragment.\n * Middleware can inspect and modify requests before they reach handlers.\n */\n withMiddleware(handler: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>): this {\n if (this.#middlewareHandler) {\n throw new Error(\"Middleware already set\");\n }\n this.#middlewareHandler = handler;\n return this;\n }\n\n /**\n * Run a callback within the fragment's request context with initialized storage.\n * This is a shared helper used by inContext(), handler(), and callRouteRaw().\n * @private\n */\n #withRequestStorage<T>(callback: () => T): T;\n #withRequestStorage<T>(callback: () => Promise<T>): Promise<T>;\n #withRequestStorage<T>(callback: () => T | Promise<T>): T | Promise<T> {\n if (!this.#serviceThisContext && !this.#handlerThisContext) {\n // No request context configured - just run callback directly\n return callback();\n }\n\n // Initialize storage with fresh data for this request\n const storageData = this.#createRequestStorage\n ? this.#createRequestStorage()\n : ({} as TRequestStorage);\n return this.#contextStorage.run(storageData, callback);\n }\n\n /**\n * Execute a callback within a request context.\n * Establishes an async context for the duration of the callback, allowing services\n * to access the `this` context. The callback's `this` will be bound to the fragment's\n * handler context (with full capabilities including execute methods).\n * Useful for calling services outside of route handlers (e.g., in tests, background jobs).\n *\n * @param callback - The callback to run within the context\n *\n * @example\n * ```typescript\n * const result = await fragment.inContext(function () {\n * // `this` is bound to the handler context (can call execute methods)\n * await this.getUnitOfWork().executeRetrieve();\n * return this.someContextMethod();\n * });\n * ```\n */\n inContext<T>(callback: (this: THandlerThisContext) => T): T;\n inContext<T>(callback: (this: THandlerThisContext) => Promise<T>): Promise<T>;\n inContext<T>(callback: (this: THandlerThisContext) => T | Promise<T>): T | Promise<T> {\n // Always use handler context for inContext - it has full capabilities\n if (this.#handlerThisContext) {\n const boundCallback = callback.bind(this.#handlerThisContext);\n return this.#withRequestStorage(boundCallback);\n }\n return this.#withRequestStorage(callback);\n }\n\n /**\n * Get framework-specific handlers for this fragment.\n * Use this to integrate the fragment with different fullstack frameworks.\n */\n handlersFor<T extends FullstackFrameworks>(framework: T): HandlersByFramework[T] {\n const handler = this.handler.bind(this);\n\n // LLMs hallucinate these values sometimes, solution isn't obvious so we throw this error\n // @ts-expect-error TS2367\n if (framework === \"h3\" || framework === \"nuxt\") {\n throw new Error(`To get handlers for h3, use the 'fromWebHandler' utility function:\n import { fromWebHandler } from \"h3\";\n export default fromWebHandler(myFragment().handler);`);\n }\n\n const allHandlers = {\n astro: { ALL: handler },\n \"react-router\": {\n loader: ({ request }: { request: Request }) => handler(request),\n action: ({ request }: { request: Request }) => handler(request),\n },\n \"next-js\": {\n GET: handler,\n POST: handler,\n PUT: handler,\n DELETE: handler,\n PATCH: handler,\n HEAD: handler,\n OPTIONS: handler,\n },\n \"svelte-kit\": {\n GET: handler,\n POST: handler,\n PUT: handler,\n DELETE: handler,\n PATCH: handler,\n HEAD: handler,\n OPTIONS: handler,\n },\n \"solid-start\": {\n GET: ({ request }: { request: Request }) => handler(request),\n POST: ({ request }: { request: Request }) => handler(request),\n PUT: ({ request }: { request: Request }) => handler(request),\n DELETE: ({ request }: { request: Request }) => handler(request),\n PATCH: ({ request }: { request: Request }) => handler(request),\n HEAD: ({ request }: { request: Request }) => handler(request),\n OPTIONS: ({ request }: { request: Request }) => handler(request),\n },\n \"tanstack-start\": {\n GET: ({ request }: { request: Request }) => handler(request),\n POST: ({ request }: { request: Request }) => handler(request),\n PUT: ({ request }: { request: Request }) => handler(request),\n DELETE: ({ request }: { request: Request }) => handler(request),\n PATCH: ({ request }: { request: Request }) => handler(request),\n HEAD: ({ request }: { request: Request }) => handler(request),\n OPTIONS: ({ request }: { request: Request }) => handler(request),\n },\n } satisfies HandlersByFramework;\n\n return allHandlers[framework];\n }\n\n /**\n * Main request handler for this fragment.\n * Handles routing, middleware, and error handling.\n */\n async handler(req: Request): Promise<Response> {\n const url = new URL(req.url);\n const pathname = url.pathname;\n\n // Match route\n const matchRoute = pathname.startsWith(this.#mountRoute)\n ? pathname.slice(this.#mountRoute.length)\n : null;\n\n if (matchRoute === null) {\n return Response.json(\n {\n error:\n `Fragno: Route for '${this.#name}' not found. Is the fragment mounted on the right route? ` +\n `Expecting: '${this.#mountRoute}'.`,\n code: \"ROUTE_NOT_FOUND\",\n },\n { status: 404 },\n );\n }\n\n const route = findRoute(this.#router, req.method, matchRoute);\n\n if (!route) {\n return Response.json(\n { error: `Fragno: Route for '${this.#name}' not found`, code: \"ROUTE_NOT_FOUND\" },\n { status: 404 },\n );\n }\n\n // Parse request body\n let requestBody: RequestBodyType = undefined;\n let rawBody: string | undefined = undefined;\n\n if (req.body instanceof ReadableStream) {\n // Clone request to make sure we don't consume body stream\n const clonedReq = req.clone();\n\n // Get raw text\n rawBody = await clonedReq.text();\n\n // Parse JSON if body is not empty\n if (rawBody) {\n try {\n requestBody = JSON.parse(rawBody);\n } catch {\n // If JSON parsing fails, keep body as undefined\n // This handles cases where body is not JSON\n requestBody = undefined;\n }\n }\n }\n\n const requestState = new MutableRequestState({\n pathParams: route.params ?? {},\n searchParams: url.searchParams,\n body: requestBody,\n headers: new Headers(req.headers),\n });\n\n // Execute middleware and handler\n const executeRequest = async (): Promise<Response> => {\n // Middleware execution (if present)\n if (this.#middlewareHandler) {\n const middlewareResult = await this.#executeMiddleware(req, route, requestState);\n if (middlewareResult !== undefined) {\n return middlewareResult;\n }\n }\n\n // Handler execution\n return this.#executeHandler(req, route, requestState, rawBody);\n };\n\n // Wrap with request storage context if provided\n return this.#withRequestStorage(executeRequest);\n }\n\n /**\n * Call a route directly with typed inputs and outputs.\n * Useful for testing and server-side route calls.\n */\n async callRoute<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(\n method: TMethod,\n path: TPath,\n inputOptions?: RouteHandlerInputOptions<\n TPath,\n ExtractRouteByPath<TRoutes, TPath, TMethod>[\"inputSchema\"]\n >,\n ): Promise<\n FragnoResponse<\n InferOrUnknown<NonNullable<ExtractRouteByPath<TRoutes, TPath, TMethod>[\"outputSchema\"]>>\n >\n > {\n const response = await this.callRouteRaw(method, path, inputOptions);\n return parseFragnoResponse(response);\n }\n\n /**\n * Call a route directly and get the raw Response object.\n * Useful for testing and server-side route calls.\n */\n async callRouteRaw<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(\n method: TMethod,\n path: TPath,\n inputOptions?: RouteHandlerInputOptions<\n TPath,\n ExtractRouteByPath<TRoutes, TPath, TMethod>[\"inputSchema\"]\n >,\n ): Promise<Response> {\n // Find route in this.#routes\n const route = this.#routes.find((r) => r.method === method && r.path === path);\n\n if (!route) {\n return Response.json(\n {\n error: `Route ${method} ${path} not found`,\n code: \"ROUTE_NOT_FOUND\",\n },\n { status: 404 },\n );\n }\n\n const { pathParams = {}, body, query, headers } = inputOptions || {};\n\n // Convert query to URLSearchParams if needed\n const searchParams =\n query instanceof URLSearchParams\n ? query\n : query\n ? new URLSearchParams(query)\n : new URLSearchParams();\n\n // Convert headers to Headers if needed\n const requestHeaders =\n headers instanceof Headers ? headers : headers ? new Headers(headers) : new Headers();\n\n // Construct RequestInputContext\n const inputContext = new RequestInputContext({\n path: route.path,\n method: route.method,\n pathParams: pathParams as ExtractPathParams<typeof route.path>,\n searchParams,\n headers: requestHeaders,\n parsedBody: body,\n inputSchema: route.inputSchema,\n shouldValidateInput: true, // Enable validation for production use\n });\n\n // Construct RequestOutputContext\n const outputContext = new RequestOutputContext(route.outputSchema);\n\n // Execute handler\n const executeHandler = async (): Promise<Response> => {\n try {\n // Use handler context (full capabilities)\n const thisContext = this.#handlerThisContext ?? ({} as RequestThisContext);\n return await route.handler.call(thisContext, inputContext, outputContext);\n } catch (error) {\n console.error(\"Error in callRoute handler\", error);\n\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n };\n\n // Wrap with request storage context if provided\n return this.#withRequestStorage(executeHandler);\n }\n\n /**\n * Execute middleware for a request.\n * Returns undefined if middleware allows the request to continue to the handler.\n */\n async #executeMiddleware(\n req: Request,\n route: ReturnType<typeof findRoute>,\n requestState: MutableRequestState,\n ): Promise<Response | undefined> {\n if (!this.#middlewareHandler || !route) {\n return undefined;\n }\n\n const { path } = route.data as AnyFragnoRouteConfig;\n\n const middlewareInputContext = new RequestMiddlewareInputContext(this.#routes, {\n method: req.method as HTTPMethod,\n path,\n request: req,\n state: requestState,\n });\n\n const middlewareOutputContext = new RequestMiddlewareOutputContext(this.#deps, this.#services);\n\n try {\n const middlewareResult = await this.#middlewareHandler(\n middlewareInputContext,\n middlewareOutputContext,\n );\n if (middlewareResult !== undefined) {\n return middlewareResult;\n }\n } catch (error) {\n console.error(\"Error in middleware\", error);\n\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n\n return undefined;\n }\n\n /**\n * Execute a route handler with proper error handling.\n */\n async #executeHandler(\n req: Request,\n route: ReturnType<typeof findRoute>,\n requestState: MutableRequestState,\n rawBody?: string,\n ): Promise<Response> {\n if (!route) {\n return Response.json({ error: \"Route not found\", code: \"ROUTE_NOT_FOUND\" }, { status: 404 });\n }\n\n const { handler, inputSchema, outputSchema, path } = route.data as AnyFragnoRouteConfig;\n\n const inputContext = await RequestInputContext.fromRequest({\n request: req,\n method: req.method,\n path,\n pathParams: (route.params ?? {}) as ExtractPathParams<typeof path>,\n inputSchema,\n state: requestState,\n rawBody,\n });\n\n const outputContext = new RequestOutputContext(outputSchema);\n\n try {\n // Note: We don't call .run() here because the storage should already be initialized\n // by the handler() method or inContext() method before this point\n // Use handler context (full capabilities)\n const contextForHandler = this.#handlerThisContext ?? ({} as RequestThisContext);\n const result = await handler.call(contextForHandler, inputContext, outputContext);\n return result;\n } catch (error) {\n console.error(\"Error in handler\", error);\n\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n }\n}\n\n/**\n * Core instantiation function that creates a fragment instance from a definition.\n * This function validates dependencies, calls all callbacks, and wires everything together.\n */\nexport function instantiateFragment<\n const TConfig,\n const TOptions extends FragnoPublicConfig,\n const TDeps,\n const TBaseServices extends Record<string, unknown>,\n const TServices extends Record<string, unknown>,\n const TServiceDependencies,\n const TPrivateServices extends Record<string, unknown>,\n const TServiceThisContext extends RequestThisContext,\n const THandlerThisContext extends RequestThisContext,\n const TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n const TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n config: TConfig,\n routesOrFactories: TRoutesOrFactories,\n options: TOptions,\n serviceImplementations?: TServiceDependencies,\n): FragnoInstantiatedFragment<\n FlattenRouteFactories<TRoutesOrFactories>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n> {\n // 1. Validate service dependencies\n const serviceDependencies = definition.serviceDependencies;\n if (serviceDependencies) {\n for (const [serviceName, meta] of Object.entries(serviceDependencies)) {\n const metadata = meta as { name: string; required: boolean };\n const implementation = serviceImplementations?.[serviceName as keyof TServiceDependencies];\n if (metadata.required && !implementation) {\n throw new Error(\n `Fragment '${definition.name}' requires service '${metadata.name}' but it was not provided`,\n );\n }\n }\n }\n\n // 2. Call dependencies callback\n const deps = definition.dependencies?.({ config, options }) ?? ({} as TDeps);\n\n // 3. Instantiate linked fragments FIRST (before any services)\n // Their services will be merged into private services\n const linkedFragmentInstances = {} as TLinkedFragments;\n const linkedFragmentServices: Record<string, unknown> = {};\n\n if (definition.linkedFragments) {\n for (const [name, callback] of Object.entries(definition.linkedFragments)) {\n const linkedFragment = callback({\n config,\n options,\n serviceDependencies: serviceImplementations,\n });\n (linkedFragmentInstances as Record<string, AnyFragnoInstantiatedFragment>)[name] =\n linkedFragment;\n\n // Merge all services from linked fragment into private services directly by their service name\n const services = linkedFragment.services as Record<string, unknown>;\n for (const [serviceName, service] of Object.entries(services)) {\n linkedFragmentServices[serviceName] = service;\n }\n }\n }\n\n // Identity function for service definition (used to set 'this' context)\n const defineService = <T>(services: T & ThisType<TServiceThisContext>): T => services;\n\n // 4. Call privateServices factories\n // Private services are instantiated in order, so earlier ones are available to later ones\n // Start with linked fragment services, then add explicitly defined private services\n const privateServices = { ...linkedFragmentServices } as TPrivateServices;\n if (definition.privateServices) {\n for (const [serviceName, factory] of Object.entries(definition.privateServices)) {\n const serviceFactory = factory as (context: {\n config: TConfig;\n options: TOptions;\n deps: TDeps;\n serviceDeps: TServiceDependencies;\n privateServices: TPrivateServices;\n defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;\n }) => unknown;\n (privateServices as Record<string, unknown>)[serviceName] = serviceFactory({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices, // Pass the current state of private services (earlier ones are available)\n defineService,\n });\n }\n }\n\n // 5. Call baseServices callback (with access to private services including linked fragment services)\n const baseServices =\n definition.baseServices?.({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices,\n defineService,\n }) ?? ({} as TBaseServices);\n\n // 6. Call namedServices factories (with access to private services including linked fragment services)\n const namedServices = {} as TServices;\n if (definition.namedServices) {\n for (const [serviceName, factory] of Object.entries(definition.namedServices)) {\n const serviceFactory = factory as (context: {\n config: TConfig;\n options: TOptions;\n deps: TDeps;\n serviceDeps: TServiceDependencies;\n privateServices: TPrivateServices;\n defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;\n }) => unknown;\n (namedServices as Record<string, unknown>)[serviceName] = serviceFactory({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices,\n defineService,\n });\n }\n }\n\n // 7. Merge public services (NOT including private services)\n const services = {\n ...baseServices,\n ...namedServices,\n };\n\n // 8. Create request context storage and both service & handler contexts\n // Use external storage if provided, otherwise create new storage\n const storage = definition.getExternalStorage\n ? definition.getExternalStorage({ config, options, deps })\n : new RequestContextStorage<TRequestStorage>();\n\n // Create both contexts using createThisContext (returns { serviceContext, handlerContext })\n const contexts = definition.createThisContext?.({\n config,\n options,\n deps,\n storage,\n });\n\n const serviceContext = contexts?.serviceContext;\n const handlerContext = contexts?.handlerContext;\n\n // 9. Bind services to serviceContext (restricted)\n // Services get the restricted context (for database fragments, this excludes execute methods)\n const boundServices = serviceContext ? bindServicesToContext(services, serviceContext) : services;\n\n // 10. Resolve routes with bound services\n const context = {\n config,\n deps,\n services: boundServices,\n serviceDeps: serviceImplementations ?? ({} as TServiceDependencies),\n };\n const routes = resolveRouteFactories(context, routesOrFactories);\n\n // 11. Calculate mount route\n const mountRoute = getMountRoute({\n name: definition.name,\n mountRoute: options.mountRoute,\n });\n\n // 12. Wrap createRequestStorage to capture context\n const createRequestStorageWithContext = definition.createRequestStorage\n ? () => definition.createRequestStorage!({ config, options, deps })\n : undefined;\n\n // 13. Create and return fragment instance\n // Pass bound services so they have access to serviceContext via 'this'\n // Handlers get handlerContext which may have more capabilities than serviceContext\n return new FragnoInstantiatedFragment({\n name: definition.name,\n routes,\n deps,\n services: boundServices as BoundServices<TBaseServices & TServices>,\n mountRoute,\n serviceThisContext: serviceContext,\n handlerThisContext: handlerContext,\n storage,\n createRequestStorage: createRequestStorageWithContext,\n options,\n linkedFragments: linkedFragmentInstances,\n });\n}\n\n/**\n * Fluent builder for instantiating fragments.\n * Provides a type-safe API for configuring and building fragment instances.\n */\nexport class FragmentInstantiationBuilder<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n> {\n #definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >;\n #config?: TConfig;\n #routes?: TRoutesOrFactories;\n #options?: TOptions;\n #services?: TServiceDependencies;\n\n constructor(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n routes?: TRoutesOrFactories,\n ) {\n this.#definition = definition;\n this.#routes = routes;\n }\n\n /**\n * Get the fragment definition\n */\n get definition(): FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n > {\n return this.#definition;\n }\n\n /**\n * Get the configured routes\n */\n get routes(): TRoutesOrFactories {\n return this.#routes ?? ([] as const as unknown as TRoutesOrFactories);\n }\n\n /**\n * Get the configuration\n */\n get config(): TConfig | undefined {\n return this.#config;\n }\n\n /**\n * Get the options\n */\n get options(): TOptions | undefined {\n return this.#options;\n }\n\n /**\n * Set the configuration for the fragment\n */\n withConfig(config: TConfig): this {\n this.#config = config;\n return this;\n }\n\n /**\n * Set the routes for the fragment\n */\n withRoutes<const TNewRoutes extends readonly AnyRouteOrFactory[]>(\n routes: TNewRoutes,\n ): FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TNewRoutes,\n TLinkedFragments\n > {\n const newBuilder = new FragmentInstantiationBuilder(this.#definition, routes);\n // Preserve config, options, and services from the current instance\n newBuilder.#config = this.#config;\n newBuilder.#options = this.#options;\n newBuilder.#services = this.#services;\n return newBuilder;\n }\n\n /**\n * Set the options for the fragment (e.g., mountRoute, databaseAdapter)\n */\n withOptions(options: TOptions): this {\n this.#options = options;\n return this;\n }\n\n /**\n * Provide implementations for services that this fragment uses\n */\n withServices(services: TServiceDependencies): this {\n this.#services = services;\n return this;\n }\n\n /**\n * Build and return the instantiated fragment\n */\n build(): FragnoInstantiatedFragment<\n FlattenRouteFactories<TRoutesOrFactories>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n > {\n return instantiateFragment(\n this.#definition,\n this.#config ?? ({} as TConfig),\n this.#routes ?? ([] as const as unknown as TRoutesOrFactories),\n this.#options ?? ({} as TOptions),\n this.#services,\n );\n }\n}\n\n/**\n * Create a fluent builder for instantiating a fragment.\n *\n * @example\n * ```ts\n * const fragment = instantiate(myFragmentDefinition)\n * .withConfig({ apiKey: \"key\" })\n * .withRoutes([route1, route2])\n * .withOptions({ mountRoute: \"/api\" })\n * .build();\n * ```\n */\nexport function instantiate<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n): FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n readonly [],\n TLinkedFragments\n> {\n return new FragmentInstantiationBuilder(definition);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fragment-instantiator.js","names":["linkedRoutes: InternalLinkedRouteConfig[]","#name","#routes","#deps","#services","#mountRoute","#serviceThisContext","#handlerThisContext","#contextStorage","#createRequestStorage","#options","#linkedFragments","#internalData","#router","#middlewareHandler","#withRequestStorage","requestBody: RequestBodyType","rawBody: string | undefined","decodedRouteParams: Record<string, string>","#executeMiddleware","#runMiddlewareForFragment","#executeHandler","deps: TDeps","linkedFragmentServices: Record<string, unknown>","services","baseServices: TBaseServices","#definition","#config"],"sources":["../../src/api/fragment-instantiator.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { type FragnoRouteConfig, type HTTPMethod, type RequestThisContext } from \"./api\";\nimport { FragnoApiError } from \"./error\";\nimport { getMountRoute } from \"./internal/route\";\nimport { addRoute, createRouter, findRoute } from \"rou3\";\nimport { RequestInputContext, type RequestBodyType } from \"./request-input-context\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport { RequestOutputContext } from \"./request-output-context\";\nimport {\n type AnyFragnoRouteConfig,\n type AnyRouteOrFactory,\n type FlattenRouteFactories,\n resolveRouteFactories,\n} from \"./route\";\nimport {\n RequestMiddlewareInputContext,\n RequestMiddlewareOutputContext,\n type FragnoMiddlewareCallback,\n} from \"./request-middleware\";\nimport { MutableRequestState } from \"./mutable-request-state\";\nimport type { RouteHandlerInputOptions } from \"./route-handler-input-options\";\nimport type { ExtractRouteByPath, ExtractRoutePath } from \"../client/client\";\nimport { type FragnoResponse, parseFragnoResponse } from \"./fragno-response\";\nimport type { InferOrUnknown } from \"../util/types-util\";\nimport type { FragmentDefinition } from \"./fragment-definition-builder\";\nimport type { FragnoPublicConfig } from \"./shared-types\";\nimport { RequestContextStorage } from \"./request-context-storage\";\nimport { bindServicesToContext, type BoundServices } from \"./bind-services\";\nimport { instantiatedFragmentFakeSymbol } from \"../internal/symbols\";\nimport { recordTraceEvent } from \"../internal/trace-context\";\n\n// Re-export types needed by consumers\nexport type { BoundServices };\n\ntype InternalRoutePrefix = \"/_internal\";\n\ntype JoinInternalRoutePath<TPath extends string> = TPath extends \"\" | \"/\"\n ? InternalRoutePrefix\n : TPath extends `/${string}`\n ? `${InternalRoutePrefix}${TPath}`\n : `${InternalRoutePrefix}/${TPath}`;\n\ntype PrefixInternalRoute<TRoute> =\n TRoute extends FragnoRouteConfig<\n infer TMethod,\n infer TPath,\n infer TInputSchema,\n infer TOutputSchema,\n infer TErrorCode,\n infer TQueryParameters,\n infer TThisContext\n >\n ? FragnoRouteConfig<\n TMethod,\n JoinInternalRoutePath<TPath>,\n TInputSchema,\n TOutputSchema,\n TErrorCode,\n TQueryParameters,\n TThisContext\n >\n : never;\n\ntype PrefixInternalRoutes<TRoutes extends readonly AnyFragnoRouteConfig[]> =\n TRoutes extends readonly [...infer TRoutesTuple]\n ? { [K in keyof TRoutesTuple]: PrefixInternalRoute<TRoutesTuple[K]> }\n : readonly AnyFragnoRouteConfig[];\n\ntype ExtractRoutesFromFragment<T> =\n T extends FragnoInstantiatedFragment<\n infer TRoutes,\n infer _TDeps,\n infer _TServices,\n infer _TServiceThisContext,\n infer _THandlerThisContext,\n infer _TRequestStorage,\n infer _TOptions,\n infer _TLinkedFragments\n >\n ? TRoutes\n : never;\n\ntype InternalLinkedRoutes<TLinkedFragments> =\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>\n ? TLinkedFragments extends { _fragno_internal: infer TInternal }\n ? ExtractRoutesFromFragment<TInternal> extends readonly AnyFragnoRouteConfig[]\n ? PrefixInternalRoutes<ExtractRoutesFromFragment<TInternal>>\n : readonly []\n : readonly []\n : readonly [];\n\nexport type RoutesWithInternal<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TLinkedFragments,\n> = readonly [...TRoutes, ...InternalLinkedRoutes<TLinkedFragments>];\n\n/**\n * Helper type to extract the instantiated fragment type from a fragment definition.\n * This is useful for typing functions that accept instantiated fragments based on their definition.\n *\n * @example\n * ```typescript\n * const myFragmentDef = defineFragment(\"my-fragment\").build();\n * type MyInstantiatedFragment = InstantiatedFragmentFromDefinition<typeof myFragmentDef>;\n * ```\n */\nexport type InstantiatedFragmentFromDefinition<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TDef extends FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>,\n> =\n TDef extends FragmentDefinition<\n infer _TConfig,\n infer TOptions,\n infer TDeps,\n infer TBaseServices,\n infer TServices,\n infer _TServiceDependencies,\n infer _TPrivateServices,\n infer TServiceThisContext,\n infer THandlerThisContext,\n infer TRequestStorage,\n infer TLinkedFragments\n >\n ? FragnoInstantiatedFragment<\n RoutesWithInternal<readonly AnyFragnoRouteConfig[], TLinkedFragments>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n >\n : never;\n\ntype AstroHandlers = {\n ALL: (req: Request) => Promise<Response>;\n};\n\ntype ReactRouterHandlers = {\n loader: (args: { request: Request }) => Promise<Response>;\n action: (args: { request: Request }) => Promise<Response>;\n};\n\nconst serializeHeadersForTrace = (headers: Headers): [string, string][] =>\n Array.from(headers.entries()).sort(([a], [b]) => a.localeCompare(b));\n\nconst serializeQueryForTrace = (query: URLSearchParams): [string, string][] =>\n Array.from(query.entries()).sort(([a], [b]) => a.localeCompare(b));\n\nconst serializeBodyForTrace = (body: RequestBodyType): unknown => {\n if (body instanceof FormData) {\n const entries = Array.from(body.entries()).map(([key, value]) => {\n if (value instanceof Blob) {\n return [key, { type: \"blob\", size: value.size, mime: value.type }] as const;\n }\n return [key, value] as const;\n });\n return { type: \"form-data\", entries };\n }\n\n if (body instanceof Blob) {\n return { type: \"blob\", size: body.size, mime: body.type };\n }\n\n if (body instanceof ReadableStream) {\n return { type: \"stream\" };\n }\n\n return body;\n};\n\ntype SolidStartHandlers = {\n GET: (args: { request: Request }) => Promise<Response>;\n POST: (args: { request: Request }) => Promise<Response>;\n PUT: (args: { request: Request }) => Promise<Response>;\n DELETE: (args: { request: Request }) => Promise<Response>;\n PATCH: (args: { request: Request }) => Promise<Response>;\n HEAD: (args: { request: Request }) => Promise<Response>;\n OPTIONS: (args: { request: Request }) => Promise<Response>;\n};\n\ntype TanStackStartHandlers = SolidStartHandlers;\n\ntype StandardHandlers = {\n GET: (req: Request) => Promise<Response>;\n POST: (req: Request) => Promise<Response>;\n PUT: (req: Request) => Promise<Response>;\n DELETE: (req: Request) => Promise<Response>;\n PATCH: (req: Request) => Promise<Response>;\n HEAD: (req: Request) => Promise<Response>;\n OPTIONS: (req: Request) => Promise<Response>;\n};\n\ntype HandlersByFramework = {\n astro: AstroHandlers;\n \"react-router\": ReactRouterHandlers;\n \"next-js\": StandardHandlers;\n \"svelte-kit\": StandardHandlers;\n \"solid-start\": SolidStartHandlers;\n \"tanstack-start\": TanStackStartHandlers;\n};\n\ntype FullstackFrameworks = keyof HandlersByFramework;\n\nexport type AnyFragnoInstantiatedFragment = FragnoInstantiatedFragment<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n>;\n\nconst INTERNAL_LINKED_FRAGMENT_NAME = \"_fragno_internal\";\nconst INTERNAL_ROUTE_PREFIX = \"/_internal\";\n\ntype InternalLinkedRouteMeta = {\n fragment: AnyFragnoInstantiatedFragment;\n originalPath: string;\n routes: readonly AnyFragnoRouteConfig[];\n};\n\ntype InternalLinkedRouteConfig = AnyFragnoRouteConfig & {\n __internal?: InternalLinkedRouteMeta;\n};\n\nfunction normalizeRoutePrefix(prefix: string): string {\n if (!prefix.startsWith(\"/\")) {\n prefix = `/${prefix}`;\n }\n return prefix.endsWith(\"/\") && prefix.length > 1 ? prefix.slice(0, -1) : prefix;\n}\n\nfunction joinRoutePath(prefix: string, path: string): string {\n const normalizedPrefix = normalizeRoutePrefix(prefix);\n if (!path || path === \"/\") {\n return normalizedPrefix;\n }\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${normalizedPrefix}${normalizedPath}`;\n}\n\nfunction collectLinkedFragmentRoutes(\n linkedFragments: Record<string, AnyFragnoInstantiatedFragment>,\n): InternalLinkedRouteConfig[] {\n const linkedRoutes: InternalLinkedRouteConfig[] = [];\n\n for (const [name, fragment] of Object.entries(linkedFragments)) {\n if (name !== INTERNAL_LINKED_FRAGMENT_NAME) {\n continue;\n }\n\n const internalRoutes = (fragment.routes ?? []) as readonly AnyFragnoRouteConfig[];\n if (internalRoutes.length === 0) {\n continue;\n }\n\n for (const route of internalRoutes) {\n linkedRoutes.push({\n ...route,\n path: joinRoutePath(INTERNAL_ROUTE_PREFIX, route.path),\n __internal: {\n fragment,\n originalPath: route.path,\n routes: internalRoutes,\n },\n });\n }\n }\n\n return linkedRoutes;\n}\n\nexport interface FragnoFragmentSharedConfig<\n TRoutes extends readonly FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string\n >[],\n> {\n name: string;\n routes: TRoutes;\n}\n\n/**\n * Instantiated fragment class with encapsulated state.\n * Provides the same public API as the old FragnoInstantiatedFragment but with better encapsulation.\n */\nexport class FragnoInstantiatedFragment<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TDeps,\n TServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage = {},\n TOptions extends FragnoPublicConfig = FragnoPublicConfig,\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},\n> implements IFragnoInstantiatedFragment\n{\n readonly [instantiatedFragmentFakeSymbol] = instantiatedFragmentFakeSymbol;\n\n // Private fields\n #name: string;\n #routes: TRoutes;\n #deps: TDeps;\n #services: TServices;\n #mountRoute: string;\n #router: ReturnType<typeof createRouter>;\n #middlewareHandler?: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>;\n #serviceThisContext?: TServiceThisContext; // Context for services (may have restricted capabilities)\n #handlerThisContext?: THandlerThisContext; // Context for handlers (full capabilities)\n #contextStorage: RequestContextStorage<TRequestStorage>;\n #createRequestStorage?: () => TRequestStorage;\n #options: TOptions;\n #linkedFragments: TLinkedFragments;\n #internalData: Record<string, unknown>;\n\n constructor(params: {\n name: string;\n routes: TRoutes;\n deps: TDeps;\n services: TServices;\n mountRoute: string;\n serviceThisContext?: TServiceThisContext;\n handlerThisContext?: THandlerThisContext;\n storage: RequestContextStorage<TRequestStorage>;\n createRequestStorage?: () => TRequestStorage;\n options: TOptions;\n linkedFragments?: TLinkedFragments;\n internalData?: Record<string, unknown>;\n }) {\n this.#name = params.name;\n this.#routes = params.routes;\n this.#deps = params.deps;\n this.#services = params.services;\n this.#mountRoute = params.mountRoute;\n this.#serviceThisContext = params.serviceThisContext;\n this.#handlerThisContext = params.handlerThisContext;\n this.#contextStorage = params.storage;\n this.#createRequestStorage = params.createRequestStorage;\n this.#options = params.options;\n this.#linkedFragments = params.linkedFragments ?? ({} as TLinkedFragments);\n this.#internalData = params.internalData ?? {};\n\n // Build router\n this.#router =\n createRouter<\n FragnoRouteConfig<\n HTTPMethod,\n string,\n StandardSchemaV1 | undefined,\n StandardSchemaV1 | undefined,\n string,\n string,\n RequestThisContext\n >\n >();\n\n for (const routeConfig of this.#routes) {\n addRoute(this.#router, routeConfig.method.toUpperCase(), routeConfig.path, routeConfig);\n }\n\n // Bind handler method to maintain 'this' context\n this.handler = this.handler.bind(this);\n }\n\n // Public getters\n get name(): string {\n return this.#name;\n }\n\n get routes(): TRoutes {\n return this.#routes;\n }\n\n get services(): TServices {\n return this.#services;\n }\n\n get mountRoute(): string {\n return this.#mountRoute;\n }\n\n /**\n * @internal\n */\n get $internal() {\n return {\n deps: this.#deps,\n options: this.#options,\n linkedFragments: this.#linkedFragments,\n ...this.#internalData,\n };\n }\n\n /**\n * Add middleware to this fragment.\n * Middleware can inspect and modify requests before they reach handlers.\n */\n withMiddleware(handler: FragnoMiddlewareCallback<TRoutes, TDeps, TServices>): this {\n if (this.#middlewareHandler) {\n throw new Error(\"Middleware already set\");\n }\n this.#middlewareHandler = handler;\n return this;\n }\n\n /**\n * Run a callback within the fragment's request context with initialized storage.\n * This is a shared helper used by inContext(), handler(), and callRouteRaw().\n * @private\n */\n #withRequestStorage<T>(callback: () => T): T;\n #withRequestStorage<T>(callback: () => Promise<T>): Promise<T>;\n #withRequestStorage<T>(callback: () => T | Promise<T>): T | Promise<T> {\n if (!this.#serviceThisContext && !this.#handlerThisContext) {\n // No request context configured - just run callback directly\n return callback();\n }\n\n // Initialize storage with fresh data for this request\n const storageData = this.#createRequestStorage\n ? this.#createRequestStorage()\n : ({} as TRequestStorage);\n return this.#contextStorage.run(storageData, callback);\n }\n\n /**\n * Execute a callback within a request context.\n * Establishes an async context for the duration of the callback, allowing services\n * to access the `this` context. The callback's `this` will be bound to the fragment's\n * handler context (with full capabilities including execute methods).\n * Useful for calling services outside of route handlers (e.g., in tests, background jobs).\n *\n * @param callback - The callback to run within the context\n *\n * @example\n * ```typescript\n * const result = await fragment.inContext(function () {\n * // `this` is bound to the handler context (can call execute methods)\n * await this.getUnitOfWork().executeRetrieve();\n * return this.someContextMethod();\n * });\n * ```\n */\n inContext<T>(callback: (this: THandlerThisContext) => T): T;\n inContext<T>(callback: (this: THandlerThisContext) => Promise<T>): Promise<T>;\n inContext<T>(callback: (this: THandlerThisContext) => T | Promise<T>): T | Promise<T> {\n // Always use handler context for inContext - it has full capabilities\n if (this.#handlerThisContext) {\n const boundCallback = callback.bind(this.#handlerThisContext);\n return this.#withRequestStorage(boundCallback);\n }\n return this.#withRequestStorage(callback);\n }\n\n /**\n * Get framework-specific handlers for this fragment.\n * Use this to integrate the fragment with different fullstack frameworks.\n */\n handlersFor<T extends FullstackFrameworks>(framework: T): HandlersByFramework[T] {\n const handler = this.handler.bind(this);\n\n // LLMs hallucinate these values sometimes, solution isn't obvious so we throw this error\n // @ts-expect-error TS2367\n if (framework === \"h3\" || framework === \"nuxt\") {\n throw new Error(`To get handlers for h3, use the 'fromWebHandler' utility function:\n import { fromWebHandler } from \"h3\";\n export default fromWebHandler(myFragment().handler);`);\n }\n\n const allHandlers = {\n astro: { ALL: handler },\n \"react-router\": {\n loader: ({ request }: { request: Request }) => handler(request),\n action: ({ request }: { request: Request }) => handler(request),\n },\n \"next-js\": {\n GET: handler,\n POST: handler,\n PUT: handler,\n DELETE: handler,\n PATCH: handler,\n HEAD: handler,\n OPTIONS: handler,\n },\n \"svelte-kit\": {\n GET: handler,\n POST: handler,\n PUT: handler,\n DELETE: handler,\n PATCH: handler,\n HEAD: handler,\n OPTIONS: handler,\n },\n \"solid-start\": {\n GET: ({ request }: { request: Request }) => handler(request),\n POST: ({ request }: { request: Request }) => handler(request),\n PUT: ({ request }: { request: Request }) => handler(request),\n DELETE: ({ request }: { request: Request }) => handler(request),\n PATCH: ({ request }: { request: Request }) => handler(request),\n HEAD: ({ request }: { request: Request }) => handler(request),\n OPTIONS: ({ request }: { request: Request }) => handler(request),\n },\n \"tanstack-start\": {\n GET: ({ request }: { request: Request }) => handler(request),\n POST: ({ request }: { request: Request }) => handler(request),\n PUT: ({ request }: { request: Request }) => handler(request),\n DELETE: ({ request }: { request: Request }) => handler(request),\n PATCH: ({ request }: { request: Request }) => handler(request),\n HEAD: ({ request }: { request: Request }) => handler(request),\n OPTIONS: ({ request }: { request: Request }) => handler(request),\n },\n } satisfies HandlersByFramework;\n\n return allHandlers[framework];\n }\n\n /**\n * Main request handler for this fragment.\n * Handles routing, middleware, and error handling.\n */\n async handler(req: Request): Promise<Response> {\n const url = new URL(req.url);\n const pathname = url.pathname;\n\n // Match route\n const matchRoute = pathname.startsWith(this.#mountRoute)\n ? pathname.slice(this.#mountRoute.length)\n : null;\n\n if (matchRoute === null) {\n return Response.json(\n {\n error:\n `Fragno: Route for '${this.#name}' not found. Is the fragment mounted on the right route? ` +\n `Expecting: '${this.#mountRoute}'.`,\n code: \"ROUTE_NOT_FOUND\",\n },\n { status: 404 },\n );\n }\n\n const route = findRoute(this.#router, req.method, matchRoute);\n\n if (!route) {\n return Response.json(\n { error: `Fragno: Route for '${this.#name}' not found`, code: \"ROUTE_NOT_FOUND\" },\n { status: 404 },\n );\n }\n\n // Get the expected content type from route config (default: application/json)\n const routeConfig = route.data as InternalLinkedRouteConfig;\n const expectedContentType = routeConfig.contentType ?? \"application/json\";\n\n // Parse request body based on route's expected content type\n let requestBody: RequestBodyType = undefined;\n let rawBody: string | undefined = undefined;\n\n if (req.body instanceof ReadableStream) {\n const requestContentType = (req.headers.get(\"content-type\") ?? \"\").toLowerCase();\n\n if (expectedContentType === \"multipart/form-data\") {\n // Route expects FormData (file uploads)\n if (!requestContentType.includes(\"multipart/form-data\")) {\n return Response.json(\n {\n error: `This endpoint expects multipart/form-data, but received: ${requestContentType || \"no content-type\"}`,\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n },\n { status: 415 },\n );\n }\n\n try {\n requestBody = await req.formData();\n } catch {\n return Response.json(\n { error: \"Failed to parse multipart form data\", code: \"INVALID_REQUEST_BODY\" },\n { status: 400 },\n );\n }\n } else if (expectedContentType === \"application/octet-stream\") {\n if (!requestContentType.includes(\"application/octet-stream\")) {\n return Response.json(\n {\n error: `This endpoint expects application/octet-stream, but received: ${requestContentType || \"no content-type\"}`,\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n },\n { status: 415 },\n );\n }\n\n requestBody = req.body ?? new ReadableStream<Uint8Array>();\n } else {\n // Route expects JSON (default)\n // Note: We're lenient here - we accept requests without Content-Type header\n // or with application/json. We reject multipart/form-data for JSON routes.\n if (requestContentType.includes(\"multipart/form-data\")) {\n return Response.json(\n {\n error: `This endpoint expects JSON, but received multipart/form-data. Use a route with contentType: \"multipart/form-data\" for file uploads.`,\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n },\n { status: 415 },\n );\n }\n\n // Clone request to make sure we don't consume body stream\n const clonedReq = req.clone();\n\n // Get raw text\n rawBody = await clonedReq.text();\n\n // Parse JSON if body is not empty\n if (rawBody) {\n try {\n requestBody = JSON.parse(rawBody);\n } catch {\n // If JSON parsing fails, keep body as undefined\n // This handles cases where body is not JSON\n requestBody = undefined;\n }\n }\n }\n }\n\n // URL decode path params from rou3 (which returns encoded values)\n const decodedRouteParams: Record<string, string> = {};\n for (const [key, value] of Object.entries(route.params ?? {})) {\n decodedRouteParams[key] = decodeURIComponent(value);\n }\n\n const requestState = new MutableRequestState({\n pathParams: decodedRouteParams,\n searchParams: url.searchParams,\n body: requestBody,\n headers: new Headers(req.headers),\n });\n\n // Execute middleware and handler\n const executeRequest = async (): Promise<Response> => {\n // Parent middleware execution\n const middlewareResult = await this.#executeMiddleware(req, route, requestState);\n if (middlewareResult !== undefined) {\n return middlewareResult;\n }\n\n // Internal fragment middleware execution (if linked)\n const internalMeta = routeConfig.__internal;\n if (internalMeta) {\n const internalResult = await FragnoInstantiatedFragment.#runMiddlewareForFragment(\n internalMeta.fragment as AnyFragnoInstantiatedFragment,\n {\n req,\n method: routeConfig.method,\n path: internalMeta.originalPath,\n requestState,\n routes: internalMeta.routes,\n },\n );\n if (internalResult !== undefined) {\n return internalResult;\n }\n }\n\n // Handler execution\n return this.#executeHandler(req, route, requestState, rawBody);\n };\n\n // Wrap with request storage context if provided\n return this.#withRequestStorage(executeRequest);\n }\n\n /**\n * Call a route directly with typed inputs and outputs.\n * Useful for testing and server-side route calls.\n */\n async callRoute<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(\n method: TMethod,\n path: TPath,\n inputOptions?: RouteHandlerInputOptions<\n TPath,\n ExtractRouteByPath<TRoutes, TPath, TMethod>[\"inputSchema\"]\n >,\n ): Promise<\n FragnoResponse<\n InferOrUnknown<NonNullable<ExtractRouteByPath<TRoutes, TPath, TMethod>[\"outputSchema\"]>>\n >\n > {\n const response = await this.callRouteRaw(method, path, inputOptions);\n return parseFragnoResponse(response);\n }\n\n /**\n * Call a route directly and get the raw Response object.\n * Useful for testing and server-side route calls.\n */\n async callRouteRaw<TMethod extends HTTPMethod, TPath extends ExtractRoutePath<TRoutes, TMethod>>(\n method: TMethod,\n path: TPath,\n inputOptions?: RouteHandlerInputOptions<\n TPath,\n ExtractRouteByPath<TRoutes, TPath, TMethod>[\"inputSchema\"]\n >,\n ): Promise<Response> {\n // Find route in this.#routes\n const route = this.#routes.find((r) => r.method === method && r.path === path);\n\n if (!route) {\n return Response.json(\n {\n error: `Route ${method} ${path} not found`,\n code: \"ROUTE_NOT_FOUND\",\n },\n { status: 404 },\n );\n }\n\n const { pathParams = {}, body, query, headers } = inputOptions || {};\n\n // Convert query to URLSearchParams if needed\n const searchParams =\n query instanceof URLSearchParams\n ? query\n : query\n ? new URLSearchParams(query)\n : new URLSearchParams();\n\n const requestHeaders =\n headers instanceof Headers ? headers : headers ? new Headers(headers) : new Headers();\n\n // Construct RequestInputContext\n const inputContext = new RequestInputContext({\n path: route.path,\n method: route.method,\n pathParams: pathParams as ExtractPathParams<typeof route.path>,\n searchParams,\n headers: requestHeaders,\n parsedBody: body,\n inputSchema: route.inputSchema,\n shouldValidateInput: true, // Enable validation for production use\n });\n\n recordTraceEvent({\n type: \"route-input\",\n method: route.method,\n path: route.path,\n pathParams: (pathParams ?? {}) as Record<string, string>,\n queryParams: serializeQueryForTrace(searchParams),\n headers: serializeHeadersForTrace(requestHeaders),\n body: serializeBodyForTrace(body),\n });\n\n // Construct RequestOutputContext\n const outputContext = new RequestOutputContext(route.outputSchema);\n\n // Execute handler\n const executeHandler = async (): Promise<Response> => {\n try {\n // Use handler context (full capabilities)\n const thisContext = this.#handlerThisContext ?? ({} as RequestThisContext);\n return await route.handler.call(thisContext, inputContext, outputContext);\n } catch (error) {\n console.error(\"Error in callRoute handler\", error);\n\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n };\n\n // Wrap with request storage context if provided\n return this.#withRequestStorage(executeHandler);\n }\n\n /**\n * Execute middleware for a request.\n * Returns undefined if middleware allows the request to continue to the handler.\n */\n async #executeMiddleware(\n req: Request,\n route: ReturnType<typeof findRoute>,\n requestState: MutableRequestState,\n ): Promise<Response | undefined> {\n if (!route) {\n return undefined;\n }\n\n const { path } = route.data as AnyFragnoRouteConfig;\n return FragnoInstantiatedFragment.#runMiddlewareForFragment(this, {\n req,\n method: req.method as HTTPMethod,\n path,\n requestState,\n });\n }\n\n static async #runMiddlewareForFragment(\n fragment: AnyFragnoInstantiatedFragment,\n options: {\n req: Request;\n method: HTTPMethod;\n path: string;\n requestState: MutableRequestState;\n routes?: readonly AnyFragnoRouteConfig[];\n },\n ): Promise<Response | undefined> {\n if (!fragment.#middlewareHandler) {\n return undefined;\n }\n\n const middlewareInputContext = new RequestMiddlewareInputContext(\n (options.routes ?? fragment.#routes) as readonly AnyFragnoRouteConfig[],\n {\n method: options.method,\n path: options.path,\n request: options.req,\n state: options.requestState,\n },\n );\n\n const middlewareOutputContext = new RequestMiddlewareOutputContext(\n fragment.#deps,\n fragment.#services,\n );\n\n try {\n const middlewareResult = await fragment.#middlewareHandler(\n middlewareInputContext,\n middlewareOutputContext,\n );\n recordTraceEvent({\n type: \"middleware-decision\",\n method: options.method,\n path: options.path,\n outcome: middlewareResult ? \"deny\" : \"allow\",\n status: middlewareResult?.status,\n });\n if (middlewareResult !== undefined) {\n return middlewareResult;\n }\n } catch (error) {\n console.error(\"Error in middleware\", error);\n\n recordTraceEvent({\n type: \"middleware-decision\",\n method: options.method,\n path: options.path,\n outcome: \"deny\",\n status: error instanceof FragnoApiError ? error.status : 500,\n });\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n\n return undefined;\n }\n\n /**\n * Execute a route handler with proper error handling.\n */\n async #executeHandler(\n req: Request,\n route: ReturnType<typeof findRoute>,\n requestState: MutableRequestState,\n rawBody?: string,\n ): Promise<Response> {\n if (!route) {\n return Response.json({ error: \"Route not found\", code: \"ROUTE_NOT_FOUND\" }, { status: 404 });\n }\n\n const { handler, inputSchema, outputSchema, path } = route.data as AnyFragnoRouteConfig;\n\n const inputContext = await RequestInputContext.fromRequest({\n request: req,\n method: req.method,\n path,\n pathParams: (route.params ?? {}) as ExtractPathParams<typeof path>,\n inputSchema,\n state: requestState,\n rawBody,\n });\n\n recordTraceEvent({\n type: \"route-input\",\n method: req.method,\n path,\n pathParams: inputContext.pathParams as Record<string, string>,\n queryParams: serializeQueryForTrace(requestState.searchParams),\n headers: serializeHeadersForTrace(requestState.headers),\n body: serializeBodyForTrace(requestState.body),\n });\n\n const outputContext = new RequestOutputContext(outputSchema);\n\n try {\n // Note: We don't call .run() here because the storage should already be initialized\n // by the handler() method or inContext() method before this point\n // Use handler context (full capabilities)\n const contextForHandler = this.#handlerThisContext ?? ({} as RequestThisContext);\n const result = await handler.call(contextForHandler, inputContext, outputContext);\n return result;\n } catch (error) {\n console.error(\"Error in handler\", error);\n\n if (error instanceof FragnoApiError) {\n return error.toResponse();\n }\n\n return Response.json(\n { error: \"Internal server error\", code: \"INTERNAL_SERVER_ERROR\" },\n { status: 500 },\n );\n }\n }\n}\n\n/**\n * Options for fragment instantiation.\n */\nexport interface InstantiationOptions {\n /**\n * If true, catches errors during initialization and returns stub implementations.\n * This is useful for CLI tools that need to extract metadata (like database schemas)\n * without requiring all dependencies to be fully initialized.\n */\n dryRun?: boolean;\n}\n\n/**\n * Core instantiation function that creates a fragment instance from a definition.\n * This function validates dependencies, calls all callbacks, and wires everything together.\n */\nexport function instantiateFragment<\n const TConfig,\n const TOptions extends FragnoPublicConfig,\n const TDeps,\n const TBaseServices extends Record<string, unknown>,\n const TServices extends Record<string, unknown>,\n const TServiceDependencies,\n const TPrivateServices extends Record<string, unknown>,\n const TServiceThisContext extends RequestThisContext,\n const THandlerThisContext extends RequestThisContext,\n const TRequestStorage,\n const TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n const TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n config: TConfig,\n routesOrFactories: TRoutesOrFactories,\n options: TOptions,\n serviceImplementations?: TServiceDependencies,\n instantiationOptions?: InstantiationOptions,\n): FragnoInstantiatedFragment<\n RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n> {\n const { dryRun = false } = instantiationOptions ?? {};\n\n // 1. Validate service dependencies\n const serviceDependencies = definition.serviceDependencies;\n if (serviceDependencies) {\n for (const [serviceName, meta] of Object.entries(serviceDependencies)) {\n const metadata = meta as { name: string; required: boolean };\n const implementation = serviceImplementations?.[serviceName as keyof TServiceDependencies];\n if (metadata.required && !implementation) {\n throw new Error(\n `Fragment '${definition.name}' requires service '${metadata.name}' but it was not provided`,\n );\n }\n }\n }\n\n // 2. Call dependencies callback\n let deps: TDeps;\n try {\n deps = definition.dependencies?.({ config, options }) ?? ({} as TDeps);\n } catch (error) {\n if (dryRun) {\n console.warn(\n \"Warning: Failed to initialize dependencies in dry run mode:\",\n error instanceof Error ? error.message : String(error),\n );\n // Return empty deps - database fragments will add implicit deps later\n deps = {} as TDeps;\n } else {\n throw error;\n }\n }\n\n // 3. Instantiate linked fragments FIRST (before any services)\n // Their services will be merged into private services\n const linkedFragmentInstances = {} as TLinkedFragments;\n const linkedFragmentServices: Record<string, unknown> = {};\n\n if (definition.linkedFragments) {\n for (const [name, callback] of Object.entries(definition.linkedFragments)) {\n const linkedFragment = callback({\n config,\n options,\n serviceDependencies: serviceImplementations,\n });\n (linkedFragmentInstances as Record<string, AnyFragnoInstantiatedFragment>)[name] =\n linkedFragment;\n\n // Merge all services from linked fragment into private services directly by their service name\n const services = linkedFragment.services as Record<string, unknown>;\n for (const [serviceName, service] of Object.entries(services)) {\n linkedFragmentServices[serviceName] = service;\n }\n }\n }\n\n // Identity function for service definition (used to set 'this' context)\n const defineService = <T>(services: T & ThisType<TServiceThisContext>): T => services;\n\n // 4. Call privateServices factories\n // Private services are instantiated in order, so earlier ones are available to later ones\n // Start with linked fragment services, then add explicitly defined private services\n const privateServices = { ...linkedFragmentServices } as TPrivateServices;\n if (definition.privateServices) {\n for (const [serviceName, factory] of Object.entries(definition.privateServices)) {\n const serviceFactory = factory as (context: {\n config: TConfig;\n options: TOptions;\n deps: TDeps;\n serviceDeps: TServiceDependencies;\n privateServices: TPrivateServices;\n defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;\n }) => unknown;\n\n try {\n (privateServices as Record<string, unknown>)[serviceName] = serviceFactory({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices, // Pass the current state of private services (earlier ones are available)\n defineService,\n });\n } catch (error) {\n if (dryRun) {\n console.warn(\n `Warning: Failed to initialize private service '${serviceName}' in dry run mode:`,\n error instanceof Error ? error.message : String(error),\n );\n (privateServices as Record<string, unknown>)[serviceName] = {};\n } else {\n throw error;\n }\n }\n }\n }\n\n // 5. Call baseServices callback (with access to private services including linked fragment services)\n let baseServices: TBaseServices;\n try {\n baseServices =\n definition.baseServices?.({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices,\n defineService,\n }) ?? ({} as TBaseServices);\n } catch (error) {\n if (dryRun) {\n console.warn(\n \"Warning: Failed to initialize base services in dry run mode:\",\n error instanceof Error ? error.message : String(error),\n );\n baseServices = {} as TBaseServices;\n } else {\n throw error;\n }\n }\n\n // 6. Call namedServices factories (with access to private services including linked fragment services)\n const namedServices = {} as TServices;\n if (definition.namedServices) {\n for (const [serviceName, factory] of Object.entries(definition.namedServices)) {\n const serviceFactory = factory as (context: {\n config: TConfig;\n options: TOptions;\n deps: TDeps;\n serviceDeps: TServiceDependencies;\n privateServices: TPrivateServices;\n defineService: <T>(svc: T & ThisType<TServiceThisContext>) => T;\n }) => unknown;\n\n try {\n (namedServices as Record<string, unknown>)[serviceName] = serviceFactory({\n config,\n options,\n deps,\n serviceDeps: (serviceImplementations ?? {}) as TServiceDependencies,\n privateServices,\n defineService,\n });\n } catch (error) {\n if (dryRun) {\n console.warn(\n `Warning: Failed to initialize service '${serviceName}' in dry run mode:`,\n error instanceof Error ? error.message : String(error),\n );\n (namedServices as Record<string, unknown>)[serviceName] = {};\n } else {\n throw error;\n }\n }\n }\n }\n\n // 7. Merge public services (NOT including private services)\n const services = {\n ...baseServices,\n ...namedServices,\n };\n\n // 8. Create request context storage and both service & handler contexts\n // Use external storage if provided, otherwise create new storage\n const storage = definition.getExternalStorage\n ? definition.getExternalStorage({ config, options, deps })\n : new RequestContextStorage<TRequestStorage>();\n\n // Create both contexts using createThisContext (returns { serviceContext, handlerContext })\n const contexts = definition.createThisContext?.({\n config,\n options,\n deps,\n storage,\n });\n\n const serviceContext = contexts?.serviceContext;\n const handlerContext = contexts?.handlerContext;\n const internalData =\n definition.internalDataFactory?.({\n config,\n options,\n deps,\n linkedFragments: linkedFragmentInstances,\n }) ?? {};\n\n // 9. Bind services to serviceContext (restricted)\n // Services get the restricted context (for database fragments, this excludes execute methods)\n const boundServices = serviceContext ? bindServicesToContext(services, serviceContext) : services;\n\n // 10. Resolve routes with bound services\n const context = {\n config,\n deps,\n services: boundServices,\n serviceDeps: serviceImplementations ?? ({} as TServiceDependencies),\n };\n const routes = resolveRouteFactories(context, routesOrFactories) as AnyFragnoRouteConfig[];\n const linkedRoutes = collectLinkedFragmentRoutes(\n linkedFragmentInstances as Record<string, AnyFragnoInstantiatedFragment>,\n );\n const finalRoutes =\n linkedRoutes.length > 0 ? [...routes, ...linkedRoutes] : (routes as AnyFragnoRouteConfig[]);\n\n // 11. Calculate mount route\n const mountRoute = getMountRoute({\n name: definition.name,\n mountRoute: options.mountRoute,\n });\n\n // 12. Wrap createRequestStorage to capture context\n const createRequestStorageWithContext = definition.createRequestStorage\n ? () => definition.createRequestStorage!({ config, options, deps })\n : undefined;\n\n // 13. Create and return fragment instance\n // Pass bound services so they have access to serviceContext via 'this'\n // Handlers get handlerContext which may have more capabilities than serviceContext\n return new FragnoInstantiatedFragment({\n name: definition.name,\n routes: finalRoutes as unknown as RoutesWithInternal<\n FlattenRouteFactories<TRoutesOrFactories>,\n TLinkedFragments\n >,\n deps,\n services: boundServices as BoundServices<TBaseServices & TServices>,\n mountRoute,\n serviceThisContext: serviceContext,\n handlerThisContext: handlerContext,\n storage,\n createRequestStorage: createRequestStorageWithContext,\n options,\n linkedFragments: linkedFragmentInstances,\n internalData: internalData as Record<string, unknown>,\n });\n}\n\n/**\n * Interface that defines the public API for a fragment instantiation builder.\n * Used to ensure consistency between real implementations and stubs.\n */\ninterface IFragmentInstantiationBuilder {\n /**\n * Get the fragment definition\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n get definition(): any;\n\n /**\n * Get the configured routes\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n get routes(): any;\n\n /**\n * Get the configuration\n */\n get config(): unknown;\n\n /**\n * Get the options\n */\n get options(): unknown;\n\n /**\n * Set the configuration for the fragment\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n withConfig(config: any): unknown;\n\n /**\n * Set the routes for the fragment\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n withRoutes(routes: any): unknown;\n\n /**\n * Set the options for the fragment (e.g., mountRoute, databaseAdapter)\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n withOptions(options: any): unknown;\n\n /**\n * Provide implementations for services that this fragment uses\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n withServices(services: any): unknown;\n\n /**\n * Build and return the instantiated fragment\n */\n build(): IFragnoInstantiatedFragment;\n}\n\n/**\n * Interface that defines the public API for an instantiated fragment.\n * Used to ensure consistency between real implementations and stubs.\n */\ninterface IFragnoInstantiatedFragment {\n readonly [instantiatedFragmentFakeSymbol]: typeof instantiatedFragmentFakeSymbol;\n\n get name(): string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n get routes(): any;\n get services(): Record<string, unknown>;\n get mountRoute(): string;\n get $internal(): {\n deps: unknown;\n options: unknown;\n linkedFragments: unknown;\n } & Record<string, unknown>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n withMiddleware(handler: any): this;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inContext<T>(callback: any): T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inContext<T>(callback: any): Promise<T>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handlersFor(framework: FullstackFrameworks): any;\n\n handler(req: Request): Promise<Response>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n callRoute(method: HTTPMethod, path: string, inputOptions?: any): Promise<any>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n callRouteRaw(method: HTTPMethod, path: string, inputOptions?: any): Promise<Response>;\n}\n\n/**\n * Fluent builder for instantiating fragments.\n * Provides a type-safe API for configuring and building fragment instances.\n */\nexport class FragmentInstantiationBuilder<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n> implements IFragmentInstantiationBuilder\n{\n #definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >;\n #config?: TConfig;\n #routes?: TRoutesOrFactories;\n #options?: TOptions;\n #services?: TServiceDependencies;\n\n constructor(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n routes?: TRoutesOrFactories,\n ) {\n this.#definition = definition;\n this.#routes = routes;\n }\n\n /**\n * Get the fragment definition\n */\n get definition(): FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n > {\n return this.#definition;\n }\n\n /**\n * Get the configured routes\n */\n get routes(): TRoutesOrFactories {\n return this.#routes ?? ([] as const as unknown as TRoutesOrFactories);\n }\n\n /**\n * Get the configuration\n */\n get config(): TConfig | undefined {\n return this.#config;\n }\n\n /**\n * Get the options\n */\n get options(): TOptions | undefined {\n return this.#options;\n }\n\n /**\n * Set the configuration for the fragment\n */\n withConfig(config: TConfig): this {\n this.#config = config;\n return this;\n }\n\n /**\n * Set the routes for the fragment\n */\n withRoutes<const TNewRoutes extends readonly AnyRouteOrFactory[]>(\n routes: TNewRoutes,\n ): FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TNewRoutes,\n TLinkedFragments\n > {\n const newBuilder = new FragmentInstantiationBuilder(this.#definition, routes);\n // Preserve config, options, and services from the current instance\n newBuilder.#config = this.#config;\n newBuilder.#options = this.#options;\n newBuilder.#services = this.#services;\n return newBuilder;\n }\n\n /**\n * Set the options for the fragment (e.g., mountRoute, databaseAdapter)\n */\n withOptions(options: TOptions): this {\n this.#options = options;\n return this;\n }\n\n /**\n * Provide implementations for services that this fragment uses\n */\n withServices(services: TServiceDependencies): this {\n this.#services = services;\n return this;\n }\n\n /**\n * Build and return the instantiated fragment\n */\n build(): FragnoInstantiatedFragment<\n RoutesWithInternal<FlattenRouteFactories<TRoutesOrFactories>, TLinkedFragments>,\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TOptions,\n TLinkedFragments\n > {\n // This variable is set by the frango-cli when extracting database schemas\n const dryRun = process.env[\"FRAGNO_INIT_DRY_RUN\"] === \"true\";\n\n return instantiateFragment(\n this.#definition,\n this.#config ?? ({} as TConfig),\n this.#routes ?? ([] as const as unknown as TRoutesOrFactories),\n this.#options ?? ({} as TOptions),\n this.#services,\n { dryRun },\n );\n }\n}\n\n/**\n * Create a fluent builder for instantiating a fragment.\n *\n * @example\n * ```ts\n * const fragment = instantiate(myFragmentDefinition)\n * .withConfig({ apiKey: \"key\" })\n * .withRoutes([route1, route2])\n * .withOptions({ mountRoute: \"/api\" })\n * .build();\n * ```\n */\nexport function instantiate<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>,\n>(\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >,\n): FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n readonly [],\n TLinkedFragments\n> {\n return new FragmentInstantiationBuilder(definition);\n}\n\n// Export interfaces for stub implementations\nexport type { IFragmentInstantiationBuilder, IFragnoInstantiatedFragment };\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { HTTPMethod } from "./api.js";
|
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
|
|
6
6
|
//#region src/api/request-input-context.d.ts
|
|
7
|
-
type RequestBodyType = unknown | FormData | Blob | null | undefined;
|
|
7
|
+
type RequestBodyType = unknown | FormData | Blob | ReadableStream<Uint8Array> | null | undefined;
|
|
8
8
|
declare class RequestInputContext<TPath extends string = string, TInputSchema extends StandardSchemaV1 | undefined = undefined> {
|
|
9
9
|
#private;
|
|
10
10
|
constructor(config: {
|
|
@@ -75,6 +75,62 @@ declare class RequestInputContext<TPath extends string = string, TInputSchema ex
|
|
|
75
75
|
*/
|
|
76
76
|
get headers(): Headers;
|
|
77
77
|
get rawBody(): string | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Access the request body as FormData.
|
|
80
|
+
*
|
|
81
|
+
* Use this method when handling file uploads or multipart form submissions.
|
|
82
|
+
* The request must have been sent with Content-Type: multipart/form-data.
|
|
83
|
+
*
|
|
84
|
+
* @throws Error if the request body is not FormData
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* defineRoute({
|
|
89
|
+
* method: "POST",
|
|
90
|
+
* path: "/upload",
|
|
91
|
+
* async handler(ctx, res) {
|
|
92
|
+
* const formData = ctx.formData();
|
|
93
|
+
* const file = formData.get("file") as File;
|
|
94
|
+
* const description = formData.get("description") as string;
|
|
95
|
+
* // ... process file
|
|
96
|
+
* }
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
formData(): FormData;
|
|
101
|
+
/**
|
|
102
|
+
* Check if the request body is FormData.
|
|
103
|
+
*
|
|
104
|
+
* Useful for routes that accept both JSON and FormData payloads.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* defineRoute({
|
|
109
|
+
* method: "POST",
|
|
110
|
+
* path: "/upload",
|
|
111
|
+
* async handler(ctx, res) {
|
|
112
|
+
* if (ctx.isFormData()) {
|
|
113
|
+
* const formData = ctx.formData();
|
|
114
|
+
* // handle file upload
|
|
115
|
+
* } else {
|
|
116
|
+
* const json = await ctx.input.valid();
|
|
117
|
+
* // handle JSON payload
|
|
118
|
+
* }
|
|
119
|
+
* }
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
isFormData(): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Access the request body as a ReadableStream (application/octet-stream).
|
|
126
|
+
*
|
|
127
|
+
* @throws Error if the request body is not a ReadableStream
|
|
128
|
+
*/
|
|
129
|
+
bodyStream(): ReadableStream<Uint8Array>;
|
|
130
|
+
/**
|
|
131
|
+
* Check if the request body is a ReadableStream.
|
|
132
|
+
*/
|
|
133
|
+
isBodyStream(): boolean;
|
|
78
134
|
/**
|
|
79
135
|
* Input validation context (only if inputSchema is defined)
|
|
80
136
|
* @remarks `InputContext`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-input-context.d.ts","names":[],"sources":["../../src/api/request-input-context.ts"],"sourcesContent":[],"mappings":";;;;;;KAKY,eAAA,aAER,WACA;
|
|
1
|
+
{"version":3,"file":"request-input-context.d.ts","names":[],"sources":["../../src/api/request-input-context.ts"],"sourcesContent":[],"mappings":";;;;;;KAKY,eAAA,aAER,WACA,OACA,eAAe;cAIN,wEAEU;EAVX,CAAA,OAAA;EAER,WAAA,CAAA,MAAA,EAAA;IACA,IAAA,EAoBM,KApBN;IACe,MAAA,EAAA,MAAA;IAAf,UAAA,EAqBY,iBArBZ,CAqB8B,KArB9B,CAAA;IAAc,YAAA,EAsBA,eAtBA;IAIL,UAAA,EAmBG,eAnBgB;IAET,OAAA,CAAA,EAAA,MAAA;IAab,OAAA,EAMG,OANH;IAEwB,OAAA,CAAA,EAKpB,OALoB;IAAlB,WAAA,CAAA,EAME,YANF;IACE,mBAAA,CAAA,EAAA,OAAA;EACF,CAAA;EAEH;;;EAqBY,OAAA,WAAA,CAAA,cAAA,MAAA,EAAA,qBAAA,gBAAA,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA,MAAA,EAAA;IAEZ,OAAA,EAAA,OAAA;IAEH,MAAA,EAAA,MAAA;IACwB,IAAA,EADxB,KACwB;IAAlB,UAAA,EAAA,iBAAA,CAAkB,KAAlB,CAAA;IACE,WAAA,CAAA,EAAA,YAAA;IAEP,mBAAA,CAAA,EAAA,OAAA;IAEuB,KAAA,EAFvB,mBAEuB;IAAO,OAAA,CAAA,EAAA,MAAA;EAA3B,CAAA,CAAA,EAAR,OAAQ,CAAA,mBAAA,CAAoB,KAApB,EAA2B,YAA3B,CAAA,CAAA;EAAR;;;EA0BkC,OAAA,cAAA,CAAA,cAAA,MAAA,EAAA,qBANf,gBAMe,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA,MAAA,EAAA;IAAlB,MAAA,EAAA,KAAA;IACG,IAAA,EAFT,KAES;IACL,UAAA,EAFE,iBAEF,CAFoB,KAEpB,CAAA;IAGM,YAAA,CAAA,EAJD,eAIC;IAAR,OAAA,CAAA,EAHE,OAGF;EACF,CAAA,GAAA;IACwB,MAAA,EAFtB,OAEsB,CAFd,UAEc,EAAA,KAAA,CAAA;IAAlB,IAAA,EADN,KACM;IACG,UAAA,EADH,iBACG,CADe,KACf,CAAA;IACL,YAAA,CAAA,EADK,eACL;IACJ,OAAA,CAAA,EADI,OACJ;IACQ,IAAA,EADR,eACQ;IAEC,WAAA,CAAA,EAFD,YAEC;EAAO,CAAA,CAAA,EAA3B,mBAA2B,CAAP,KAAO,EAAA,YAAA,CAAA;EAA3B;;;EA8Be,IAAA,MAAA,CAAA,CAAA,EAAA,MAAA;EAOL;;;;EA6EC,IAAA,IAAA,CAAA,CAAA,EA3FF,KA2FE;EAoBD;;;;EAM4B,IAAA,UAAA,CAAA,CAAA,EA9GvB,iBA8GuB,CA9GL,KA8GK,CAAA;EAA7B;;;;eAvGC;;;;;iBAOE;;;;;;;;;;;;;;;;;;;;;;;;cA8BH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAwCE,eAAe;;;;;;;;;eAoBhB;YAGC;iBACK,QACX,qBAAqB,mBACjB,gBAAA,CAAiB,YAAY"}
|
|
@@ -91,6 +91,72 @@ var RequestInputContext = class RequestInputContext {
|
|
|
91
91
|
return this.#body;
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
+
* Access the request body as FormData.
|
|
95
|
+
*
|
|
96
|
+
* Use this method when handling file uploads or multipart form submissions.
|
|
97
|
+
* The request must have been sent with Content-Type: multipart/form-data.
|
|
98
|
+
*
|
|
99
|
+
* @throws Error if the request body is not FormData
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* defineRoute({
|
|
104
|
+
* method: "POST",
|
|
105
|
+
* path: "/upload",
|
|
106
|
+
* async handler(ctx, res) {
|
|
107
|
+
* const formData = ctx.formData();
|
|
108
|
+
* const file = formData.get("file") as File;
|
|
109
|
+
* const description = formData.get("description") as string;
|
|
110
|
+
* // ... process file
|
|
111
|
+
* }
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
formData() {
|
|
116
|
+
if (!(this.#parsedBody instanceof FormData)) throw new Error("Request body is not FormData. Ensure the request was sent with Content-Type: multipart/form-data.");
|
|
117
|
+
return this.#parsedBody;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if the request body is FormData.
|
|
121
|
+
*
|
|
122
|
+
* Useful for routes that accept both JSON and FormData payloads.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* defineRoute({
|
|
127
|
+
* method: "POST",
|
|
128
|
+
* path: "/upload",
|
|
129
|
+
* async handler(ctx, res) {
|
|
130
|
+
* if (ctx.isFormData()) {
|
|
131
|
+
* const formData = ctx.formData();
|
|
132
|
+
* // handle file upload
|
|
133
|
+
* } else {
|
|
134
|
+
* const json = await ctx.input.valid();
|
|
135
|
+
* // handle JSON payload
|
|
136
|
+
* }
|
|
137
|
+
* }
|
|
138
|
+
* });
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
isFormData() {
|
|
142
|
+
return this.#parsedBody instanceof FormData;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Access the request body as a ReadableStream (application/octet-stream).
|
|
146
|
+
*
|
|
147
|
+
* @throws Error if the request body is not a ReadableStream
|
|
148
|
+
*/
|
|
149
|
+
bodyStream() {
|
|
150
|
+
if (!(this.#parsedBody instanceof ReadableStream)) throw new Error("Request body is not a ReadableStream. Ensure the request was sent with Content-Type: application/octet-stream.");
|
|
151
|
+
return this.#parsedBody;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Check if the request body is a ReadableStream.
|
|
155
|
+
*/
|
|
156
|
+
isBodyStream() {
|
|
157
|
+
return this.#parsedBody instanceof ReadableStream;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
94
160
|
* Input validation context (only if inputSchema is defined)
|
|
95
161
|
* @remarks `InputContext`
|
|
96
162
|
*/
|
|
@@ -107,6 +173,7 @@ var RequestInputContext = class RequestInputContext {
|
|
|
107
173
|
async #validateInput() {
|
|
108
174
|
if (!this.#inputSchema) throw new Error("No input schema defined for this route");
|
|
109
175
|
if (this.#parsedBody instanceof FormData || this.#parsedBody instanceof Blob) throw new Error("Schema validation is only supported for JSON data, not FormData or Blob");
|
|
176
|
+
if (this.#parsedBody instanceof ReadableStream) throw new Error("Schema validation is only supported for JSON data, not FormData, Blob, or ReadableStream");
|
|
110
177
|
const result = await this.#inputSchema["~standard"].validate(this.#parsedBody);
|
|
111
178
|
if (result.issues) throw new FragnoApiValidationError("Validation failed", result.issues);
|
|
112
179
|
return result.value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-input-context.js","names":["#path","#method","#pathParams","#searchParams","#headers","#body","#parsedBody","#inputSchema","#shouldValidateInput","#validateInput"],"sources":["../../src/api/request-input-context.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport { FragnoApiValidationError, type HTTPMethod } from \"./api\";\nimport type { MutableRequestState } from \"./mutable-request-state\";\n\nexport type RequestBodyType =\n | unknown // JSON\n | FormData\n | Blob\n | null\n | undefined;\n\nexport class RequestInputContext<\n TPath extends string = string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n> {\n readonly #path: TPath;\n readonly #method: string;\n readonly #pathParams: ExtractPathParams<TPath>;\n readonly #searchParams: URLSearchParams;\n readonly #headers: Headers;\n readonly #body: string | undefined;\n readonly #parsedBody: RequestBodyType;\n readonly #inputSchema: TInputSchema | undefined;\n readonly #shouldValidateInput: boolean;\n\n constructor(config: {\n path: TPath;\n method: string;\n pathParams: ExtractPathParams<TPath>;\n searchParams: URLSearchParams;\n parsedBody: RequestBodyType;\n rawBody?: string;\n headers: Headers;\n request?: Request;\n inputSchema?: TInputSchema;\n shouldValidateInput?: boolean;\n }) {\n this.#path = config.path;\n this.#method = config.method;\n this.#pathParams = config.pathParams;\n this.#searchParams = config.searchParams;\n this.#headers = config.headers;\n this.#body = config.rawBody;\n this.#parsedBody = config.parsedBody;\n this.#inputSchema = config.inputSchema;\n this.#shouldValidateInput = config.shouldValidateInput ?? true;\n }\n\n /**\n * Create a RequestContext from a Request object for server-side handling\n */\n static async fromRequest<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n >(config: {\n request: Request;\n method: string;\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n inputSchema?: TInputSchema;\n shouldValidateInput?: boolean;\n state: MutableRequestState;\n rawBody?: string;\n }): Promise<RequestInputContext<TPath, TInputSchema>> {\n // Use the mutable state (potentially modified by middleware)\n return new RequestInputContext({\n method: config.method,\n path: config.path,\n pathParams: config.state.pathParams as ExtractPathParams<TPath>,\n searchParams: config.state.searchParams,\n headers: config.state.headers,\n parsedBody: config.state.body,\n rawBody: config.rawBody,\n inputSchema: config.inputSchema,\n shouldValidateInput: config.shouldValidateInput,\n });\n }\n\n /**\n * Create a RequestContext for server-side rendering contexts (no Request object)\n */\n static fromSSRContext<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n >(\n config:\n | {\n method: \"GET\";\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n searchParams?: URLSearchParams;\n headers?: Headers;\n }\n | {\n method: Exclude<HTTPMethod, \"GET\">;\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n searchParams?: URLSearchParams;\n headers?: Headers;\n body: RequestBodyType;\n inputSchema?: TInputSchema;\n },\n ): RequestInputContext<TPath, TInputSchema> {\n return new RequestInputContext({\n method: config.method,\n path: config.path,\n pathParams: config.pathParams,\n searchParams: config.searchParams ?? new URLSearchParams(),\n headers: config.headers ?? new Headers(),\n parsedBody: \"body\" in config ? config.body : undefined,\n inputSchema: \"inputSchema\" in config ? config.inputSchema : undefined,\n shouldValidateInput: false, // No input validation in SSR context\n });\n }\n\n /**\n * The HTTP method as string (e.g., `GET`, `POST`)\n */\n get method(): string {\n return this.#method;\n }\n /**\n * The matched route path (e.g., `/users/:id`)\n * @remarks `string`\n */\n get path(): TPath {\n return this.#path;\n }\n /**\n * Extracted path parameters as object (e.g., `{ id: '123' }`)\n * @remarks `Record<string, string>`\n */\n get pathParams(): ExtractPathParams<TPath> {\n return this.#pathParams;\n }\n /**\n * [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object for query parameters\n * @remarks `URLSearchParams`\n */\n get query(): URLSearchParams {\n return this.#searchParams;\n }\n /**\n * [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object for request headers\n * @remarks `Headers`\n */\n get headers(): Headers {\n return this.#headers;\n }\n\n get rawBody(): string | undefined {\n return this.#body;\n }\n\n /**\n * Input validation context (only if inputSchema is defined)\n * @remarks `InputContext`\n */\n get input(): TInputSchema extends undefined\n ? undefined\n : {\n schema: TInputSchema;\n valid: () => Promise<\n TInputSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TInputSchema>\n : unknown\n >;\n } {\n if (!this.#inputSchema) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return undefined as any;\n }\n\n return {\n schema: this.#inputSchema,\n valid: async () => {\n if (!this.#shouldValidateInput) {\n // In SSR context, return the body directly without validation\n return this.#parsedBody;\n }\n\n return this.#validateInput();\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n }\n\n async #validateInput(): Promise<\n TInputSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TInputSchema> : never\n > {\n if (!this.#inputSchema) {\n throw new Error(\"No input schema defined for this route\");\n }\n\n if (this.#parsedBody instanceof FormData || this.#parsedBody instanceof Blob) {\n throw new Error(\"Schema validation is only supported for JSON data, not FormData or Blob\");\n }\n\n const result = await this.#inputSchema[\"~standard\"].validate(this.#parsedBody);\n\n if (result.issues) {\n throw new FragnoApiValidationError(\"Validation failed\", result.issues);\n }\n\n return result.value as TInputSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TInputSchema>\n : never;\n }\n}\n"],"mappings":";;;AAYA,IAAa,sBAAb,MAAa,oBAGX;CACA,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,QAWT;AACD,QAAKR,OAAQ,OAAO;AACpB,QAAKC,SAAU,OAAO;AACtB,QAAKC,aAAc,OAAO;AAC1B,QAAKC,eAAgB,OAAO;AAC5B,QAAKC,UAAW,OAAO;AACvB,QAAKC,OAAQ,OAAO;AACpB,QAAKC,aAAc,OAAO;AAC1B,QAAKC,cAAe,OAAO;AAC3B,QAAKC,sBAAuB,OAAO,uBAAuB;;;;;CAM5D,aAAa,YAGX,QASoD;AAEpD,SAAO,IAAI,oBAAoB;GAC7B,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,YAAY,OAAO,MAAM;GACzB,cAAc,OAAO,MAAM;GAC3B,SAAS,OAAO,MAAM;GACtB,YAAY,OAAO,MAAM;GACzB,SAAS,OAAO;GAChB,aAAa,OAAO;GACpB,qBAAqB,OAAO;GAC7B,CAAC;;;;;CAMJ,OAAO,eAIL,QAiB0C;AAC1C,SAAO,IAAI,oBAAoB;GAC7B,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,YAAY,OAAO;GACnB,cAAc,OAAO,gBAAgB,IAAI,iBAAiB;GAC1D,SAAS,OAAO,WAAW,IAAI,SAAS;GACxC,YAAY,UAAU,SAAS,OAAO,OAAO;GAC7C,aAAa,iBAAiB,SAAS,OAAO,cAAc;GAC5D,qBAAqB;GACtB,CAAC;;;;;CAMJ,IAAI,SAAiB;AACnB,SAAO,MAAKP;;;;;;CAMd,IAAI,OAAc;AAChB,SAAO,MAAKD;;;;;;CAMd,IAAI,aAAuC;AACzC,SAAO,MAAKE;;;;;;CAMd,IAAI,QAAyB;AAC3B,SAAO,MAAKC;;;;;;CAMd,IAAI,UAAmB;AACrB,SAAO,MAAKC;;CAGd,IAAI,UAA8B;AAChC,SAAO,MAAKC;;;;;;CAOd,IAAI,QASE;AACJ,MAAI,CAAC,MAAKE,YAER;AAGF,SAAO;GACL,QAAQ,MAAKA;GACb,OAAO,YAAY;AACjB,QAAI,CAAC,MAAKC,oBAER,QAAO,MAAKF;AAGd,WAAO,MAAKG,eAAgB;;GAG/B;;CAGH,OAAMA,gBAEJ;AACA,MAAI,CAAC,MAAKF,YACR,OAAM,IAAI,MAAM,yCAAyC;AAG3D,MAAI,MAAKD,sBAAuB,YAAY,MAAKA,sBAAuB,KACtE,OAAM,IAAI,MAAM,0EAA0E;EAG5F,MAAM,SAAS,MAAM,MAAKC,YAAa,aAAa,SAAS,MAAKD,WAAY;AAE9E,MAAI,OAAO,OACT,OAAM,IAAI,yBAAyB,qBAAqB,OAAO,OAAO;AAGxE,SAAO,OAAO"}
|
|
1
|
+
{"version":3,"file":"request-input-context.js","names":["#path","#method","#pathParams","#searchParams","#headers","#body","#parsedBody","#inputSchema","#shouldValidateInput","#validateInput"],"sources":["../../src/api/request-input-context.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport { FragnoApiValidationError, type HTTPMethod } from \"./api\";\nimport type { MutableRequestState } from \"./mutable-request-state\";\n\nexport type RequestBodyType =\n | unknown // JSON\n | FormData\n | Blob\n | ReadableStream<Uint8Array>\n | null\n | undefined;\n\nexport class RequestInputContext<\n TPath extends string = string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n> {\n readonly #path: TPath;\n readonly #method: string;\n readonly #pathParams: ExtractPathParams<TPath>;\n readonly #searchParams: URLSearchParams;\n readonly #headers: Headers;\n readonly #body: string | undefined;\n readonly #parsedBody: RequestBodyType;\n readonly #inputSchema: TInputSchema | undefined;\n readonly #shouldValidateInput: boolean;\n\n constructor(config: {\n path: TPath;\n method: string;\n pathParams: ExtractPathParams<TPath>;\n searchParams: URLSearchParams;\n parsedBody: RequestBodyType;\n rawBody?: string;\n headers: Headers;\n request?: Request;\n inputSchema?: TInputSchema;\n shouldValidateInput?: boolean;\n }) {\n this.#path = config.path;\n this.#method = config.method;\n this.#pathParams = config.pathParams;\n this.#searchParams = config.searchParams;\n this.#headers = config.headers;\n this.#body = config.rawBody;\n this.#parsedBody = config.parsedBody;\n this.#inputSchema = config.inputSchema;\n this.#shouldValidateInput = config.shouldValidateInput ?? true;\n }\n\n /**\n * Create a RequestContext from a Request object for server-side handling\n */\n static async fromRequest<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n >(config: {\n request: Request;\n method: string;\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n inputSchema?: TInputSchema;\n shouldValidateInput?: boolean;\n state: MutableRequestState;\n rawBody?: string;\n }): Promise<RequestInputContext<TPath, TInputSchema>> {\n // Use the mutable state (potentially modified by middleware)\n return new RequestInputContext({\n method: config.method,\n path: config.path,\n pathParams: config.state.pathParams as ExtractPathParams<TPath>,\n searchParams: config.state.searchParams,\n headers: config.state.headers,\n parsedBody: config.state.body,\n rawBody: config.rawBody,\n inputSchema: config.inputSchema,\n shouldValidateInput: config.shouldValidateInput,\n });\n }\n\n /**\n * Create a RequestContext for server-side rendering contexts (no Request object)\n */\n static fromSSRContext<\n TPath extends string,\n TInputSchema extends StandardSchemaV1 | undefined = undefined,\n >(\n config:\n | {\n method: \"GET\";\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n searchParams?: URLSearchParams;\n headers?: Headers;\n }\n | {\n method: Exclude<HTTPMethod, \"GET\">;\n path: TPath;\n pathParams: ExtractPathParams<TPath>;\n searchParams?: URLSearchParams;\n headers?: Headers;\n body: RequestBodyType;\n inputSchema?: TInputSchema;\n },\n ): RequestInputContext<TPath, TInputSchema> {\n return new RequestInputContext({\n method: config.method,\n path: config.path,\n pathParams: config.pathParams,\n searchParams: config.searchParams ?? new URLSearchParams(),\n headers: config.headers ?? new Headers(),\n parsedBody: \"body\" in config ? config.body : undefined,\n inputSchema: \"inputSchema\" in config ? config.inputSchema : undefined,\n shouldValidateInput: false, // No input validation in SSR context\n });\n }\n\n /**\n * The HTTP method as string (e.g., `GET`, `POST`)\n */\n get method(): string {\n return this.#method;\n }\n /**\n * The matched route path (e.g., `/users/:id`)\n * @remarks `string`\n */\n get path(): TPath {\n return this.#path;\n }\n /**\n * Extracted path parameters as object (e.g., `{ id: '123' }`)\n * @remarks `Record<string, string>`\n */\n get pathParams(): ExtractPathParams<TPath> {\n return this.#pathParams;\n }\n /**\n * [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object for query parameters\n * @remarks `URLSearchParams`\n */\n get query(): URLSearchParams {\n return this.#searchParams;\n }\n /**\n * [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object for request headers\n * @remarks `Headers`\n */\n get headers(): Headers {\n return this.#headers;\n }\n\n get rawBody(): string | undefined {\n return this.#body;\n }\n\n /**\n * Access the request body as FormData.\n *\n * Use this method when handling file uploads or multipart form submissions.\n * The request must have been sent with Content-Type: multipart/form-data.\n *\n * @throws Error if the request body is not FormData\n *\n * @example\n * ```typescript\n * defineRoute({\n * method: \"POST\",\n * path: \"/upload\",\n * async handler(ctx, res) {\n * const formData = ctx.formData();\n * const file = formData.get(\"file\") as File;\n * const description = formData.get(\"description\") as string;\n * // ... process file\n * }\n * });\n * ```\n */\n formData(): FormData {\n if (!(this.#parsedBody instanceof FormData)) {\n throw new Error(\n \"Request body is not FormData. Ensure the request was sent with Content-Type: multipart/form-data.\",\n );\n }\n return this.#parsedBody;\n }\n\n /**\n * Check if the request body is FormData.\n *\n * Useful for routes that accept both JSON and FormData payloads.\n *\n * @example\n * ```typescript\n * defineRoute({\n * method: \"POST\",\n * path: \"/upload\",\n * async handler(ctx, res) {\n * if (ctx.isFormData()) {\n * const formData = ctx.formData();\n * // handle file upload\n * } else {\n * const json = await ctx.input.valid();\n * // handle JSON payload\n * }\n * }\n * });\n * ```\n */\n isFormData(): boolean {\n return this.#parsedBody instanceof FormData;\n }\n\n /**\n * Access the request body as a ReadableStream (application/octet-stream).\n *\n * @throws Error if the request body is not a ReadableStream\n */\n bodyStream(): ReadableStream<Uint8Array> {\n if (!(this.#parsedBody instanceof ReadableStream)) {\n throw new Error(\n \"Request body is not a ReadableStream. Ensure the request was sent with Content-Type: application/octet-stream.\",\n );\n }\n return this.#parsedBody;\n }\n\n /**\n * Check if the request body is a ReadableStream.\n */\n isBodyStream(): boolean {\n return this.#parsedBody instanceof ReadableStream;\n }\n\n /**\n * Input validation context (only if inputSchema is defined)\n * @remarks `InputContext`\n */\n get input(): TInputSchema extends undefined\n ? undefined\n : {\n schema: TInputSchema;\n valid: () => Promise<\n TInputSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TInputSchema>\n : unknown\n >;\n } {\n if (!this.#inputSchema) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return undefined as any;\n }\n\n return {\n schema: this.#inputSchema,\n valid: async () => {\n if (!this.#shouldValidateInput) {\n // In SSR context, return the body directly without validation\n return this.#parsedBody;\n }\n\n return this.#validateInput();\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n }\n\n async #validateInput(): Promise<\n TInputSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TInputSchema> : never\n > {\n if (!this.#inputSchema) {\n throw new Error(\"No input schema defined for this route\");\n }\n\n if (this.#parsedBody instanceof FormData || this.#parsedBody instanceof Blob) {\n throw new Error(\"Schema validation is only supported for JSON data, not FormData or Blob\");\n }\n\n if (this.#parsedBody instanceof ReadableStream) {\n throw new Error(\n \"Schema validation is only supported for JSON data, not FormData, Blob, or ReadableStream\",\n );\n }\n\n const result = await this.#inputSchema[\"~standard\"].validate(this.#parsedBody);\n\n if (result.issues) {\n throw new FragnoApiValidationError(\"Validation failed\", result.issues);\n }\n\n return result.value as TInputSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<TInputSchema>\n : never;\n }\n}\n"],"mappings":";;;AAaA,IAAa,sBAAb,MAAa,oBAGX;CACA,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,QAWT;AACD,QAAKR,OAAQ,OAAO;AACpB,QAAKC,SAAU,OAAO;AACtB,QAAKC,aAAc,OAAO;AAC1B,QAAKC,eAAgB,OAAO;AAC5B,QAAKC,UAAW,OAAO;AACvB,QAAKC,OAAQ,OAAO;AACpB,QAAKC,aAAc,OAAO;AAC1B,QAAKC,cAAe,OAAO;AAC3B,QAAKC,sBAAuB,OAAO,uBAAuB;;;;;CAM5D,aAAa,YAGX,QASoD;AAEpD,SAAO,IAAI,oBAAoB;GAC7B,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,YAAY,OAAO,MAAM;GACzB,cAAc,OAAO,MAAM;GAC3B,SAAS,OAAO,MAAM;GACtB,YAAY,OAAO,MAAM;GACzB,SAAS,OAAO;GAChB,aAAa,OAAO;GACpB,qBAAqB,OAAO;GAC7B,CAAC;;;;;CAMJ,OAAO,eAIL,QAiB0C;AAC1C,SAAO,IAAI,oBAAoB;GAC7B,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,YAAY,OAAO;GACnB,cAAc,OAAO,gBAAgB,IAAI,iBAAiB;GAC1D,SAAS,OAAO,WAAW,IAAI,SAAS;GACxC,YAAY,UAAU,SAAS,OAAO,OAAO;GAC7C,aAAa,iBAAiB,SAAS,OAAO,cAAc;GAC5D,qBAAqB;GACtB,CAAC;;;;;CAMJ,IAAI,SAAiB;AACnB,SAAO,MAAKP;;;;;;CAMd,IAAI,OAAc;AAChB,SAAO,MAAKD;;;;;;CAMd,IAAI,aAAuC;AACzC,SAAO,MAAKE;;;;;;CAMd,IAAI,QAAyB;AAC3B,SAAO,MAAKC;;;;;;CAMd,IAAI,UAAmB;AACrB,SAAO,MAAKC;;CAGd,IAAI,UAA8B;AAChC,SAAO,MAAKC;;;;;;;;;;;;;;;;;;;;;;;;CAyBd,WAAqB;AACnB,MAAI,EAAE,MAAKC,sBAAuB,UAChC,OAAM,IAAI,MACR,oGACD;AAEH,SAAO,MAAKA;;;;;;;;;;;;;;;;;;;;;;;;CAyBd,aAAsB;AACpB,SAAO,MAAKA,sBAAuB;;;;;;;CAQrC,aAAyC;AACvC,MAAI,EAAE,MAAKA,sBAAuB,gBAChC,OAAM,IAAI,MACR,iHACD;AAEH,SAAO,MAAKA;;;;;CAMd,eAAwB;AACtB,SAAO,MAAKA,sBAAuB;;;;;;CAOrC,IAAI,QASE;AACJ,MAAI,CAAC,MAAKC,YAER;AAGF,SAAO;GACL,QAAQ,MAAKA;GACb,OAAO,YAAY;AACjB,QAAI,CAAC,MAAKC,oBAER,QAAO,MAAKF;AAGd,WAAO,MAAKG,eAAgB;;GAG/B;;CAGH,OAAMA,gBAEJ;AACA,MAAI,CAAC,MAAKF,YACR,OAAM,IAAI,MAAM,yCAAyC;AAG3D,MAAI,MAAKD,sBAAuB,YAAY,MAAKA,sBAAuB,KACtE,OAAM,IAAI,MAAM,0EAA0E;AAG5F,MAAI,MAAKA,sBAAuB,eAC9B,OAAM,IAAI,MACR,2FACD;EAGH,MAAM,SAAS,MAAM,MAAKC,YAAa,aAAa,SAAS,MAAKD,WAAY;AAE9E,MAAI,OAAO,OACT,OAAM,IAAI,yBAAyB,qBAAqB,OAAO,OAAO;AAGxE,SAAO,OAAO"}
|
|
@@ -43,7 +43,7 @@ declare class RequestMiddlewareInputContext<const TRoutes extends readonly AnyFr
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
get requestState(): MutableRequestState;
|
|
46
|
-
ifMatchesRoute: <const TMethod extends HTTPMethod, const TPath extends ExtractRoutePath<TRoutes>, const TRoute extends ExtractRouteByPath<TRoutes, TPath, TMethod> = ExtractRouteByPath<TRoutes, TPath, TMethod>>(method: TMethod, path: TPath, handler: (...args: Parameters<TRoute["handler"]>) => Promise<Response | undefined | void> | Response | undefined | void) => Promise<Response | undefined>;
|
|
46
|
+
ifMatchesRoute: <const TMethod extends HTTPMethod, const TPath extends ExtractRoutePath<TRoutes, TMethod>, const TRoute extends ExtractRouteByPath<TRoutes, TPath, TMethod> = ExtractRouteByPath<TRoutes, TPath, TMethod>>(method: TMethod, path: TPath, handler: (...args: Parameters<TRoute["handler"]>) => Promise<Response | undefined | void> | Response | undefined | void) => Promise<Response | undefined>;
|
|
47
47
|
}
|
|
48
48
|
//#endregion
|
|
49
49
|
export { FragnoMiddlewareCallback, RequestMiddlewareInputContext, RequestMiddlewareOptions, RequestMiddlewareOutputContext };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-middleware.d.ts","names":[],"sources":["../../src/api/request-middleware.ts"],"sourcesContent":[],"mappings":";;;;;;;;KASY,kDACe,iDAEP,0CAEJ,8BAA8B,yBAC7B,+BAA+B,OAAO,eAClD,QAAQ,wBAAwB;UAEpB,wBAAA;EATL,IAAA,EAAA,MAAA;EACe,MAAA,EAUjB,UAViB;EAEP,OAAA,EAST,OATS;EAE0B,KAAA,EAQrC,mBARqC;;AACE,cAUnC,8BAVmC,CAAA,WAAA,EAAA,wBAYtB,MAZsB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,SAatC,aAbsC,CAAA,OAAA,EAAA,MAAA,CAAA,CAAA;EAAO,CAAA,OAAA;EAAtC,WAAA,CAAA,IAAA,EAiBG,KAjBH,EAAA,QAAA,EAiBoB,SAjBpB;EACJ,IAAA,IAAA,CAAA,CAAA,EAsBC,KAtBD;EAAR,IAAA,QAAA,CAAA,CAAA,EA0Ba,SA1Bb;;AAAwC,cA+BhC,6BA/BgC,CAAA,sBAAA,SA+B6B,oBA/B7B,EAAA,CAAA,CAAA;EAE5B,CAAA,OAAA;EAEP,WAAA,CAAA,MAAA,EAgCY,OAhCZ,EAAA,OAAA,EAgC8B,wBAhC9B;EACC,IAAA,IAAA,CAAA,CAAA,EAAA,MAAA;EACF,IAAA,MAAA,CAAA,CAAA,EAiDO,UAjDP;EAAmB,IAAA,UAAA,CAAA,CAAA,EAqDR,MArDQ,CAAA,MAAA,EAAA,MAAA,CAAA;EAGf,IAAA,WAAA,CAAA,CAAA,EAsDQ,eAtDsB;EAEjB,IAAA,OAAA,CAAA,CAAA,EAwDT,OAxDS;EAKN,IAAA,WAAA,CAAA,CAAA,EAuDC,gBAvDD,GAAA,SAAA;EAAiB,IAAA,YAAA,CAAA,CAAA,EA2Df,gBA3De,GAAA,SAAA;EAMvB;;;;AASd;;;;;;;;;EA4CsB,IAAA,YAAA,CAAA,CAAA,EAiBA,mBAjBA;EAiBA,cAAA,EAAA,CAAA,sBAMI,UANJ,EAAA,oBAOE,gBAPF,CAOmB,OAPnB,CAAA,EAAA,qBAQG,kBARH,CAQsB,OARtB,EAQ+B,KAR/B,EAQsC,OARtC,CAAA,GAQiD,kBARjD,CAShB,OATgB,EAUhB,KAVgB,EAWhB,OAXgB,CAAA,CAAA,CAAA,MAAA,EAcV,OAdU,EAAA,IAAA,EAeZ,KAfY,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAiBP,UAjBO,CAiBI,MAjBJ,CAAA,SAAA,CAAA,CAAA,EAAA,GAkBb,OAlBa,CAkBL,QAlBK,GAAA,SAAA,GAAA,IAAA,CAAA,GAkB0B,QAlB1B,GAAA,SAAA,GAAA,IAAA,EAAA,GAmBjB,OAnBiB,CAmBT,QAnBS,GAAA,SAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"request-middleware.d.ts","names":[],"sources":["../../src/api/request-middleware.ts"],"sourcesContent":[],"mappings":";;;;;;;;KASY,kDACe,iDAEP,0CAEJ,8BAA8B,yBAC7B,+BAA+B,OAAO,eAClD,QAAQ,wBAAwB;UAEpB,wBAAA;EATL,IAAA,EAAA,MAAA;EACe,MAAA,EAUjB,UAViB;EAEP,OAAA,EAST,OATS;EAE0B,KAAA,EAQrC,mBARqC;;AACE,cAUnC,8BAVmC,CAAA,WAAA,EAAA,wBAYtB,MAZsB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,SAatC,aAbsC,CAAA,OAAA,EAAA,MAAA,CAAA,CAAA;EAAO,CAAA,OAAA;EAAtC,WAAA,CAAA,IAAA,EAiBG,KAjBH,EAAA,QAAA,EAiBoB,SAjBpB;EACJ,IAAA,IAAA,CAAA,CAAA,EAsBC,KAtBD;EAAR,IAAA,QAAA,CAAA,CAAA,EA0Ba,SA1Bb;;AAAwC,cA+BhC,6BA/BgC,CAAA,sBAAA,SA+B6B,oBA/B7B,EAAA,CAAA,CAAA;EAE5B,CAAA,OAAA;EAEP,WAAA,CAAA,MAAA,EAgCY,OAhCZ,EAAA,OAAA,EAgC8B,wBAhC9B;EACC,IAAA,IAAA,CAAA,CAAA,EAAA,MAAA;EACF,IAAA,MAAA,CAAA,CAAA,EAiDO,UAjDP;EAAmB,IAAA,UAAA,CAAA,CAAA,EAqDR,MArDQ,CAAA,MAAA,EAAA,MAAA,CAAA;EAGf,IAAA,WAAA,CAAA,CAAA,EAsDQ,eAtDsB;EAEjB,IAAA,OAAA,CAAA,CAAA,EAwDT,OAxDS;EAKN,IAAA,WAAA,CAAA,CAAA,EAuDC,gBAvDD,GAAA,SAAA;EAAiB,IAAA,YAAA,CAAA,CAAA,EA2Df,gBA3De,GAAA,SAAA;EAMvB;;;;AASd;;;;;;;;;EA4CsB,IAAA,YAAA,CAAA,CAAA,EAiBA,mBAjBA;EAiBA,cAAA,EAAA,CAAA,sBAMI,UANJ,EAAA,oBAOE,gBAPF,CAOmB,OAPnB,EAO4B,OAP5B,CAAA,EAAA,qBAQG,kBARH,CAQsB,OARtB,EAQ+B,KAR/B,EAQsC,OARtC,CAAA,GAQiD,kBARjD,CAShB,OATgB,EAUhB,KAVgB,EAWhB,OAXgB,CAAA,CAAA,CAAA,MAAA,EAcV,OAdU,EAAA,IAAA,EAeZ,KAfY,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAiBP,UAjBO,CAiBI,MAjBJ,CAAA,SAAA,CAAA,CAAA,EAAA,GAkBb,OAlBa,CAkBL,QAlBK,GAAA,SAAA,GAAA,IAAA,CAAA,GAkB0B,QAlB1B,GAAA,SAAA,GAAA,IAAA,EAAA,GAmBjB,OAnBiB,CAmBT,QAnBS,GAAA,SAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-middleware.js","names":["#deps","#services","#options","#route","#state","route"],"sources":["../../src/api/request-middleware.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExtractRouteByPath, ExtractRoutePath } from \"../client/client\";\nimport type { HTTPMethod } from \"./api\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport type { AnyFragnoRouteConfig } from \"./route\";\nimport { RequestInputContext } from \"./request-input-context\";\nimport { OutputContext, RequestOutputContext } from \"./request-output-context\";\nimport { MutableRequestState } from \"./mutable-request-state\";\n\nexport type FragnoMiddlewareCallback<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TDeps,\n TServices extends Record<string, unknown>,\n> = (\n inputContext: RequestMiddlewareInputContext<TRoutes>,\n outputContext: RequestMiddlewareOutputContext<TDeps, TServices>,\n) => Promise<Response | undefined> | Response | undefined;\n\nexport interface RequestMiddlewareOptions {\n path: string;\n method: HTTPMethod;\n request: Request;\n state: MutableRequestState;\n}\n\nexport class RequestMiddlewareOutputContext<\n const TDeps,\n const TServices extends Record<string, unknown>,\n> extends OutputContext<unknown, string> {\n readonly #deps: TDeps;\n readonly #services: TServices;\n\n constructor(deps: TDeps, services: TServices) {\n super();\n this.#deps = deps;\n this.#services = services;\n }\n\n get deps(): TDeps {\n return this.#deps;\n }\n\n get services(): TServices {\n return this.#services;\n }\n}\n\nexport class RequestMiddlewareInputContext<const TRoutes extends readonly AnyFragnoRouteConfig[]> {\n readonly #options: RequestMiddlewareOptions;\n readonly #route: TRoutes[number];\n readonly #state: MutableRequestState;\n\n constructor(routes: TRoutes, options: RequestMiddlewareOptions) {\n this.#options = options;\n this.#state = options.state;\n\n const route = routes.find(\n (route) => route.path === options.path && route.method === options.method,\n );\n\n if (!route) {\n throw new Error(`Route not found: ${options.path} ${options.method}`);\n }\n\n this.#route = route;\n }\n\n get path(): string {\n return this.#options.path;\n }\n\n get method(): HTTPMethod {\n return this.#options.method;\n }\n\n get pathParams(): Record<string, string> {\n return this.#state.pathParams;\n }\n\n get queryParams(): URLSearchParams {\n return this.#state.searchParams;\n }\n\n get headers(): Headers {\n return this.#state.headers;\n }\n\n get inputSchema(): StandardSchemaV1 | undefined {\n return this.#route.inputSchema;\n }\n\n get outputSchema(): StandardSchemaV1 | undefined {\n return this.#route.outputSchema;\n }\n\n /**\n * Access to the mutable request state.\n * Use this to modify query parameters, path parameters, or request body.\n *\n * @example\n * ```typescript\n * // Modify body\n * requestState.setBody({ modified: true });\n *\n * // Query params are already accessible via queryParams getter\n * // Path params are already accessible via pathParams getter\n * ```\n */\n get requestState(): MutableRequestState {\n return this.#state;\n }\n\n // Defined as a field so that `this` reference stays in tact when destructuring\n ifMatchesRoute = async <\n const TMethod extends HTTPMethod,\n const TPath extends ExtractRoutePath<TRoutes>,\n const TRoute extends ExtractRouteByPath<TRoutes, TPath, TMethod> = ExtractRouteByPath<\n TRoutes,\n TPath,\n TMethod\n >,\n >(\n method: TMethod,\n path: TPath,\n handler: (\n ...args: Parameters<TRoute[\"handler\"]>\n ) => Promise<Response | undefined | void> | Response | undefined | void,\n ): Promise<Response | undefined> => {\n if (this.path !== path || this.method !== method) {\n return undefined;\n }\n\n // TODO(Wilco): We should support reading/modifying headers here.\n const inputContext = await RequestInputContext.fromRequest({\n request: this.#options.request,\n method: this.#options.method,\n path: path,\n pathParams: this.pathParams as ExtractPathParams<TPath>,\n inputSchema: this.#route.inputSchema,\n state: this.#state,\n });\n\n const outputContext = new RequestOutputContext(this.#route.outputSchema);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (handler as any)(inputContext, outputContext);\n };\n}\n"],"mappings":";;;;AAyBA,IAAa,iCAAb,cAGU,cAA+B;CACvC,CAASA;CACT,CAASC;CAET,YAAY,MAAa,UAAqB;AAC5C,SAAO;AACP,QAAKD,OAAQ;AACb,QAAKC,WAAY;;CAGnB,IAAI,OAAc;AAChB,SAAO,MAAKD;;CAGd,IAAI,WAAsB;AACxB,SAAO,MAAKC;;;AAIhB,IAAa,gCAAb,MAAkG;CAChG,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,QAAiB,SAAmC;AAC9D,QAAKF,UAAW;AAChB,QAAKE,QAAS,QAAQ;EAEtB,MAAM,QAAQ,OAAO,MAClB,YAAUC,QAAM,SAAS,QAAQ,QAAQA,QAAM,WAAW,QAAQ,OACpE;AAED,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,oBAAoB,QAAQ,KAAK,GAAG,QAAQ,SAAS;AAGvE,QAAKF,QAAS;;CAGhB,IAAI,OAAe;AACjB,SAAO,MAAKD,QAAS;;CAGvB,IAAI,SAAqB;AACvB,SAAO,MAAKA,QAAS;;CAGvB,IAAI,aAAqC;AACvC,SAAO,MAAKE,MAAO;;CAGrB,IAAI,cAA+B;AACjC,SAAO,MAAKA,MAAO;;CAGrB,IAAI,UAAmB;AACrB,SAAO,MAAKA,MAAO;;CAGrB,IAAI,cAA4C;AAC9C,SAAO,MAAKD,MAAO;;CAGrB,IAAI,eAA6C;AAC/C,SAAO,MAAKA,MAAO;;;;;;;;;;;;;;;CAgBrB,IAAI,eAAoC;AACtC,SAAO,MAAKC;;CAId,iBAAiB,OASf,QACA,MACA,YAGkC;AAClC,MAAI,KAAK,SAAS,QAAQ,KAAK,WAAW,OACxC;AAgBF,SAAO,MAAO,QAZO,MAAM,oBAAoB,YAAY;GACzD,SAAS,MAAKF,QAAS;GACvB,QAAQ,MAAKA,QAAS;GAChB;GACN,YAAY,KAAK;GACjB,aAAa,MAAKC,MAAO;GACzB,OAAO,MAAKC;GACb,CAAC,EAEoB,IAAI,qBAAqB,MAAKD,MAAO,aAAa,CAGd"}
|
|
1
|
+
{"version":3,"file":"request-middleware.js","names":["#deps","#services","#options","#route","#state","route"],"sources":["../../src/api/request-middleware.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExtractRouteByPath, ExtractRoutePath } from \"../client/client\";\nimport type { HTTPMethod } from \"./api\";\nimport type { ExtractPathParams } from \"./internal/path\";\nimport type { AnyFragnoRouteConfig } from \"./route\";\nimport { RequestInputContext } from \"./request-input-context\";\nimport { OutputContext, RequestOutputContext } from \"./request-output-context\";\nimport { MutableRequestState } from \"./mutable-request-state\";\n\nexport type FragnoMiddlewareCallback<\n TRoutes extends readonly AnyFragnoRouteConfig[],\n TDeps,\n TServices extends Record<string, unknown>,\n> = (\n inputContext: RequestMiddlewareInputContext<TRoutes>,\n outputContext: RequestMiddlewareOutputContext<TDeps, TServices>,\n) => Promise<Response | undefined> | Response | undefined;\n\nexport interface RequestMiddlewareOptions {\n path: string;\n method: HTTPMethod;\n request: Request;\n state: MutableRequestState;\n}\n\nexport class RequestMiddlewareOutputContext<\n const TDeps,\n const TServices extends Record<string, unknown>,\n> extends OutputContext<unknown, string> {\n readonly #deps: TDeps;\n readonly #services: TServices;\n\n constructor(deps: TDeps, services: TServices) {\n super();\n this.#deps = deps;\n this.#services = services;\n }\n\n get deps(): TDeps {\n return this.#deps;\n }\n\n get services(): TServices {\n return this.#services;\n }\n}\n\nexport class RequestMiddlewareInputContext<const TRoutes extends readonly AnyFragnoRouteConfig[]> {\n readonly #options: RequestMiddlewareOptions;\n readonly #route: TRoutes[number];\n readonly #state: MutableRequestState;\n\n constructor(routes: TRoutes, options: RequestMiddlewareOptions) {\n this.#options = options;\n this.#state = options.state;\n\n const route = routes.find(\n (route) => route.path === options.path && route.method === options.method,\n );\n\n if (!route) {\n throw new Error(`Route not found: ${options.path} ${options.method}`);\n }\n\n this.#route = route;\n }\n\n get path(): string {\n return this.#options.path;\n }\n\n get method(): HTTPMethod {\n return this.#options.method;\n }\n\n get pathParams(): Record<string, string> {\n return this.#state.pathParams;\n }\n\n get queryParams(): URLSearchParams {\n return this.#state.searchParams;\n }\n\n get headers(): Headers {\n return this.#state.headers;\n }\n\n get inputSchema(): StandardSchemaV1 | undefined {\n return this.#route.inputSchema;\n }\n\n get outputSchema(): StandardSchemaV1 | undefined {\n return this.#route.outputSchema;\n }\n\n /**\n * Access to the mutable request state.\n * Use this to modify query parameters, path parameters, or request body.\n *\n * @example\n * ```typescript\n * // Modify body\n * requestState.setBody({ modified: true });\n *\n * // Query params are already accessible via queryParams getter\n * // Path params are already accessible via pathParams getter\n * ```\n */\n get requestState(): MutableRequestState {\n return this.#state;\n }\n\n // Defined as a field so that `this` reference stays in tact when destructuring\n ifMatchesRoute = async <\n const TMethod extends HTTPMethod,\n const TPath extends ExtractRoutePath<TRoutes, TMethod>,\n const TRoute extends ExtractRouteByPath<TRoutes, TPath, TMethod> = ExtractRouteByPath<\n TRoutes,\n TPath,\n TMethod\n >,\n >(\n method: TMethod,\n path: TPath,\n handler: (\n ...args: Parameters<TRoute[\"handler\"]>\n ) => Promise<Response | undefined | void> | Response | undefined | void,\n ): Promise<Response | undefined> => {\n if (this.path !== path || this.method !== method) {\n return undefined;\n }\n\n // TODO(Wilco): We should support reading/modifying headers here.\n const inputContext = await RequestInputContext.fromRequest({\n request: this.#options.request,\n method: this.#options.method,\n path: path,\n pathParams: this.pathParams as ExtractPathParams<TPath>,\n inputSchema: this.#route.inputSchema,\n state: this.#state,\n });\n\n const outputContext = new RequestOutputContext(this.#route.outputSchema);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await (handler as any)(inputContext, outputContext);\n };\n}\n"],"mappings":";;;;AAyBA,IAAa,iCAAb,cAGU,cAA+B;CACvC,CAASA;CACT,CAASC;CAET,YAAY,MAAa,UAAqB;AAC5C,SAAO;AACP,QAAKD,OAAQ;AACb,QAAKC,WAAY;;CAGnB,IAAI,OAAc;AAChB,SAAO,MAAKD;;CAGd,IAAI,WAAsB;AACxB,SAAO,MAAKC;;;AAIhB,IAAa,gCAAb,MAAkG;CAChG,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,QAAiB,SAAmC;AAC9D,QAAKF,UAAW;AAChB,QAAKE,QAAS,QAAQ;EAEtB,MAAM,QAAQ,OAAO,MAClB,YAAUC,QAAM,SAAS,QAAQ,QAAQA,QAAM,WAAW,QAAQ,OACpE;AAED,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,oBAAoB,QAAQ,KAAK,GAAG,QAAQ,SAAS;AAGvE,QAAKF,QAAS;;CAGhB,IAAI,OAAe;AACjB,SAAO,MAAKD,QAAS;;CAGvB,IAAI,SAAqB;AACvB,SAAO,MAAKA,QAAS;;CAGvB,IAAI,aAAqC;AACvC,SAAO,MAAKE,MAAO;;CAGrB,IAAI,cAA+B;AACjC,SAAO,MAAKA,MAAO;;CAGrB,IAAI,UAAmB;AACrB,SAAO,MAAKA,MAAO;;CAGrB,IAAI,cAA4C;AAC9C,SAAO,MAAKD,MAAO;;CAGrB,IAAI,eAA6C;AAC/C,SAAO,MAAKA,MAAO;;;;;;;;;;;;;;;CAgBrB,IAAI,eAAoC;AACtC,SAAO,MAAKC;;CAId,iBAAiB,OASf,QACA,MACA,YAGkC;AAClC,MAAI,KAAK,SAAS,QAAQ,KAAK,WAAW,OACxC;AAgBF,SAAO,MAAO,QAZO,MAAM,oBAAoB,YAAY;GACzD,SAAS,MAAKF,QAAS;GACvB,QAAQ,MAAKA,QAAS;GAChB;GACN,YAAY,KAAK;GACjB,aAAa,MAAKC,MAAO;GACzB,OAAO,MAAKC;GACb,CAAC,EAEoB,IAAI,qBAAqB,MAAKD,MAAO,aAAa,CAGd"}
|
package/dist/api/route.d.ts
CHANGED
|
@@ -31,13 +31,13 @@ declare function defineRoute<const TMethod extends HTTPMethod, const TPath exten
|
|
|
31
31
|
declare function defineRoute<const TMethod extends HTTPMethod, const TPath extends string, const TInputSchema extends StandardSchemaV1, const TOutputSchema extends StandardSchemaV1 | undefined, const TErrorCode extends string = string, const TQueryParameters extends string = string, const TThisContext extends RequestThisContext = RequestThisContext>(config: FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters, TThisContext> & {
|
|
32
32
|
inputSchema: TInputSchema;
|
|
33
33
|
}): FragnoRouteConfig<TMethod, TPath, TInputSchema, TOutputSchema, TErrorCode, TQueryParameters, TThisContext>;
|
|
34
|
-
type AnyFragmentDefinition = FragmentDefinition<any, any, any, any, any, any, any, any, any, any>;
|
|
35
|
-
type ExtractFragmentConfig<T> = T extends FragmentDefinition<infer TConfig, any, any, any, any, any, any, any, any, any> ? TConfig : never;
|
|
36
|
-
type ExtractFragmentDeps<T> = T extends FragmentDefinition<any, any, infer TDeps, any, any, any, any, any, any, any> ? TDeps : never;
|
|
37
|
-
type ExtractFragmentServices<T> = T extends FragmentDefinition<any, any, any, infer TBaseServices, infer TServices, any, any, any, any, any> ? BoundServices<TBaseServices & TServices> : never;
|
|
38
|
-
type ExtractFragmentServiceDeps<T> = T extends FragmentDefinition<any, any, any, any, any, infer TServiceDependencies, any, any, any, any> ? TServiceDependencies : never;
|
|
39
|
-
type ExtractFragmentServiceThisContext<T> = T extends FragmentDefinition<any, any, any, any, any, any, any, infer TServiceThisContext, any, any> ? TServiceThisContext : RequestThisContext;
|
|
40
|
-
type ExtractFragmentHandlerThisContext<T> = T extends FragmentDefinition<any, any, any, any, any, any, any, any, infer THandlerThisContext, any> ? THandlerThisContext : RequestThisContext;
|
|
34
|
+
type AnyFragmentDefinition = FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>;
|
|
35
|
+
type ExtractFragmentConfig<T> = T extends FragmentDefinition<infer TConfig, any, any, any, any, any, any, any, any, any, any> ? TConfig : never;
|
|
36
|
+
type ExtractFragmentDeps<T> = T extends FragmentDefinition<any, any, infer TDeps, any, any, any, any, any, any, any, any> ? TDeps : never;
|
|
37
|
+
type ExtractFragmentServices<T> = T extends FragmentDefinition<any, any, any, infer TBaseServices, infer TServices, any, any, any, any, any, any> ? BoundServices<TBaseServices & TServices> : never;
|
|
38
|
+
type ExtractFragmentServiceDeps<T> = T extends FragmentDefinition<any, any, any, any, any, infer TServiceDependencies, any, any, any, any, any> ? TServiceDependencies : never;
|
|
39
|
+
type ExtractFragmentServiceThisContext<T> = T extends FragmentDefinition<any, any, any, any, any, any, any, infer TServiceThisContext, any, any, any> ? TServiceThisContext : RequestThisContext;
|
|
40
|
+
type ExtractFragmentHandlerThisContext<T> = T extends FragmentDefinition<any, any, any, any, any, any, any, any, infer THandlerThisContext, any, any> ? THandlerThisContext : RequestThisContext;
|
|
41
41
|
type ExtractFragmentThisContext<T> = ExtractFragmentServiceThisContext<T>;
|
|
42
42
|
declare function defineRoutes<const TDefinition extends AnyFragmentDefinition>(definition: TDefinition): {
|
|
43
43
|
create: <const TRoutes$1 extends readonly FragnoRouteConfig<HTTPMethod, string, StandardSchemaV1 | undefined, StandardSchemaV1 | undefined, string, string, ExtractFragmentHandlerThisContext<TDefinition>>[]>(fn: (context: RouteFactoryContext<ExtractFragmentConfig<TDefinition>, ExtractFragmentDeps<TDefinition>, ExtractFragmentServices<TDefinition>, ExtractFragmentServiceDeps<TDefinition>> & {
|
package/dist/api/route.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","names":[],"sources":["../../src/api/route.ts"],"sourcesContent":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"route.d.ts","names":[],"sources":["../../src/api/route.ts"],"sourcesContent":[],"mappings":"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"}
|