@blixis-io/http 0.3.1 → 0.4.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/dist/index.d.ts +19 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,10 @@ export declare function getControllerPrefix(target: object): string | undefined;
|
|
|
30
30
|
interface ExecutionContext {
|
|
31
31
|
request: Request;
|
|
32
32
|
params: Readonly<Record<string, string>>;
|
|
33
|
+
/** The controller class whose route is being handled. Read route metadata with `getRouteMetadata(KEY, context)`. */
|
|
34
|
+
controller: Class;
|
|
35
|
+
/** The name of the controller method handling this request. */
|
|
36
|
+
handler: string | symbol;
|
|
33
37
|
}
|
|
34
38
|
interface CanActivate {
|
|
35
39
|
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
|
|
@@ -43,6 +47,20 @@ interface CanActivate {
|
|
|
43
47
|
export declare function UseGuards(...guards: Class<CanActivate>[]): ClassDecorator & MethodDecorator;
|
|
44
48
|
export declare function getClassGuards(target: object): Class<CanActivate>[];
|
|
45
49
|
export declare function getMethodGuards(target: object, propertyKey: string | symbol): Class<CanActivate>[];
|
|
50
|
+
/**
|
|
51
|
+
* Marks a guard class as global: once it is registered as a provider, it runs on **every** route, before
|
|
52
|
+
* the route's own `@UseGuards`. Found through the application's providers, so there is nothing else to
|
|
53
|
+
* wire up. Several global guards run in dependency order; the first to deny stops the request.
|
|
54
|
+
*/
|
|
55
|
+
export declare function GlobalGuard(): ClassDecorator;
|
|
56
|
+
export declare function isGlobalGuard(target: object): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* A decorator that attaches `value` under `key` to a controller (every route in it) or to one route, so a
|
|
59
|
+
* guard or interceptor can read it with `getRouteMetadata`. `@Roles("admin")` and `@Public()` are built on this.
|
|
60
|
+
*/
|
|
61
|
+
export declare function SetRouteMetadata(key: symbol, value: unknown): ClassDecorator & MethodDecorator;
|
|
62
|
+
/** What `SetRouteMetadata` stored for the route being handled: the method's own value if it has one, else the controller's. */
|
|
63
|
+
export declare function getRouteMetadata(key: symbol, context: Pick<ExecutionContext, "controller" | "handler">): unknown;
|
|
46
64
|
//#endregion
|
|
47
65
|
//#region src/decorators/interceptors.d.ts
|
|
48
66
|
interface Interceptor {
|
|
@@ -216,13 +234,7 @@ interface RouteEntry {
|
|
|
216
234
|
guards: Class<CanActivate>[];
|
|
217
235
|
interceptors: Class<Interceptor>[];
|
|
218
236
|
}
|
|
219
|
-
|
|
220
|
-
* Flattens every `@Controller`'s route metadata into one `Router`. Guard
|
|
221
|
-
* classes referenced via `@UseGuards` must also be registered as providers
|
|
222
|
-
* on the owning module — this doesn't instantiate them, it only records
|
|
223
|
-
* which classes `createHandler` will later ask the `Application` for.
|
|
224
|
-
*/
|
|
225
|
-
export declare function buildRouter(controllers: readonly Class[]): Router<RouteEntry>;
|
|
237
|
+
export declare function buildRouter(controllers: readonly Class[], globalGuards?: readonly Class<CanActivate>[]): Router<RouteEntry>;
|
|
226
238
|
/** Builds a single `(Request) => Promise<Response>` function serving every controller's routes. */
|
|
227
239
|
export declare function createHandler(controllers: readonly Class[], application: Application, options?: HandlerOptions): (request: Request) => Promise<Response>;
|
|
228
240
|
//#endregion
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/decorators/api-metadata.ts","../src/decorators/controller.ts","../src/decorators/guards.ts","../src/decorators/interceptors.ts","../src/decorators/params.ts","../src/types.ts","../src/decorators/routes.ts","../src/exceptions.ts","../src/router.ts","../src/handler.ts","../src/http-application.ts","../src/fetch-handler.ts","../src/node-adapter.ts","../src/params.ts","../src/request-context.ts","../src/response.ts"],"mappings":";;;;;UAEiB;EACf;EACA;;EAEA;;;wBAOc,aAAa,SAAS,sBAAsB;wBAM5C,gBAAgB,gBAAgB,+BAA+B;;;;;;;wBAU/D,WAAW,iBAAiB,iBAAiB;wBAa7C,gBAAgB;wBAIhB,iBAAiB,gBAAgB;;;wBCtCjC,WAAW,kBAAc;wBAMzB,oBAAoB;;;UCZnB;EACf,SAAS;EACT,QAAQ,SAAS;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/decorators/api-metadata.ts","../src/decorators/controller.ts","../src/decorators/guards.ts","../src/decorators/interceptors.ts","../src/decorators/params.ts","../src/types.ts","../src/decorators/routes.ts","../src/exceptions.ts","../src/router.ts","../src/handler.ts","../src/http-application.ts","../src/fetch-handler.ts","../src/node-adapter.ts","../src/params.ts","../src/request-context.ts","../src/response.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -82,6 +82,36 @@ function getClassGuards(target) {
|
|
|
82
82
|
function getMethodGuards(target, propertyKey) {
|
|
83
83
|
return getMetadata(GUARDS, target, propertyKey) ?? [];
|
|
84
84
|
}
|
|
85
|
+
const GLOBAL_GUARD = Symbol("blixis:global-guard");
|
|
86
|
+
/**
|
|
87
|
+
* Marks a guard class as global: once it is registered as a provider, it runs on **every** route, before
|
|
88
|
+
* the route's own `@UseGuards`. Found through the application's providers, so there is nothing else to
|
|
89
|
+
* wire up. Several global guards run in dependency order; the first to deny stops the request.
|
|
90
|
+
*/
|
|
91
|
+
function GlobalGuard() {
|
|
92
|
+
return (target) => {
|
|
93
|
+
defineMetadata(GLOBAL_GUARD, true, target);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function isGlobalGuard(target) {
|
|
97
|
+
return getMetadata(GLOBAL_GUARD, target) === true;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A decorator that attaches `value` under `key` to a controller (every route in it) or to one route, so a
|
|
101
|
+
* guard or interceptor can read it with `getRouteMetadata`. `@Roles("admin")` and `@Public()` are built on this.
|
|
102
|
+
*/
|
|
103
|
+
function SetRouteMetadata(key, value) {
|
|
104
|
+
const decorator = (target, propertyKey) => {
|
|
105
|
+
if (propertyKey === void 0) defineMetadata(key, value, target);
|
|
106
|
+
else defineMetadata(key, value, target, propertyKey);
|
|
107
|
+
};
|
|
108
|
+
return decorator;
|
|
109
|
+
}
|
|
110
|
+
/** What `SetRouteMetadata` stored for the route being handled: the method's own value if it has one, else the controller's. */
|
|
111
|
+
function getRouteMetadata(key, context) {
|
|
112
|
+
const prototype = context.controller.prototype;
|
|
113
|
+
return (typeof prototype === "object" && prototype !== null ? getMetadata(key, prototype, context.handler) : void 0) ?? getMetadata(key, context.controller);
|
|
114
|
+
}
|
|
85
115
|
//#endregion
|
|
86
116
|
//#region src/decorators/interceptors.ts
|
|
87
117
|
const INTERCEPTORS = Symbol("blixis:interceptors");
|
|
@@ -495,7 +525,21 @@ var NotAControllerError = class extends Error {
|
|
|
495
525
|
* on the owning module — this doesn't instantiate them, it only records
|
|
496
526
|
* which classes `createHandler` will later ask the `Application` for.
|
|
497
527
|
*/
|
|
498
|
-
|
|
528
|
+
/** Guard classes marked `@GlobalGuard()` among the application's resolved providers, in dependency order. */
|
|
529
|
+
function discoverGlobalGuards(application) {
|
|
530
|
+
const found = [];
|
|
531
|
+
for (const [, instance] of application.resolved()) {
|
|
532
|
+
if (typeof instance !== "object" || instance === null || !isGlobalGuard(instance.constructor)) continue;
|
|
533
|
+
const guardClass = instance.constructor;
|
|
534
|
+
if (!isGuardClass(guardClass)) throw new TypeError(`${instance.constructor.name} is marked @GlobalGuard() but has no canActivate() method.`);
|
|
535
|
+
found.push(guardClass);
|
|
536
|
+
}
|
|
537
|
+
return found;
|
|
538
|
+
}
|
|
539
|
+
function isGuardClass(value) {
|
|
540
|
+
return typeof value === "function" && typeof value.prototype === "object" && value.prototype !== null && "canActivate" in value.prototype;
|
|
541
|
+
}
|
|
542
|
+
function buildRouter(controllers, globalGuards = []) {
|
|
499
543
|
const router = new Router();
|
|
500
544
|
for (const controller of controllers) {
|
|
501
545
|
const prefix = getControllerPrefix(controller);
|
|
@@ -510,7 +554,11 @@ function buildRouter(controllers) {
|
|
|
510
554
|
httpCode: getHttpCode(prototype, route.propertyKey),
|
|
511
555
|
responseSchema: getReturnsSchema(prototype, route.propertyKey),
|
|
512
556
|
validateResponse: getReturnsValidate(prototype, route.propertyKey),
|
|
513
|
-
guards: [
|
|
557
|
+
guards: [
|
|
558
|
+
...globalGuards,
|
|
559
|
+
...classGuards,
|
|
560
|
+
...getMethodGuards(prototype, route.propertyKey)
|
|
561
|
+
],
|
|
514
562
|
interceptors: [...classInterceptors, ...getMethodInterceptors(prototype, route.propertyKey)]
|
|
515
563
|
});
|
|
516
564
|
}
|
|
@@ -611,7 +659,7 @@ function raceAbort(work, signal, clientSignal) {
|
|
|
611
659
|
}
|
|
612
660
|
/** Builds a single `(Request) => Promise<Response>` function serving every controller's routes. */
|
|
613
661
|
function createHandler(controllers, application, options = {}) {
|
|
614
|
-
const router = buildRouter(controllers);
|
|
662
|
+
const router = buildRouter(controllers, discoverGlobalGuards(application));
|
|
615
663
|
const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;
|
|
616
664
|
const requestTimeout = options.requestTimeout;
|
|
617
665
|
const validateByDefault = options.responseValidation !== "never";
|
|
@@ -630,7 +678,9 @@ function createHandler(controllers, application, options = {}) {
|
|
|
630
678
|
try {
|
|
631
679
|
for (const guardClass of route.guards) if (!await application.get(guardClass).canActivate({
|
|
632
680
|
request,
|
|
633
|
-
params: match.params
|
|
681
|
+
params: match.params,
|
|
682
|
+
controller: route.controller,
|
|
683
|
+
handler: route.propertyKey
|
|
634
684
|
})) throw new ForbiddenException();
|
|
635
685
|
const invoke = async () => {
|
|
636
686
|
let cachedBody;
|
|
@@ -648,7 +698,9 @@ function createHandler(controllers, application, options = {}) {
|
|
|
648
698
|
const interceptor = application.get(interceptorClass);
|
|
649
699
|
return async () => interceptor.intercept({
|
|
650
700
|
request,
|
|
651
|
-
params: match.params
|
|
701
|
+
params: match.params,
|
|
702
|
+
controller: route.controller,
|
|
703
|
+
handler: route.propertyKey
|
|
652
704
|
}, next);
|
|
653
705
|
}, invoke);
|
|
654
706
|
return await (requestTimeout === void 0 ? pipeline() : raceAbort(pipeline(), request.signal, incoming.signal));
|
|
@@ -875,6 +927,6 @@ const HTTP_METHODS = [
|
|
|
875
927
|
"OPTIONS"
|
|
876
928
|
];
|
|
877
929
|
//#endregion
|
|
878
|
-
export { ApiOperation, ApiTags, BadRequestException, Body, ConflictException, Controller, Delete, ForbiddenException, GatewayTimeoutException, Get, HTTP_METHODS, Headers$1 as Headers, HttpApplication, HttpCode, HttpException, NotAControllerError, NotFoundException, Param, Patch, PayloadTooLargeException, Post, Put, Query, Req, RequestContext, RequestContextError, ResponseValidationError, Returns, Router, UnauthorizedException, UnsupportedMediaTypeException, UseGuards, UseInterceptors, buildRouter, createFetchHandler, createHandler, createHttpApplication, getApiOperation, getClassApiTags, getClassGuards, getClassInterceptors, getControllerPrefix, getHttpCode, getMethodApiTags, getMethodGuards, getMethodInterceptors, getParamSources, getReturnsSchema, getReturnsValidate, getRoutes, resolveHandlerArgs, runInRequestContext, sendWebResponse, toWebRequest, validateResponse };
|
|
930
|
+
export { ApiOperation, ApiTags, BadRequestException, Body, ConflictException, Controller, Delete, ForbiddenException, GatewayTimeoutException, Get, GlobalGuard, HTTP_METHODS, Headers$1 as Headers, HttpApplication, HttpCode, HttpException, NotAControllerError, NotFoundException, Param, Patch, PayloadTooLargeException, Post, Put, Query, Req, RequestContext, RequestContextError, ResponseValidationError, Returns, Router, SetRouteMetadata, UnauthorizedException, UnsupportedMediaTypeException, UseGuards, UseInterceptors, buildRouter, createFetchHandler, createHandler, createHttpApplication, getApiOperation, getClassApiTags, getClassGuards, getClassInterceptors, getControllerPrefix, getHttpCode, getMethodApiTags, getMethodGuards, getMethodInterceptors, getParamSources, getReturnsSchema, getReturnsValidate, getRouteMetadata, getRoutes, isGlobalGuard, resolveHandlerArgs, runInRequestContext, sendWebResponse, toWebRequest, validateResponse };
|
|
879
931
|
|
|
880
932
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Headers","exhaustive"],"sources":["../src/decorators/api-metadata.ts","../src/decorators/controller.ts","../src/decorators/guards.ts","../src/decorators/interceptors.ts","../src/decorators/params.ts","../src/decorators/routes.ts","../src/exceptions.ts","../src/params.ts","../src/request-context.ts","../src/response.ts","../src/router.ts","../src/handler.ts","../src/node-adapter.ts","../src/http-application.ts","../src/fetch-handler.ts","../src/types.ts"],"sourcesContent":["import { defineMetadata, getMetadata } from \"@blixis-io/di\";\n\nexport interface ApiOperationOptions {\n summary?: string;\n description?: string;\n /** Defaults to `${ControllerName}_${methodName}` when omitted — every operation needs a unique one, valid or not. */\n operationId?: string;\n}\n\nconst API_OPERATION = Symbol(\"blixis:api-operation\");\nconst API_TAGS = Symbol(\"blixis:api-tags\");\n\n/** Documents one route for `@blixis-io/openapi` — entirely optional, a route without it still gets a valid (derived) operationId. */\nexport function ApiOperation(options: ApiOperationOptions): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(API_OPERATION, options, target, propertyKey);\n };\n}\n\nexport function getApiOperation(target: object, propertyKey: string | symbol): ApiOperationOptions | undefined {\n return getMetadata(API_OPERATION, target, propertyKey);\n}\n\n/**\n * Applies OpenAPI tags to a controller (class position) or a single route\n * (method position) — same dual-shape decorator as `@UseGuards`. Class-level\n * and method-level tags both apply, concatenated, not one replacing the\n * other.\n */\nexport function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<string[]>(API_TAGS, target) ?? [];\n defineMetadata(API_TAGS, [...existing, ...tags], target);\n } else {\n const existing = getMetadata<string[]>(API_TAGS, target, propertyKey) ?? [];\n defineMetadata(API_TAGS, [...existing, ...tags], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassApiTags(target: object): string[] {\n return getMetadata(API_TAGS, target) ?? [];\n}\n\nexport function getMethodApiTags(target: object, propertyKey: string | symbol): string[] {\n return getMetadata(API_TAGS, target, propertyKey) ?? [];\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\n\nconst CONTROLLER_PREFIX = Symbol(\"blixis:controller-prefix\");\n\nfunction stripSlashes(value: string): string {\n return value.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nexport function Controller(prefix = \"\"): ClassDecorator {\n return (target) => {\n defineMetadata(CONTROLLER_PREFIX, stripSlashes(prefix), target);\n };\n}\n\nexport function getControllerPrefix(target: object): string | undefined {\n return getMetadata(CONTROLLER_PREFIX, target);\n}\n","import { defineMetadata, getMetadata, type Class } from \"@blixis-io/di\";\n\nexport interface ExecutionContext {\n request: Request;\n params: Readonly<Record<string, string>>;\n}\n\nexport interface CanActivate {\n canActivate(context: ExecutionContext): boolean | Promise<boolean>;\n}\n\nconst GUARDS = Symbol(\"blixis:guards\");\n\n/**\n * Applies guards to a controller (class position) or a single route\n * (method position) — the same decorator function works in both spots,\n * matching the shape TS's legacy decorators give it: a class decorator\n * receives no `propertyKey`, a method decorator does.\n */\nexport function UseGuards(...guards: Class<CanActivate>[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<Class<CanActivate>[]>(GUARDS, target) ?? [];\n defineMetadata(GUARDS, [...existing, ...guards], target);\n } else {\n const existing = getMetadata<Class<CanActivate>[]>(GUARDS, target, propertyKey) ?? [];\n defineMetadata(GUARDS, [...existing, ...guards], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassGuards(target: object): Class<CanActivate>[] {\n return getMetadata(GUARDS, target) ?? [];\n}\n\nexport function getMethodGuards(target: object, propertyKey: string | symbol): Class<CanActivate>[] {\n return getMetadata(GUARDS, target, propertyKey) ?? [];\n}\n","import { defineMetadata, getMetadata, type Class } from \"@blixis-io/di\";\nimport type { ExecutionContext } from \"./guards.js\";\n\nexport interface Interceptor {\n intercept(context: ExecutionContext, next: () => Promise<Response>): Response | Promise<Response>;\n}\n\nconst INTERCEPTORS = Symbol(\"blixis:interceptors\");\n\n/**\n * Applies interceptors to a controller (class position) or a single route\n * (method position) — same dual-shape decorator as `@UseGuards`. Interceptors\n * wrap param resolution + handler invocation (guards already ran and\n * already decided the request is allowed through); class-level interceptors\n * wrap outermost, method-level innermost, closest to the handler.\n */\nexport function UseInterceptors(...interceptors: Class<Interceptor>[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<Class<Interceptor>[]>(INTERCEPTORS, target) ?? [];\n defineMetadata(INTERCEPTORS, [...existing, ...interceptors], target);\n } else {\n const existing = getMetadata<Class<Interceptor>[]>(INTERCEPTORS, target, propertyKey) ?? [];\n defineMetadata(INTERCEPTORS, [...existing, ...interceptors], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassInterceptors(target: object): Class<Interceptor>[] {\n return getMetadata(INTERCEPTORS, target) ?? [];\n}\n\nexport function getMethodInterceptors(target: object, propertyKey: string | symbol): Class<Interceptor>[] {\n return getMetadata(INTERCEPTORS, target, propertyKey) ?? [];\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\n\nexport type ParamSource =\n | { kind: \"body\"; schema?: ZodType | undefined }\n | { kind: \"query\"; schema?: ZodType | undefined }\n | { kind: \"param\"; name: string; schema?: ZodType | undefined }\n | { kind: \"headers\"; name?: string | undefined }\n | { kind: \"req\" };\n\nconst PARAMS = Symbol(\"blixis:params\");\n\nfunction paramDecorator(source: ParamSource): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) {\n throw new Error(\"@Body/@Query/@Param/@Headers/@Req can only decorate route handler method parameters, not constructor parameters.\");\n }\n const sources =\n getMetadata<Map<number, ParamSource>>(PARAMS, target, propertyKey) ?? new Map<number, ParamSource>();\n sources.set(parameterIndex, source);\n defineMetadata(PARAMS, sources, target, propertyKey);\n };\n}\n\n/** Parses the JSON request body, validating it with `schema` when given. */\nexport function Body(schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"body\", schema });\n}\n\n/** The parsed query string as an object, validated with `schema` when given. */\nexport function Query(schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"query\", schema });\n}\n\n/** One route param by name, validated with `schema` when given (otherwise the raw string). */\nexport function Param(name: string, schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"param\", name, schema });\n}\n\n/** One request header by name, or all headers as an object when `name` is omitted. */\nexport function Headers(name?: string): ParameterDecorator {\n return paramDecorator({ kind: \"headers\", name });\n}\n\n/** The raw Web-standard `Request`. */\nexport function Req(): ParameterDecorator {\n return paramDecorator({ kind: \"req\" });\n}\n\nexport function getParamSources(target: object, propertyKey: string | symbol): Map<number, ParamSource> {\n return getMetadata(PARAMS, target, propertyKey) ?? new Map();\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\nimport type { HttpMethod } from \"../types.js\";\n\nexport interface RouteDefinition {\n method: HttpMethod;\n path: string;\n propertyKey: string | symbol;\n}\n\nconst ROUTES = Symbol(\"blixis:routes\");\nconst HTTP_CODE = Symbol(\"blixis:http-code\");\nconst RETURNS = Symbol(\"blixis:returns\");\nconst RETURNS_VALIDATE = Symbol(\"blixis:returns-validate\");\n\nfunction routeDecorator(method: HttpMethod, path: string): MethodDecorator {\n return (target, propertyKey) => {\n const ctor = target.constructor;\n const routes = getMetadata<RouteDefinition[]>(ROUTES, ctor) ?? [];\n routes.push({ method, path, propertyKey });\n defineMetadata(ROUTES, routes, ctor);\n };\n}\n\nexport const Get = (path = \"\"): MethodDecorator => routeDecorator(\"GET\", path);\nexport const Post = (path = \"\"): MethodDecorator => routeDecorator(\"POST\", path);\nexport const Put = (path = \"\"): MethodDecorator => routeDecorator(\"PUT\", path);\nexport const Patch = (path = \"\"): MethodDecorator => routeDecorator(\"PATCH\", path);\nexport const Delete = (path = \"\"): MethodDecorator => routeDecorator(\"DELETE\", path);\n\nexport function getRoutes(target: object): RouteDefinition[] {\n return getMetadata(ROUTES, target) ?? [];\n}\n\n/** Overrides the default 200 (or 204 for an `undefined` return) status for one route. */\nexport function HttpCode(code: number): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(HTTP_CODE, code, target, propertyKey);\n };\n}\n\nexport function getHttpCode(target: object, propertyKey: string | symbol): number | undefined {\n return getMetadata(HTTP_CODE, target, propertyKey);\n}\n\n/**\n * Declares the response body's shape for one route. Validated after the\n * handler returns, before serialization — a mismatch is a server bug (the\n * implementation drifted from its own declared contract), not a client\n * error, so it never reaches the client as anything but a generic 500.\n */\nexport function Returns(schema: ZodType, options: ReturnsOptions = {}): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(RETURNS, schema, target, propertyKey);\n if (options.validate !== undefined) {\n defineMetadata(RETURNS_VALIDATE, options.validate, target, propertyKey);\n }\n };\n}\n\nexport interface ReturnsOptions {\n /**\n * Overrides the app-wide `responseValidation` for this route, in either direction. `false` sends the\n * handler's value as-is: no parsing, so none of the schema's coercion, defaults or stripping of unknown\n * keys applies. The schema still describes the route in the OpenAPI document.\n */\n validate?: boolean;\n}\n\n/** The per-route `validate` override, or `undefined` when the route follows the app-wide setting. */\nexport function getReturnsValidate(target: object, propertyKey: string | symbol): boolean | undefined {\n return getMetadata(RETURNS_VALIDATE, target, propertyKey);\n}\n\nexport function getReturnsSchema(target: object, propertyKey: string | symbol): ZodType | undefined {\n return getMetadata(RETURNS, target, propertyKey);\n}\n","/** Thrown from a guard or handler to short-circuit the response with a specific status. */\nexport class HttpException extends Error {\n constructor(\n public readonly status: number,\n public readonly detail: string,\n public readonly extra?: Record<string, unknown>,\n ) {\n super(detail);\n this.name = \"HttpException\";\n }\n}\n\nexport class BadRequestException extends HttpException {\n constructor(detail = \"Bad Request\", extra?: Record<string, unknown>) {\n super(400, detail, extra);\n this.name = \"BadRequestException\";\n }\n}\n\nexport class UnauthorizedException extends HttpException {\n constructor(detail = \"Unauthorized\") {\n super(401, detail);\n this.name = \"UnauthorizedException\";\n }\n}\n\nexport class ForbiddenException extends HttpException {\n constructor(detail = \"Forbidden\") {\n super(403, detail);\n this.name = \"ForbiddenException\";\n }\n}\n\nexport class NotFoundException extends HttpException {\n constructor(detail = \"Not Found\") {\n super(404, detail);\n this.name = \"NotFoundException\";\n }\n}\n\nexport class ConflictException extends HttpException {\n constructor(detail = \"Conflict\") {\n super(409, detail);\n this.name = \"ConflictException\";\n }\n}\n\nexport class PayloadTooLargeException extends HttpException {\n constructor(detail = \"Payload Too Large\") {\n super(413, detail);\n this.name = \"PayloadTooLargeException\";\n }\n}\n\nexport class UnsupportedMediaTypeException extends HttpException {\n constructor(detail = \"Unsupported Media Type\") {\n super(415, detail);\n this.name = \"UnsupportedMediaTypeException\";\n }\n}\n\nexport class GatewayTimeoutException extends HttpException {\n constructor(detail = \"Gateway Timeout\") {\n super(504, detail);\n this.name = \"GatewayTimeoutException\";\n }\n}\n","import type { ZodType } from \"zod\";\nimport type { ParamSource } from \"./decorators/params.js\";\nimport { BadRequestException } from \"./exceptions.js\";\n\nexport interface ParamResolutionContext {\n request: Request;\n routeParams: Readonly<Record<string, string>>;\n /** Reads and JSON-parses the body, memoized per-request by the caller. Rejects with an HttpException (400/413/415) on failure. */\n getBody: () => Promise<unknown>;\n}\n\nasync function parseWithSchema(schema: ZodType, value: unknown): Promise<unknown> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new BadRequestException(\"Validation failed\", { issues: result.error.issues });\n }\n return result.data;\n}\n\nasync function resolveOne(source: ParamSource, context: ParamResolutionContext): Promise<unknown> {\n switch (source.kind) {\n case \"req\":\n return context.request;\n case \"headers\":\n return source.name ? context.request.headers.get(source.name) : Object.fromEntries(context.request.headers);\n case \"param\": {\n const raw = context.routeParams[source.name];\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n case \"query\": {\n const raw = Object.fromEntries(new URL(context.request.url).searchParams);\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n case \"body\": {\n const raw = await context.getBody();\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n /* v8 ignore start -- @preserve: exhaustiveness guard, unreachable through the public API */\n default: {\n const exhaustive: never = source;\n throw new Error(`Unreachable: unknown param source kind ${(exhaustive as ParamSource).kind}`);\n }\n /* v8 ignore stop */\n }\n}\n\n/** Builds the positional argument list for a route handler from its `@Body`/`@Query`/`@Param`/`@Headers`/`@Req` metadata. */\nexport async function resolveHandlerArgs(\n sources: ReadonlyMap<number, ParamSource>,\n context: ParamResolutionContext,\n): Promise<unknown[]> {\n if (sources.size === 0) {\n return [];\n }\n\n const args: unknown[] = Array.from<unknown>({ length: Math.max(...sources.keys()) + 1 });\n\n await Promise.all(\n [...sources.entries()].map(async ([index, source]) => {\n args[index] = await resolveOne(source, context);\n }),\n );\n\n return args;\n}\n","import { Injectable } from \"@blixis-io/di\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nconst storage = new AsyncLocalStorage<Map<string, unknown>>();\n\n/** Thrown by `RequestContext.set()` when called outside an active request scope. */\nexport class RequestContextError extends Error {\n constructor() {\n super(\"RequestContext.set() can only be called while handling a request.\");\n this.name = \"RequestContextError\";\n }\n}\n\n/**\n * Per-request key/value store. Injectable as a singleton because its\n * methods don't hold state themselves — they read/write whichever\n * `Map` `runInRequestContext` made current for the request now being\n * handled, via `AsyncLocalStorage`. Reading (`get`/`has`) outside a\n * request is a legitimate \"no request\" answer; writing isn't, so\n * `set()` throws instead of silently doing nothing.\n */\n@Injectable()\nexport class RequestContext {\n get<T = unknown>(key: string): T | undefined {\n return storage.getStore()?.get(key) as T | undefined;\n }\n\n has(key: string): boolean {\n return storage.getStore()?.has(key) ?? false;\n }\n\n set(key: string, value: unknown): void {\n const store = storage.getStore();\n if (!store) {\n throw new RequestContextError();\n }\n store.set(key, value);\n }\n}\n\n/** Runs `fn` with a fresh, isolated store current for its whole async call chain — what `createHandler` wraps around each request. */\nexport function runInRequestContext<T>(fn: () => T): T {\n return storage.run(new Map(), fn);\n}\n","import { z, type ZodType } from \"zod\";\n\ninterface ResponseValidationIssue {\n path: PropertyKey[];\n message: string;\n}\n\n/**\n * Thrown when a handler's return value doesn't match its `@Returns`\n * schema. Deliberately not an `HttpException` — this is a server-side\n * contract bug, not the client's fault, so it falls through to the\n * generic 500 path instead of exposing the mismatch (and the app's\n * internal shape) to the client.\n */\nexport class ResponseValidationError extends Error {\n constructor(public readonly issues: readonly ResponseValidationIssue[]) {\n super(`Response failed its declared schema: ${issues.map((issue) => `${issue.path.join(\".\")}: ${issue.message}`).join(\"; \")}`);\n this.name = \"ResponseValidationError\";\n }\n}\n\n/**\n * Sync parsing is 3-4x faster than `safeParseAsync` on large payloads, so try it first and only fall back\n * when the schema contains an async refinement/transform (Zod signals that with `$ZodAsyncError`).\n */\nasync function parse(schema: ZodType, value: unknown) {\n try {\n return schema.safeParse(value);\n } catch (error) {\n if (error instanceof z.core.$ZodAsyncError) {\n return schema.safeParseAsync(value);\n }\n throw error;\n }\n}\n\n/** Validates a handler's return value against its `@Returns` schema, if any — returns the value (possibly parsed/coerced) unchanged when there's no schema. */\nexport async function validateResponse(schema: ZodType | undefined, value: unknown): Promise<unknown> {\n if (!schema) {\n return value;\n }\n const result = await parse(schema, value);\n if (!result.success) {\n throw new ResponseValidationError(result.error.issues);\n }\n return result.data;\n}\n","import type { HttpMethod } from \"./types.js\";\n\nexport interface RouteFound<T> {\n kind: \"found\";\n handler: T;\n params: Record<string, string>;\n}\n\nexport interface RouteNotFound {\n kind: \"not-found\";\n}\n\nexport interface RouteMethodNotAllowed {\n kind: \"method-not-allowed\";\n allowed: HttpMethod[];\n}\n\nexport type RouteLookupResult<T> = RouteFound<T> | RouteNotFound | RouteMethodNotAllowed;\n\nexport class DuplicateRouteError extends Error {\n override readonly name = \"DuplicateRouteError\";\n\n constructor(method: HttpMethod, path: string) {\n super(`Duplicate route: ${method} ${path} is already registered — each method+path pair must be unique.`);\n }\n}\n\ninterface TrieNode<T> {\n staticChildren: Map<string, TrieNode<T>>;\n paramChild?: { name: string; node: TrieNode<T> };\n wildcardChild?: { name: string; node: TrieNode<T> };\n handlers: Map<HttpMethod, T>;\n}\n\nfunction createNode<T>(): TrieNode<T> {\n return { staticChildren: new Map(), handlers: new Map() };\n}\n\nfunction splitPath(path: string): string[] {\n return path.split(\"/\").filter((segment) => segment.length > 0);\n}\n\ninterface WalkResult<T> {\n node: TrieNode<T>;\n params: Record<string, string>;\n}\n\n/**\n * Depth-first with backtracking: a static/param branch that leads to a dead\n * end (no route registered there) must not block a sibling branch — e.g.\n * `/posts/new` (static, POST only) must not swallow `/posts/:id` (GET) when\n * someone requests GET /posts/new.\n */\nfunction walk<T>(\n node: TrieNode<T>,\n segments: readonly string[],\n index: number,\n params: Record<string, string>,\n): WalkResult<T> | undefined {\n if (index === segments.length) {\n return node.handlers.size > 0 ? { node, params } : undefined;\n }\n\n const segment = segments[index] as string;\n\n const staticChild = node.staticChildren.get(segment);\n if (staticChild) {\n const result = walk(staticChild, segments, index + 1, params);\n if (result) {\n return result;\n }\n }\n\n if (node.paramChild) {\n const { name, node: paramNode } = node.paramChild;\n const result = walk(paramNode, segments, index + 1, { ...params, [name]: segment });\n if (result) {\n return result;\n }\n }\n\n if (node.wildcardChild) {\n const { name, node: wildcardNode } = node.wildcardChild;\n if (wildcardNode.handlers.size > 0) {\n return { node: wildcardNode, params: { ...params, [name]: segments.slice(index).join(\"/\") } };\n }\n }\n\n return undefined;\n}\n\n/**\n * A path router keyed on an opaque handler type `T` — this package plugs in\n * controller method references; tests plug in plain strings.\n */\nexport class Router<T> {\n readonly #root = createNode<T>();\n\n add(method: HttpMethod, path: string, handler: T): void {\n const segments = splitPath(path);\n let node = this.#root;\n\n for (const segment of segments) {\n if (segment.startsWith(\":\")) {\n const name = segment.slice(1);\n node.paramChild ??= { name, node: createNode() };\n node = node.paramChild.node;\n } else if (segment === \"*\") {\n node.wildcardChild ??= { name: \"*\", node: createNode() };\n node = node.wildcardChild.node;\n } else {\n let child = node.staticChildren.get(segment);\n if (!child) {\n child = createNode();\n node.staticChildren.set(segment, child);\n }\n node = child;\n }\n }\n\n if (node.handlers.has(method)) {\n throw new DuplicateRouteError(method, path);\n }\n node.handlers.set(method, handler);\n }\n\n match(method: HttpMethod, path: string): RouteLookupResult<T> {\n const segments = splitPath(path);\n const result = walk(this.#root, segments, 0, {});\n\n if (!result) {\n return { kind: \"not-found\" };\n }\n\n const handler = result.node.handlers.get(method);\n if (!handler) {\n return { kind: \"method-not-allowed\", allowed: [...result.node.handlers.keys()] };\n }\n\n return { kind: \"found\", handler, params: result.params };\n }\n}\n","import type { Application } from \"@blixis-io/core\";\nimport type { Class } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\nimport { getControllerPrefix } from \"./decorators/controller.js\";\nimport { getClassGuards, getMethodGuards, type CanActivate } from \"./decorators/guards.js\";\nimport { getClassInterceptors, getMethodInterceptors, type Interceptor } from \"./decorators/interceptors.js\";\nimport { getParamSources, type ParamSource } from \"./decorators/params.js\";\nimport { getHttpCode, getReturnsSchema, getReturnsValidate, getRoutes } from \"./decorators/routes.js\";\nimport {\n BadRequestException,\n ForbiddenException,\n GatewayTimeoutException,\n HttpException,\n PayloadTooLargeException,\n UnsupportedMediaTypeException,\n} from \"./exceptions.js\";\nimport { resolveHandlerArgs } from \"./params.js\";\nimport { runInRequestContext } from \"./request-context.js\";\nimport { validateResponse } from \"./response.js\";\nimport { Router } from \"./router.js\";\nimport type { HttpMethod } from \"./types.js\";\n\nconst DEFAULT_BODY_LIMIT = 1024 * 1024; // 1 MiB\n\nexport interface HandlerOptions {\n bodyLimit?: number;\n /**\n * Milliseconds a request may take before the client gets a 504. Off by default. The handler (and\n * anything it awaits) is not killed — it sees the timeout through `request.signal` (`@Req()`), and\n * must pass that signal on to cancellable work (`fetch`, DB queries) to actually stop.\n */\n requestTimeout?: number;\n /**\n * `\"always\"` (default) validates every `@Returns` route and sends the parsed value, so the schema also\n * strips unknown keys. `\"never\"` sends handler values as-is, which skips that stripping: an object with\n * extra fields (say a `passwordHash`) is then sent whole. A route's `@Returns(..., { validate })` wins.\n */\n responseValidation?: \"always\" | \"never\";\n}\n\nexport class NotAControllerError extends Error {\n override readonly name = \"NotAControllerError\";\n\n constructor(target: { name: string }) {\n super(`${target.name} is not a controller — did you forget @Controller()?`);\n }\n}\n\ninterface RouteEntry {\n controller: Class;\n propertyKey: string | symbol;\n paramSources: ReadonlyMap<number, ParamSource>;\n httpCode?: number | undefined;\n responseSchema?: ZodType | undefined;\n /** Per-route `@Returns(..., { validate })` override; `undefined` follows the app-wide `responseValidation`. */\n validateResponse?: boolean | undefined;\n guards: Class<CanActivate>[];\n interceptors: Class<Interceptor>[];\n}\n\n/**\n * Flattens every `@Controller`'s route metadata into one `Router`. Guard\n * classes referenced via `@UseGuards` must also be registered as providers\n * on the owning module — this doesn't instantiate them, it only records\n * which classes `createHandler` will later ask the `Application` for.\n */\nexport function buildRouter(controllers: readonly Class[]): Router<RouteEntry> {\n const router = new Router<RouteEntry>();\n\n for (const controller of controllers) {\n const prefix = getControllerPrefix(controller);\n if (prefix === undefined) {\n throw new NotAControllerError(controller);\n }\n\n const prototype = controller.prototype as object;\n const classGuards = getClassGuards(controller);\n const classInterceptors = getClassInterceptors(controller);\n\n for (const route of getRoutes(controller)) {\n router.add(route.method, `/${prefix}/${route.path}`, {\n controller,\n propertyKey: route.propertyKey,\n paramSources: getParamSources(prototype, route.propertyKey),\n httpCode: getHttpCode(prototype, route.propertyKey),\n responseSchema: getReturnsSchema(prototype, route.propertyKey),\n validateResponse: getReturnsValidate(prototype, route.propertyKey),\n guards: [...classGuards, ...getMethodGuards(prototype, route.propertyKey)],\n interceptors: [...classInterceptors, ...getMethodInterceptors(prototype, route.propertyKey)],\n });\n }\n }\n\n return router;\n}\n\n/** Reads the stream chunk by chunk and cancels it the moment `limit` bytes are exceeded, so a chunked body without a `Content-Length` can't be buffered whole before being rejected. */\nasync function readBodyText(body: ReadableStream<Uint8Array>, limit: number): Promise<string> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let received = 0;\n let text = \"\";\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return text + decoder.decode();\n }\n received += value.byteLength;\n if (received > limit) {\n await reader.cancel();\n throw new PayloadTooLargeException();\n }\n text += decoder.decode(value, { stream: true });\n }\n}\n\nasync function readJsonBody(request: Request, bodyLimit: number): Promise<unknown> {\n if (request.body === null) {\n return undefined;\n }\n\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().startsWith(\"application/json\")) {\n throw new UnsupportedMediaTypeException();\n }\n\n const contentLength = request.headers.get(\"content-length\");\n if (contentLength !== null && Number(contentLength) > bodyLimit) {\n throw new PayloadTooLargeException();\n }\n\n const text = await readBodyText(request.body, bodyLimit);\n if (text.length === 0) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n throw new BadRequestException(\"Invalid JSON body\");\n }\n}\n\nconst STATUS_TITLES: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 405: \"Method Not Allowed\",\n 409: \"Conflict\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 500: \"Internal Server Error\",\n 504: \"Gateway Timeout\",\n};\n\nfunction problemResponse(status: number, detail: string, extra?: Record<string, unknown>): Response {\n const body = {\n type: \"about:blank\",\n title: STATUS_TITLES[status] ?? \"Error\",\n status,\n detail,\n ...extra,\n };\n return new Response(JSON.stringify(body), { status, headers: { \"content-type\": \"application/problem+json\" } });\n}\n\nfunction exceptionToResponse(error: unknown): Response {\n if (error instanceof HttpException) {\n return problemResponse(error.status, error.detail, error.extra);\n }\n // Internal errors are logged but never surfaced to the client.\n console.error(error);\n return problemResponse(500, \"An unexpected error occurred\");\n}\n\n/**\n * A raw `Response` or an `undefined` (204) return bypasses `responseSchema`\n * entirely — both are deliberate escape hatches from the normal JSON path,\n * not a value the schema was ever meant to describe.\n */\nasync function toResponse(\n value: unknown,\n httpCode: number | undefined,\n responseSchema: ZodType | undefined,\n validate: boolean,\n): Promise<Response> {\n if (value instanceof Response) {\n return value;\n }\n if (value === undefined) {\n return new Response(null, { status: httpCode ?? 204 });\n }\n const validated = validate ? await validateResponse(responseSchema, value) : value;\n return new Response(JSON.stringify(validated), {\n status: httpCode ?? 200,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\n/** Same request, but its `signal` also fires after `ms` — so handlers see the timeout via `request.signal`. */\nfunction withTimeout(request: Request, ms: number): Request {\n const signal = AbortSignal.any([request.signal, AbortSignal.timeout(ms)]);\n return new Request(request, { signal });\n}\n\n/** Settles with `work`, unless `signal` aborts first: a client disconnect becomes a 499, a timeout a 504. `work` keeps running; its late result/rejection is ignored. */\nfunction raceAbort(work: Promise<Response>, signal: AbortSignal, clientSignal: AbortSignal): Promise<Response> {\n const abortResponse = (): Response =>\n clientSignal.aborted ? new Response(null, { status: 499 }) : exceptionToResponse(new GatewayTimeoutException());\n if (signal.aborted) {\n return Promise.resolve(abortResponse());\n }\n return new Promise<Response>((resolve, reject) => {\n const onAbort = (): void => {\n resolve(abortResponse());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n work.then(resolve, reject).finally(() => {\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n}\n\n/** Builds a single `(Request) => Promise<Response>` function serving every controller's routes. */\nexport function createHandler(\n controllers: readonly Class[],\n application: Application,\n options: HandlerOptions = {},\n): (request: Request) => Promise<Response> {\n const router = buildRouter(controllers);\n const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;\n\n const requestTimeout = options.requestTimeout;\n const validateByDefault = options.responseValidation !== \"never\";\n\n return async function handle(incoming: Request): Promise<Response> {\n const request = requestTimeout === undefined ? incoming : withTimeout(incoming, requestTimeout);\n const url = new URL(request.url);\n const match = router.match(request.method as HttpMethod, url.pathname);\n\n if (match.kind === \"not-found\") {\n return problemResponse(404, \"No route matches this path\");\n }\n if (match.kind === \"method-not-allowed\") {\n const response = problemResponse(405, `Allowed methods: ${match.allowed.join(\", \")}`);\n response.headers.set(\"allow\", match.allowed.join(\", \"));\n return response;\n }\n\n const route = match.handler;\n\n return runInRequestContext(async () => {\n try {\n // Sequential and short-circuiting on purpose: a later guard must not\n // run once an earlier one has already denied the request.\n for (const guardClass of route.guards) {\n const guard = application.get(guardClass);\n const allowed = await guard.canActivate({ request, params: match.params });\n if (!allowed) {\n throw new ForbiddenException();\n }\n }\n\n const invoke = async (): Promise<Response> => {\n let cachedBody: { value: unknown } | undefined;\n const args = await resolveHandlerArgs(route.paramSources, {\n request,\n routeParams: match.params,\n getBody: async () => {\n cachedBody ??= { value: await readJsonBody(request, bodyLimit) };\n return cachedBody.value;\n },\n });\n\n const instance = application.get(route.controller) as Record<PropertyKey, (...args: unknown[]) => unknown>;\n const result: unknown = await instance[route.propertyKey]?.(...args);\n\n return toResponse(result, route.httpCode, route.responseSchema, route.validateResponse ?? validateByDefault);\n };\n\n // Class-level interceptors wrap outermost, method-level innermost —\n // built right-to-left so the first entry ends up as the outer call.\n const pipeline = route.interceptors.reduceRight<() => Promise<Response>>((next, interceptorClass) => {\n const interceptor = application.get(interceptorClass);\n return async () => interceptor.intercept({ request, params: match.params }, next);\n }, invoke);\n\n return await (requestTimeout === undefined ? pipeline() : raceAbort(pipeline(), request.signal, incoming.signal));\n } catch (error) {\n return exceptionToResponse(error);\n }\n });\n };\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\n\n/**\n * Builds a Web-standard `Request` from a Node `IncomingMessage`, including a body stream and abort signal.\n * Pass the matching `ServerResponse` so the signal also fires when the client disconnects before the\n * response finished — `req`'s own \"aborted\" event is deprecated and misses some disconnects.\n */\nexport function toWebRequest(req: IncomingMessage, baseUrl: string, res?: ServerResponse): Request {\n const method = req.method ?? \"GET\";\n const url = new URL(req.url ?? \"/\", baseUrl);\n\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) {\n continue;\n }\n for (const one of Array.isArray(value) ? value : [value]) {\n headers.append(name, one);\n }\n }\n\n const controller = new AbortController();\n req.once(\"aborted\", () => {\n controller.abort();\n });\n res?.once(\"close\", () => {\n if (!res.writableFinished) {\n controller.abort();\n }\n });\n\n // GET/HEAD can never carry a body (Fetch spec forbids it on the Request\n // we're about to construct). Otherwise, trust the standard signals for\n // \"the client is actually sending body bytes\" rather than the method —\n // a bodyless POST is common and must not turn into an open, empty stream.\n const hasBody =\n method !== \"GET\" &&\n method !== \"HEAD\" &&\n (req.headers[\"transfer-encoding\"] !== undefined ||\n (req.headers[\"content-length\"] !== undefined && req.headers[\"content-length\"] !== \"0\"));\n\n return new Request(url, {\n method,\n headers,\n signal: controller.signal,\n // `duplex: \"half\"` is required by Node's fetch implementation whenever body is a stream.\n ...(hasBody ? { body: Readable.toWeb(req), duplex: \"half\" as const } : {}),\n });\n}\n\n/** Writes a Web-standard `Response` back onto a Node `ServerResponse`. */\nexport async function sendWebResponse(response: Response, res: ServerResponse): Promise<void> {\n const headers: Record<string, string[]> = {};\n for (const [name, value] of response.headers) {\n (headers[name] ??= []).push(value);\n }\n res.writeHead(response.status, headers);\n\n if (!response.body) {\n res.end();\n return;\n }\n\n // `pipeline` (not `pipe`) so the streams are torn down together: a client that disconnects\n // mid-response destroys the body, which cancels the web stream's source instead of leaving\n // its producer (a DB cursor, a file handle) running. An early disconnect isn't an error.\n try {\n await pipeline(Readable.fromWeb(response.body), res);\n } catch (error) {\n if (!isPrematureClose(error)) {\n throw error;\n }\n }\n}\n\nfunction isPrematureClose(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"ERR_STREAM_PREMATURE_CLOSE\";\n}\n","import { createApplication, Module, type Application, type CreateApplicationOptions, type ModuleRef } from \"@blixis-io/core\";\nimport type { Class, Token } from \"@blixis-io/di\";\nimport { createServer, type Server } from \"node:http\";\nimport { createHandler, type HandlerOptions } from \"./handler.js\";\nimport { sendWebResponse, toWebRequest } from \"./node-adapter.js\";\nimport { RequestContext } from \"./request-context.js\";\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\n\nexport interface ShutdownOptions {\n /**\n * Milliseconds `close()` lets in-flight requests finish before their sockets are destroyed (their\n * `request.signal` aborts). Default 10 000; `Infinity` waits indefinitely.\n */\n shutdownTimeout?: number;\n}\n\nexport type HttpApplicationOptions = HandlerOptions & CreateApplicationOptions & ShutdownOptions;\n\n/** Provides `RequestContext` app-wide, without the user needing to import anything — every `createHttpApplication` root gets wrapped with this. */\n@Module({ providers: [RequestContext], exports: [RequestContext], global: true })\nclass RequestContextModule {}\n\n/** Empty static anchor for the synthetic root — its own metadata is unused, only `imports` matters. */\n@Module()\nclass HttpRootModule {}\n\nexport type MountedHandler = (request: Request) => Response | Promise<Response>;\n\nexport interface ListenHandle {\n port: number;\n}\n\n/** The core `Application` plus a request handler, optionally bound to a real Node HTTP socket. */\nexport class HttpApplication {\n readonly #app: Application;\n readonly #handle: (request: Request) => Promise<Response>;\n readonly #shutdownTimeout: number;\n readonly #mounted = new Map<string, MountedHandler>();\n #server: Server | undefined;\n\n private constructor(app: Application, handle: (request: Request) => Promise<Response>, shutdownTimeout: number) {\n this.#app = app;\n this.#handle = handle;\n this.#shutdownTimeout = shutdownTimeout;\n }\n\n static async create(rootModule: ModuleRef, options: HttpApplicationOptions = {}): Promise<HttpApplication> {\n const wrappedRoot = { module: HttpRootModule, imports: [rootModule, RequestContextModule] };\n const app = await createApplication(wrappedRoot, { overrides: options.overrides });\n const handle = createHandler(app.controllers, app, options);\n return new HttpApplication(app, handle, options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT);\n }\n\n /** Fetches an already-resolved provider directly, bypassing HTTP entirely. */\n get<T>(token: Token<T>): T {\n return this.#app.get(token);\n }\n\n /** Every controller class in this app's module graph — what `@blixis-io/openapi` walks to build a document that stays in sync with the real running app. */\n get controllers(): readonly Class[] {\n return this.#app.controllers;\n }\n\n /**\n * Serves an exact path with a plain Web-standard handler, ahead of the controller router — for\n * framework-level endpoints (such as an OpenAPI document) that need the finished app, which a\n * controller can't get at construction time. Mounted routes bypass guards and interceptors, so\n * they are public; mounting the same method and path twice throws.\n */\n mount(method: \"GET\" | \"POST\", path: string, handler: MountedHandler): void {\n const key = `${method} ${path}`;\n if (this.#mounted.has(key)) {\n throw new Error(`${key} is already mounted`);\n }\n this.#mounted.set(key, handler);\n }\n\n /** Runs a request through the handler in-process, without a socket. */\n async handle(request: Request): Promise<Response> {\n if (this.#mounted.size > 0) {\n const mounted = this.#mounted.get(`${request.method} ${new URL(request.url).pathname}`);\n if (mounted) {\n return mounted(request);\n }\n }\n return this.#handle(request);\n }\n\n listen(port: number, hostname = \"0.0.0.0\"): Promise<ListenHandle> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const request = toWebRequest(req, `http://${hostname}:${port}`, res);\n this.handle(request)\n .then((response) => sendWebResponse(response, res))\n /* v8 ignore start -- @preserve: safety net for a write failure\n (e.g. the client disconnects mid-stream); createHandler's own\n try/catch already turns every request-handling error into a\n Response, so this only fires for socket-level failures, which\n aren't reliably reproducible without a flaky, timing-dependent\n test. */\n .catch((error: unknown) => {\n console.error(error);\n if (!res.headersSent) {\n res.writeHead(500);\n }\n res.end();\n });\n /* v8 ignore stop */\n });\n\n server.once(\"error\", reject);\n server.listen(port, hostname, () => {\n this.#server = server;\n const address = server.address();\n // `address()` only returns a string for a Unix domain socket; `listen()`\n // here always takes a TCP port, so that arm is unreachable in practice.\n /* v8 ignore next -- @preserve */\n const actualPort = typeof address === \"object\" && address !== null ? address.port : port;\n resolve({ port: actualPort });\n });\n });\n }\n\n async close(signal?: string): Promise<void> {\n if (this.#server) {\n const server = this.#server;\n // Cleared before closing, not after: makes close() idempotent (a\n // second call is a no-op instead of ERR_SERVER_NOT_RUNNING) even if\n // the close below is still in flight.\n this.#server = undefined;\n await new Promise<void>((resolve, reject) => {\n // Stops accepting new connections and waits for in-flight requests;\n // idle keep-alive sockets are dropped now so they can't hold close()\n // open. Anything still running at the deadline is cut off.\n const deadline = Number.isFinite(this.#shutdownTimeout)\n ? setTimeout(() => {\n server.closeAllConnections();\n }, this.#shutdownTimeout)\n : undefined;\n server.close((error) => {\n clearTimeout(deadline);\n // #server is only ever set right after a successful listen() and\n // cleared right before this close(), so ERR_SERVER_NOT_RUNNING\n // (the one realistic cause) can't occur here.\n /* v8 ignore next 3 -- @preserve */\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n server.closeIdleConnections();\n });\n }\n await this.#app.close(signal);\n }\n}\n\nexport async function createHttpApplication(\n rootModule: ModuleRef,\n options: HttpApplicationOptions = {},\n): Promise<HttpApplication> {\n return HttpApplication.create(rootModule, options);\n}\n","import type { ModuleRef } from \"@blixis-io/core\";\nimport { createHttpApplication, type HttpApplication, type HttpApplicationOptions } from \"./http-application.js\";\n\n/** A Web-standard entry for platforms that call `fetch(request)` per request (Vercel, Netlify, Cloudflare Workers, Deno Deploy, ...). */\nexport interface FetchHandler {\n fetch(request: Request): Promise<Response>;\n /** Runs shutdown hooks. Serverless platforms rarely call this; it exists for tests and long-lived hosts. */\n close(signal?: string): Promise<void>;\n}\n\n/**\n * Boots the application lazily on the first request and reuses it afterwards, so a cold start pays for\n * module resolution once, not per request. Concurrent first requests share one boot. If boot fails the\n * error is logged, that request gets a generic 500, and the next request tries again instead of caching\n * the failure for the lifetime of the instance.\n *\n * ```ts\n * export default createFetchHandler(AppModule); // Vercel / Workers: `{ fetch }`\n * ```\n *\n * `listen()`, `shutdownTimeout` and `SIGTERM` handling don't apply here: the platform owns the socket.\n */\nexport function createFetchHandler(rootModule: ModuleRef, options: HttpApplicationOptions = {}): FetchHandler {\n let app: Promise<HttpApplication> | undefined;\n\n function boot(): Promise<HttpApplication> {\n app ??= createHttpApplication(rootModule, options).catch((error: unknown) => {\n app = undefined;\n throw error;\n });\n return app;\n }\n\n return {\n async fetch(request) {\n let application: HttpApplication;\n try {\n application = await boot();\n } catch (error) {\n console.error(error);\n return new Response(\n JSON.stringify({ type: \"about:blank\", title: \"Internal Server Error\", status: 500, detail: \"An unexpected error occurred\" }),\n { status: 500, headers: { \"content-type\": \"application/problem+json\" } },\n );\n }\n return application.handle(request);\n },\n\n async close(signal) {\n const booted = app;\n app = undefined;\n if (booted) {\n const application = await booted.catch(() => undefined);\n await application?.close(signal);\n }\n },\n };\n}\n","export const HTTP_METHODS = [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\", \"OPTIONS\"] as const;\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Headers","exhaustive"],"sources":["../src/decorators/api-metadata.ts","../src/decorators/controller.ts","../src/decorators/guards.ts","../src/decorators/interceptors.ts","../src/decorators/params.ts","../src/decorators/routes.ts","../src/exceptions.ts","../src/params.ts","../src/request-context.ts","../src/response.ts","../src/router.ts","../src/handler.ts","../src/node-adapter.ts","../src/http-application.ts","../src/fetch-handler.ts","../src/types.ts"],"sourcesContent":["import { defineMetadata, getMetadata } from \"@blixis-io/di\";\n\nexport interface ApiOperationOptions {\n summary?: string;\n description?: string;\n /** Defaults to `${ControllerName}_${methodName}` when omitted — every operation needs a unique one, valid or not. */\n operationId?: string;\n}\n\nconst API_OPERATION = Symbol(\"blixis:api-operation\");\nconst API_TAGS = Symbol(\"blixis:api-tags\");\n\n/** Documents one route for `@blixis-io/openapi` — entirely optional, a route without it still gets a valid (derived) operationId. */\nexport function ApiOperation(options: ApiOperationOptions): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(API_OPERATION, options, target, propertyKey);\n };\n}\n\nexport function getApiOperation(target: object, propertyKey: string | symbol): ApiOperationOptions | undefined {\n return getMetadata(API_OPERATION, target, propertyKey);\n}\n\n/**\n * Applies OpenAPI tags to a controller (class position) or a single route\n * (method position) — same dual-shape decorator as `@UseGuards`. Class-level\n * and method-level tags both apply, concatenated, not one replacing the\n * other.\n */\nexport function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<string[]>(API_TAGS, target) ?? [];\n defineMetadata(API_TAGS, [...existing, ...tags], target);\n } else {\n const existing = getMetadata<string[]>(API_TAGS, target, propertyKey) ?? [];\n defineMetadata(API_TAGS, [...existing, ...tags], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassApiTags(target: object): string[] {\n return getMetadata(API_TAGS, target) ?? [];\n}\n\nexport function getMethodApiTags(target: object, propertyKey: string | symbol): string[] {\n return getMetadata(API_TAGS, target, propertyKey) ?? [];\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\n\nconst CONTROLLER_PREFIX = Symbol(\"blixis:controller-prefix\");\n\nfunction stripSlashes(value: string): string {\n return value.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nexport function Controller(prefix = \"\"): ClassDecorator {\n return (target) => {\n defineMetadata(CONTROLLER_PREFIX, stripSlashes(prefix), target);\n };\n}\n\nexport function getControllerPrefix(target: object): string | undefined {\n return getMetadata(CONTROLLER_PREFIX, target);\n}\n","import { defineMetadata, getMetadata, type Class } from \"@blixis-io/di\";\n\nexport interface ExecutionContext {\n request: Request;\n params: Readonly<Record<string, string>>;\n /** The controller class whose route is being handled. Read route metadata with `getRouteMetadata(KEY, context)`. */\n controller: Class;\n /** The name of the controller method handling this request. */\n handler: string | symbol;\n}\n\nexport interface CanActivate {\n canActivate(context: ExecutionContext): boolean | Promise<boolean>;\n}\n\nconst GUARDS = Symbol(\"blixis:guards\");\n\n/**\n * Applies guards to a controller (class position) or a single route\n * (method position) — the same decorator function works in both spots,\n * matching the shape TS's legacy decorators give it: a class decorator\n * receives no `propertyKey`, a method decorator does.\n */\nexport function UseGuards(...guards: Class<CanActivate>[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<Class<CanActivate>[]>(GUARDS, target) ?? [];\n defineMetadata(GUARDS, [...existing, ...guards], target);\n } else {\n const existing = getMetadata<Class<CanActivate>[]>(GUARDS, target, propertyKey) ?? [];\n defineMetadata(GUARDS, [...existing, ...guards], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassGuards(target: object): Class<CanActivate>[] {\n return getMetadata(GUARDS, target) ?? [];\n}\n\nexport function getMethodGuards(target: object, propertyKey: string | symbol): Class<CanActivate>[] {\n return getMetadata(GUARDS, target, propertyKey) ?? [];\n}\n\nconst GLOBAL_GUARD = Symbol(\"blixis:global-guard\");\n\n/**\n * Marks a guard class as global: once it is registered as a provider, it runs on **every** route, before\n * the route's own `@UseGuards`. Found through the application's providers, so there is nothing else to\n * wire up. Several global guards run in dependency order; the first to deny stops the request.\n */\nexport function GlobalGuard(): ClassDecorator {\n return (target) => {\n defineMetadata(GLOBAL_GUARD, true, target);\n };\n}\n\nexport function isGlobalGuard(target: object): boolean {\n return getMetadata<boolean>(GLOBAL_GUARD, target) === true;\n}\n\n/**\n * A decorator that attaches `value` under `key` to a controller (every route in it) or to one route, so a\n * guard or interceptor can read it with `getRouteMetadata`. `@Roles(\"admin\")` and `@Public()` are built on this.\n */\nexport function SetRouteMetadata(key: symbol, value: unknown): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n defineMetadata(key, value, target);\n } else {\n defineMetadata(key, value, target, propertyKey);\n }\n };\n return decorator;\n}\n\n/** What `SetRouteMetadata` stored for the route being handled: the method's own value if it has one, else the controller's. */\nexport function getRouteMetadata(key: symbol, context: Pick<ExecutionContext, \"controller\" | \"handler\">): unknown {\n const prototype: unknown = context.controller.prototype;\n const onMethod: unknown = typeof prototype === \"object\" && prototype !== null ? getMetadata(key, prototype, context.handler) : undefined;\n return onMethod ?? getMetadata(key, context.controller);\n}\n","import { defineMetadata, getMetadata, type Class } from \"@blixis-io/di\";\nimport type { ExecutionContext } from \"./guards.js\";\n\nexport interface Interceptor {\n intercept(context: ExecutionContext, next: () => Promise<Response>): Response | Promise<Response>;\n}\n\nconst INTERCEPTORS = Symbol(\"blixis:interceptors\");\n\n/**\n * Applies interceptors to a controller (class position) or a single route\n * (method position) — same dual-shape decorator as `@UseGuards`. Interceptors\n * wrap param resolution + handler invocation (guards already ran and\n * already decided the request is allowed through); class-level interceptors\n * wrap outermost, method-level innermost, closest to the handler.\n */\nexport function UseInterceptors(...interceptors: Class<Interceptor>[]): ClassDecorator & MethodDecorator {\n const decorator = (target: object, propertyKey?: string | symbol): void => {\n if (propertyKey === undefined) {\n const existing = getMetadata<Class<Interceptor>[]>(INTERCEPTORS, target) ?? [];\n defineMetadata(INTERCEPTORS, [...existing, ...interceptors], target);\n } else {\n const existing = getMetadata<Class<Interceptor>[]>(INTERCEPTORS, target, propertyKey) ?? [];\n defineMetadata(INTERCEPTORS, [...existing, ...interceptors], target, propertyKey);\n }\n };\n return decorator;\n}\n\nexport function getClassInterceptors(target: object): Class<Interceptor>[] {\n return getMetadata(INTERCEPTORS, target) ?? [];\n}\n\nexport function getMethodInterceptors(target: object, propertyKey: string | symbol): Class<Interceptor>[] {\n return getMetadata(INTERCEPTORS, target, propertyKey) ?? [];\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\n\nexport type ParamSource =\n | { kind: \"body\"; schema?: ZodType | undefined }\n | { kind: \"query\"; schema?: ZodType | undefined }\n | { kind: \"param\"; name: string; schema?: ZodType | undefined }\n | { kind: \"headers\"; name?: string | undefined }\n | { kind: \"req\" };\n\nconst PARAMS = Symbol(\"blixis:params\");\n\nfunction paramDecorator(source: ParamSource): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) {\n throw new Error(\"@Body/@Query/@Param/@Headers/@Req can only decorate route handler method parameters, not constructor parameters.\");\n }\n const sources =\n getMetadata<Map<number, ParamSource>>(PARAMS, target, propertyKey) ?? new Map<number, ParamSource>();\n sources.set(parameterIndex, source);\n defineMetadata(PARAMS, sources, target, propertyKey);\n };\n}\n\n/** Parses the JSON request body, validating it with `schema` when given. */\nexport function Body(schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"body\", schema });\n}\n\n/** The parsed query string as an object, validated with `schema` when given. */\nexport function Query(schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"query\", schema });\n}\n\n/** One route param by name, validated with `schema` when given (otherwise the raw string). */\nexport function Param(name: string, schema?: ZodType): ParameterDecorator {\n return paramDecorator({ kind: \"param\", name, schema });\n}\n\n/** One request header by name, or all headers as an object when `name` is omitted. */\nexport function Headers(name?: string): ParameterDecorator {\n return paramDecorator({ kind: \"headers\", name });\n}\n\n/** The raw Web-standard `Request`. */\nexport function Req(): ParameterDecorator {\n return paramDecorator({ kind: \"req\" });\n}\n\nexport function getParamSources(target: object, propertyKey: string | symbol): Map<number, ParamSource> {\n return getMetadata(PARAMS, target, propertyKey) ?? new Map();\n}\n","import { defineMetadata, getMetadata } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\nimport type { HttpMethod } from \"../types.js\";\n\nexport interface RouteDefinition {\n method: HttpMethod;\n path: string;\n propertyKey: string | symbol;\n}\n\nconst ROUTES = Symbol(\"blixis:routes\");\nconst HTTP_CODE = Symbol(\"blixis:http-code\");\nconst RETURNS = Symbol(\"blixis:returns\");\nconst RETURNS_VALIDATE = Symbol(\"blixis:returns-validate\");\n\nfunction routeDecorator(method: HttpMethod, path: string): MethodDecorator {\n return (target, propertyKey) => {\n const ctor = target.constructor;\n const routes = getMetadata<RouteDefinition[]>(ROUTES, ctor) ?? [];\n routes.push({ method, path, propertyKey });\n defineMetadata(ROUTES, routes, ctor);\n };\n}\n\nexport const Get = (path = \"\"): MethodDecorator => routeDecorator(\"GET\", path);\nexport const Post = (path = \"\"): MethodDecorator => routeDecorator(\"POST\", path);\nexport const Put = (path = \"\"): MethodDecorator => routeDecorator(\"PUT\", path);\nexport const Patch = (path = \"\"): MethodDecorator => routeDecorator(\"PATCH\", path);\nexport const Delete = (path = \"\"): MethodDecorator => routeDecorator(\"DELETE\", path);\n\nexport function getRoutes(target: object): RouteDefinition[] {\n return getMetadata(ROUTES, target) ?? [];\n}\n\n/** Overrides the default 200 (or 204 for an `undefined` return) status for one route. */\nexport function HttpCode(code: number): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(HTTP_CODE, code, target, propertyKey);\n };\n}\n\nexport function getHttpCode(target: object, propertyKey: string | symbol): number | undefined {\n return getMetadata(HTTP_CODE, target, propertyKey);\n}\n\n/**\n * Declares the response body's shape for one route. Validated after the\n * handler returns, before serialization — a mismatch is a server bug (the\n * implementation drifted from its own declared contract), not a client\n * error, so it never reaches the client as anything but a generic 500.\n */\nexport function Returns(schema: ZodType, options: ReturnsOptions = {}): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(RETURNS, schema, target, propertyKey);\n if (options.validate !== undefined) {\n defineMetadata(RETURNS_VALIDATE, options.validate, target, propertyKey);\n }\n };\n}\n\nexport interface ReturnsOptions {\n /**\n * Overrides the app-wide `responseValidation` for this route, in either direction. `false` sends the\n * handler's value as-is: no parsing, so none of the schema's coercion, defaults or stripping of unknown\n * keys applies. The schema still describes the route in the OpenAPI document.\n */\n validate?: boolean;\n}\n\n/** The per-route `validate` override, or `undefined` when the route follows the app-wide setting. */\nexport function getReturnsValidate(target: object, propertyKey: string | symbol): boolean | undefined {\n return getMetadata(RETURNS_VALIDATE, target, propertyKey);\n}\n\nexport function getReturnsSchema(target: object, propertyKey: string | symbol): ZodType | undefined {\n return getMetadata(RETURNS, target, propertyKey);\n}\n","/** Thrown from a guard or handler to short-circuit the response with a specific status. */\nexport class HttpException extends Error {\n constructor(\n public readonly status: number,\n public readonly detail: string,\n public readonly extra?: Record<string, unknown>,\n ) {\n super(detail);\n this.name = \"HttpException\";\n }\n}\n\nexport class BadRequestException extends HttpException {\n constructor(detail = \"Bad Request\", extra?: Record<string, unknown>) {\n super(400, detail, extra);\n this.name = \"BadRequestException\";\n }\n}\n\nexport class UnauthorizedException extends HttpException {\n constructor(detail = \"Unauthorized\") {\n super(401, detail);\n this.name = \"UnauthorizedException\";\n }\n}\n\nexport class ForbiddenException extends HttpException {\n constructor(detail = \"Forbidden\") {\n super(403, detail);\n this.name = \"ForbiddenException\";\n }\n}\n\nexport class NotFoundException extends HttpException {\n constructor(detail = \"Not Found\") {\n super(404, detail);\n this.name = \"NotFoundException\";\n }\n}\n\nexport class ConflictException extends HttpException {\n constructor(detail = \"Conflict\") {\n super(409, detail);\n this.name = \"ConflictException\";\n }\n}\n\nexport class PayloadTooLargeException extends HttpException {\n constructor(detail = \"Payload Too Large\") {\n super(413, detail);\n this.name = \"PayloadTooLargeException\";\n }\n}\n\nexport class UnsupportedMediaTypeException extends HttpException {\n constructor(detail = \"Unsupported Media Type\") {\n super(415, detail);\n this.name = \"UnsupportedMediaTypeException\";\n }\n}\n\nexport class GatewayTimeoutException extends HttpException {\n constructor(detail = \"Gateway Timeout\") {\n super(504, detail);\n this.name = \"GatewayTimeoutException\";\n }\n}\n","import type { ZodType } from \"zod\";\nimport type { ParamSource } from \"./decorators/params.js\";\nimport { BadRequestException } from \"./exceptions.js\";\n\nexport interface ParamResolutionContext {\n request: Request;\n routeParams: Readonly<Record<string, string>>;\n /** Reads and JSON-parses the body, memoized per-request by the caller. Rejects with an HttpException (400/413/415) on failure. */\n getBody: () => Promise<unknown>;\n}\n\nasync function parseWithSchema(schema: ZodType, value: unknown): Promise<unknown> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new BadRequestException(\"Validation failed\", { issues: result.error.issues });\n }\n return result.data;\n}\n\nasync function resolveOne(source: ParamSource, context: ParamResolutionContext): Promise<unknown> {\n switch (source.kind) {\n case \"req\":\n return context.request;\n case \"headers\":\n return source.name ? context.request.headers.get(source.name) : Object.fromEntries(context.request.headers);\n case \"param\": {\n const raw = context.routeParams[source.name];\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n case \"query\": {\n const raw = Object.fromEntries(new URL(context.request.url).searchParams);\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n case \"body\": {\n const raw = await context.getBody();\n return source.schema ? parseWithSchema(source.schema, raw) : raw;\n }\n /* v8 ignore start -- @preserve: exhaustiveness guard, unreachable through the public API */\n default: {\n const exhaustive: never = source;\n throw new Error(`Unreachable: unknown param source kind ${(exhaustive as ParamSource).kind}`);\n }\n /* v8 ignore stop */\n }\n}\n\n/** Builds the positional argument list for a route handler from its `@Body`/`@Query`/`@Param`/`@Headers`/`@Req` metadata. */\nexport async function resolveHandlerArgs(\n sources: ReadonlyMap<number, ParamSource>,\n context: ParamResolutionContext,\n): Promise<unknown[]> {\n if (sources.size === 0) {\n return [];\n }\n\n const args: unknown[] = Array.from<unknown>({ length: Math.max(...sources.keys()) + 1 });\n\n await Promise.all(\n [...sources.entries()].map(async ([index, source]) => {\n args[index] = await resolveOne(source, context);\n }),\n );\n\n return args;\n}\n","import { Injectable } from \"@blixis-io/di\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nconst storage = new AsyncLocalStorage<Map<string, unknown>>();\n\n/** Thrown by `RequestContext.set()` when called outside an active request scope. */\nexport class RequestContextError extends Error {\n constructor() {\n super(\"RequestContext.set() can only be called while handling a request.\");\n this.name = \"RequestContextError\";\n }\n}\n\n/**\n * Per-request key/value store. Injectable as a singleton because its\n * methods don't hold state themselves — they read/write whichever\n * `Map` `runInRequestContext` made current for the request now being\n * handled, via `AsyncLocalStorage`. Reading (`get`/`has`) outside a\n * request is a legitimate \"no request\" answer; writing isn't, so\n * `set()` throws instead of silently doing nothing.\n */\n@Injectable()\nexport class RequestContext {\n get<T = unknown>(key: string): T | undefined {\n return storage.getStore()?.get(key) as T | undefined;\n }\n\n has(key: string): boolean {\n return storage.getStore()?.has(key) ?? false;\n }\n\n set(key: string, value: unknown): void {\n const store = storage.getStore();\n if (!store) {\n throw new RequestContextError();\n }\n store.set(key, value);\n }\n}\n\n/** Runs `fn` with a fresh, isolated store current for its whole async call chain — what `createHandler` wraps around each request. */\nexport function runInRequestContext<T>(fn: () => T): T {\n return storage.run(new Map(), fn);\n}\n","import { z, type ZodType } from \"zod\";\n\ninterface ResponseValidationIssue {\n path: PropertyKey[];\n message: string;\n}\n\n/**\n * Thrown when a handler's return value doesn't match its `@Returns`\n * schema. Deliberately not an `HttpException` — this is a server-side\n * contract bug, not the client's fault, so it falls through to the\n * generic 500 path instead of exposing the mismatch (and the app's\n * internal shape) to the client.\n */\nexport class ResponseValidationError extends Error {\n constructor(public readonly issues: readonly ResponseValidationIssue[]) {\n super(`Response failed its declared schema: ${issues.map((issue) => `${issue.path.join(\".\")}: ${issue.message}`).join(\"; \")}`);\n this.name = \"ResponseValidationError\";\n }\n}\n\n/**\n * Sync parsing is 3-4x faster than `safeParseAsync` on large payloads, so try it first and only fall back\n * when the schema contains an async refinement/transform (Zod signals that with `$ZodAsyncError`).\n */\nasync function parse(schema: ZodType, value: unknown) {\n try {\n return schema.safeParse(value);\n } catch (error) {\n if (error instanceof z.core.$ZodAsyncError) {\n return schema.safeParseAsync(value);\n }\n throw error;\n }\n}\n\n/** Validates a handler's return value against its `@Returns` schema, if any — returns the value (possibly parsed/coerced) unchanged when there's no schema. */\nexport async function validateResponse(schema: ZodType | undefined, value: unknown): Promise<unknown> {\n if (!schema) {\n return value;\n }\n const result = await parse(schema, value);\n if (!result.success) {\n throw new ResponseValidationError(result.error.issues);\n }\n return result.data;\n}\n","import type { HttpMethod } from \"./types.js\";\n\nexport interface RouteFound<T> {\n kind: \"found\";\n handler: T;\n params: Record<string, string>;\n}\n\nexport interface RouteNotFound {\n kind: \"not-found\";\n}\n\nexport interface RouteMethodNotAllowed {\n kind: \"method-not-allowed\";\n allowed: HttpMethod[];\n}\n\nexport type RouteLookupResult<T> = RouteFound<T> | RouteNotFound | RouteMethodNotAllowed;\n\nexport class DuplicateRouteError extends Error {\n override readonly name = \"DuplicateRouteError\";\n\n constructor(method: HttpMethod, path: string) {\n super(`Duplicate route: ${method} ${path} is already registered — each method+path pair must be unique.`);\n }\n}\n\ninterface TrieNode<T> {\n staticChildren: Map<string, TrieNode<T>>;\n paramChild?: { name: string; node: TrieNode<T> };\n wildcardChild?: { name: string; node: TrieNode<T> };\n handlers: Map<HttpMethod, T>;\n}\n\nfunction createNode<T>(): TrieNode<T> {\n return { staticChildren: new Map(), handlers: new Map() };\n}\n\nfunction splitPath(path: string): string[] {\n return path.split(\"/\").filter((segment) => segment.length > 0);\n}\n\ninterface WalkResult<T> {\n node: TrieNode<T>;\n params: Record<string, string>;\n}\n\n/**\n * Depth-first with backtracking: a static/param branch that leads to a dead\n * end (no route registered there) must not block a sibling branch — e.g.\n * `/posts/new` (static, POST only) must not swallow `/posts/:id` (GET) when\n * someone requests GET /posts/new.\n */\nfunction walk<T>(\n node: TrieNode<T>,\n segments: readonly string[],\n index: number,\n params: Record<string, string>,\n): WalkResult<T> | undefined {\n if (index === segments.length) {\n return node.handlers.size > 0 ? { node, params } : undefined;\n }\n\n const segment = segments[index] as string;\n\n const staticChild = node.staticChildren.get(segment);\n if (staticChild) {\n const result = walk(staticChild, segments, index + 1, params);\n if (result) {\n return result;\n }\n }\n\n if (node.paramChild) {\n const { name, node: paramNode } = node.paramChild;\n const result = walk(paramNode, segments, index + 1, { ...params, [name]: segment });\n if (result) {\n return result;\n }\n }\n\n if (node.wildcardChild) {\n const { name, node: wildcardNode } = node.wildcardChild;\n if (wildcardNode.handlers.size > 0) {\n return { node: wildcardNode, params: { ...params, [name]: segments.slice(index).join(\"/\") } };\n }\n }\n\n return undefined;\n}\n\n/**\n * A path router keyed on an opaque handler type `T` — this package plugs in\n * controller method references; tests plug in plain strings.\n */\nexport class Router<T> {\n readonly #root = createNode<T>();\n\n add(method: HttpMethod, path: string, handler: T): void {\n const segments = splitPath(path);\n let node = this.#root;\n\n for (const segment of segments) {\n if (segment.startsWith(\":\")) {\n const name = segment.slice(1);\n node.paramChild ??= { name, node: createNode() };\n node = node.paramChild.node;\n } else if (segment === \"*\") {\n node.wildcardChild ??= { name: \"*\", node: createNode() };\n node = node.wildcardChild.node;\n } else {\n let child = node.staticChildren.get(segment);\n if (!child) {\n child = createNode();\n node.staticChildren.set(segment, child);\n }\n node = child;\n }\n }\n\n if (node.handlers.has(method)) {\n throw new DuplicateRouteError(method, path);\n }\n node.handlers.set(method, handler);\n }\n\n match(method: HttpMethod, path: string): RouteLookupResult<T> {\n const segments = splitPath(path);\n const result = walk(this.#root, segments, 0, {});\n\n if (!result) {\n return { kind: \"not-found\" };\n }\n\n const handler = result.node.handlers.get(method);\n if (!handler) {\n return { kind: \"method-not-allowed\", allowed: [...result.node.handlers.keys()] };\n }\n\n return { kind: \"found\", handler, params: result.params };\n }\n}\n","import type { Application } from \"@blixis-io/core\";\nimport type { Class } from \"@blixis-io/di\";\nimport type { ZodType } from \"zod\";\nimport { getControllerPrefix } from \"./decorators/controller.js\";\nimport { getClassGuards, getMethodGuards, isGlobalGuard, type CanActivate } from \"./decorators/guards.js\";\nimport { getClassInterceptors, getMethodInterceptors, type Interceptor } from \"./decorators/interceptors.js\";\nimport { getParamSources, type ParamSource } from \"./decorators/params.js\";\nimport { getHttpCode, getReturnsSchema, getReturnsValidate, getRoutes } from \"./decorators/routes.js\";\nimport {\n BadRequestException,\n ForbiddenException,\n GatewayTimeoutException,\n HttpException,\n PayloadTooLargeException,\n UnsupportedMediaTypeException,\n} from \"./exceptions.js\";\nimport { resolveHandlerArgs } from \"./params.js\";\nimport { runInRequestContext } from \"./request-context.js\";\nimport { validateResponse } from \"./response.js\";\nimport { Router } from \"./router.js\";\nimport type { HttpMethod } from \"./types.js\";\n\nconst DEFAULT_BODY_LIMIT = 1024 * 1024; // 1 MiB\n\nexport interface HandlerOptions {\n bodyLimit?: number;\n /**\n * Milliseconds a request may take before the client gets a 504. Off by default. The handler (and\n * anything it awaits) is not killed — it sees the timeout through `request.signal` (`@Req()`), and\n * must pass that signal on to cancellable work (`fetch`, DB queries) to actually stop.\n */\n requestTimeout?: number;\n /**\n * `\"always\"` (default) validates every `@Returns` route and sends the parsed value, so the schema also\n * strips unknown keys. `\"never\"` sends handler values as-is, which skips that stripping: an object with\n * extra fields (say a `passwordHash`) is then sent whole. A route's `@Returns(..., { validate })` wins.\n */\n responseValidation?: \"always\" | \"never\";\n}\n\nexport class NotAControllerError extends Error {\n override readonly name = \"NotAControllerError\";\n\n constructor(target: { name: string }) {\n super(`${target.name} is not a controller — did you forget @Controller()?`);\n }\n}\n\ninterface RouteEntry {\n controller: Class;\n propertyKey: string | symbol;\n paramSources: ReadonlyMap<number, ParamSource>;\n httpCode?: number | undefined;\n responseSchema?: ZodType | undefined;\n /** Per-route `@Returns(..., { validate })` override; `undefined` follows the app-wide `responseValidation`. */\n validateResponse?: boolean | undefined;\n guards: Class<CanActivate>[];\n interceptors: Class<Interceptor>[];\n}\n\n/**\n * Flattens every `@Controller`'s route metadata into one `Router`. Guard\n * classes referenced via `@UseGuards` must also be registered as providers\n * on the owning module — this doesn't instantiate them, it only records\n * which classes `createHandler` will later ask the `Application` for.\n */\n/** Guard classes marked `@GlobalGuard()` among the application's resolved providers, in dependency order. */\nfunction discoverGlobalGuards(application: Application): Class<CanActivate>[] {\n const found: Class<CanActivate>[] = [];\n for (const [, instance] of application.resolved()) {\n if (typeof instance !== \"object\" || instance === null || !isGlobalGuard(instance.constructor)) {\n continue;\n }\n const guardClass: unknown = instance.constructor;\n if (!isGuardClass(guardClass)) {\n throw new TypeError(`${instance.constructor.name} is marked @GlobalGuard() but has no canActivate() method.`);\n }\n found.push(guardClass);\n }\n return found;\n}\n\nfunction isGuardClass(value: unknown): value is Class<CanActivate> {\n return typeof value === \"function\" && typeof value.prototype === \"object\" && value.prototype !== null && \"canActivate\" in value.prototype;\n}\n\nexport function buildRouter(controllers: readonly Class[], globalGuards: readonly Class<CanActivate>[] = []): Router<RouteEntry> {\n const router = new Router<RouteEntry>();\n\n for (const controller of controllers) {\n const prefix = getControllerPrefix(controller);\n if (prefix === undefined) {\n throw new NotAControllerError(controller);\n }\n\n const prototype = controller.prototype as object;\n const classGuards = getClassGuards(controller);\n const classInterceptors = getClassInterceptors(controller);\n\n for (const route of getRoutes(controller)) {\n router.add(route.method, `/${prefix}/${route.path}`, {\n controller,\n propertyKey: route.propertyKey,\n paramSources: getParamSources(prototype, route.propertyKey),\n httpCode: getHttpCode(prototype, route.propertyKey),\n responseSchema: getReturnsSchema(prototype, route.propertyKey),\n validateResponse: getReturnsValidate(prototype, route.propertyKey),\n guards: [...globalGuards, ...classGuards, ...getMethodGuards(prototype, route.propertyKey)],\n interceptors: [...classInterceptors, ...getMethodInterceptors(prototype, route.propertyKey)],\n });\n }\n }\n\n return router;\n}\n\n/** Reads the stream chunk by chunk and cancels it the moment `limit` bytes are exceeded, so a chunked body without a `Content-Length` can't be buffered whole before being rejected. */\nasync function readBodyText(body: ReadableStream<Uint8Array>, limit: number): Promise<string> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let received = 0;\n let text = \"\";\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return text + decoder.decode();\n }\n received += value.byteLength;\n if (received > limit) {\n await reader.cancel();\n throw new PayloadTooLargeException();\n }\n text += decoder.decode(value, { stream: true });\n }\n}\n\nasync function readJsonBody(request: Request, bodyLimit: number): Promise<unknown> {\n if (request.body === null) {\n return undefined;\n }\n\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().startsWith(\"application/json\")) {\n throw new UnsupportedMediaTypeException();\n }\n\n const contentLength = request.headers.get(\"content-length\");\n if (contentLength !== null && Number(contentLength) > bodyLimit) {\n throw new PayloadTooLargeException();\n }\n\n const text = await readBodyText(request.body, bodyLimit);\n if (text.length === 0) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n throw new BadRequestException(\"Invalid JSON body\");\n }\n}\n\nconst STATUS_TITLES: Record<number, string> = {\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 405: \"Method Not Allowed\",\n 409: \"Conflict\",\n 413: \"Payload Too Large\",\n 415: \"Unsupported Media Type\",\n 500: \"Internal Server Error\",\n 504: \"Gateway Timeout\",\n};\n\nfunction problemResponse(status: number, detail: string, extra?: Record<string, unknown>): Response {\n const body = {\n type: \"about:blank\",\n title: STATUS_TITLES[status] ?? \"Error\",\n status,\n detail,\n ...extra,\n };\n return new Response(JSON.stringify(body), { status, headers: { \"content-type\": \"application/problem+json\" } });\n}\n\nfunction exceptionToResponse(error: unknown): Response {\n if (error instanceof HttpException) {\n return problemResponse(error.status, error.detail, error.extra);\n }\n // Internal errors are logged but never surfaced to the client.\n console.error(error);\n return problemResponse(500, \"An unexpected error occurred\");\n}\n\n/**\n * A raw `Response` or an `undefined` (204) return bypasses `responseSchema`\n * entirely — both are deliberate escape hatches from the normal JSON path,\n * not a value the schema was ever meant to describe.\n */\nasync function toResponse(\n value: unknown,\n httpCode: number | undefined,\n responseSchema: ZodType | undefined,\n validate: boolean,\n): Promise<Response> {\n if (value instanceof Response) {\n return value;\n }\n if (value === undefined) {\n return new Response(null, { status: httpCode ?? 204 });\n }\n const validated = validate ? await validateResponse(responseSchema, value) : value;\n return new Response(JSON.stringify(validated), {\n status: httpCode ?? 200,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\n/** Same request, but its `signal` also fires after `ms` — so handlers see the timeout via `request.signal`. */\nfunction withTimeout(request: Request, ms: number): Request {\n const signal = AbortSignal.any([request.signal, AbortSignal.timeout(ms)]);\n return new Request(request, { signal });\n}\n\n/** Settles with `work`, unless `signal` aborts first: a client disconnect becomes a 499, a timeout a 504. `work` keeps running; its late result/rejection is ignored. */\nfunction raceAbort(work: Promise<Response>, signal: AbortSignal, clientSignal: AbortSignal): Promise<Response> {\n const abortResponse = (): Response =>\n clientSignal.aborted ? new Response(null, { status: 499 }) : exceptionToResponse(new GatewayTimeoutException());\n if (signal.aborted) {\n return Promise.resolve(abortResponse());\n }\n return new Promise<Response>((resolve, reject) => {\n const onAbort = (): void => {\n resolve(abortResponse());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n work.then(resolve, reject).finally(() => {\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n}\n\n/** Builds a single `(Request) => Promise<Response>` function serving every controller's routes. */\nexport function createHandler(\n controllers: readonly Class[],\n application: Application,\n options: HandlerOptions = {},\n): (request: Request) => Promise<Response> {\n const router = buildRouter(controllers, discoverGlobalGuards(application));\n const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;\n\n const requestTimeout = options.requestTimeout;\n const validateByDefault = options.responseValidation !== \"never\";\n\n return async function handle(incoming: Request): Promise<Response> {\n const request = requestTimeout === undefined ? incoming : withTimeout(incoming, requestTimeout);\n const url = new URL(request.url);\n const match = router.match(request.method as HttpMethod, url.pathname);\n\n if (match.kind === \"not-found\") {\n return problemResponse(404, \"No route matches this path\");\n }\n if (match.kind === \"method-not-allowed\") {\n const response = problemResponse(405, `Allowed methods: ${match.allowed.join(\", \")}`);\n response.headers.set(\"allow\", match.allowed.join(\", \"));\n return response;\n }\n\n const route = match.handler;\n\n return runInRequestContext(async () => {\n try {\n // Sequential and short-circuiting on purpose: a later guard must not\n // run once an earlier one has already denied the request.\n for (const guardClass of route.guards) {\n const guard = application.get(guardClass);\n const allowed = await guard.canActivate({ request, params: match.params, controller: route.controller, handler: route.propertyKey });\n if (!allowed) {\n throw new ForbiddenException();\n }\n }\n\n const invoke = async (): Promise<Response> => {\n let cachedBody: { value: unknown } | undefined;\n const args = await resolveHandlerArgs(route.paramSources, {\n request,\n routeParams: match.params,\n getBody: async () => {\n cachedBody ??= { value: await readJsonBody(request, bodyLimit) };\n return cachedBody.value;\n },\n });\n\n const instance = application.get(route.controller) as Record<PropertyKey, (...args: unknown[]) => unknown>;\n const result: unknown = await instance[route.propertyKey]?.(...args);\n\n return toResponse(result, route.httpCode, route.responseSchema, route.validateResponse ?? validateByDefault);\n };\n\n // Class-level interceptors wrap outermost, method-level innermost —\n // built right-to-left so the first entry ends up as the outer call.\n const pipeline = route.interceptors.reduceRight<() => Promise<Response>>((next, interceptorClass) => {\n const interceptor = application.get(interceptorClass);\n return async () => interceptor.intercept({ request, params: match.params, controller: route.controller, handler: route.propertyKey }, next);\n }, invoke);\n\n return await (requestTimeout === undefined ? pipeline() : raceAbort(pipeline(), request.signal, incoming.signal));\n } catch (error) {\n return exceptionToResponse(error);\n }\n });\n };\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\n\n/**\n * Builds a Web-standard `Request` from a Node `IncomingMessage`, including a body stream and abort signal.\n * Pass the matching `ServerResponse` so the signal also fires when the client disconnects before the\n * response finished — `req`'s own \"aborted\" event is deprecated and misses some disconnects.\n */\nexport function toWebRequest(req: IncomingMessage, baseUrl: string, res?: ServerResponse): Request {\n const method = req.method ?? \"GET\";\n const url = new URL(req.url ?? \"/\", baseUrl);\n\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) {\n continue;\n }\n for (const one of Array.isArray(value) ? value : [value]) {\n headers.append(name, one);\n }\n }\n\n const controller = new AbortController();\n req.once(\"aborted\", () => {\n controller.abort();\n });\n res?.once(\"close\", () => {\n if (!res.writableFinished) {\n controller.abort();\n }\n });\n\n // GET/HEAD can never carry a body (Fetch spec forbids it on the Request\n // we're about to construct). Otherwise, trust the standard signals for\n // \"the client is actually sending body bytes\" rather than the method —\n // a bodyless POST is common and must not turn into an open, empty stream.\n const hasBody =\n method !== \"GET\" &&\n method !== \"HEAD\" &&\n (req.headers[\"transfer-encoding\"] !== undefined ||\n (req.headers[\"content-length\"] !== undefined && req.headers[\"content-length\"] !== \"0\"));\n\n return new Request(url, {\n method,\n headers,\n signal: controller.signal,\n // `duplex: \"half\"` is required by Node's fetch implementation whenever body is a stream.\n ...(hasBody ? { body: Readable.toWeb(req), duplex: \"half\" as const } : {}),\n });\n}\n\n/** Writes a Web-standard `Response` back onto a Node `ServerResponse`. */\nexport async function sendWebResponse(response: Response, res: ServerResponse): Promise<void> {\n const headers: Record<string, string[]> = {};\n for (const [name, value] of response.headers) {\n (headers[name] ??= []).push(value);\n }\n res.writeHead(response.status, headers);\n\n if (!response.body) {\n res.end();\n return;\n }\n\n // `pipeline` (not `pipe`) so the streams are torn down together: a client that disconnects\n // mid-response destroys the body, which cancels the web stream's source instead of leaving\n // its producer (a DB cursor, a file handle) running. An early disconnect isn't an error.\n try {\n await pipeline(Readable.fromWeb(response.body), res);\n } catch (error) {\n if (!isPrematureClose(error)) {\n throw error;\n }\n }\n}\n\nfunction isPrematureClose(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"ERR_STREAM_PREMATURE_CLOSE\";\n}\n","import { createApplication, Module, type Application, type CreateApplicationOptions, type ModuleRef } from \"@blixis-io/core\";\nimport type { Class, Token } from \"@blixis-io/di\";\nimport { createServer, type Server } from \"node:http\";\nimport { createHandler, type HandlerOptions } from \"./handler.js\";\nimport { sendWebResponse, toWebRequest } from \"./node-adapter.js\";\nimport { RequestContext } from \"./request-context.js\";\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\n\nexport interface ShutdownOptions {\n /**\n * Milliseconds `close()` lets in-flight requests finish before their sockets are destroyed (their\n * `request.signal` aborts). Default 10 000; `Infinity` waits indefinitely.\n */\n shutdownTimeout?: number;\n}\n\nexport type HttpApplicationOptions = HandlerOptions & CreateApplicationOptions & ShutdownOptions;\n\n/** Provides `RequestContext` app-wide, without the user needing to import anything — every `createHttpApplication` root gets wrapped with this. */\n@Module({ providers: [RequestContext], exports: [RequestContext], global: true })\nclass RequestContextModule {}\n\n/** Empty static anchor for the synthetic root — its own metadata is unused, only `imports` matters. */\n@Module()\nclass HttpRootModule {}\n\nexport type MountedHandler = (request: Request) => Response | Promise<Response>;\n\nexport interface ListenHandle {\n port: number;\n}\n\n/** The core `Application` plus a request handler, optionally bound to a real Node HTTP socket. */\nexport class HttpApplication {\n readonly #app: Application;\n readonly #handle: (request: Request) => Promise<Response>;\n readonly #shutdownTimeout: number;\n readonly #mounted = new Map<string, MountedHandler>();\n #server: Server | undefined;\n\n private constructor(app: Application, handle: (request: Request) => Promise<Response>, shutdownTimeout: number) {\n this.#app = app;\n this.#handle = handle;\n this.#shutdownTimeout = shutdownTimeout;\n }\n\n static async create(rootModule: ModuleRef, options: HttpApplicationOptions = {}): Promise<HttpApplication> {\n const wrappedRoot = { module: HttpRootModule, imports: [rootModule, RequestContextModule] };\n const app = await createApplication(wrappedRoot, { overrides: options.overrides });\n const handle = createHandler(app.controllers, app, options);\n return new HttpApplication(app, handle, options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT);\n }\n\n /** Fetches an already-resolved provider directly, bypassing HTTP entirely. */\n get<T>(token: Token<T>): T {\n return this.#app.get(token);\n }\n\n /** Every controller class in this app's module graph — what `@blixis-io/openapi` walks to build a document that stays in sync with the real running app. */\n get controllers(): readonly Class[] {\n return this.#app.controllers;\n }\n\n /**\n * Serves an exact path with a plain Web-standard handler, ahead of the controller router — for\n * framework-level endpoints (such as an OpenAPI document) that need the finished app, which a\n * controller can't get at construction time. Mounted routes bypass guards and interceptors, so\n * they are public; mounting the same method and path twice throws.\n */\n mount(method: \"GET\" | \"POST\", path: string, handler: MountedHandler): void {\n const key = `${method} ${path}`;\n if (this.#mounted.has(key)) {\n throw new Error(`${key} is already mounted`);\n }\n this.#mounted.set(key, handler);\n }\n\n /** Runs a request through the handler in-process, without a socket. */\n async handle(request: Request): Promise<Response> {\n if (this.#mounted.size > 0) {\n const mounted = this.#mounted.get(`${request.method} ${new URL(request.url).pathname}`);\n if (mounted) {\n return mounted(request);\n }\n }\n return this.#handle(request);\n }\n\n listen(port: number, hostname = \"0.0.0.0\"): Promise<ListenHandle> {\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n const request = toWebRequest(req, `http://${hostname}:${port}`, res);\n this.handle(request)\n .then((response) => sendWebResponse(response, res))\n /* v8 ignore start -- @preserve: safety net for a write failure\n (e.g. the client disconnects mid-stream); createHandler's own\n try/catch already turns every request-handling error into a\n Response, so this only fires for socket-level failures, which\n aren't reliably reproducible without a flaky, timing-dependent\n test. */\n .catch((error: unknown) => {\n console.error(error);\n if (!res.headersSent) {\n res.writeHead(500);\n }\n res.end();\n });\n /* v8 ignore stop */\n });\n\n server.once(\"error\", reject);\n server.listen(port, hostname, () => {\n this.#server = server;\n const address = server.address();\n // `address()` only returns a string for a Unix domain socket; `listen()`\n // here always takes a TCP port, so that arm is unreachable in practice.\n /* v8 ignore next -- @preserve */\n const actualPort = typeof address === \"object\" && address !== null ? address.port : port;\n resolve({ port: actualPort });\n });\n });\n }\n\n async close(signal?: string): Promise<void> {\n if (this.#server) {\n const server = this.#server;\n // Cleared before closing, not after: makes close() idempotent (a\n // second call is a no-op instead of ERR_SERVER_NOT_RUNNING) even if\n // the close below is still in flight.\n this.#server = undefined;\n await new Promise<void>((resolve, reject) => {\n // Stops accepting new connections and waits for in-flight requests;\n // idle keep-alive sockets are dropped now so they can't hold close()\n // open. Anything still running at the deadline is cut off.\n const deadline = Number.isFinite(this.#shutdownTimeout)\n ? setTimeout(() => {\n server.closeAllConnections();\n }, this.#shutdownTimeout)\n : undefined;\n server.close((error) => {\n clearTimeout(deadline);\n // #server is only ever set right after a successful listen() and\n // cleared right before this close(), so ERR_SERVER_NOT_RUNNING\n // (the one realistic cause) can't occur here.\n /* v8 ignore next 3 -- @preserve */\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n server.closeIdleConnections();\n });\n }\n await this.#app.close(signal);\n }\n}\n\nexport async function createHttpApplication(\n rootModule: ModuleRef,\n options: HttpApplicationOptions = {},\n): Promise<HttpApplication> {\n return HttpApplication.create(rootModule, options);\n}\n","import type { ModuleRef } from \"@blixis-io/core\";\nimport { createHttpApplication, type HttpApplication, type HttpApplicationOptions } from \"./http-application.js\";\n\n/** A Web-standard entry for platforms that call `fetch(request)` per request (Vercel, Netlify, Cloudflare Workers, Deno Deploy, ...). */\nexport interface FetchHandler {\n fetch(request: Request): Promise<Response>;\n /** Runs shutdown hooks. Serverless platforms rarely call this; it exists for tests and long-lived hosts. */\n close(signal?: string): Promise<void>;\n}\n\n/**\n * Boots the application lazily on the first request and reuses it afterwards, so a cold start pays for\n * module resolution once, not per request. Concurrent first requests share one boot. If boot fails the\n * error is logged, that request gets a generic 500, and the next request tries again instead of caching\n * the failure for the lifetime of the instance.\n *\n * ```ts\n * export default createFetchHandler(AppModule); // Vercel / Workers: `{ fetch }`\n * ```\n *\n * `listen()`, `shutdownTimeout` and `SIGTERM` handling don't apply here: the platform owns the socket.\n */\nexport function createFetchHandler(rootModule: ModuleRef, options: HttpApplicationOptions = {}): FetchHandler {\n let app: Promise<HttpApplication> | undefined;\n\n function boot(): Promise<HttpApplication> {\n app ??= createHttpApplication(rootModule, options).catch((error: unknown) => {\n app = undefined;\n throw error;\n });\n return app;\n }\n\n return {\n async fetch(request) {\n let application: HttpApplication;\n try {\n application = await boot();\n } catch (error) {\n console.error(error);\n return new Response(\n JSON.stringify({ type: \"about:blank\", title: \"Internal Server Error\", status: 500, detail: \"An unexpected error occurred\" }),\n { status: 500, headers: { \"content-type\": \"application/problem+json\" } },\n );\n }\n return application.handle(request);\n },\n\n async close(signal) {\n const booted = app;\n app = undefined;\n if (booted) {\n const application = await booted.catch(() => undefined);\n await application?.close(signal);\n }\n },\n };\n}\n","export const HTTP_METHODS = [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\", \"OPTIONS\"] as const;\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number];\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAgB,OAAO,sBAAsB;AACnD,MAAM,WAAW,OAAO,iBAAiB;;AAGzC,SAAgB,aAAa,SAA+C;CAC1E,QAAQ,QAAQ,gBAAgB;EAC9B,eAAe,eAAe,SAAS,QAAQ,WAAW;CAC5D;AACF;AAEA,SAAgB,gBAAgB,QAAgB,aAA+D;CAC7G,OAAO,YAAY,eAAe,QAAQ,WAAW;AACvD;;;;;;;AAQA,SAAgB,QAAQ,GAAG,MAAkD;CAC3E,MAAM,aAAa,QAAgB,gBAAwC;EACzE,IAAI,gBAAgB,KAAA,GAAW;GAC7B,MAAM,WAAW,YAAsB,UAAU,MAAM,KAAK,CAAC;GAC7D,eAAe,UAAU,CAAC,GAAG,UAAU,GAAG,IAAI,GAAG,MAAM;EACzD,OAAO;GACL,MAAM,WAAW,YAAsB,UAAU,QAAQ,WAAW,KAAK,CAAC;GAC1E,eAAe,UAAU,CAAC,GAAG,UAAU,GAAG,IAAI,GAAG,QAAQ,WAAW;EACtE;CACF;CACA,OAAO;AACT;AAEA,SAAgB,gBAAgB,QAA0B;CACxD,OAAO,YAAY,UAAU,MAAM,KAAK,CAAC;AAC3C;AAEA,SAAgB,iBAAiB,QAAgB,aAAwC;CACvF,OAAO,YAAY,UAAU,QAAQ,WAAW,KAAK,CAAC;AACxD;;;AC9CA,MAAM,oBAAoB,OAAO,0BAA0B;AAE3D,SAAS,aAAa,OAAuB;CAC3C,OAAO,MAAM,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACrD;AAEA,SAAgB,WAAW,SAAS,IAAoB;CACtD,QAAQ,WAAW;EACjB,eAAe,mBAAmB,aAAa,MAAM,GAAG,MAAM;CAChE;AACF;AAEA,SAAgB,oBAAoB,QAAoC;CACtE,OAAO,YAAY,mBAAmB,MAAM;AAC9C;;;ACDA,MAAM,SAAS,OAAO,eAAe;;;;;;;AAQrC,SAAgB,UAAU,GAAG,QAAgE;CAC3F,MAAM,aAAa,QAAgB,gBAAwC;EACzE,IAAI,gBAAgB,KAAA,GAAW;GAC7B,MAAM,WAAW,YAAkC,QAAQ,MAAM,KAAK,CAAC;GACvE,eAAe,QAAQ,CAAC,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM;EACzD,OAAO;GACL,MAAM,WAAW,YAAkC,QAAQ,QAAQ,WAAW,KAAK,CAAC;GACpF,eAAe,QAAQ,CAAC,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,WAAW;EACtE;CACF;CACA,OAAO;AACT;AAEA,SAAgB,eAAe,QAAsC;CACnE,OAAO,YAAY,QAAQ,MAAM,KAAK,CAAC;AACzC;AAEA,SAAgB,gBAAgB,QAAgB,aAAoD;CAClG,OAAO,YAAY,QAAQ,QAAQ,WAAW,KAAK,CAAC;AACtD;AAEA,MAAM,eAAe,OAAO,qBAAqB;;;;;;AAOjD,SAAgB,cAA8B;CAC5C,QAAQ,WAAW;EACjB,eAAe,cAAc,MAAM,MAAM;CAC3C;AACF;AAEA,SAAgB,cAAc,QAAyB;CACrD,OAAO,YAAqB,cAAc,MAAM,MAAM;AACxD;;;;;AAMA,SAAgB,iBAAiB,KAAa,OAAkD;CAC9F,MAAM,aAAa,QAAgB,gBAAwC;EACzE,IAAI,gBAAgB,KAAA,GAClB,eAAe,KAAK,OAAO,MAAM;OAEjC,eAAe,KAAK,OAAO,QAAQ,WAAW;CAElD;CACA,OAAO;AACT;;AAGA,SAAgB,iBAAiB,KAAa,SAAoE;CAChH,MAAM,YAAqB,QAAQ,WAAW;CAE9C,QAD0B,OAAO,cAAc,YAAY,cAAc,OAAO,YAAY,KAAK,WAAW,QAAQ,OAAO,IAAI,KAAA,MAC5G,YAAY,KAAK,QAAQ,UAAU;AACxD;;;AC1EA,MAAM,eAAe,OAAO,qBAAqB;;;;;;;;AASjD,SAAgB,gBAAgB,GAAG,cAAsE;CACvG,MAAM,aAAa,QAAgB,gBAAwC;EACzE,IAAI,gBAAgB,KAAA,GAAW;GAC7B,MAAM,WAAW,YAAkC,cAAc,MAAM,KAAK,CAAC;GAC7E,eAAe,cAAc,CAAC,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM;EACrE,OAAO;GACL,MAAM,WAAW,YAAkC,cAAc,QAAQ,WAAW,KAAK,CAAC;GAC1F,eAAe,cAAc,CAAC,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,WAAW;EAClF;CACF;CACA,OAAO;AACT;AAEA,SAAgB,qBAAqB,QAAsC;CACzE,OAAO,YAAY,cAAc,MAAM,KAAK,CAAC;AAC/C;AAEA,SAAgB,sBAAsB,QAAgB,aAAoD;CACxG,OAAO,YAAY,cAAc,QAAQ,WAAW,KAAK,CAAC;AAC5D;;;ACzBA,MAAM,SAAS,OAAO,eAAe;AAErC,SAAS,eAAe,QAAyC;CAC/D,QAAQ,QAAQ,aAAa,mBAAmB;EAC9C,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,kHAAkH;EAEpI,MAAM,UACJ,YAAsC,QAAQ,QAAQ,WAAW,qBAAK,IAAI,IAAyB;EACrG,QAAQ,IAAI,gBAAgB,MAAM;EAClC,eAAe,QAAQ,SAAS,QAAQ,WAAW;CACrD;AACF;;AAGA,SAAgB,KAAK,QAAsC;CACzD,OAAO,eAAe;EAAE,MAAM;EAAQ;CAAO,CAAC;AAChD;;AAGA,SAAgB,MAAM,QAAsC;CAC1D,OAAO,eAAe;EAAE,MAAM;EAAS;CAAO,CAAC;AACjD;;AAGA,SAAgB,MAAM,MAAc,QAAsC;CACxE,OAAO,eAAe;EAAE,MAAM;EAAS;EAAM;CAAO,CAAC;AACvD;;AAGA,SAAgBA,UAAQ,MAAmC;CACzD,OAAO,eAAe;EAAE,MAAM;EAAW;CAAK,CAAC;AACjD;;AAGA,SAAgB,MAA0B;CACxC,OAAO,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC;AAEA,SAAgB,gBAAgB,QAAgB,aAAwD;CACtG,OAAO,YAAY,QAAQ,QAAQ,WAAW,qBAAK,IAAI,IAAI;AAC7D;;;ACzCA,MAAM,SAAS,OAAO,eAAe;AACrC,MAAM,YAAY,OAAO,kBAAkB;AAC3C,MAAM,UAAU,OAAO,gBAAgB;AACvC,MAAM,mBAAmB,OAAO,yBAAyB;AAEzD,SAAS,eAAe,QAAoB,MAA+B;CACzE,QAAQ,QAAQ,gBAAgB;EAC9B,MAAM,OAAO,OAAO;EACpB,MAAM,SAAS,YAA+B,QAAQ,IAAI,KAAK,CAAC;EAChE,OAAO,KAAK;GAAE;GAAQ;GAAM;EAAY,CAAC;EACzC,eAAe,QAAQ,QAAQ,IAAI;CACrC;AACF;AAEA,MAAa,OAAO,OAAO,OAAwB,eAAe,OAAO,IAAI;AAC7E,MAAa,QAAQ,OAAO,OAAwB,eAAe,QAAQ,IAAI;AAC/E,MAAa,OAAO,OAAO,OAAwB,eAAe,OAAO,IAAI;AAC7E,MAAa,SAAS,OAAO,OAAwB,eAAe,SAAS,IAAI;AACjF,MAAa,UAAU,OAAO,OAAwB,eAAe,UAAU,IAAI;AAEnF,SAAgB,UAAU,QAAmC;CAC3D,OAAO,YAAY,QAAQ,MAAM,KAAK,CAAC;AACzC;;AAGA,SAAgB,SAAS,MAA+B;CACtD,QAAQ,QAAQ,gBAAgB;EAC9B,eAAe,WAAW,MAAM,QAAQ,WAAW;CACrD;AACF;AAEA,SAAgB,YAAY,QAAgB,aAAkD;CAC5F,OAAO,YAAY,WAAW,QAAQ,WAAW;AACnD;;;;;;;AAQA,SAAgB,QAAQ,QAAiB,UAA0B,CAAC,GAAoB;CACtF,QAAQ,QAAQ,gBAAgB;EAC9B,eAAe,SAAS,QAAQ,QAAQ,WAAW;EACnD,IAAI,QAAQ,aAAa,KAAA,GACvB,eAAe,kBAAkB,QAAQ,UAAU,QAAQ,WAAW;CAE1E;AACF;;AAYA,SAAgB,mBAAmB,QAAgB,aAAmD;CACpG,OAAO,YAAY,kBAAkB,QAAQ,WAAW;AAC1D;AAEA,SAAgB,iBAAiB,QAAgB,aAAmD;CAClG,OAAO,YAAY,SAAS,QAAQ,WAAW;AACjD;;;;AC3EA,IAAa,gBAAb,cAAmC,MAAM;CACvC,YACE,QACA,QACA,OACA;EACA,MAAM,MAAM;EAJI,KAAA,SAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EAGhB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,cAAc;CACrD,YAAY,SAAS,eAAe,OAAiC;EACnE,MAAM,KAAK,QAAQ,KAAK;EACxB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,cAAc;CACvD,YAAY,SAAS,gBAAgB;EACnC,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,qBAAb,cAAwC,cAAc;CACpD,YAAY,SAAS,aAAa;EAChC,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,cAAc;CACnD,YAAY,SAAS,aAAa;EAChC,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,cAAc;CACnD,YAAY,SAAS,YAAY;EAC/B,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,2BAAb,cAA8C,cAAc;CAC1D,YAAY,SAAS,qBAAqB;EACxC,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,gCAAb,cAAmD,cAAc;CAC/D,YAAY,SAAS,0BAA0B;EAC7C,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,0BAAb,cAA6C,cAAc;CACzD,YAAY,SAAS,mBAAmB;EACtC,MAAM,KAAK,MAAM;EACjB,KAAK,OAAO;CACd;AACF;;;ACvDA,eAAe,gBAAgB,QAAiB,OAAkC;CAChF,MAAM,SAAS,MAAM,OAAO,eAAe,KAAK;CAChD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,oBAAoB,qBAAqB,EAAE,QAAQ,OAAO,MAAM,OAAO,CAAC;CAEpF,OAAO,OAAO;AAChB;AAEA,eAAe,WAAW,QAAqB,SAAmD;CAChG,QAAQ,OAAO,MAAf;EACE,KAAK,OACH,OAAO,QAAQ;EACjB,KAAK,WACH,OAAO,OAAO,OAAO,QAAQ,QAAQ,QAAQ,IAAI,OAAO,IAAI,IAAI,OAAO,YAAY,QAAQ,QAAQ,OAAO;EAC5G,KAAK,SAAS;GACZ,MAAM,MAAM,QAAQ,YAAY,OAAO;GACvC,OAAO,OAAO,SAAS,gBAAgB,OAAO,QAAQ,GAAG,IAAI;EAC/D;EACA,KAAK,SAAS;GACZ,MAAM,MAAM,OAAO,YAAY,IAAI,IAAI,QAAQ,QAAQ,GAAG,CAAC,CAAC,YAAY;GACxE,OAAO,OAAO,SAAS,gBAAgB,OAAO,QAAQ,GAAG,IAAI;EAC/D;EACA,KAAK,QAAQ;GACX,MAAM,MAAM,MAAM,QAAQ,QAAQ;GAClC,OAAO,OAAO,SAAS,gBAAgB,OAAO,QAAQ,GAAG,IAAI;EAC/D;;EAEA,SAEE,MAAM,IAAI,MAAM,0CAA2CC,OAA2B,MAAM;CAGhG;AACF;;AAGA,eAAsB,mBACpB,SACA,SACoB;CACpB,IAAI,QAAQ,SAAS,GACnB,OAAO,CAAC;CAGV,MAAM,OAAkB,MAAM,KAAc,EAAE,QAAQ,KAAK,IAAI,GAAG,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;CAEvF,MAAM,QAAQ,IACZ,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,YAAY;EACpD,KAAK,SAAS,MAAM,WAAW,QAAQ,OAAO;CAChD,CAAC,CACH;CAEA,OAAO;AACT;;;;;;;;;;;AC7DA,MAAM,UAAU,IAAI,kBAAwC;;AAG5D,IAAa,sBAAb,cAAyC,MAAM;CAC7C,cAAc;EACZ,MAAM,mEAAmE;EACzE,KAAK,OAAO;CACd;AACF;AAWO,IAAM,iBAAN,MAAM,eAAe;CAC1B,IAAiB,KAA4B;EAC3C,OAAO,QAAQ,SAAS,CAAC,EAAE,IAAI,GAAG;CACpC;CAEA,IAAI,KAAsB;EACxB,OAAO,QAAQ,SAAS,CAAC,EAAE,IAAI,GAAG,KAAK;CACzC;CAEA,IAAI,KAAa,OAAsB;EACrC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI,CAAC,OACH,MAAM,IAAI,oBAAoB;EAEhC,MAAM,IAAI,KAAK,KAAK;CACtB;AACF;AAjBC,iBAAA,WAAA,CAAA,WAAW,CAAA,GAAA,cAAA;;AAoBZ,SAAgB,oBAAuB,IAAgB;CACrD,OAAO,QAAQ,oBAAI,IAAI,IAAI,GAAG,EAAE;AAClC;;;;;;;;;;AC7BA,IAAa,0BAAb,cAA6C,MAAM;CACjD,YAAY,QAA4D;EACtE,MAAM,wCAAwC,OAAO,KAAK,UAAU,GAAG,MAAM,KAAK,KAAK,GAAG,EAAE,IAAI,MAAM,SAAS,CAAC,CAAC,KAAK,IAAI,GAAG;EADnG,KAAA,SAAA;EAE1B,KAAK,OAAO;CACd;AACF;;;;;AAMA,eAAe,MAAM,QAAiB,OAAgB;CACpD,IAAI;EACF,OAAO,OAAO,UAAU,KAAK;CAC/B,SAAS,OAAO;EACd,IAAI,iBAAiB,EAAE,KAAK,gBAC1B,OAAO,OAAO,eAAe,KAAK;EAEpC,MAAM;CACR;AACF;;AAGA,eAAsB,iBAAiB,QAA6B,OAAkC;CACpG,IAAI,CAAC,QACH,OAAO;CAET,MAAM,SAAS,MAAM,MAAM,QAAQ,KAAK;CACxC,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,wBAAwB,OAAO,MAAM,MAAM;CAEvD,OAAO,OAAO;AAChB;;;AC3BA,IAAa,sBAAb,cAAyC,MAAM;CAG7C,YAAY,QAAoB,MAAc;EAC5C,MAAM,oBAAoB,OAAO,GAAG,KAAK,+DAA+D;EAHjF,KAAA,OAAA;CAIzB;AACF;AASA,SAAS,aAA6B;CACpC,OAAO;EAAE,gCAAgB,IAAI,IAAI;EAAG,0BAAU,IAAI,IAAI;CAAE;AAC1D;AAEA,SAAS,UAAU,MAAwB;CACzC,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,QAAQ,YAAY,QAAQ,SAAS,CAAC;AAC/D;;;;;;;AAaA,SAAS,KACP,MACA,UACA,OACA,QAC2B;CAC3B,IAAI,UAAU,SAAS,QACrB,OAAO,KAAK,SAAS,OAAO,IAAI;EAAE;EAAM;CAAO,IAAI,KAAA;CAGrD,MAAM,UAAU,SAAS;CAEzB,MAAM,cAAc,KAAK,eAAe,IAAI,OAAO;CACnD,IAAI,aAAa;EACf,MAAM,SAAS,KAAK,aAAa,UAAU,QAAQ,GAAG,MAAM;EAC5D,IAAI,QACF,OAAO;CAEX;CAEA,IAAI,KAAK,YAAY;EACnB,MAAM,EAAE,MAAM,MAAM,cAAc,KAAK;EACvC,MAAM,SAAS,KAAK,WAAW,UAAU,QAAQ,GAAG;GAAE,GAAG;IAAS,OAAO;EAAQ,CAAC;EAClF,IAAI,QACF,OAAO;CAEX;CAEA,IAAI,KAAK,eAAe;EACtB,MAAM,EAAE,MAAM,MAAM,iBAAiB,KAAK;EAC1C,IAAI,aAAa,SAAS,OAAO,GAC/B,OAAO;GAAE,MAAM;GAAc,QAAQ;IAAE,GAAG;KAAS,OAAO,SAAS,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG;GAAE;EAAE;CAEhG;AAGF;;;;;AAMA,IAAa,SAAb,MAAuB;CACrB,QAAiB,WAAc;CAE/B,IAAI,QAAoB,MAAc,SAAkB;EACtD,MAAM,WAAW,UAAU,IAAI;EAC/B,IAAI,OAAO,KAAK;EAEhB,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,WAAW,GAAG,GAAG;GAC3B,MAAM,OAAO,QAAQ,MAAM,CAAC;GAC5B,KAAK,eAAe;IAAE;IAAM,MAAM,WAAW;GAAE;GAC/C,OAAO,KAAK,WAAW;EACzB,OAAO,IAAI,YAAY,KAAK;GAC1B,KAAK,kBAAkB;IAAE,MAAM;IAAK,MAAM,WAAW;GAAE;GACvD,OAAO,KAAK,cAAc;EAC5B,OAAO;GACL,IAAI,QAAQ,KAAK,eAAe,IAAI,OAAO;GAC3C,IAAI,CAAC,OAAO;IACV,QAAQ,WAAW;IACnB,KAAK,eAAe,IAAI,SAAS,KAAK;GACxC;GACA,OAAO;EACT;EAGF,IAAI,KAAK,SAAS,IAAI,MAAM,GAC1B,MAAM,IAAI,oBAAoB,QAAQ,IAAI;EAE5C,KAAK,SAAS,IAAI,QAAQ,OAAO;CACnC;CAEA,MAAM,QAAoB,MAAoC;EAC5D,MAAM,WAAW,UAAU,IAAI;EAC/B,MAAM,SAAS,KAAK,KAAK,OAAO,UAAU,GAAG,CAAC,CAAC;EAE/C,IAAI,CAAC,QACH,OAAO,EAAE,MAAM,YAAY;EAG7B,MAAM,UAAU,OAAO,KAAK,SAAS,IAAI,MAAM;EAC/C,IAAI,CAAC,SACH,OAAO;GAAE,MAAM;GAAsB,SAAS,CAAC,GAAG,OAAO,KAAK,SAAS,KAAK,CAAC;EAAE;EAGjF,OAAO;GAAE,MAAM;GAAS;GAAS,QAAQ,OAAO;EAAO;CACzD;AACF;;;ACvHA,MAAM,qBAAqB;AAkB3B,IAAa,sBAAb,cAAyC,MAAM;CAG7C,YAAY,QAA0B;EACpC,MAAM,GAAG,OAAO,KAAK,qDAAqD;EAHnD,KAAA,OAAA;CAIzB;AACF;;;;;;;;AAqBA,SAAS,qBAAqB,aAAgD;CAC5E,MAAM,QAA8B,CAAC;CACrC,KAAK,MAAM,GAAG,aAAa,YAAY,SAAS,GAAG;EACjD,IAAI,OAAO,aAAa,YAAY,aAAa,QAAQ,CAAC,cAAc,SAAS,WAAW,GAC1F;EAEF,MAAM,aAAsB,SAAS;EACrC,IAAI,CAAC,aAAa,UAAU,GAC1B,MAAM,IAAI,UAAU,GAAG,SAAS,YAAY,KAAK,2DAA2D;EAE9G,MAAM,KAAK,UAAU;CACvB;CACA,OAAO;AACT;AAEA,SAAS,aAAa,OAA6C;CACjE,OAAO,OAAO,UAAU,cAAc,OAAO,MAAM,cAAc,YAAY,MAAM,cAAc,QAAQ,iBAAiB,MAAM;AAClI;AAEA,SAAgB,YAAY,aAA+B,eAA8C,CAAC,GAAuB;CAC/H,MAAM,SAAS,IAAI,OAAmB;CAEtC,KAAK,MAAM,cAAc,aAAa;EACpC,MAAM,SAAS,oBAAoB,UAAU;EAC7C,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,oBAAoB,UAAU;EAG1C,MAAM,YAAY,WAAW;EAC7B,MAAM,cAAc,eAAe,UAAU;EAC7C,MAAM,oBAAoB,qBAAqB,UAAU;EAEzD,KAAK,MAAM,SAAS,UAAU,UAAU,GACtC,OAAO,IAAI,MAAM,QAAQ,IAAI,OAAO,GAAG,MAAM,QAAQ;GACnD;GACA,aAAa,MAAM;GACnB,cAAc,gBAAgB,WAAW,MAAM,WAAW;GAC1D,UAAU,YAAY,WAAW,MAAM,WAAW;GAClD,gBAAgB,iBAAiB,WAAW,MAAM,WAAW;GAC7D,kBAAkB,mBAAmB,WAAW,MAAM,WAAW;GACjE,QAAQ;IAAC,GAAG;IAAc,GAAG;IAAa,GAAG,gBAAgB,WAAW,MAAM,WAAW;GAAC;GAC1F,cAAc,CAAC,GAAG,mBAAmB,GAAG,sBAAsB,WAAW,MAAM,WAAW,CAAC;EAC7F,CAAC;CAEL;CAEA,OAAO;AACT;;AAGA,eAAe,aAAa,MAAkC,OAAgC;CAC5F,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,WAAW;CACf,IAAI,OAAO;CACX,SAAS;EACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;EAC1C,IAAI,MACF,OAAO,OAAO,QAAQ,OAAO;EAE/B,YAAY,MAAM;EAClB,IAAI,WAAW,OAAO;GACpB,MAAM,OAAO,OAAO;GACpB,MAAM,IAAI,yBAAyB;EACrC;EACA,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;CAChD;AACF;AAEA,eAAe,aAAa,SAAkB,WAAqC;CACjF,IAAI,QAAQ,SAAS,MACnB;CAIF,IAAI,EADgB,QAAQ,QAAQ,IAAI,cAAc,KAAK,GAAA,CAC1C,YAAY,CAAC,CAAC,WAAW,kBAAkB,GAC1D,MAAM,IAAI,8BAA8B;CAG1C,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,gBAAgB;CAC1D,IAAI,kBAAkB,QAAQ,OAAO,aAAa,IAAI,WACpD,MAAM,IAAI,yBAAyB;CAGrC,MAAM,OAAO,MAAM,aAAa,QAAQ,MAAM,SAAS;CACvD,IAAI,KAAK,WAAW,GAClB;CAGF,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,MAAM,IAAI,oBAAoB,mBAAmB;CACnD;AACF;AAEA,MAAM,gBAAwC;CAC5C,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,SAAS,gBAAgB,QAAgB,QAAgB,OAA2C;CAClG,MAAM,OAAO;EACX,MAAM;EACN,OAAO,cAAc,WAAW;EAChC;EACA;EACA,GAAG;CACL;CACA,OAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;EAAE;EAAQ,SAAS,EAAE,gBAAgB,2BAA2B;CAAE,CAAC;AAC/G;AAEA,SAAS,oBAAoB,OAA0B;CACrD,IAAI,iBAAiB,eACnB,OAAO,gBAAgB,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK;CAGhE,QAAQ,MAAM,KAAK;CACnB,OAAO,gBAAgB,KAAK,8BAA8B;AAC5D;;;;;;AAOA,eAAe,WACb,OACA,UACA,gBACA,UACmB;CACnB,IAAI,iBAAiB,UACnB,OAAO;CAET,IAAI,UAAU,KAAA,GACZ,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,YAAY,IAAI,CAAC;CAEvD,MAAM,YAAY,WAAW,MAAM,iBAAiB,gBAAgB,KAAK,IAAI;CAC7E,OAAO,IAAI,SAAS,KAAK,UAAU,SAAS,GAAG;EAC7C,QAAQ,YAAY;EACpB,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH;;AAGA,SAAS,YAAY,SAAkB,IAAqB;CAC1D,MAAM,SAAS,YAAY,IAAI,CAAC,QAAQ,QAAQ,YAAY,QAAQ,EAAE,CAAC,CAAC;CACxE,OAAO,IAAI,QAAQ,SAAS,EAAE,OAAO,CAAC;AACxC;;AAGA,SAAS,UAAU,MAAyB,QAAqB,cAA8C;CAC7G,MAAM,sBACJ,aAAa,UAAU,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC,IAAI,oBAAoB,IAAI,wBAAwB,CAAC;CAChH,IAAI,OAAO,SACT,OAAO,QAAQ,QAAQ,cAAc,CAAC;CAExC,OAAO,IAAI,SAAmB,SAAS,WAAW;EAChD,MAAM,gBAAsB;GAC1B,QAAQ,cAAc,CAAC;EACzB;EACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,KAAK,KAAK,SAAS,MAAM,CAAC,CAAC,cAAc;GACvC,OAAO,oBAAoB,SAAS,OAAO;EAC7C,CAAC;CACH,CAAC;AACH;;AAGA,SAAgB,cACd,aACA,aACA,UAA0B,CAAC,GACc;CACzC,MAAM,SAAS,YAAY,aAAa,qBAAqB,WAAW,CAAC;CACzE,MAAM,YAAY,QAAQ,aAAa;CAEvC,MAAM,iBAAiB,QAAQ;CAC/B,MAAM,oBAAoB,QAAQ,uBAAuB;CAEzD,OAAO,eAAe,OAAO,UAAsC;EACjE,MAAM,UAAU,mBAAmB,KAAA,IAAY,WAAW,YAAY,UAAU,cAAc;EAC9F,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;EAC/B,MAAM,QAAQ,OAAO,MAAM,QAAQ,QAAsB,IAAI,QAAQ;EAErE,IAAI,MAAM,SAAS,aACjB,OAAO,gBAAgB,KAAK,4BAA4B;EAE1D,IAAI,MAAM,SAAS,sBAAsB;GACvC,MAAM,WAAW,gBAAgB,KAAK,oBAAoB,MAAM,QAAQ,KAAK,IAAI,GAAG;GACpF,SAAS,QAAQ,IAAI,SAAS,MAAM,QAAQ,KAAK,IAAI,CAAC;GACtD,OAAO;EACT;EAEA,MAAM,QAAQ,MAAM;EAEpB,OAAO,oBAAoB,YAAY;GACrC,IAAI;IAGF,KAAK,MAAM,cAAc,MAAM,QAG7B,IAAI,CAAC,MAFS,YAAY,IAAI,UACJ,CAAC,CAAC,YAAY;KAAE;KAAS,QAAQ,MAAM;KAAQ,YAAY,MAAM;KAAY,SAAS,MAAM;IAAY,CAAC,GAEjI,MAAM,IAAI,mBAAmB;IAIjC,MAAM,SAAS,YAA+B;KAC5C,IAAI;KACJ,MAAM,OAAO,MAAM,mBAAmB,MAAM,cAAc;MACxD;MACA,aAAa,MAAM;MACnB,SAAS,YAAY;OACnB,eAAe,EAAE,OAAO,MAAM,aAAa,SAAS,SAAS,EAAE;OAC/D,OAAO,WAAW;MACpB;KACF,CAAC;KAKD,OAAO,WAAW,MAHD,YAAY,IAAI,MAAM,UACF,CAAC,CAAC,MAAM,YAAY,GAAG,GAAG,IAAI,GAEzC,MAAM,UAAU,MAAM,gBAAgB,MAAM,oBAAoB,iBAAiB;IAC7G;IAIA,MAAM,WAAW,MAAM,aAAa,aAAsC,MAAM,qBAAqB;KACnG,MAAM,cAAc,YAAY,IAAI,gBAAgB;KACpD,OAAO,YAAY,YAAY,UAAU;MAAE;MAAS,QAAQ,MAAM;MAAQ,YAAY,MAAM;MAAY,SAAS,MAAM;KAAY,GAAG,IAAI;IAC5I,GAAG,MAAM;IAET,OAAO,OAAO,mBAAmB,KAAA,IAAY,SAAS,IAAI,UAAU,SAAS,GAAG,QAAQ,QAAQ,SAAS,MAAM;GACjH,SAAS,OAAO;IACd,OAAO,oBAAoB,KAAK;GAClC;EACF,CAAC;CACH;AACF;;;;;;;;ACjTA,SAAgB,aAAa,KAAsB,SAAiB,KAA+B;CACjG,MAAM,SAAS,IAAI,UAAU;CAC7B,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,OAAO;CAE3C,MAAM,UAAU,IAAI,QAAQ;CAC5B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,IAAI,OAAO,GAAG;EACvD,IAAI,UAAU,KAAA,GACZ;EAEF,KAAK,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GACrD,QAAQ,OAAO,MAAM,GAAG;CAE5B;CAEA,MAAM,aAAa,IAAI,gBAAgB;CACvC,IAAI,KAAK,iBAAiB;EACxB,WAAW,MAAM;CACnB,CAAC;CACD,KAAK,KAAK,eAAe;EACvB,IAAI,CAAC,IAAI,kBACP,WAAW,MAAM;CAErB,CAAC;CAMD,MAAM,UACJ,WAAW,SACX,WAAW,WACV,IAAI,QAAQ,yBAAyB,KAAA,KACnC,IAAI,QAAQ,sBAAsB,KAAA,KAAa,IAAI,QAAQ,sBAAsB;CAEtF,OAAO,IAAI,QAAQ,KAAK;EACtB;EACA;EACA,QAAQ,WAAW;EAEnB,GAAI,UAAU;GAAE,MAAM,SAAS,MAAM,GAAG;GAAG,QAAQ;EAAgB,IAAI,CAAC;CAC1E,CAAC;AACH;;AAGA,eAAsB,gBAAgB,UAAoB,KAAoC;CAC5F,MAAM,UAAoC,CAAC;CAC3C,KAAK,MAAM,CAAC,MAAM,UAAU,SAAS,SACnC,CAAC,QAAQ,UAAU,CAAC,EAAA,CAAG,KAAK,KAAK;CAEnC,IAAI,UAAU,SAAS,QAAQ,OAAO;CAEtC,IAAI,CAAC,SAAS,MAAM;EAClB,IAAI,IAAI;EACR;CACF;CAKA,IAAI;EACF,MAAM,SAAS,SAAS,QAAQ,SAAS,IAAI,GAAG,GAAG;CACrD,SAAS,OAAO;EACd,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM;CAEV;AACF;AAEA,SAAS,iBAAiB,OAAyB;CACjD,OAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;AACrE;;;ACxEA,MAAM,2BAA2B;;AAajC,IACM,uBADN,MACM,qBAAqB,CAAC;AAD3B,uBAAA,WAAA,CAAA,OAAO;CAAE,WAAW,CAAC,cAAc;CAAG,SAAS,CAAC,cAAc;CAAG,QAAQ;AAAK,CAAC,CAAA,GAAA,oBAAA;;AAIhF,IACM,iBADN,MACM,eAAe,CAAC;AADrB,iBAAA,WAAA,CAAA,OAAO,CAAA,GAAA,cAAA;;AAUR,IAAa,kBAAb,MAAa,gBAAgB;CAC3B;CACA;CACA;CACA,2BAAoB,IAAI,IAA4B;CACpD;CAEA,YAAoB,KAAkB,QAAiD,iBAAyB;EAC9G,KAAK,OAAO;EACZ,KAAK,UAAU;EACf,KAAK,mBAAmB;CAC1B;CAEA,aAAa,OAAO,YAAuB,UAAkC,CAAC,GAA6B;EAEzG,MAAM,MAAM,MAAM,kBAAkB;GADd,QAAQ;GAAgB,SAAS,CAAC,YAAY,oBAAoB;EAC1C,GAAG,EAAE,WAAW,QAAQ,UAAU,CAAC;EACjF,MAAM,SAAS,cAAc,IAAI,aAAa,KAAK,OAAO;EAC1D,OAAO,IAAI,gBAAgB,KAAK,QAAQ,QAAQ,mBAAmB,wBAAwB;CAC7F;;CAGA,IAAO,OAAoB;EACzB,OAAO,KAAK,KAAK,IAAI,KAAK;CAC5B;;CAGA,IAAI,cAAgC;EAClC,OAAO,KAAK,KAAK;CACnB;;;;;;;CAQA,MAAM,QAAwB,MAAc,SAA+B;EACzE,MAAM,MAAM,GAAG,OAAO,GAAG;EACzB,IAAI,KAAK,SAAS,IAAI,GAAG,GACvB,MAAM,IAAI,MAAM,GAAG,IAAI,oBAAoB;EAE7C,KAAK,SAAS,IAAI,KAAK,OAAO;CAChC;;CAGA,MAAM,OAAO,SAAqC;EAChD,IAAI,KAAK,SAAS,OAAO,GAAG;GAC1B,MAAM,UAAU,KAAK,SAAS,IAAI,GAAG,QAAQ,OAAO,GAAG,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC,UAAU;GACtF,IAAI,SACF,OAAO,QAAQ,OAAO;EAE1B;EACA,OAAO,KAAK,QAAQ,OAAO;CAC7B;CAEA,OAAO,MAAc,WAAW,WAAkC;EAChE,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,SAAS,cAAc,KAAK,QAAQ;IACxC,MAAM,UAAU,aAAa,KAAK,UAAU,SAAS,GAAG,QAAQ,GAAG;IACnE,KAAK,OAAO,OAAO,CAAC,CACjB,MAAM,aAAa,gBAAgB,UAAU,GAAG,CAAC,CAAC,CAOlD,OAAO,UAAmB;KACzB,QAAQ,MAAM,KAAK;KACnB,IAAI,CAAC,IAAI,aACP,IAAI,UAAU,GAAG;KAEnB,IAAI,IAAI;IACV,CAAC;;GAEL,CAAC;GAED,OAAO,KAAK,SAAS,MAAM;GAC3B,OAAO,OAAO,MAAM,gBAAgB;IAClC,KAAK,UAAU;IACf,MAAM,UAAU,OAAO,QAAQ;IAK/B,QAAQ,EAAE,MADS,OAAO,YAAY,YAAY,YAAY,OAAO,QAAQ,OAAO,KACzD,CAAC;GAC9B,CAAC;EACH,CAAC;CACH;CAEA,MAAM,MAAM,QAAgC;EAC1C,IAAI,KAAK,SAAS;GAChB,MAAM,SAAS,KAAK;GAIpB,KAAK,UAAU,KAAA;GACf,MAAM,IAAI,SAAe,SAAS,WAAW;IAI3C,MAAM,WAAW,OAAO,SAAS,KAAK,gBAAgB,IAClD,iBAAiB;KACf,OAAO,oBAAoB;IAC7B,GAAG,KAAK,gBAAgB,IACxB,KAAA;IACJ,OAAO,OAAO,UAAU;KACtB,aAAa,QAAQ;;KAKrB,IAAI,OAAO;MACT,OAAO,KAAK;MACZ;KACF;KACA,QAAQ;IACV,CAAC;IACD,OAAO,qBAAqB;GAC9B,CAAC;EACH;EACA,MAAM,KAAK,KAAK,MAAM,MAAM;CAC9B;AACF;AAEA,eAAsB,sBACpB,YACA,UAAkC,CAAC,GACT;CAC1B,OAAO,gBAAgB,OAAO,YAAY,OAAO;AACnD;;;;;;;;;;;;;;;AC9IA,SAAgB,mBAAmB,YAAuB,UAAkC,CAAC,GAAiB;CAC5G,IAAI;CAEJ,SAAS,OAAiC;EACxC,QAAQ,sBAAsB,YAAY,OAAO,CAAC,CAAC,OAAO,UAAmB;GAC3E,MAAM,KAAA;GACN,MAAM;EACR,CAAC;EACD,OAAO;CACT;CAEA,OAAO;EACL,MAAM,MAAM,SAAS;GACnB,IAAI;GACJ,IAAI;IACF,cAAc,MAAM,KAAK;GAC3B,SAAS,OAAO;IACd,QAAQ,MAAM,KAAK;IACnB,OAAO,IAAI,SACT,KAAK,UAAU;KAAE,MAAM;KAAe,OAAO;KAAyB,QAAQ;KAAK,QAAQ;IAA+B,CAAC,GAC3H;KAAE,QAAQ;KAAK,SAAS,EAAE,gBAAgB,2BAA2B;IAAE,CACzE;GACF;GACA,OAAO,YAAY,OAAO,OAAO;EACnC;EAEA,MAAM,MAAM,QAAQ;GAClB,MAAM,SAAS;GACf,MAAM,KAAA;GACN,IAAI,QAEF,OAAM,MADoB,OAAO,YAAY,KAAA,CAAS,EAAA,EACnC,MAAM,MAAM;EAEnC;CACF;AACF;;;ACzDA,MAAa,eAAe;CAAC;CAAO;CAAQ;CAAO;CAAS;CAAU;CAAQ;AAAS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blixis-io/http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The HTTP layer built on @blixis-io/core — routing, controllers, request validation with Zod, guards, RFC 9457 error responses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Michael Voeten <michael@voeten.online>",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@blixis-io/core": "0.3.
|
|
38
|
-
"@blixis-io/di": "0.1.
|
|
37
|
+
"@blixis-io/core": "0.3.1",
|
|
38
|
+
"@blixis-io/di": "0.1.2",
|
|
39
39
|
"zod": "^4.6.5"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|