@blixis-io/http 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +32 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +90 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,9 @@ export declare class PayloadTooLargeException extends HttpException {
|
|
|
145
145
|
export declare class UnsupportedMediaTypeException extends HttpException {
|
|
146
146
|
constructor(detail?: string);
|
|
147
147
|
}
|
|
148
|
+
export declare class GatewayTimeoutException extends HttpException {
|
|
149
|
+
constructor(detail?: string);
|
|
150
|
+
}
|
|
148
151
|
//#endregion
|
|
149
152
|
//#region src/router.d.ts
|
|
150
153
|
interface RouteFound<T> {
|
|
@@ -173,6 +176,12 @@ export declare class Router<T> {
|
|
|
173
176
|
//#region src/handler.d.ts
|
|
174
177
|
interface HandlerOptions {
|
|
175
178
|
bodyLimit?: number;
|
|
179
|
+
/**
|
|
180
|
+
* Milliseconds a request may take before the client gets a 504. Off by default. The handler (and
|
|
181
|
+
* anything it awaits) is not killed — it sees the timeout through `request.signal` (`@Req()`), and
|
|
182
|
+
* must pass that signal on to cancellable work (`fetch`, DB queries) to actually stop.
|
|
183
|
+
*/
|
|
184
|
+
requestTimeout?: number;
|
|
176
185
|
}
|
|
177
186
|
export declare class NotAControllerError extends Error {
|
|
178
187
|
readonly name = "NotAControllerError";
|
|
@@ -200,7 +209,15 @@ export declare function buildRouter(controllers: readonly Class[]): Router<Route
|
|
|
200
209
|
export declare function createHandler(controllers: readonly Class[], application: Application, options?: HandlerOptions): (request: Request) => Promise<Response>;
|
|
201
210
|
//#endregion
|
|
202
211
|
//#region src/http-application.d.ts
|
|
203
|
-
|
|
212
|
+
interface ShutdownOptions {
|
|
213
|
+
/**
|
|
214
|
+
* Milliseconds `close()` lets in-flight requests finish before their sockets are destroyed (their
|
|
215
|
+
* `request.signal` aborts). Default 10 000; `Infinity` waits indefinitely.
|
|
216
|
+
*/
|
|
217
|
+
shutdownTimeout?: number;
|
|
218
|
+
}
|
|
219
|
+
type HttpApplicationOptions = HandlerOptions & CreateApplicationOptions & ShutdownOptions;
|
|
220
|
+
type MountedHandler = (request: Request) => Response | Promise<Response>;
|
|
204
221
|
interface ListenHandle {
|
|
205
222
|
port: number;
|
|
206
223
|
}
|
|
@@ -213,6 +230,13 @@ export declare class HttpApplication {
|
|
|
213
230
|
get<T>(token: Token<T>): T;
|
|
214
231
|
/** 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. */
|
|
215
232
|
get controllers(): readonly Class[];
|
|
233
|
+
/**
|
|
234
|
+
* Serves an exact path with a plain Web-standard handler, ahead of the controller router — for
|
|
235
|
+
* framework-level endpoints (such as an OpenAPI document) that need the finished app, which a
|
|
236
|
+
* controller can't get at construction time. Mounted routes bypass guards and interceptors, so
|
|
237
|
+
* they are public; mounting the same method and path twice throws.
|
|
238
|
+
*/
|
|
239
|
+
mount(method: "GET" | "POST", path: string, handler: MountedHandler): void;
|
|
216
240
|
/** Runs a request through the handler in-process, without a socket. */
|
|
217
241
|
handle(request: Request): Promise<Response>;
|
|
218
242
|
listen(port: number, hostname?: string): Promise<ListenHandle>;
|
|
@@ -221,8 +245,12 @@ export declare class HttpApplication {
|
|
|
221
245
|
export declare function createHttpApplication(rootModule: ModuleRef, options?: HttpApplicationOptions): Promise<HttpApplication>;
|
|
222
246
|
//#endregion
|
|
223
247
|
//#region src/node-adapter.d.ts
|
|
224
|
-
/**
|
|
225
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Builds a Web-standard `Request` from a Node `IncomingMessage`, including a body stream and abort signal.
|
|
250
|
+
* Pass the matching `ServerResponse` so the signal also fires when the client disconnects before the
|
|
251
|
+
* response finished — `req`'s own "aborted" event is deprecated and misses some disconnects.
|
|
252
|
+
*/
|
|
253
|
+
export declare function toWebRequest(req: IncomingMessage, baseUrl: string, res?: ServerResponse): Request;
|
|
226
254
|
/** Writes a Web-standard `Response` back onto a Node `ServerResponse`. */
|
|
227
255
|
export declare function sendWebResponse(response: Response, res: ServerResponse): Promise<void>;
|
|
228
256
|
//#endregion
|
|
@@ -276,5 +304,5 @@ export declare class ResponseValidationError extends Error {
|
|
|
276
304
|
/** Validates a handler's return value against its `@Returns` schema, if any — returns the value (possibly parsed/coerced) unchanged when there's no schema. */
|
|
277
305
|
export declare function validateResponse(schema: ZodType | undefined, value: unknown): Promise<unknown>;
|
|
278
306
|
//#endregion
|
|
279
|
-
export type { ApiOperationOptions, CanActivate, ExecutionContext, HandlerOptions, HttpApplicationOptions, HttpMethod, Interceptor, ListenHandle, ParamResolutionContext, ParamSource, RouteDefinition, RouteFound, RouteLookupResult, RouteMethodNotAllowed, RouteNotFound };
|
|
307
|
+
export type { ApiOperationOptions, CanActivate, ExecutionContext, HandlerOptions, HttpApplicationOptions, HttpMethod, Interceptor, ListenHandle, MountedHandler, ParamResolutionContext, ParamSource, RouteDefinition, RouteFound, RouteLookupResult, RouteMethodNotAllowed, RouteNotFound, ShutdownOptions };
|
|
280
308
|
//# sourceMappingURL=index.d.ts.map
|
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/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;;UAGF;EACf,YAAY,SAAS,6BAA6B;;;;;;;;wBAWpC,aAAa,QAAQ,MAAM,iBAAiB,iBAAiB;wBAa7D,eAAe,iBAAiB,MAAM;wBAItC,gBAAgB,gBAAgB,+BAA+B,MAAM;;;UCjCpE;EACf,UAAU,SAAS,kBAAkB,YAAY,QAAQ,YAAY,WAAW,QAAQ;;;;;;;;;wBAY1E,mBAAmB,cAAc,MAAM,iBAAiB,iBAAiB;wBAazE,qBAAqB,iBAAiB,MAAM;wBAI5C,sBAAsB,gBAAgB,+BAA+B,MAAM;;;KC9B/E;EACN;EAAc,SAAS;;EACvB;EAAe,SAAS;;EACxB;EAAe;EAAc,SAAS;;EACtC;EAAiB;;EACjB;;;wBAiBU,KAAK,SAAS,UAAU;;wBAKxB,MAAM,SAAS,UAAU;;wBAKzB,MAAM,cAAc,SAAS,UAAU;;wBAKvC,QAAQ,gBAAgB;;wBAKxB,OAAO;wBAIP,gBAAgB,gBAAgB,+BAA+B,YAAY;;;qBCjD9E;KAED,qBAAqB;;;UCEhB;EACf,QAAQ;EACR;EACA;;qBAgBW,MAAG,kBAAgB;qBACnB,OAAI,kBAAgB;qBACpB,MAAG,kBAAgB;qBACnB,QAAK,kBAAgB;qBACrB,SAAM,kBAAgB;wBAEnB,UAAU,iBAAiB;;wBAK3B,SAAS,eAAe;wBAMxB,YAAY,gBAAgB;;;;;;;wBAU5B,QAAQ,QAAQ,UAAU;wBAM1B,iBAAiB,gBAAgB,+BAA+B;;;;qBCvDnE,sBAAsB;WAEf;WACA;WACA,QAAQ;EAH1B,YACkB,gBACA,gBACA,QAAQ;;qBAOf,4BAA4B;EACvC,YAAY,iBAAwB,QAAQ;;qBAMjC,8BAA8B;EACzC,YAAY;;qBAMD,2BAA2B;EACtC,YAAY;;qBAMD,0BAA0B;EACrC,YAAY;;qBAMD,0BAA0B;EACrC,YAAY;;qBAMD,iCAAiC;EAC5C,YAAY;;qBAMD,sCAAsC;EACjD,YAAY;;;;
|
|
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/node-adapter.ts","../src/params.ts","../src/request-context.ts","../src/response.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -257,6 +257,12 @@ var UnsupportedMediaTypeException = class extends HttpException {
|
|
|
257
257
|
this.name = "UnsupportedMediaTypeException";
|
|
258
258
|
}
|
|
259
259
|
};
|
|
260
|
+
var GatewayTimeoutException = class extends HttpException {
|
|
261
|
+
constructor(detail = "Gateway Timeout") {
|
|
262
|
+
super(504, detail);
|
|
263
|
+
this.name = "GatewayTimeoutException";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
260
266
|
//#endregion
|
|
261
267
|
//#region src/params.ts
|
|
262
268
|
async function parseWithSchema(schema, value) {
|
|
@@ -489,13 +495,29 @@ function buildRouter(controllers) {
|
|
|
489
495
|
}
|
|
490
496
|
return router;
|
|
491
497
|
}
|
|
498
|
+
/** 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. */
|
|
499
|
+
async function readBodyText(body, limit) {
|
|
500
|
+
const reader = body.getReader();
|
|
501
|
+
const decoder = new TextDecoder();
|
|
502
|
+
let received = 0;
|
|
503
|
+
let text = "";
|
|
504
|
+
for (;;) {
|
|
505
|
+
const { done, value } = await reader.read();
|
|
506
|
+
if (done) return text + decoder.decode();
|
|
507
|
+
received += value.byteLength;
|
|
508
|
+
if (received > limit) {
|
|
509
|
+
await reader.cancel();
|
|
510
|
+
throw new PayloadTooLargeException();
|
|
511
|
+
}
|
|
512
|
+
text += decoder.decode(value, { stream: true });
|
|
513
|
+
}
|
|
514
|
+
}
|
|
492
515
|
async function readJsonBody(request, bodyLimit) {
|
|
493
516
|
if (request.body === null) return;
|
|
494
517
|
if (!(request.headers.get("content-type") ?? "").toLowerCase().startsWith("application/json")) throw new UnsupportedMediaTypeException();
|
|
495
518
|
const contentLength = request.headers.get("content-length");
|
|
496
519
|
if (contentLength !== null && Number(contentLength) > bodyLimit) throw new PayloadTooLargeException();
|
|
497
|
-
const text = await request.
|
|
498
|
-
if (new TextEncoder().encode(text).length > bodyLimit) throw new PayloadTooLargeException();
|
|
520
|
+
const text = await readBodyText(request.body, bodyLimit);
|
|
499
521
|
if (text.length === 0) return;
|
|
500
522
|
try {
|
|
501
523
|
return JSON.parse(text);
|
|
@@ -512,7 +534,8 @@ const STATUS_TITLES = {
|
|
|
512
534
|
409: "Conflict",
|
|
513
535
|
413: "Payload Too Large",
|
|
514
536
|
415: "Unsupported Media Type",
|
|
515
|
-
500: "Internal Server Error"
|
|
537
|
+
500: "Internal Server Error",
|
|
538
|
+
504: "Gateway Timeout"
|
|
516
539
|
};
|
|
517
540
|
function problemResponse(status, detail, extra) {
|
|
518
541
|
const body = {
|
|
@@ -546,11 +569,32 @@ async function toResponse(value, httpCode, responseSchema) {
|
|
|
546
569
|
headers: { "content-type": "application/json" }
|
|
547
570
|
});
|
|
548
571
|
}
|
|
572
|
+
/** Same request, but its `signal` also fires after `ms` — so handlers see the timeout via `request.signal`. */
|
|
573
|
+
function withTimeout(request, ms) {
|
|
574
|
+
const signal = AbortSignal.any([request.signal, AbortSignal.timeout(ms)]);
|
|
575
|
+
return new Request(request, { signal });
|
|
576
|
+
}
|
|
577
|
+
/** 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. */
|
|
578
|
+
function raceAbort(work, signal, clientSignal) {
|
|
579
|
+
const abortResponse = () => clientSignal.aborted ? new Response(null, { status: 499 }) : exceptionToResponse(new GatewayTimeoutException());
|
|
580
|
+
if (signal.aborted) return Promise.resolve(abortResponse());
|
|
581
|
+
return new Promise((resolve, reject) => {
|
|
582
|
+
const onAbort = () => {
|
|
583
|
+
resolve(abortResponse());
|
|
584
|
+
};
|
|
585
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
586
|
+
work.then(resolve, reject).finally(() => {
|
|
587
|
+
signal.removeEventListener("abort", onAbort);
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
}
|
|
549
591
|
/** Builds a single `(Request) => Promise<Response>` function serving every controller's routes. */
|
|
550
592
|
function createHandler(controllers, application, options = {}) {
|
|
551
593
|
const router = buildRouter(controllers);
|
|
552
594
|
const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;
|
|
553
|
-
|
|
595
|
+
const requestTimeout = options.requestTimeout;
|
|
596
|
+
return async function handle(incoming) {
|
|
597
|
+
const request = requestTimeout === void 0 ? incoming : withTimeout(incoming, requestTimeout);
|
|
554
598
|
const url = new URL(request.url);
|
|
555
599
|
const match = router.match(request.method, url.pathname);
|
|
556
600
|
if (match.kind === "not-found") return problemResponse(404, "No route matches this path");
|
|
@@ -578,13 +622,14 @@ function createHandler(controllers, application, options = {}) {
|
|
|
578
622
|
});
|
|
579
623
|
return toResponse(await application.get(route.controller)[route.propertyKey]?.(...args), route.httpCode, route.responseSchema);
|
|
580
624
|
};
|
|
581
|
-
|
|
625
|
+
const pipeline = route.interceptors.reduceRight((next, interceptorClass) => {
|
|
582
626
|
const interceptor = application.get(interceptorClass);
|
|
583
627
|
return async () => interceptor.intercept({
|
|
584
628
|
request,
|
|
585
629
|
params: match.params
|
|
586
630
|
}, next);
|
|
587
|
-
}, invoke)
|
|
631
|
+
}, invoke);
|
|
632
|
+
return await (requestTimeout === void 0 ? pipeline() : raceAbort(pipeline(), request.signal, incoming.signal));
|
|
588
633
|
} catch (error) {
|
|
589
634
|
return exceptionToResponse(error);
|
|
590
635
|
}
|
|
@@ -593,8 +638,12 @@ function createHandler(controllers, application, options = {}) {
|
|
|
593
638
|
}
|
|
594
639
|
//#endregion
|
|
595
640
|
//#region src/node-adapter.ts
|
|
596
|
-
/**
|
|
597
|
-
|
|
641
|
+
/**
|
|
642
|
+
* Builds a Web-standard `Request` from a Node `IncomingMessage`, including a body stream and abort signal.
|
|
643
|
+
* Pass the matching `ServerResponse` so the signal also fires when the client disconnects before the
|
|
644
|
+
* response finished — `req`'s own "aborted" event is deprecated and misses some disconnects.
|
|
645
|
+
*/
|
|
646
|
+
function toWebRequest(req, baseUrl, res) {
|
|
598
647
|
const method = req.method ?? "GET";
|
|
599
648
|
const url = new URL(req.url ?? "/", baseUrl);
|
|
600
649
|
const headers = new Headers();
|
|
@@ -606,6 +655,9 @@ function toWebRequest(req, baseUrl) {
|
|
|
606
655
|
req.once("aborted", () => {
|
|
607
656
|
controller.abort();
|
|
608
657
|
});
|
|
658
|
+
res?.once("close", () => {
|
|
659
|
+
if (!res.writableFinished) controller.abort();
|
|
660
|
+
});
|
|
609
661
|
const hasBody = method !== "GET" && method !== "HEAD" && (req.headers["transfer-encoding"] !== void 0 || req.headers["content-length"] !== void 0 && req.headers["content-length"] !== "0");
|
|
610
662
|
return new Request(url, {
|
|
611
663
|
method,
|
|
@@ -636,6 +688,7 @@ async function sendWebResponse(response, res) {
|
|
|
636
688
|
}
|
|
637
689
|
//#endregion
|
|
638
690
|
//#region src/http-application.ts
|
|
691
|
+
const DEFAULT_SHUTDOWN_TIMEOUT = 1e4;
|
|
639
692
|
/** Provides `RequestContext` app-wide, without the user needing to import anything — every `createHttpApplication` root gets wrapped with this. */
|
|
640
693
|
let RequestContextModule = class RequestContextModule {};
|
|
641
694
|
RequestContextModule = __decorate([Module({
|
|
@@ -650,10 +703,13 @@ HttpRootModule = __decorate([Module()], HttpRootModule);
|
|
|
650
703
|
var HttpApplication = class HttpApplication {
|
|
651
704
|
#app;
|
|
652
705
|
#handle;
|
|
706
|
+
#shutdownTimeout;
|
|
707
|
+
#mounted = /* @__PURE__ */ new Map();
|
|
653
708
|
#server;
|
|
654
|
-
constructor(app, handle) {
|
|
709
|
+
constructor(app, handle, shutdownTimeout) {
|
|
655
710
|
this.#app = app;
|
|
656
711
|
this.#handle = handle;
|
|
712
|
+
this.#shutdownTimeout = shutdownTimeout;
|
|
657
713
|
}
|
|
658
714
|
static async create(rootModule, options = {}) {
|
|
659
715
|
const app = await createApplication({
|
|
@@ -661,7 +717,7 @@ var HttpApplication = class HttpApplication {
|
|
|
661
717
|
imports: [rootModule, RequestContextModule]
|
|
662
718
|
}, { overrides: options.overrides });
|
|
663
719
|
const handle = createHandler(app.controllers, app, options);
|
|
664
|
-
return new HttpApplication(app, handle);
|
|
720
|
+
return new HttpApplication(app, handle, options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT);
|
|
665
721
|
}
|
|
666
722
|
/** Fetches an already-resolved provider directly, bypassing HTTP entirely. */
|
|
667
723
|
get(token) {
|
|
@@ -671,15 +727,30 @@ var HttpApplication = class HttpApplication {
|
|
|
671
727
|
get controllers() {
|
|
672
728
|
return this.#app.controllers;
|
|
673
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Serves an exact path with a plain Web-standard handler, ahead of the controller router — for
|
|
732
|
+
* framework-level endpoints (such as an OpenAPI document) that need the finished app, which a
|
|
733
|
+
* controller can't get at construction time. Mounted routes bypass guards and interceptors, so
|
|
734
|
+
* they are public; mounting the same method and path twice throws.
|
|
735
|
+
*/
|
|
736
|
+
mount(method, path, handler) {
|
|
737
|
+
const key = `${method} ${path}`;
|
|
738
|
+
if (this.#mounted.has(key)) throw new Error(`${key} is already mounted`);
|
|
739
|
+
this.#mounted.set(key, handler);
|
|
740
|
+
}
|
|
674
741
|
/** Runs a request through the handler in-process, without a socket. */
|
|
675
|
-
handle(request) {
|
|
742
|
+
async handle(request) {
|
|
743
|
+
if (this.#mounted.size > 0) {
|
|
744
|
+
const mounted = this.#mounted.get(`${request.method} ${new URL(request.url).pathname}`);
|
|
745
|
+
if (mounted) return mounted(request);
|
|
746
|
+
}
|
|
676
747
|
return this.#handle(request);
|
|
677
748
|
}
|
|
678
749
|
listen(port, hostname = "0.0.0.0") {
|
|
679
750
|
return new Promise((resolve, reject) => {
|
|
680
751
|
const server = createServer((req, res) => {
|
|
681
|
-
const request = toWebRequest(req, `http://${hostname}:${port}
|
|
682
|
-
this
|
|
752
|
+
const request = toWebRequest(req, `http://${hostname}:${port}`, res);
|
|
753
|
+
this.handle(request).then((response) => sendWebResponse(response, res)).catch((error) => {
|
|
683
754
|
console.error(error);
|
|
684
755
|
if (!res.headersSent) res.writeHead(500);
|
|
685
756
|
res.end();
|
|
@@ -699,7 +770,11 @@ var HttpApplication = class HttpApplication {
|
|
|
699
770
|
const server = this.#server;
|
|
700
771
|
this.#server = void 0;
|
|
701
772
|
await new Promise((resolve, reject) => {
|
|
773
|
+
const deadline = Number.isFinite(this.#shutdownTimeout) ? setTimeout(() => {
|
|
774
|
+
server.closeAllConnections();
|
|
775
|
+
}, this.#shutdownTimeout) : void 0;
|
|
702
776
|
server.close((error) => {
|
|
777
|
+
clearTimeout(deadline);
|
|
703
778
|
/* v8 ignore next 3 -- @preserve */
|
|
704
779
|
if (error) {
|
|
705
780
|
reject(error);
|
|
@@ -707,6 +782,7 @@ var HttpApplication = class HttpApplication {
|
|
|
707
782
|
}
|
|
708
783
|
resolve();
|
|
709
784
|
});
|
|
785
|
+
server.closeIdleConnections();
|
|
710
786
|
});
|
|
711
787
|
}
|
|
712
788
|
await this.#app.close(signal);
|
|
@@ -727,6 +803,6 @@ const HTTP_METHODS = [
|
|
|
727
803
|
"OPTIONS"
|
|
728
804
|
];
|
|
729
805
|
//#endregion
|
|
730
|
-
export { ApiOperation, ApiTags, BadRequestException, Body, ConflictException, Controller, Delete, ForbiddenException, 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, createHandler, createHttpApplication, getApiOperation, getClassApiTags, getClassGuards, getClassInterceptors, getControllerPrefix, getHttpCode, getMethodApiTags, getMethodGuards, getMethodInterceptors, getParamSources, getReturnsSchema, getRoutes, resolveHandlerArgs, runInRequestContext, sendWebResponse, toWebRequest, validateResponse };
|
|
806
|
+
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, createHandler, createHttpApplication, getApiOperation, getClassApiTags, getClassGuards, getClassInterceptors, getControllerPrefix, getHttpCode, getMethodApiTags, getMethodGuards, getMethodInterceptors, getParamSources, getReturnsSchema, getRoutes, resolveHandlerArgs, runInRequestContext, sendWebResponse, toWebRequest, validateResponse };
|
|
731
807
|
|
|
732
808
|
//# 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/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\");\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): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(RETURNS, schema, target, propertyKey);\n };\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","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 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/** 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 schema.safeParseAsync(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, getRoutes } from \"./decorators/routes.js\";\nimport {\n BadRequestException,\n ForbiddenException,\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\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 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 guards: [...classGuards, ...getMethodGuards(prototype, route.propertyKey)],\n interceptors: [...classInterceptors, ...getMethodInterceptors(prototype, route.propertyKey)],\n });\n }\n }\n\n return router;\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 request.text();\n if (new TextEncoder().encode(text).length > bodyLimit) {\n throw new PayloadTooLargeException();\n }\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};\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(value: unknown, httpCode: number | undefined, responseSchema: ZodType | undefined): 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 = await validateResponse(responseSchema, value);\n return new Response(JSON.stringify(validated), {\n status: httpCode ?? 200,\n headers: { \"content-type\": \"application/json\" },\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 return async function handle(request: Request): Promise<Response> {\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);\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 pipeline();\n } catch (error) {\n return exceptionToResponse(error);\n }\n });\n };\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\n\n/** Builds a Web-standard `Request` from a Node `IncomingMessage`, including a body stream and abort signal. */\nexport function toWebRequest(req: IncomingMessage, baseUrl: string): 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\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 const body = Readable.fromWeb(response.body);\n await new Promise<void>((resolve, reject) => {\n res.once(\"finish\", resolve);\n res.once(\"error\", reject);\n body.once(\"error\", reject);\n body.pipe(res);\n });\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\nexport type HttpApplicationOptions = HandlerOptions & CreateApplicationOptions;\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 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 #server: Server | undefined;\n\n private constructor(app: Application, handle: (request: Request) => Promise<Response>) {\n this.#app = app;\n this.#handle = handle;\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);\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 /** Runs a request through the handler in-process, without a socket. */\n handle(request: Request): Promise<Response> {\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}`);\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 server.close((error) => {\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 });\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","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/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\");\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): MethodDecorator {\n return (target, propertyKey) => {\n defineMetadata(RETURNS, schema, target, propertyKey);\n };\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 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/** 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 schema.safeParseAsync(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, 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\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 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 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(value: unknown, httpCode: number | undefined, responseSchema: ZodType | undefined): 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 = await validateResponse(responseSchema, 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\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);\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\";\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 const body = Readable.fromWeb(response.body);\n await new Promise<void>((resolve, reject) => {\n res.once(\"finish\", resolve);\n res.once(\"error\", reject);\n body.once(\"error\", reject);\n body.pipe(res);\n });\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","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;;;ACLA,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;;;AC/BA,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;AAEvC,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,QAAkC;CACxD,QAAQ,QAAQ,gBAAgB;EAC9B,eAAe,SAAS,QAAQ,QAAQ,WAAW;CACrD;AACF;AAEA,SAAgB,iBAAiB,QAAgB,aAAmD;CAClG,OAAO,YAAY,SAAS,QAAQ,WAAW;AACjD;;;;ACzDA,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;;AAGA,eAAsB,iBAAiB,QAA6B,OAAkC;CACpG,IAAI,CAAC,QACH,OAAO;CAET,MAAM,SAAS,MAAM,OAAO,eAAe,KAAK;CAChD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,wBAAwB,OAAO,MAAM,MAAM;CAEvD,OAAO,OAAO;AAChB;;;ACZA,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;AAY3B,IAAa,sBAAb,cAAyC,MAAM;CAG7C,YAAY,QAA0B;EACpC,MAAM,GAAG,OAAO,KAAK,qDAAqD;EAHnD,KAAA,OAAA;CAIzB;AACF;;;;;;;AAkBA,SAAgB,YAAY,aAAmD;CAC7E,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,QAAQ,CAAC,GAAG,aAAa,GAAG,gBAAgB,WAAW,MAAM,WAAW,CAAC;GACzE,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,WAAW,OAAgB,UAA8B,gBAAwD;CAC9H,IAAI,iBAAiB,UACnB,OAAO;CAET,IAAI,UAAU,KAAA,GACZ,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,YAAY,IAAI,CAAC;CAEvD,MAAM,YAAY,MAAM,iBAAiB,gBAAgB,KAAK;CAC9D,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,WAAW;CACtC,MAAM,YAAY,QAAQ,aAAa;CAEvC,MAAM,iBAAiB,QAAQ;CAE/B,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;IAAO,CAAC,GAEvE,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,cAAc;IAChE;IAIA,MAAM,WAAW,MAAM,aAAa,aAAsC,MAAM,qBAAqB;KACnG,MAAM,cAAc,YAAY,IAAI,gBAAgB;KACpD,OAAO,YAAY,YAAY,UAAU;MAAE;MAAS,QAAQ,MAAM;KAAO,GAAG,IAAI;IAClF,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;;;;;;;;AC/QA,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;CAEA,MAAM,OAAO,SAAS,QAAQ,SAAS,IAAI;CAC3C,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,IAAI,KAAK,UAAU,OAAO;EAC1B,IAAI,KAAK,SAAS,MAAM;EACxB,KAAK,KAAK,SAAS,MAAM;EACzB,KAAK,KAAK,GAAG;CACf,CAAC;AACH;;;AChEA,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;;;ACpKA,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.2.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,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@blixis-io/core": "0.
|
|
37
|
+
"@blixis-io/core": "0.2.0",
|
|
38
38
|
"@blixis-io/di": "0.1.0",
|
|
39
39
|
"zod": "^4.6.5"
|
|
40
40
|
},
|