routup 5.2.0 → 6.0.0-beta.1

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.
@@ -0,0 +1,1844 @@
1
+ import QuickLRU from "quick-lru";
2
+ import { FastURL, ServerRequest } from "srvx";
3
+ import { HTTPError, HTTPErrorInput, HTTPErrorInput as HTTPErrorInput$1 } from "@ebec/http";
4
+ import Negotiator from "negotiator";
5
+ import { Key, ParseOptions, PathToRegexpOptions } from "path-to-regexp";
6
+ import { IncomingMessage, ServerResponse } from "node:http";
7
+
8
+ //#region src/error/module.d.ts
9
+ declare const ErrorSymbol: unique symbol;
10
+ declare class AppError extends HTTPError {
11
+ constructor(input?: HTTPErrorInput$1);
12
+ }
13
+ //#endregion
14
+ //#region src/constants.d.ts
15
+ declare const MethodName: {
16
+ readonly GET: "GET";
17
+ readonly POST: "POST";
18
+ readonly PUT: "PUT";
19
+ readonly PATCH: "PATCH";
20
+ readonly DELETE: "DELETE";
21
+ readonly OPTIONS: "OPTIONS";
22
+ readonly HEAD: "HEAD";
23
+ };
24
+ type MethodName = typeof MethodName[keyof typeof MethodName];
25
+ /**
26
+ * `MethodName` plus the open-enum escape hatch for non-standard
27
+ * methods (`PROPFIND`, `MKCOL`, custom verbs). The `(string & {})`
28
+ * intersection is structurally identical to `string` but TypeScript
29
+ * doesn't collapse the union — so callers still get autocomplete
30
+ * for the canonical methods while remaining free to pass anything.
31
+ */
32
+ type MethodNameLike = MethodName | (string & {});
33
+ declare const HeaderName: {
34
+ readonly ACCEPT: "accept";
35
+ readonly ACCEPT_CHARSET: "accept-charset";
36
+ readonly ACCEPT_ENCODING: "accept-encoding";
37
+ readonly ACCEPT_LANGUAGE: "accept-language";
38
+ readonly ACCEPT_RANGES: "accept-ranges";
39
+ readonly ALLOW: "allow";
40
+ readonly CACHE_CONTROL: "cache-control";
41
+ readonly CONTENT_DISPOSITION: "content-disposition";
42
+ readonly CONTENT_ENCODING: "content-encoding";
43
+ readonly CONTENT_LENGTH: "content-length";
44
+ readonly CONTENT_RANGE: "content-range";
45
+ readonly CONTENT_TYPE: "content-type";
46
+ readonly CONNECTION: "connection";
47
+ readonly COOKIE: "cookie";
48
+ readonly ETag: "etag";
49
+ readonly HOST: "host";
50
+ readonly IF_MODIFIED_SINCE: "if-modified-since";
51
+ readonly IF_NONE_MATCH: "if-none-match";
52
+ readonly LAST_MODIFIED: "last-modified";
53
+ readonly LOCATION: "location";
54
+ readonly RANGE: "range";
55
+ readonly RATE_LIMIT_LIMIT: "ratelimit-limit";
56
+ readonly RATE_LIMIT_REMAINING: "ratelimit-remaining";
57
+ readonly RATE_LIMIT_RESET: "ratelimit-reset";
58
+ readonly RETRY_AFTER: "retry-after";
59
+ readonly SET_COOKIE: "set-cookie";
60
+ readonly TRANSFER_ENCODING: "transfer-encoding";
61
+ readonly X_ACCEL_BUFFERING: "x-accel-buffering";
62
+ readonly X_FORWARDED_HOST: "x-forwarded-host";
63
+ readonly X_FORWARDED_FOR: "x-forwarded-for";
64
+ readonly X_FORWARDED_PROTO: "x-forwarded-proto";
65
+ };
66
+ type HeaderName = typeof HeaderName[keyof typeof HeaderName];
67
+ //#endregion
68
+ //#region src/event/types.d.ts
69
+ type AppResponse = {
70
+ status: number;
71
+ headers: Headers;
72
+ };
73
+ type AppRequest = ServerRequest;
74
+ type NextFn = (error?: Error) => unknown | Promise<unknown>;
75
+ interface IAppEvent {
76
+ /**
77
+ * The srvx ServerRequest (extends Web Standard Request).
78
+ */
79
+ readonly request: AppRequest;
80
+ /**
81
+ * Route parameters extracted from the URL path pattern. Values
82
+ * are `string` (or `undefined` for an optional param that
83
+ * didn't match) — both the trie router (`extractTrieParams`)
84
+ * and the linear router (path-to-regexp output) only ever
85
+ * produce string values.
86
+ */
87
+ readonly params: Record<string, string | undefined>;
88
+ /**
89
+ * Current request path, adjusted relative to the mount point during router nesting.
90
+ */
91
+ readonly path: string;
92
+ /**
93
+ * HTTP method (GET, POST, PUT, etc.). Typed as the canonical
94
+ * `MethodName` set with an open-enum escape hatch — non-
95
+ * standard methods (`PROPFIND`, custom verbs) still type-check
96
+ * while standard ones autocomplete.
97
+ */
98
+ readonly method: MethodNameLike;
99
+ /**
100
+ * Prefix the active route was matched on (the substring of the
101
+ * request path the matcher consumed). Set per dispatched handler
102
+ * and restored when the handler returns; useful for static-asset
103
+ * / mount-aware helpers that need to strip this off `path` to
104
+ * recover a mount-relative path.
105
+ */
106
+ readonly mountPath: string;
107
+ /**
108
+ * Web Standard Headers from the request.
109
+ */
110
+ readonly headers: Headers;
111
+ /**
112
+ * Lazily-parsed URL search parameters.
113
+ *
114
+ * For advanced query parsing (arrays, nesting), use `@routup/query`.
115
+ */
116
+ readonly searchParams: URLSearchParams;
117
+ /**
118
+ * Response accumulator — set status/headers before returning a plain value.
119
+ *
120
+ * If the handler returns a `Response` object directly, these values are
121
+ * ignored. They only apply when returning plain values (string, object, etc.)
122
+ * that go through `toResponse()`.
123
+ */
124
+ readonly response: AppResponse;
125
+ /**
126
+ * Per-request store for caching and plugin state.
127
+ *
128
+ * Use symbol keys (e.g., `Symbol.for('routup:body')`) to avoid collisions.
129
+ * Data is garbage collected with the event when the request completes.
130
+ */
131
+ readonly store: Record<string | symbol, unknown>;
132
+ /**
133
+ * Pre-resolved router options for the current dispatch context.
134
+ *
135
+ * Contains merged options from the router path stack with defaults applied.
136
+ */
137
+ readonly appOptions: Readonly<AppOptions>;
138
+ /**
139
+ * Abort signal tied to the request lifecycle.
140
+ *
141
+ * When a `timeout` router option is set, this signal aborts after the
142
+ * specified duration. Handlers performing long I/O (fetch, streams, DB queries)
143
+ * can pass this signal to those operations for cooperative cancellation.
144
+ */
145
+ readonly signal: AbortSignal;
146
+ /**
147
+ * Call the next handler in the pipeline (onion model).
148
+ *
149
+ * The result is cached — calling `next()` multiple times returns the same response.
150
+ * Returns the downstream `Response`, or `undefined` if no handler matched.
151
+ */
152
+ next(error?: Error): Promise<Response | undefined>;
153
+ /**
154
+ * Whether `next()` has been invoked on this event.
155
+ *
156
+ * Used by the dispatch pipeline to disambiguate an `undefined` return value:
157
+ * a handler that returns `undefined` after calling `next()` is forwarding the
158
+ * downstream result; one that returns `undefined` without calling `next()` is
159
+ * unresolved and will wait on `signal` (timeout-bounded).
160
+ */
161
+ readonly nextCalled: boolean;
162
+ /**
163
+ * The cached promise returned by the first `next()` call on this event,
164
+ * or `undefined` if `next()` has not been invoked.
165
+ */
166
+ readonly nextResult: Promise<Response | undefined> | undefined;
167
+ /**
168
+ * Returns a promise that resolves the first time `next()` is invoked on this event.
169
+ *
170
+ * If `next()` has already been called, the returned promise is already resolved.
171
+ * Used by the dispatch pipeline so a handler that returns `undefined` and later
172
+ * calls `next()` asynchronously (e.g. from a `setTimeout`) still propagates the
173
+ * downstream response instead of hanging until `signal` aborts.
174
+ */
175
+ whenNextCalled(): Promise<void>;
176
+ }
177
+ //#endregion
178
+ //#region src/event/module.d.ts
179
+ type AppEventCreateContext = {
180
+ request: AppRequest;
181
+ params: Record<string, string | undefined>;
182
+ path: string;
183
+ method: MethodNameLike;
184
+ mountPath: string;
185
+ headers: Headers;
186
+ searchParams: URLSearchParams;
187
+ response: AppResponse;
188
+ store: Record<string | symbol, unknown>;
189
+ signal: AbortSignal;
190
+ appOptions: Readonly<AppOptions>;
191
+ next: (event: IAppEvent, error?: Error) => Promise<Response | undefined>;
192
+ };
193
+ declare class AppEvent implements IAppEvent {
194
+ readonly request: AppRequest;
195
+ readonly params: Record<string, string | undefined>;
196
+ readonly path: string;
197
+ readonly method: MethodNameLike;
198
+ readonly mountPath: string;
199
+ readonly headers: Headers;
200
+ readonly searchParams: URLSearchParams;
201
+ readonly response: AppResponse;
202
+ readonly store: Record<string | symbol, unknown>;
203
+ readonly signal: AbortSignal;
204
+ readonly appOptions: Readonly<AppOptions>;
205
+ protected _context: AppEventCreateContext;
206
+ protected _nextCalled: boolean;
207
+ protected _nextResult: Promise<Response | undefined> | undefined;
208
+ protected _nextCalledDeferred: {
209
+ promise: Promise<void>;
210
+ resolve: () => void;
211
+ } | undefined;
212
+ constructor(context: AppEventCreateContext);
213
+ get nextCalled(): boolean;
214
+ get nextResult(): Promise<Response | undefined> | undefined;
215
+ whenNextCalled(): Promise<void>;
216
+ next(error?: Error): Promise<Response | undefined>;
217
+ }
218
+ //#endregion
219
+ //#region src/dispatcher/types.d.ts
220
+ interface IDispatcherEvent {
221
+ /**
222
+ * The srvx ServerRequest (extends Web Standard Request).
223
+ */
224
+ readonly request: AppRequest;
225
+ /**
226
+ * Route parameters extracted from the URL path pattern. Values
227
+ * are `string` (or `undefined` for an optional param that
228
+ * didn't match).
229
+ */
230
+ params: Record<string, string | undefined>;
231
+ /**
232
+ * Current request path, adjusted relative to the mount point during router nesting.
233
+ */
234
+ path: string;
235
+ /**
236
+ * HTTP method (GET, POST, PUT, etc.). See `IAppEvent.method`
237
+ * for the open-enum typing rationale.
238
+ */
239
+ readonly method: MethodNameLike;
240
+ /**
241
+ * Prefix the active route was matched on. Set per dispatched
242
+ * handler to the resolver's `match.path` (the substring of the
243
+ * request path the matcher consumed) and restored to the prior
244
+ * value when the handler returns. Static-asset / mount-aware
245
+ * helpers strip this off `event.path` to recover a mount-relative
246
+ * path.
247
+ */
248
+ mountPath: string;
249
+ /**
250
+ * Response accumulator — set status/headers before returning a plain value.
251
+ */
252
+ readonly response: AppResponse;
253
+ /**
254
+ * Whether a response has been produced.
255
+ */
256
+ dispatched: boolean;
257
+ /**
258
+ * Error that occurred during dispatch, if any.
259
+ */
260
+ error?: AppError;
261
+ /**
262
+ * Options of the App currently dispatching this event. Set on
263
+ * entry to `App.dispatch`; restored on exit so that re-entering
264
+ * `App.dispatch` for the same event (programmatic use of the
265
+ * `IDispatcher` interface) leaves the caller's view intact.
266
+ */
267
+ appOptions: Readonly<AppOptions>;
268
+ /**
269
+ * `true` while an `App.dispatch` call is on the stack for this
270
+ * event. Used by `App.dispatch` to derive whether the current
271
+ * call is the root (and so should drive root-only behaviour like
272
+ * OPTIONS auto-Allow synthesis). Saved/restored around the
273
+ * dispatch body so re-entrant calls behave correctly.
274
+ */
275
+ isDispatching: boolean;
276
+ /**
277
+ * Abort signal for cooperative cancellation.
278
+ *
279
+ * When a `timeout` router option is set, this signal aborts after the
280
+ * specified duration. Handlers can pass it to fetch(), streams, or other
281
+ * AbortSignal-aware APIs.
282
+ */
283
+ signal: AbortSignal;
284
+ /**
285
+ * Collected allowed methods for the current path (used for OPTIONS / 405 responses).
286
+ */
287
+ methodsAllowed: Set<string>;
288
+ /**
289
+ * Set the continuation function for this event.
290
+ *
291
+ * Replaces the current continuation. The provided function receives
292
+ * an optional error and may return any value — it will be converted
293
+ * to a `Response` via `toResponse()`.
294
+ *
295
+ * Passing `undefined` clears the continuation function.
296
+ */
297
+ setNext(fn?: NextFn): void;
298
+ /**
299
+ * Build a public AppEvent from the current dispatch state.
300
+ *
301
+ * Creates a lightweight snapshot with shared references (store, response, headers)
302
+ * and the current App's options. This is the event passed to handler functions.
303
+ *
304
+ * @param signal - Optional AbortSignal override. When provided, the built event
305
+ * uses this signal instead of the dispatcher event's own signal.
306
+ * Used by per-handler timeout to provide a handler-scoped signal.
307
+ */
308
+ build(signal?: AbortSignal): IAppEvent;
309
+ }
310
+ interface IDispatcher {
311
+ dispatch(event: IDispatcherEvent): Promise<Response | undefined>;
312
+ }
313
+ //#endregion
314
+ //#region src/dispatcher/module.d.ts
315
+ declare class DispatcherEvent implements IDispatcherEvent {
316
+ readonly request: AppRequest;
317
+ params: Record<string, string | undefined>;
318
+ path: string;
319
+ readonly method: MethodNameLike;
320
+ /**
321
+ * Collected allowed methods (for OPTIONS).
322
+ */
323
+ methodsAllowed: Set<string>;
324
+ mountPath: string;
325
+ error?: AppError;
326
+ /**
327
+ * Options of the App currently dispatching this event. Set on
328
+ * entry to `App.dispatch` and restored on exit so re-entrant
329
+ * dispatch calls leave the caller's view intact. Initialized to
330
+ * `{}` so consumers reading before any dispatch get a valid
331
+ * (empty) shape.
332
+ */
333
+ appOptions: Readonly<AppOptions>;
334
+ /**
335
+ * `true` while an `App.dispatch` call is on the stack for this
336
+ * event. `App.dispatch` reads this on entry to derive `isRoot`
337
+ * and writes it on entry/exit so re-entrant calls behave
338
+ * correctly.
339
+ */
340
+ isDispatching: boolean;
341
+ protected _dispatched: boolean;
342
+ protected _response?: AppResponse;
343
+ protected _store?: Record<string | symbol, unknown>;
344
+ /**
345
+ * Cached parsed URL (avoids double-parsing).
346
+ */
347
+ protected _url: InstanceType<typeof FastURL>;
348
+ /**
349
+ * Continuation function for middleware onion model.
350
+ */
351
+ protected _next?: (event: IAppEvent, error?: Error) => Promise<Response | undefined>;
352
+ protected _signal?: AbortSignal;
353
+ protected _signalCleanup?: () => void;
354
+ /**
355
+ * Whether _next has already been called (guard against double-invocation).
356
+ */
357
+ protected _nextCalled: boolean;
358
+ /**
359
+ * The cached result of the next handler.
360
+ */
361
+ protected _nextResult?: Promise<Response | undefined>;
362
+ constructor(request: AppRequest);
363
+ get response(): AppResponse;
364
+ get signal(): AbortSignal;
365
+ set signal(value: AbortSignal);
366
+ get dispatched(): boolean;
367
+ set dispatched(value: boolean);
368
+ protected next(event: IAppEvent, error?: Error): Promise<Response | undefined>;
369
+ setNext(fn?: NextFn): void;
370
+ build(signal?: AbortSignal): AppEvent;
371
+ protected get store(): Record<string | symbol, unknown>;
372
+ }
373
+ //#endregion
374
+ //#region src/handler/constants.d.ts
375
+ declare const HandlerType: {
376
+ readonly CORE: "core";
377
+ readonly ERROR: "error";
378
+ };
379
+ type HandlerType = typeof HandlerType[keyof typeof HandlerType];
380
+ declare const HandlerSymbol: unique symbol;
381
+ //#endregion
382
+ //#region src/error/create.d.ts
383
+ /**
384
+ * Create an internal error object by
385
+ * - an existing AppError (returned as-is)
386
+ * - an HTTPError (wrapped into a AppError preserving status)
387
+ * - an Error (wrapped preserving message and cause)
388
+ * - an options object (status, message, etc.)
389
+ * - a message string
390
+ *
391
+ * @param input
392
+ */
393
+ declare function createError(input: HTTPErrorInput | unknown): AppError;
394
+ //#endregion
395
+ //#region src/error/is.d.ts
396
+ declare function isError(input: unknown): input is AppError;
397
+ //#endregion
398
+ //#region src/path/type.d.ts
399
+ type PathMatcherOptions = PathToRegexpOptions & ParseOptions;
400
+ type PathMatcherExecResult = {
401
+ path: string;
402
+ params: Record<string, any>;
403
+ };
404
+ type Path = string;
405
+ interface IPathMatcher {
406
+ test(path: string): boolean;
407
+ exec(path: string): PathMatcherExecResult | undefined;
408
+ }
409
+ //#endregion
410
+ //#region src/path/matcher.d.ts
411
+ declare class PathMatcher implements IPathMatcher {
412
+ protected path: Path;
413
+ protected regexp: RegExp;
414
+ protected regexpKeys: Key[];
415
+ protected regexpOptions: PathMatcherOptions;
416
+ constructor(path: Path, options?: PathMatcherOptions);
417
+ test(path: string): boolean;
418
+ exec(path: string): PathMatcherExecResult | undefined;
419
+ }
420
+ //#endregion
421
+ //#region src/path/utils.d.ts
422
+ declare function isPath(input: unknown): input is Path;
423
+ //#endregion
424
+ //#region src/handler/types-base.d.ts
425
+ /**
426
+ * Side-effect callback fired before the handler's `fn` is invoked.
427
+ * Receives the same `AppEvent` the handler will see. Throwing here is
428
+ * equivalent to the handler throwing — `onError` (if set) fires next
429
+ * and the error propagates to the surrounding error chain. Return
430
+ * value is ignored; if you need to short-circuit, use middleware.
431
+ */
432
+ type HandlerBeforeListener = (event: IAppEvent) => unknown | Promise<unknown>;
433
+ /**
434
+ * Side-effect callback fired after the handler's `fn` returns and
435
+ * `toResponse` builds the final response. Receives the same
436
+ * `AppEvent` `fn` saw plus the produced `Response` (`undefined` when
437
+ * the handler did not produce a response). Throwing here is treated
438
+ * like the handler throwing — `onError` (if set) fires next and the
439
+ * already-built response is dropped in favour of the error path.
440
+ */
441
+ type HandlerAfterListener = (event: IAppEvent, response: Response | undefined) => unknown | Promise<unknown>;
442
+ /**
443
+ * Side-effect callback fired when the handler's `fn`, `onBefore`, or
444
+ * `onAfter` throws. Receives the resolved `AppError` and the
445
+ * handler's event. Throwing here replaces `event.error` with the
446
+ * new error before the pipeline observes it; returning normally
447
+ * lets the original error propagate.
448
+ */
449
+ type HandlerErrorListener = (error: AppError, event: IAppEvent) => unknown | Promise<unknown>;
450
+ type HandlerBaseOptions = {
451
+ method?: Uppercase<MethodName> | Lowercase<MethodName>;
452
+ path?: Path;
453
+ /**
454
+ * Per-handler timeout in milliseconds.
455
+ *
456
+ * Overrides the router's `handlerTimeout` default. Whether this value
457
+ * can extend or only narrow the default is controlled by the router's
458
+ * `handlerTimeoutOverridable` option.
459
+ */
460
+ timeout?: number;
461
+ /**
462
+ * Instrumentation hook fired before `fn` is invoked. Plain
463
+ * optional callback — no event-name dispatch, no priorities;
464
+ * for cross-handler instrumentation, prefer middleware.
465
+ */
466
+ onBefore?: HandlerBeforeListener;
467
+ /**
468
+ * Instrumentation hook fired after the response is built (or
469
+ * the handler resolved without one). Receives `(event, response)`.
470
+ */
471
+ onAfter?: HandlerAfterListener;
472
+ /**
473
+ * Instrumentation hook fired when `fn` (or `onBefore`) throws.
474
+ * Receives `(error, event)`. Re-throwing replaces the active
475
+ * `event.error`; returning normally lets the original error
476
+ * propagate.
477
+ */
478
+ onError?: HandlerErrorListener;
479
+ };
480
+ //#endregion
481
+ //#region src/handler/error/types.d.ts
482
+ type ErrorHandler = (error: AppError, event: IAppEvent) => unknown | Promise<unknown>;
483
+ type ErrorHandlerOptions = HandlerBaseOptions & {
484
+ type: typeof HandlerType.ERROR;
485
+ fn: ErrorHandler;
486
+ };
487
+ //#endregion
488
+ //#region src/handler/error/define.d.ts
489
+ /**
490
+ * Create an error handler.
491
+ *
492
+ * Error handlers receive errors thrown by preceding handlers in the pipeline.
493
+ *
494
+ * @param input - Handler function `(error, event) => value` or options object `{ fn, path? }`
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * router.use(defineErrorHandler((error, event) => {
499
+ * return { message: error.message };
500
+ * }));
501
+ * ```
502
+ */
503
+ declare function defineErrorHandler(input: Omit<ErrorHandlerOptions, 'type'>): Handler;
504
+ declare function defineErrorHandler(input: ErrorHandler): Handler;
505
+ //#endregion
506
+ //#region src/handler/types.d.ts
507
+ type HandlerOptions = CoreHandlerOptions | ErrorHandlerOptions;
508
+ //#endregion
509
+ //#region src/handler/module.d.ts
510
+ declare class Handler implements IDispatcher {
511
+ protected config: HandlerOptions;
512
+ readonly method: MethodName | undefined;
513
+ constructor(handler: HandlerOptions);
514
+ get type(): "error" | "core";
515
+ get path(): string | undefined;
516
+ dispatch(event: IDispatcherEvent): Promise<Response | undefined>;
517
+ /**
518
+ * Resolve a handler's return value into the final value handed to `toResponse`.
519
+ *
520
+ * Contract:
521
+ * - non-undefined value → return as-is (becomes the response)
522
+ * - `undefined` + `event.next()` was called → forward downstream result
523
+ * - `undefined` + `event.next()` not yet called → wait until either `next()` is
524
+ * invoked (e.g. from an async callback) or `signal` aborts. A global or
525
+ * per-handler timeout aborts `signal` and surfaces as 408. With no timeout
526
+ * configured and no eventual `next()` call, the request hangs by design.
527
+ */
528
+ protected resolveHandlerResult(invocation: unknown | Promise<unknown>, handlerEvent: IAppEvent): Promise<unknown>;
529
+ protected executeWithTimeout(fn: () => unknown | Promise<unknown>, effectiveTimeout: number | undefined, controller?: AbortController): Promise<unknown>;
530
+ protected resolveTimeout(appOptions: AppOptions): number | undefined;
531
+ }
532
+ //#endregion
533
+ //#region src/handler/core/types.d.ts
534
+ type CoreHandler = (event: IAppEvent) => unknown | Promise<unknown>;
535
+ type CoreHandlerOptions = HandlerBaseOptions & {
536
+ type: typeof HandlerType.CORE;
537
+ fn: CoreHandler;
538
+ };
539
+ //#endregion
540
+ //#region src/handler/core/define.d.ts
541
+ /**
542
+ * Create a request handler.
543
+ *
544
+ * @param input - Handler function `(event) => value` or options object `{ fn, path?, method? }`
545
+ *
546
+ * @example
547
+ * ```typescript
548
+ * // Shorthand — function only
549
+ * router.get('/', defineCoreHandler((event) => 'Hello'));
550
+ *
551
+ * // Verbose — with path and method
552
+ * router.use(defineCoreHandler({
553
+ * path: '/users/:id',
554
+ * method: 'GET',
555
+ * fn: (event) => ({ id: event.params.id }),
556
+ * }));
557
+ * ```
558
+ */
559
+ declare function defineCoreHandler(input: Omit<CoreHandlerOptions, 'type'>): Handler;
560
+ declare function defineCoreHandler(input: CoreHandler): Handler;
561
+ //#endregion
562
+ //#region src/handler/adapters/node/types.d.ts
563
+ type NodeHandler = (req: IncomingMessage, res: ServerResponse) => unknown | Promise<unknown>;
564
+ type NodeMiddleware = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => unknown | Promise<unknown>;
565
+ //#endregion
566
+ //#region src/handler/adapters/node/define.d.ts
567
+ /**
568
+ * Wraps a Node.js `(req, res)` handler for use in the routup pipeline.
569
+ *
570
+ * @example
571
+ * ```typescript
572
+ * import { fromNodeHandler } from 'routup/node';
573
+ *
574
+ * router.use(fromNodeHandler((req, res) => {
575
+ * res.end('Hello');
576
+ * }));
577
+ * ```
578
+ */
579
+ declare function fromNodeHandler(handler: NodeHandler): Handler;
580
+ /**
581
+ * Wraps a Node.js `(req, res, next)` middleware for use in the routup pipeline.
582
+ *
583
+ * @example
584
+ * ```typescript
585
+ * import cors from 'cors';
586
+ * import { fromNodeMiddleware } from 'routup/node';
587
+ *
588
+ * router.use(fromNodeMiddleware(cors()));
589
+ * ```
590
+ */
591
+ declare function fromNodeMiddleware(handler: NodeMiddleware): Handler;
592
+ //#endregion
593
+ //#region src/handler/adapters/web/types.d.ts
594
+ /**
595
+ * A plain function that follows the Web Fetch API signature.
596
+ * Compatible with any framework that exposes a fetch-style entry point.
597
+ */
598
+ interface WebHandler {
599
+ (request: Request): Response | Promise<Response>;
600
+ }
601
+ /**
602
+ * An object with a `fetch` method (e.g. another router, Hono app, etc.).
603
+ */
604
+ interface WebHandlerProvider {
605
+ fetch(request: Request): Response | Promise<Response>;
606
+ }
607
+ //#endregion
608
+ //#region src/handler/adapters/web/define.d.ts
609
+ /**
610
+ * Create a handler from a Web Fetch API-compatible function or object.
611
+ *
612
+ * Wraps an external app (e.g. Hono, another App) so it can be mounted
613
+ * via `router.use()`. The original request is passed through as-is.
614
+ *
615
+ * @param input - Fetch function `(request) => Response` or object with a `fetch` method
616
+ *
617
+ * @experimental
618
+ *
619
+ * @example
620
+ * ```ts
621
+ * // Mount an object with a fetch method
622
+ * router.use('/api', fromWebHandler(honoApp));
623
+ *
624
+ * // Mount a plain fetch function
625
+ * router.use('/proxy', fromWebHandler((req) => fetch(req)));
626
+ * ```
627
+ */
628
+ declare function fromWebHandler(input: WebHandler): Handler;
629
+ declare function fromWebHandler(input: WebHandlerProvider): Handler;
630
+ //#endregion
631
+ //#region src/handler/adapters/web/is.d.ts
632
+ declare function isWebHandlerProvider(input: unknown): input is WebHandlerProvider;
633
+ declare function isWebHandler(input: unknown): input is WebHandler;
634
+ //#endregion
635
+ //#region src/handler/is.d.ts
636
+ declare function isHandlerOptions(input: unknown): input is HandlerOptions;
637
+ declare function isHandler(input: unknown): input is Handler;
638
+ //#endregion
639
+ //#region src/handler/utils.d.ts
640
+ /**
641
+ * Match a request method against a handler's bound method.
642
+ *
643
+ * - When the handler has no method bound, matches every request method.
644
+ * - Otherwise matches when the request method is the same.
645
+ * - HEAD requests additionally match GET handlers.
646
+ */
647
+ declare function matchHandlerMethod(handlerMethod: MethodName | undefined, requestMethod: MethodName): boolean;
648
+ //#endregion
649
+ //#region src/plugin/error/constants.d.ts
650
+ declare const PluginErrorCode: {
651
+ readonly PLUGIN: "PLUGIN";
652
+ readonly NOT_INSTALLED: "PLUGIN_NOT_INSTALLED";
653
+ readonly ALREADY_INSTALLED: "PLUGIN_ALREADY_INSTALLED";
654
+ readonly INSTALL: "PLUGIN_INSTALL";
655
+ };
656
+ type PluginErrorCode = typeof PluginErrorCode[keyof typeof PluginErrorCode];
657
+ //#endregion
658
+ //#region src/plugin/error/module.d.ts
659
+ declare class PluginError extends AppError {
660
+ constructor(input?: HTTPErrorInput);
661
+ }
662
+ //#endregion
663
+ //#region src/plugin/error/is.d.ts
664
+ declare function isPluginError(input: unknown): input is PluginError;
665
+ //#endregion
666
+ //#region src/plugin/error/sub/already-installed.d.ts
667
+ declare class PluginAlreadyInstalledError extends PluginError {
668
+ readonly pluginName: string;
669
+ constructor(pluginName: string);
670
+ }
671
+ //#endregion
672
+ //#region src/plugin/error/sub/install.d.ts
673
+ declare class PluginInstallError extends PluginError {
674
+ readonly pluginName: string;
675
+ constructor(pluginName: string, cause?: Error);
676
+ }
677
+ //#endregion
678
+ //#region src/plugin/error/sub/not-installed.d.ts
679
+ declare class PluginNotInstalledError extends PluginError {
680
+ readonly pluginName: string;
681
+ readonly helperName: string;
682
+ constructor(pluginName: string, helperName: string);
683
+ }
684
+ //#endregion
685
+ //#region src/plugin/types.d.ts
686
+ type PluginInstallFn = (router: IApp) => any;
687
+ type Plugin = {
688
+ /**
689
+ * The name of the plugin.
690
+ */
691
+ name: string;
692
+ /**
693
+ * The version of the plugin (semver).
694
+ */
695
+ version?: string;
696
+ /**
697
+ * The installation function called on registration.
698
+ */
699
+ install: PluginInstallFn;
700
+ };
701
+ type PluginInstallContext = {
702
+ /**
703
+ * Mount-path prefix to prepend onto every route the plugin
704
+ * registers. Equivalent to passing the same prefix to
705
+ * `app.use(path, plugin)`. The plugin installs into a scratch
706
+ * `App`; that scratch is then flattened into the host App with
707
+ * this prefix joined onto each route.
708
+ */
709
+ path?: Path;
710
+ };
711
+ //#endregion
712
+ //#region src/plugin/is.d.ts
713
+ declare function isPlugin(input: unknown): input is Plugin;
714
+ //#endregion
715
+ //#region src/utils/etag/types.d.ts
716
+ type EtagOptions = {
717
+ /**
718
+ * Create a weak ETag?
719
+ * Output is prefixed with: /W
720
+ */
721
+ weak?: boolean;
722
+ /**
723
+ * Threshold of bytes from which an etag is generated.
724
+ *
725
+ * default: undefined
726
+ */
727
+ threshold?: number;
728
+ };
729
+ type EtagFn = (body: string, size?: number) => Promise<string | undefined>;
730
+ type EtagInput = boolean | null | EtagOptions | EtagFn;
731
+ //#endregion
732
+ //#region src/utils/trust-proxy/type.d.ts
733
+ type TrustProxyFn = (address: string, hop: number) => boolean;
734
+ type TrustProxyInput = boolean | number | string | string[] | TrustProxyFn;
735
+ //#endregion
736
+ //#region src/cache/types.d.ts
737
+ /**
738
+ * Pluggable cache strategy used by `IRouter` implementations to
739
+ * memoize `lookup(path)` results by request path. The default
740
+ * implementation (`LruCache`) is a `quick-lru`-backed bounded LRU;
741
+ * users can supply their own `ICache` (e.g. wrapping `lru-cache` for
742
+ * TTL/size-based eviction) via `BaseRouterOptions.cache`, or pass
743
+ * `null` to disable caching.
744
+ *
745
+ * The cache is opaque about value type so the same `ICache`
746
+ * implementation can be reused for non-router caching needs.
747
+ */
748
+ interface ICache<V> {
749
+ /**
750
+ * Return the cached value for `key`, or `undefined` when the key
751
+ * is absent (or has been evicted). Implementations should treat
752
+ * `undefined` as "no entry" — callers cannot store `undefined`.
753
+ * Other falsy values (`null`, `0`, `''`, `false`) are storable
754
+ * and must be returned unchanged on hit.
755
+ */
756
+ get(key: string): V | undefined;
757
+ /**
758
+ * Store `value` under `key`. Bounded implementations (LRU, TTL,
759
+ * size-based) decide eviction at this point.
760
+ */
761
+ set(key: string, value: V): void;
762
+ /**
763
+ * Remove a single entry. No-op when `key` is absent.
764
+ */
765
+ delete(key: string): void;
766
+ /**
767
+ * Drop every entry. Routers call this from inside `add()` so a
768
+ * newly registered route can never be hidden by stale matches
769
+ * cached against an earlier route set.
770
+ */
771
+ clear(): void;
772
+ /**
773
+ * Return a fresh, **empty** cache of the same shape — same class
774
+ * for leaf implementations. Used by `IRouter.clone()` so the
775
+ * clone preserves the configured cache family (size, eviction
776
+ * policy, …) without inheriting the parent's cached values.
777
+ * Mirrors `IRouter.clone()`.
778
+ */
779
+ clone(): ICache<V>;
780
+ }
781
+ //#endregion
782
+ //#region src/cache/lru.d.ts
783
+ type LruCacheOptions = {
784
+ /**
785
+ * Maximum number of entries before the least-recently-used entry
786
+ * is evicted on `set`. Default: `1024`.
787
+ */
788
+ maxSize?: number;
789
+ };
790
+ /**
791
+ * Default `ICache` implementation — a bounded LRU backed by
792
+ * [`quick-lru`](https://github.com/sindresorhus/quick-lru). Picked for
793
+ * its small footprint (~1kB), ESM-only build (matches routup), and
794
+ * stable API.
795
+ *
796
+ * For TTL, size-based eviction, or dispose hooks, write your own
797
+ * `ICache` (e.g. wrapping `lru-cache`) and pass it via the router's
798
+ * `BaseRouterOptions.cache` slot.
799
+ */
800
+ declare class LruCache<V> implements ICache<V> {
801
+ protected options: LruCacheOptions;
802
+ protected inner: QuickLRU<string, V>;
803
+ constructor(options?: LruCacheOptions);
804
+ get(key: string): V | undefined;
805
+ set(key: string, value: V): void;
806
+ delete(key: string): void;
807
+ clear(): void;
808
+ clone(): ICache<V>;
809
+ }
810
+ //#endregion
811
+ //#region src/types.d.ts
812
+ /**
813
+ * Constraint on `IRouter<T>`'s data slot — routers store object-shaped
814
+ * per-route data (handlers, child apps, custom records). Primitives
815
+ * (`string`, `number`) aren't supported as route data; if you need to
816
+ * carry a primitive, wrap it in an object.
817
+ */
818
+ type ObjectLiteral = Record<string, any>;
819
+ /**
820
+ * A registered route — what `IRouter.add` consumes. Only `path` and
821
+ * `method` are routing-relevant; `data` is opaque to the router and
822
+ * returned as-is on match. Apps store their own discrimination
823
+ * (e.g. handler-vs-nested-app) inside `data`.
824
+ *
825
+ * **Match-semantics convention:**
826
+ * - `method !== undefined` → router treats the entry as method-bound
827
+ * and matches the path **exactly**.
828
+ * - `method === undefined` → entry is method-agnostic (middleware /
829
+ * nested app) and matches by **prefix**.
830
+ *
831
+ * Custom `IRouter` implementations should honor this convention so
832
+ * apps can swap routers transparently.
833
+ */
834
+ type Route<T extends ObjectLiteral = ObjectLiteral> = {
835
+ /**
836
+ * Mount path.
837
+ * - `undefined` means "no path" (route matches every request).
838
+ * - `'/'` behaves like "no path" for method-agnostic prefix routes
839
+ * (middleware / mount-less nested apps).
840
+ * - Method-bound `'/'` is treated as an exact root match
841
+ * (`app.get('/', …)` matches only the root).
842
+ */
843
+ path?: Path;
844
+ /**
845
+ * Bound HTTP method. When set, the router treats this route as an
846
+ * exact match; when undefined, the route matches by prefix.
847
+ */
848
+ method?: MethodName;
849
+ /**
850
+ * Opaque to the router. Returned via `RouteMatch.route.data` on
851
+ * match; consumers (typically `App`) decide what's inside.
852
+ */
853
+ data: T;
854
+ };
855
+ /**
856
+ * A single matched route returned by `IRouter.lookup`. The dispatch
857
+ * loop consumes these instead of walking the raw routes — `params`
858
+ * are pre-extracted at lookup time so we don't re-run the matcher
859
+ * later, and `path` (when set) tells the loop how much of
860
+ * `event.path` to strip when recursing into a child app.
861
+ */
862
+ type RouteMatch<T extends ObjectLiteral = ObjectLiteral> = {
863
+ route: Route<T>;
864
+ /**
865
+ * Registration index in the router. Used by the dispatch loop's
866
+ * `setNext` continuation ("resume from index + 1") and by
867
+ * `App.clone()` to re-register routes in their original order.
868
+ */
869
+ index: number;
870
+ /**
871
+ * Path params extracted from the route's matcher. Values are
872
+ * `string` (or `undefined` for an optional param that didn't
873
+ * match). Empty object when the route has no path or no params.
874
+ */
875
+ params: Record<string, string | undefined>;
876
+ /**
877
+ * For routes with a matcher: the path substring the matcher
878
+ * consumed. Used by `executePipelineStepChildDispatch` to strip
879
+ * the matched prefix off `event.path` before dispatching into a
880
+ * child app. Undefined for routes without a matcher.
881
+ */
882
+ path?: string;
883
+ };
884
+ //#endregion
885
+ //#region src/router/types.d.ts
886
+ /**
887
+ * Options shared by every built-in router. Custom `IRouter`
888
+ * implementations are encouraged to extend this so users can swap
889
+ * routers without rewiring caching.
890
+ *
891
+ * - `cache` (omitted): no caching — every `lookup()` runs the
892
+ * router's full match logic. This is the default.
893
+ * - `cache: <ICache>`: enable lookup memoization. Pass `LruCache`
894
+ * for the built-in bounded LRU, or your own `ICache` (e.g.
895
+ * wrapping `lru-cache` for TTL or size-based eviction).
896
+ *
897
+ * The router is responsible for invalidating its own cache whenever
898
+ * `add()` is called — registering a new route can change the match
899
+ * set for any cached path.
900
+ */
901
+ type BaseRouterOptions<T extends ObjectLiteral = ObjectLiteral> = {
902
+ cache?: ICache<readonly RouteMatch<T>[]>;
903
+ };
904
+ /**
905
+ * Pluggable strategy for storing routes and answering "which entries
906
+ * match this path?". The default `LinearRouter` walks the stored
907
+ * entries linearly. Alternative implementations (radix tree,
908
+ * aggregated regex, …) can swap in via `AppContext.router` to
909
+ * skip the walk entirely on apps with many routes.
910
+ *
911
+ * The router operates on `Route<T>` where `T` is opaque data; the
912
+ * router never inspects `entry.data`. Only `entry.path` and
913
+ * `entry.method` are routing-relevant.
914
+ *
915
+ * **Match-semantics convention** (custom implementations must honor):
916
+ * - `entry.method !== undefined` → match the path **exactly** (the
917
+ * entry is method-bound, e.g. a verb-shortcut handler).
918
+ * - `entry.method === undefined` → match by **prefix** (middleware,
919
+ * nested apps).
920
+ *
921
+ * Method matching against the request method is kept at the dispatch-
922
+ * loop call site, not here, because method semantics differ between
923
+ * handler and nested-app entries (only handler entries are
924
+ * method-bound).
925
+ */
926
+ interface IRouter<T extends ObjectLiteral = ObjectLiteral> {
927
+ /**
928
+ * Register a route. Entries are stored in registration order —
929
+ * the order they were passed to `App.use` / `.get` / `.post` /
930
+ * etc. — and lookup results preserve that order.
931
+ */
932
+ add(route: Route<T>): void;
933
+ /**
934
+ * Return every entry that matches the given path, in registration
935
+ * order. The dispatch loop iterates this list; nested `setNext`
936
+ * re-entries resume from a later index in the same list.
937
+ *
938
+ * `method`, when provided, is the request HTTP method. Routers
939
+ * MAY use it to filter at lookup time (e.g. method-bucketed
940
+ * tries) — but the App's dispatch loop still runs its own
941
+ * method check on every returned match, so a router that
942
+ * ignores `method` and emits more candidates than necessary
943
+ * stays correct, just not optimally fast.
944
+ *
945
+ * When the request method is `OPTIONS` (auto-Allow surface) or
946
+ * `HEAD` (falls through to GET), method-aware routers should
947
+ * widen their emission to cover those cases — see the OPTIONS /
948
+ * HEAD handling notes on `TrieRouter` for the canonical
949
+ * implementation.
950
+ */
951
+ lookup(path: string, method?: MethodNameLike): readonly RouteMatch<T>[];
952
+ /**
953
+ * Return a fresh, **empty** router of the same shape — same class
954
+ * for leaf implementations; composable wrappers should recursively
955
+ * clone their inner router. Used by `App.install()` and
956
+ * `App.clone()` so plugin sub-apps and cloned apps preserve the
957
+ * active router family instead of silently downgrading to
958
+ * `LinearRouter`.
959
+ */
960
+ clone(): IRouter<T>;
961
+ }
962
+ //#endregion
963
+ //#region src/app/types.d.ts
964
+ type AppOptions = {
965
+ /**
966
+ * Global request timeout in milliseconds.
967
+ *
968
+ * Applies to the entire dispatch pipeline in `fetch()`. When exceeded,
969
+ * the request is aborted and a 408 response is returned. The AbortSignal
970
+ * on the event is also aborted for cooperative cancellation.
971
+ */
972
+ timeout?: number;
973
+ /**
974
+ * Default per-handler timeout in milliseconds.
975
+ *
976
+ * Applies individually to each handler's `fn()` execution. Handlers can
977
+ * override this value via their own `timeout` option — see
978
+ * `handlerTimeoutOverridable` to control whether overrides can extend
979
+ * or only narrow this default.
980
+ */
981
+ handlerTimeout?: number;
982
+ /**
983
+ * Whether handlers can extend the `handlerTimeout` default.
984
+ *
985
+ * When `false` (default), a handler's `timeout` is clamped to
986
+ * `Math.min(handlerTimeout, handler.timeout)`. When `true`, the
987
+ * handler's `timeout` fully replaces the router default.
988
+ */
989
+ handlerTimeoutOverridable?: boolean;
990
+ /**
991
+ * Number of trailing labels in the request hostname that make up
992
+ * the registrable domain (e.g. `example.com` → 2). Subdomain
993
+ * helpers strip this many labels from the right before returning
994
+ * the subdomain portion.
995
+ */
996
+ subdomainOffset?: number;
997
+ /**
998
+ * Maximum number of proxy IPs to walk when resolving the client
999
+ * IP from `X-Forwarded-For`. Caps how far back the chain is
1000
+ * trusted, regardless of `trustProxy`.
1001
+ */
1002
+ proxyIpMax?: number;
1003
+ /**
1004
+ * ETag generator, or `null` to disable ETag/304 entirely.
1005
+ *
1006
+ * - `undefined` (the default): consumers fall back to a
1007
+ * framework-provided `EtagFn`.
1008
+ * - `null`: explicit opt-out — the response pipeline branches
1009
+ * synchronously and skips the `await applyEtag(...)` microtask hop.
1010
+ * - `EtagFn`: the user's own generator.
1011
+ */
1012
+ etag?: EtagFn | null;
1013
+ /**
1014
+ * Predicate that decides whether a given upstream address is a
1015
+ * trusted proxy when resolving the client IP / protocol /
1016
+ * hostname from forwarding headers.
1017
+ */
1018
+ trustProxy?: TrustProxyFn;
1019
+ };
1020
+ /**
1021
+ * User-facing input variant of `AppOptions`.
1022
+ *
1023
+ * Accepts looser shapes for `etag` and `trustProxy` (string,
1024
+ * boolean, list-of-CIDRs, …) which `normalizeAppOptions` lowers to
1025
+ * the resolved `EtagFn | null` / `TrustProxyFn` shape stored on the
1026
+ * App.
1027
+ */
1028
+ type AppOptionsInput = Omit<AppOptions, 'etag' | 'trustProxy'> & {
1029
+ /**
1030
+ * ETag input — accepts an `EtagFn`, `false`/`null` to disable,
1031
+ * or other shapes accepted by `buildEtagFn`. Normalized to
1032
+ * `EtagFn | null` on the App.
1033
+ */
1034
+ etag?: EtagInput;
1035
+ /**
1036
+ * Trust-proxy input — accepts a predicate, a list of trusted
1037
+ * CIDRs, `'loopback'`, etc., as accepted by `buildTrustProxyFn`.
1038
+ * Normalized to `TrustProxyFn` on the App.
1039
+ */
1040
+ trustProxy?: TrustProxyInput;
1041
+ };
1042
+ /**
1043
+ * Constructor input for `App`.
1044
+ *
1045
+ * Splits true runtime options (which propagate to mounted children
1046
+ * via mount-time inheritance) from App-local identity (`name`,
1047
+ * `path`) and constructor injectables (`plugins`, `router`). Keeping
1048
+ * these separate prevents identity from leaking across the mount
1049
+ * boundary — e.g. a parent's `path: '/api'` would otherwise propagate
1050
+ * into a child whose own `path` is unset and silently double-prefix
1051
+ * on registration.
1052
+ */
1053
+ type AppContext = {
1054
+ /**
1055
+ * Optional label for the App instance.
1056
+ */
1057
+ name?: string;
1058
+ /**
1059
+ * Registration-time path prefix for entries registered on this
1060
+ * App.
1061
+ *
1062
+ * When set, every entry registered via `use`, `get`, `post`, …
1063
+ * has this prefix prepended to its mount path.
1064
+ * `new App({ path: '/api' })` followed by `app.get('/users', h)`
1065
+ * is equivalent to `app.get('/api/users', h)` on an App without
1066
+ * a `path`.
1067
+ *
1068
+ * Path matching itself still happens inside the active `IRouter`
1069
+ * — this only affects how entries are registered, not how
1070
+ * lookup is performed. Local to this App; not propagated to
1071
+ * mounted children.
1072
+ */
1073
+ path?: Path;
1074
+ /**
1075
+ * Runtime options that propagate to mounted children via
1076
+ * mount-time inheritance.
1077
+ */
1078
+ options?: AppOptionsInput;
1079
+ /**
1080
+ * Map of installed plugin name → version. Defaults to an empty
1081
+ * map. Used by `clone()` to carry the installed-plugin registry
1082
+ * over so duplicate installs are still rejected on the copy.
1083
+ */
1084
+ plugins?: Map<string, string | undefined>;
1085
+ /**
1086
+ * Pluggable router (route table). Defaults to `LinearRouter` —
1087
+ * walks registered entries linearly per request. Swap in an
1088
+ * alternative (e.g. `TrieRouter`) on apps with many routes.
1089
+ */
1090
+ router?: IRouter<Handler>;
1091
+ };
1092
+ /**
1093
+ * Per-dispatch state threaded through the match loop. Used internally
1094
+ * by `App.dispatch` and the `setNext` continuation; not part of the
1095
+ * public surface.
1096
+ */
1097
+ type AppPipelineContext = {
1098
+ /**
1099
+ * The dispatcher event being processed. Carries request, path,
1100
+ * params, and the response accumulator across pipeline steps.
1101
+ */
1102
+ event: IDispatcherEvent;
1103
+ /**
1104
+ * `true` when this dispatch is the outermost App on the call
1105
+ * stack (the root). Used to gate root-only behaviour like
1106
+ * OPTIONS auto-Allow.
1107
+ */
1108
+ isRoot: boolean;
1109
+ /**
1110
+ * Resolved matches for the current `event.path`, populated on
1111
+ * first lookup and threaded through `setNext` recursion so we
1112
+ * don't re-run `IRouter.lookup` per cycle. Refreshed when
1113
+ * `event.path` changes mid-walk.
1114
+ */
1115
+ matches: readonly RouteMatch<Handler>[];
1116
+ /**
1117
+ * The `event.path` that was used to compute `matches`. Stored so
1118
+ * we can detect a mid-walk path mutation and refresh the cache.
1119
+ */
1120
+ matchesPath: string;
1121
+ /**
1122
+ * Position within `matches` for the *next* handler the walk
1123
+ * should consider. The current handler's `setNext` continuation
1124
+ * captures this and resumes the walk on `event.next()`.
1125
+ */
1126
+ matchIndex: number;
1127
+ /**
1128
+ * The Response produced by the pipeline. Set by handlers (via
1129
+ * `toResponse`) or by the OPTIONS auto-Allow path; returned from
1130
+ * `App.dispatch`.
1131
+ */
1132
+ response?: Response;
1133
+ };
1134
+ interface IApp extends IDispatcher {
1135
+ /**
1136
+ * Optional label for the router instance.
1137
+ */
1138
+ readonly name?: string;
1139
+ /**
1140
+ * Public entry point — processes a request through the pipeline
1141
+ * and returns a Response (with 404/500 fallbacks).
1142
+ */
1143
+ fetch(request: AppRequest): Promise<Response>;
1144
+ /**
1145
+ * Return a new App that mirrors this one but owns independent
1146
+ * mountable state — fresh `IRouter` of the same family seeded
1147
+ * with this App's routes, shallow copy of options, and a fresh
1148
+ * plugins map carrying the same entries.
1149
+ *
1150
+ * Intended for mounting the same logical App under multiple
1151
+ * paths without sharing mutable state across mount points.
1152
+ */
1153
+ clone(): IApp;
1154
+ /**
1155
+ * Swap the active `IRouter`. Every previously-registered route
1156
+ * is replayed onto the new router so lookups stay correct. Any
1157
+ * cache the previous router carried is dropped along with it.
1158
+ *
1159
+ * Useful when the right router family is only known after
1160
+ * routes are registered (a SmartRouter-style decision), or for
1161
+ * comparing implementations mid-flight without rebuilding the
1162
+ * App.
1163
+ */
1164
+ setRouter(router: IRouter<Handler>): void;
1165
+ /**
1166
+ * Check if a plugin with the given name is installed on this
1167
+ * App. Plugins installed on a mounted child are merged into the
1168
+ * parent at mount time, so this reflects the flattened view.
1169
+ */
1170
+ hasPlugin(name: string): boolean;
1171
+ /**
1172
+ * Get the version of an installed plugin by name, or `undefined`
1173
+ * if the plugin is not installed.
1174
+ */
1175
+ getPluginVersion(name: string): string | undefined;
1176
+ /**
1177
+ * Register a handler, App, or plugin.
1178
+ *
1179
+ * When another App is passed, its routes are snapshotted, the
1180
+ * mount path is prefixed onto each, and the entries are
1181
+ * registered on this App's router. The child's plugin registry
1182
+ * is merged into this one. The child is discarded post-mount —
1183
+ * later mutations on it do **not** propagate.
1184
+ */
1185
+ use(app: IApp): this;
1186
+ use(handler: Handler | HandlerOptions): this;
1187
+ use(plugin: Plugin): this;
1188
+ use(path: Path, app: IApp): this;
1189
+ use(path: Path, handler: Handler | HandlerOptions): this;
1190
+ use(path: Path, plugin: Plugin): this;
1191
+ /** Register GET handler(s). */
1192
+ get(...handlers: (Handler | HandlerOptions)[]): this;
1193
+ get(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1194
+ /** Register POST handler(s). */
1195
+ post(...handlers: (Handler | HandlerOptions)[]): this;
1196
+ post(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1197
+ /** Register PUT handler(s). */
1198
+ put(...handlers: (Handler | HandlerOptions)[]): this;
1199
+ put(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1200
+ /** Register PATCH handler(s). */
1201
+ patch(...handlers: (Handler | HandlerOptions)[]): this;
1202
+ patch(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1203
+ /** Register DELETE handler(s). */
1204
+ delete(...handlers: (Handler | HandlerOptions)[]): this;
1205
+ delete(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1206
+ /** Register HEAD handler(s). */
1207
+ head(...handlers: (Handler | HandlerOptions)[]): this;
1208
+ head(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1209
+ /** Register OPTIONS handler(s). */
1210
+ options(...handlers: (Handler | HandlerOptions)[]): this;
1211
+ options(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1212
+ }
1213
+ //#endregion
1214
+ //#region src/response/helpers/cache.d.ts
1215
+ type ResponseCacheHeadersOptions = {
1216
+ maxAge?: number;
1217
+ modifiedTime?: string | Date;
1218
+ cacheControls?: string[];
1219
+ };
1220
+ declare function setResponseCacheHeaders(event: IAppEvent, options?: ResponseCacheHeadersOptions): void;
1221
+ //#endregion
1222
+ //#region src/response/helpers/event-stream/types.d.ts
1223
+ /**
1224
+ * https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format
1225
+ */
1226
+ type EventStreamMessage = {
1227
+ /**
1228
+ * The event ID to set the EventSource object's last event ID value.
1229
+ */
1230
+ id?: string;
1231
+ /**
1232
+ * The reconnection time.
1233
+ * If the connection to the server is lost, the browser will wait for the specified time before attempting to reconnect.
1234
+ * This must be an integer, specifying the reconnection time in milliseconds.
1235
+ */
1236
+ retry?: number;
1237
+ /**
1238
+ * The data field for the message.
1239
+ */
1240
+ data: string;
1241
+ /**
1242
+ * A string identifying the type of event described.
1243
+ */
1244
+ event?: string;
1245
+ };
1246
+ type EventStreamListener<T = any> = (err: Error | null, data: T) => void | Promise<void>;
1247
+ //#endregion
1248
+ //#region src/response/helpers/event-stream/module.d.ts
1249
+ type EventStreamOptions = {
1250
+ maxMessageSize?: number;
1251
+ };
1252
+ type EventStreamHandle = {
1253
+ write(message: string | EventStreamMessage): boolean;
1254
+ end(): void;
1255
+ response: Response;
1256
+ };
1257
+ declare function createEventStream(event: IAppEvent, options?: EventStreamOptions): EventStreamHandle;
1258
+ //#endregion
1259
+ //#region src/response/helpers/event-stream/utils.d.ts
1260
+ declare function serializeEventStreamMessage(message: EventStreamMessage): string;
1261
+ //#endregion
1262
+ //#region src/response/helpers/header.d.ts
1263
+ declare function appendResponseHeader(event: IAppEvent, name: string, value: string | string[]): void;
1264
+ declare function appendResponseHeaderDirective(event: IAppEvent, name: string, value: string | string[]): void;
1265
+ //#endregion
1266
+ //#region src/response/helpers/header-disposition.d.ts
1267
+ declare function setResponseHeaderAttachment(event: IAppEvent, filename?: string): void;
1268
+ declare function setResponseHeaderInline(event: IAppEvent, filename?: string): void;
1269
+ //#endregion
1270
+ //#region src/response/helpers/header-content-type.d.ts
1271
+ declare function setResponseHeaderContentType(event: IAppEvent, input: string, ifNotExists?: boolean): void;
1272
+ //#endregion
1273
+ //#region src/response/helpers/send-accepted.d.ts
1274
+ declare function sendAccepted(event: IAppEvent, data?: unknown): Promise<Response>;
1275
+ //#endregion
1276
+ //#region src/response/helpers/send-created.d.ts
1277
+ declare function sendCreated(event: IAppEvent, data?: unknown): Promise<Response>;
1278
+ //#endregion
1279
+ //#region src/response/helpers/send-file.d.ts
1280
+ type SendFileContentOptions = {
1281
+ end?: number;
1282
+ start?: number;
1283
+ };
1284
+ /**
1285
+ * File metadata used by {@link sendFile}. All fields are optional, but each
1286
+ * missing field disables related response features:
1287
+ *
1288
+ * - `size` — without it, range requests, `Accept-Ranges`, `Content-Length`,
1289
+ * `ETag`, and `Last-Modified` are all omitted (the response is sent
1290
+ * without HTTP-level caching or seekability).
1291
+ * - `mtime` — without it, `Last-Modified` is omitted and the `ETag` is not
1292
+ * emitted (`ETag` requires both `size` and `mtime`).
1293
+ * - `name` — falls back to `SendFileOptions.name` when set; if both are
1294
+ * missing, no `Content-Disposition` or extension-derived
1295
+ * `Content-Type` is set.
1296
+ */
1297
+ type SendFileStats = {
1298
+ size?: number;
1299
+ mtime?: Date | number | string;
1300
+ name?: string;
1301
+ };
1302
+ type SendFileDisposition = 'attachment' | 'inline';
1303
+ type SendFileContent = ReadableStream | ArrayBuffer | Uint8Array;
1304
+ type SendFileOptions = {
1305
+ stats: (() => Promise<SendFileStats> | SendFileStats) | SendFileStats;
1306
+ content: (options: SendFileContentOptions) => Promise<SendFileContent> | SendFileContent;
1307
+ /**
1308
+ * @deprecated Use `disposition: 'attachment'` instead. Kept for backwards
1309
+ * compatibility — when `disposition` is set, it takes precedence.
1310
+ */
1311
+ attachment?: boolean;
1312
+ disposition?: SendFileDisposition;
1313
+ name?: string;
1314
+ };
1315
+ declare function sendFile(event: IAppEvent, options: SendFileOptions): Promise<Response>;
1316
+ //#endregion
1317
+ //#region src/response/helpers/send-format.d.ts
1318
+ type ResponseFormatHandler = () => Response | unknown;
1319
+ type ResponseFormats = {
1320
+ default: ResponseFormatHandler;
1321
+ [key: string]: ResponseFormatHandler;
1322
+ };
1323
+ declare function sendFormat(event: IAppEvent, input: ResponseFormats): Response | unknown | undefined;
1324
+ //#endregion
1325
+ //#region src/response/helpers/send-redirect.d.ts
1326
+ declare function sendRedirect(event: IAppEvent, location: string, statusCode?: number): Response;
1327
+ //#endregion
1328
+ //#region src/response/helpers/send-stream.d.ts
1329
+ declare function sendStream(event: IAppEvent, stream: ReadableStream): Response;
1330
+ //#endregion
1331
+ //#region src/response/helpers/utils.d.ts
1332
+ declare function setResponseContentTypeByFileName(event: IAppEvent, fileName: string): void;
1333
+ //#endregion
1334
+ //#region src/response/to-response.d.ts
1335
+ /**
1336
+ * Convert a handler's return value into a Web `Response`.
1337
+ *
1338
+ * Returns synchronously for the common cases (string, JSON object,
1339
+ * binary, stream, blob) when ETag generation is disabled. Returns a
1340
+ * `Promise` when an ETag must be computed (the generator is async).
1341
+ *
1342
+ * Callers that want the async return uniformly can `await` the result
1343
+ * — `await` on a non-Promise still works but pays a microtask hop.
1344
+ * The App fast path branches on `instanceof Promise` to keep the
1345
+ * sync return truly sync.
1346
+ */
1347
+ declare function toResponse(value: unknown, event: IAppEvent): Response | undefined | Promise<Response | undefined>;
1348
+ //#endregion
1349
+ //#region src/request/helpers/cache.d.ts
1350
+ declare function isRequestCacheable(event: IAppEvent, modifiedTime: string | Date): boolean;
1351
+ //#endregion
1352
+ //#region src/request/helpers/header.d.ts
1353
+ declare function getRequestHeader(event: IAppEvent, name: string): string | null;
1354
+ //#endregion
1355
+ //#region src/request/helpers/header-accept.d.ts
1356
+ declare function getRequestAcceptableContentTypes(event: IAppEvent): string[];
1357
+ declare function getRequestAcceptableContentType(event: IAppEvent, input?: string | string[]): string | undefined;
1358
+ //#endregion
1359
+ //#region src/request/helpers/header-accept-charset.d.ts
1360
+ declare function getRequestAcceptableCharsets(event: IAppEvent): string[];
1361
+ declare function getRequestAcceptableCharset(event: IAppEvent, input: string | string[]): string | undefined;
1362
+ //#endregion
1363
+ //#region src/request/helpers/header-accept-encoding.d.ts
1364
+ declare function getRequestAcceptableEncodings(event: IAppEvent): string[];
1365
+ declare function getRequestAcceptableEncoding(event: IAppEvent, input: string | string[]): string | undefined;
1366
+ //#endregion
1367
+ //#region src/request/helpers/header-accept-language.d.ts
1368
+ declare function getRequestAcceptableLanguages(event: IAppEvent): string[];
1369
+ declare function getRequestAcceptableLanguage(event: IAppEvent, input?: string | string[]): string | undefined;
1370
+ //#endregion
1371
+ //#region src/request/helpers/header-content-type.d.ts
1372
+ declare function matchRequestContentType(event: IAppEvent, contentType: string): boolean;
1373
+ //#endregion
1374
+ //#region src/request/helpers/hostname.d.ts
1375
+ type RequestHostNameOptions = {
1376
+ trustProxy?: TrustProxyInput;
1377
+ };
1378
+ declare function getRequestHostName(event: IAppEvent, options?: RequestHostNameOptions): string | undefined;
1379
+ //#endregion
1380
+ //#region src/request/helpers/ip.d.ts
1381
+ type RequestIpOptions = {
1382
+ trustProxy?: TrustProxyInput;
1383
+ };
1384
+ /**
1385
+ * Get the client IP address from the request.
1386
+ *
1387
+ * When `trustProxy` is configured, walks the `X-Forwarded-For` chain
1388
+ * and returns the rightmost untrusted address (the actual client IP).
1389
+ * Falls back to `event.request.ip` (the direct connection IP).
1390
+ */
1391
+ declare function getRequestIP(event: IAppEvent, options?: RequestIpOptions): string | undefined;
1392
+ //#endregion
1393
+ //#region src/request/helpers/negotiator.d.ts
1394
+ declare function useRequestNegotiator(event: IAppEvent): Negotiator;
1395
+ //#endregion
1396
+ //#region src/request/helpers/protocol.d.ts
1397
+ type RequestProtocolOptions = {
1398
+ trustProxy?: TrustProxyInput;
1399
+ default?: string;
1400
+ };
1401
+ declare function getRequestProtocol(event: IAppEvent, options?: RequestProtocolOptions): string;
1402
+ //#endregion
1403
+ //#region src/router/linear/module.d.ts
1404
+ /**
1405
+ * Default router — walks registered routes linearly per request and
1406
+ * runs each route's mount-level matcher (built via `buildRoutePathMatcher`,
1407
+ * path-to-regexp-backed). Routes without a mount path (mount-less
1408
+ * middleware / nested apps registered via `.use(handler)`) match every
1409
+ * request directly — there is no per-route `matchPath()` fallback.
1410
+ *
1411
+ * Behaviour-preserving wrapper around the previous in-line stack walk
1412
+ * in `executePipelineStepLookup`. The matcher allocations live here
1413
+ * (not on the registered route), so routers using a different matching
1414
+ * strategy (radix tree, aggregated regex, …) can ignore this file
1415
+ * entirely.
1416
+ *
1417
+ * Optional per-router lookup cache: pass an `ICache` via
1418
+ * `BaseRouterOptions.cache` to skip the linear walk on repeated
1419
+ * requests for the same path. Default is no caching.
1420
+ */
1421
+ declare class LinearRouter<T extends ObjectLiteral = ObjectLiteral> implements IRouter<T> {
1422
+ protected _routes: Route<T>[];
1423
+ protected _matchers: (IPathMatcher | undefined)[];
1424
+ protected cache?: ICache<readonly RouteMatch<T>[]>;
1425
+ constructor(options?: BaseRouterOptions<T>);
1426
+ add(route: Route<T>): void;
1427
+ lookup(path: string, _method?: MethodNameLike): readonly RouteMatch<T>[];
1428
+ clone(): IRouter<T>;
1429
+ }
1430
+ //#endregion
1431
+ //#region src/router/smart/module.d.ts
1432
+ type SmartRouterOptions<T extends ObjectLiteral = ObjectLiteral> = BaseRouterOptions<T> & {
1433
+ /**
1434
+ * Route count at or above which `SmartRouter` switches from
1435
+ * `LinearRouter` (faster at small N) to `TrieRouter` (faster
1436
+ * at large N). Default `30`.
1437
+ */
1438
+ threshold?: number;
1439
+ };
1440
+ /**
1441
+ * Auto-selecting router. Accumulates registered routes in a pending
1442
+ * buffer; on the first `lookup()` call, picks `LinearRouter` or
1443
+ * `TrieRouter` based on the registered route count and replays the
1444
+ * pending list onto the chosen inner router. Every subsequent call
1445
+ * — `add`, `lookup`, `clone` — forwards to the inner.
1446
+ *
1447
+ * Use this when you don't want to commit to a router family up-front
1448
+ * (e.g. a library that registers a variable number of routes
1449
+ * depending on configuration). For known workloads, prefer the
1450
+ * concrete router — `SmartRouter` adds one indirection per call.
1451
+ *
1452
+ * Inspired by Hono's `SmartRouter` (which auto-selects across more
1453
+ * candidates including `RegExpRouter`); ours covers the only choice
1454
+ * that matters in routup today: linear-vs-trie at the registration-
1455
+ * size crossover.
1456
+ */
1457
+ declare class SmartRouter<T extends ObjectLiteral = ObjectLiteral> implements IRouter<T> {
1458
+ protected inner?: IRouter<T>;
1459
+ protected pending: Route<T>[];
1460
+ protected readonly threshold: number;
1461
+ /**
1462
+ * Cache handed off to whichever inner router gets chosen. Stays
1463
+ * `undefined` if the user didn't configure one.
1464
+ */
1465
+ protected readonly cache?: ICache<readonly RouteMatch<T>[]>;
1466
+ constructor(options?: SmartRouterOptions<T>);
1467
+ add(route: Route<T>): void;
1468
+ lookup(path: string, method?: string): readonly RouteMatch<T>[];
1469
+ clone(): IRouter<T>;
1470
+ /**
1471
+ * Pick the inner router based on the registered route count.
1472
+ * `LinearRouter` for tiny tables, `TrieRouter` past the
1473
+ * configured threshold.
1474
+ *
1475
+ * @protected
1476
+ */
1477
+ protected choose(): IRouter<T>;
1478
+ }
1479
+ //#endregion
1480
+ //#region src/router/trie/types.d.ts
1481
+ /**
1482
+ * Tagged param-extraction instruction. Built at registration time
1483
+ * during `insertIntoTrie`; consumed at lookup time to build the
1484
+ * params object directly from the request's pre-split segments —
1485
+ * no regex execution per match.
1486
+ *
1487
+ * - `segment`: capture `segments[depth]` as `name`.
1488
+ * - `splat`: capture `segments[depth..]` joined with `/` as `name`.
1489
+ * `name` is `'*'` for the unnamed bare splat (`/files/*`).
1490
+ */
1491
+ type ParamCapture = {
1492
+ kind: 'segment';
1493
+ depth: number;
1494
+ name: string;
1495
+ } | {
1496
+ kind: 'splat';
1497
+ depth: number;
1498
+ name: string;
1499
+ };
1500
+ /**
1501
+ * Per-variant route record stored at a trie leaf.
1502
+ *
1503
+ * `paramsIndexMap` populated for trie-walked variants so lookup can
1504
+ * extract params without running `matcher.exec`. `matcher` is only
1505
+ * populated for universal-bucket routes (registered paths the trie
1506
+ * parser couldn't handle — regex constraints, compound segments,
1507
+ * escapes — that fall back to path-to-regexp).
1508
+ *
1509
+ * `index` is shared across every variant produced from a single
1510
+ * `add()` call so the candidate list deduplicates naturally on
1511
+ * registration order.
1512
+ */
1513
+ type IndexedRoute<T extends ObjectLiteral = ObjectLiteral> = {
1514
+ route: Route<T>;
1515
+ index: number;
1516
+ /**
1517
+ * Universal-bucket-only — populated for paths the trie parser
1518
+ * couldn't handle (regex constraints, compound segments, escapes).
1519
+ * The lookup loop runs `matcher.exec(path)` to confirm the match
1520
+ * and extract params, identical to `LinearRouter`.
1521
+ */
1522
+ matcher?: IPathMatcher;
1523
+ /**
1524
+ * Trie-walked-only — populated for variants that came out of
1525
+ * `parsePath`. The lookup loop walks this list to build the
1526
+ * `params` object directly from request segments, no regex.
1527
+ */
1528
+ paramsIndexMap?: ParamCapture[];
1529
+ /**
1530
+ * Trie-walked-only — how many request segments this variant
1531
+ * consumes when matched. Used to compute `match.path` (the
1532
+ * matched prefix) at lookup time without re-running a matcher.
1533
+ *
1534
+ * - Exact / prefix variants: `segments.length` (consume up to
1535
+ * the leaf).
1536
+ * - Splat variants: depth of the splat segment (it then absorbs
1537
+ * the rest — see `splatTerminated`).
1538
+ */
1539
+ matchDepth?: number;
1540
+ /**
1541
+ * Trie-walked-only — `true` when this variant ends in a splat.
1542
+ * `match.path` is then computed from the *full* request length
1543
+ * (the splat absorbed every remaining segment), not from
1544
+ * `matchDepth` (which is only the depth of the splat node).
1545
+ */
1546
+ splatTerminated?: boolean;
1547
+ };
1548
+ type Segment = {
1549
+ kind: 'static';
1550
+ value: string;
1551
+ } | {
1552
+ kind: 'param';
1553
+ name: string;
1554
+ } | {
1555
+ kind: 'splat';
1556
+ name: string;
1557
+ };
1558
+ /**
1559
+ * Method-keyed bucket of indexed routes. The empty-string key
1560
+ * (`''`) holds method-agnostic entries (handlers registered without
1561
+ * a method binding). Stored as a prototype-less object so user-
1562
+ * controlled method strings can't collide with `Object.prototype`
1563
+ * keys (`__proto__`, `hasOwnProperty`, …).
1564
+ */
1565
+ type MethodBuckets<T extends ObjectLiteral = ObjectLiteral> = Record<string, IndexedRoute<T>[]>;
1566
+ type TrieNode<T extends ObjectLiteral = ObjectLiteral> = {
1567
+ staticChildren: Map<string, TrieNode<T>>;
1568
+ paramChild?: TrieNode<T>;
1569
+ /**
1570
+ * Entries whose path ends with a splat at this depth (`/files/*`,
1571
+ * `/foo/*name`, …). They match the current node and every deeper
1572
+ * request path. Bucketed by HTTP method (`''` = method-agnostic).
1573
+ */
1574
+ splatRoutes: MethodBuckets<T>;
1575
+ /**
1576
+ * Exact-match routes whose path ends at this node — only matched
1577
+ * when the request path is fully consumed at this depth.
1578
+ * Bucketed by HTTP method (`''` = method-agnostic).
1579
+ */
1580
+ exactRoutes: MethodBuckets<T>;
1581
+ /**
1582
+ * Prefix-match routes (middleware / nested apps) whose path ends
1583
+ * at this node — matched whenever this node is reached,
1584
+ * regardless of remaining depth. Not bucketed: middleware is
1585
+ * method-agnostic by design (the inner handler does its own
1586
+ * method discrimination).
1587
+ */
1588
+ prefixRoutes: IndexedRoute<T>[];
1589
+ };
1590
+ //#endregion
1591
+ //#region src/router/trie/module.d.ts
1592
+ /**
1593
+ * Radix-trie router — registers routes into a per-segment tree at
1594
+ * `add()` time and walks the tree at `lookup()` to collect
1595
+ * candidates by structure rather than by linear scan.
1596
+ *
1597
+ * Inspired by Hono's `TrieRouter` and rou3. The trie handles
1598
+ * routup's path vocabulary directly via its own parser
1599
+ * (`./parser.ts`):
1600
+ *
1601
+ * - Static segments (`/users`)
1602
+ * - Named params (`:id`)
1603
+ * - Optional params (`:id?`) — expanded to two route variants at
1604
+ * registration (T2)
1605
+ * - Optional groups (`/users{/edit}`) — same expansion strategy
1606
+ * - Bare and named splats (`/files/*`, `/files/*rest`)
1607
+ *
1608
+ * Per-leaf storage is bucketed by HTTP method (T4) so lookup
1609
+ * narrows to the request method's bucket(s) instead of emitting
1610
+ * every entry at the leaf and letting the dispatcher's filter
1611
+ * discard mismatches.
1612
+ *
1613
+ * Param extraction is `paramsIndexMap`-driven (T3): a pre-built
1614
+ * `Array<{ depth, name }>` per variant lets `extractTrieParams`
1615
+ * read params straight from the request's pre-split segments — no
1616
+ * regex execution per match.
1617
+ *
1618
+ * Paths the trie parser doesn't handle (compound segments like
1619
+ * `/files/:n.ext`, escape sequences `\:`, regex constraints) and
1620
+ * empty/root paths fall through to the `universal` bucket. That
1621
+ * bucket still uses `path-to-regexp` via `buildRoutePathMatcher`,
1622
+ * so correctness is preserved.
1623
+ *
1624
+ * Pure-static-spine fast path (`shortCircuit`): when the request
1625
+ * walks a static spine with no param/splat/prefix siblings on any
1626
+ * traversed node, the leaf's `exactRoutes` (filtered to the request
1627
+ * method's buckets) is the full answer — no need to walk the param
1628
+ * branch or collect prefix candidates at intermediate nodes.
1629
+ */
1630
+ declare class TrieRouter<T extends ObjectLiteral = ObjectLiteral> implements IRouter<T> {
1631
+ /**
1632
+ * Monotonic counter assigned as the registration `index` on each
1633
+ * route — the dispatch loop uses it to preserve registration
1634
+ * order across the candidate list. App owns the canonical
1635
+ * `Route<T>[]` list (Plan 019); the trie no longer keeps a
1636
+ * parallel copy.
1637
+ */
1638
+ protected _routeCount: number;
1639
+ protected root: TrieNode<T>;
1640
+ /**
1641
+ * Routes that bypass the trie — registered with no path, with
1642
+ * the root path `/`, or with a path containing syntax the
1643
+ * parser doesn't recognise. Walked linearly on every lookup,
1644
+ * merged into the result in registration order.
1645
+ */
1646
+ protected universal: IndexedRoute<T>[];
1647
+ protected cache?: ICache<readonly RouteMatch<T>[]>;
1648
+ constructor(options?: BaseRouterOptions<T>);
1649
+ add(route: Route<T>): void;
1650
+ lookup(path: string, method?: MethodNameLike): readonly RouteMatch<T>[];
1651
+ clone(): IRouter<T>;
1652
+ /**
1653
+ * T1: returns the pre-computed candidate list when the request's
1654
+ * static spine has no param sibling, no prefix routes, and no
1655
+ * splats along the way. The leaf node's `exactRoutes` (filtered
1656
+ * to the request method's buckets) is then the complete answer —
1657
+ * no need to walk the param branch or collect prefix/splat
1658
+ * candidates from intermediate nodes. When any branch is
1659
+ * encountered, returns `null` and the caller falls through to
1660
+ * the regular `walk`.
1661
+ */
1662
+ protected shortCircuit(segments: string[], method: MethodNameLike | undefined): IndexedRoute<T>[] | null;
1663
+ protected parseRequestPath(path: string): string[];
1664
+ protected insertIntoTrie(segments: Segment[], route: Route<T>, index: number): void;
1665
+ protected walk(node: TrieNode<T>, segments: string[], depth: number, collected: IndexedRoute<T>[], method: MethodNameLike | undefined): void;
1666
+ protected isExactMatchRoute(route: Route<T>): boolean;
1667
+ /**
1668
+ * T5: copy params onto a prototype-less object so downstream
1669
+ * lookups skip prototype-chain traversal and avoid `__proto__` /
1670
+ * `hasOwnProperty` shadowing from user-controlled segment values.
1671
+ */
1672
+ protected assignParams(source: Record<string, string | undefined>): Record<string, string | undefined>;
1673
+ }
1674
+ //#endregion
1675
+ //#region src/router/utils.d.ts
1676
+ /**
1677
+ * Build a path-to-regexp-backed `PathMatcher` for the route's mount
1678
+ * path, applying the exact-vs-prefix convention every router should
1679
+ * agree on:
1680
+ *
1681
+ * - `route.method !== undefined` → exact match (method-bound route)
1682
+ * - `route.method === undefined` → prefix match (middleware / nested
1683
+ * app)
1684
+ *
1685
+ * Returns `undefined` when the route has no mount path — middleware
1686
+ * registered without a path matches every request.
1687
+ *
1688
+ * Routers are free to ignore this helper and build their own match
1689
+ * mechanism (radix tree, single aggregated regex, etc.) — it's
1690
+ * provided as a convenience for routers that want path-to-regexp
1691
+ * semantics with minimal boilerplate.
1692
+ */
1693
+ declare function buildRoutePathMatcher<T extends ObjectLiteral = ObjectLiteral>(route: Route<T>): IPathMatcher | undefined;
1694
+ //#endregion
1695
+ //#region src/app/module.d.ts
1696
+ declare class App implements IApp {
1697
+ /**
1698
+ * A label for the App instance.
1699
+ */
1700
+ readonly name?: string;
1701
+ /**
1702
+ * Registration-time path prefix for entries registered on this
1703
+ * App. Local to this instance — never inherited from a parent.
1704
+ *
1705
+ * @protected
1706
+ */
1707
+ protected _path?: Path;
1708
+ /**
1709
+ * Pluggable router (route table) — owns the "which entries match
1710
+ * this path?" lookup. Defaults to `LinearRouter` (walks entries
1711
+ * linearly per request); swap in via `AppContext.router`
1712
+ * for a radix/trie implementation on apps with many routes.
1713
+ *
1714
+ * @protected
1715
+ */
1716
+ protected router: IRouter<Handler>;
1717
+ /**
1718
+ * Normalized options for this App instance.
1719
+ *
1720
+ * Frozen on construction — once published to `event.appOptions`
1721
+ * it is shared across all requests, and a handler must not be
1722
+ * able to mutate router-global state.
1723
+ */
1724
+ protected _options: Readonly<AppOptions>;
1725
+ /**
1726
+ * Registry of installed plugins (name → version) on this App.
1727
+ *
1728
+ * Read by `use(otherApp)` (via the public `plugins` getter) so
1729
+ * plugin registries merge into the parent at flatten time —
1730
+ * `parent.hasPlugin('foo')` then reflects plugins installed on
1731
+ * apps mounted into it.
1732
+ *
1733
+ * @protected
1734
+ */
1735
+ protected _plugins: Map<string, string | undefined>;
1736
+ /**
1737
+ * Every route registered on this App, in registration order.
1738
+ *
1739
+ * Read by `use(otherApp)` to snapshot routes at flatten time
1740
+ * and by `clone()` to seed the copy. Late mutations to `_routes`
1741
+ * after a flatten do not propagate.
1742
+ */
1743
+ protected _routes: Route<Handler>[];
1744
+ constructor(input?: AppContext);
1745
+ /**
1746
+ * Public read of the canonical route list. Used by `use(child)`
1747
+ * to snapshot the child's routes at flatten time. Returned
1748
+ * as `readonly` — callers must not mutate.
1749
+ */
1750
+ get routes(): readonly Route<Handler>[];
1751
+ /**
1752
+ * Public read of the installed-plugin registry. Used by
1753
+ * `use(child)` to merge child plugins into the parent at
1754
+ * flatten time. Returned as `ReadonlyMap` — callers must not
1755
+ * mutate; go through `use(plugin)` to install.
1756
+ */
1757
+ get plugins(): ReadonlyMap<string, string | undefined>;
1758
+ /**
1759
+ * Register a route with the active router and record it on the
1760
+ * App so `clone` / `setRouter` / `use(child)` can read the
1761
+ * canonical list back.
1762
+ *
1763
+ * @protected
1764
+ */
1765
+ protected register(route: Route<Handler>): void;
1766
+ /**
1767
+ * Swap the active router. Replays every previously-registered
1768
+ * route onto the new router so lookups stay correct.
1769
+ *
1770
+ * Useful for picking a router after route shape is known (e.g.
1771
+ * a SmartRouter-style decision), or for testing alternatives
1772
+ * mid-flight without rebuilding the App. Any cache the previous
1773
+ * router carried is dropped along with it.
1774
+ */
1775
+ setRouter(router: IRouter<Handler>): void;
1776
+ /**
1777
+ * Public entry point — creates a DispatcherEvent from the request,
1778
+ * runs the pipeline, and returns a Response (with 404/500 fallbacks).
1779
+ */
1780
+ fetch(request: AppRequest): Promise<Response>;
1781
+ protected buildFallbackResponse(request: AppRequest, event: IDispatcherEvent, status: number, message: string): Response;
1782
+ dispatch(event: IDispatcherEvent): Promise<Response | undefined>;
1783
+ /**
1784
+ * Walk the matched routes for the current event, dispatching each
1785
+ * handler in order. Re-entered (recursively) from the `setNext`
1786
+ * continuation so `event.next()` resumes from the next match.
1787
+ */
1788
+ protected runMatches(event: IDispatcherEvent, matches: readonly RouteMatch<Handler>[], matchesPath: string, startIndex: number): Promise<Response | undefined>;
1789
+ delete(...handlers: (Handler | HandlerOptions)[]): this;
1790
+ delete(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1791
+ get(...handlers: (Handler | HandlerOptions)[]): this;
1792
+ get(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1793
+ post(...handlers: (Handler | HandlerOptions)[]): this;
1794
+ post(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1795
+ put(...handlers: (Handler | HandlerOptions)[]): this;
1796
+ put(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1797
+ patch(...handlers: (Handler | HandlerOptions)[]): this;
1798
+ patch(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1799
+ head(...handlers: (Handler | HandlerOptions)[]): this;
1800
+ head(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1801
+ options(...handlers: (Handler | HandlerOptions)[]): this;
1802
+ options(path: Path, ...handlers: (Handler | HandlerOptions)[]): this;
1803
+ protected useForMethod(method: MethodName, ...input: (Path | Handler | HandlerOptions)[]): void;
1804
+ use(app: IApp): this;
1805
+ use(handler: Handler | HandlerOptions): this;
1806
+ use(plugin: Plugin): this;
1807
+ use(path: Path, app: IApp): this;
1808
+ use(path: Path, handler: Handler | HandlerOptions): this;
1809
+ use(path: Path, plugin: Plugin): this;
1810
+ /**
1811
+ * Snapshot a child App's routes and plugin registry into this
1812
+ * one. Each route's path is prefixed with `this._path`, the
1813
+ * supplied mount `path`, and the route's own path (in that
1814
+ * order); the resulting entry is registered on this App's
1815
+ * router. The child app is not retained — late mutations on it
1816
+ * after this call do not propagate.
1817
+ *
1818
+ * @protected
1819
+ */
1820
+ protected flatten(child: App, path: Path | undefined): void;
1821
+ /**
1822
+ * Check if a plugin with the given name is installed on this App.
1823
+ */
1824
+ hasPlugin(name: string): boolean;
1825
+ /**
1826
+ * Get the version of an installed plugin by name, or `undefined`
1827
+ * if the plugin is not installed.
1828
+ */
1829
+ getPluginVersion(name: string): string | undefined;
1830
+ protected install(plugin: Plugin, context?: PluginInstallContext): this;
1831
+ /**
1832
+ * Return a new `App` that mirrors this one but owns independent
1833
+ * mountable state — fresh router of the same family seeded with
1834
+ * the current routes, shallow copy of options, and a fresh
1835
+ * plugins map carrying the same entries.
1836
+ */
1837
+ clone(): IApp;
1838
+ }
1839
+ //#endregion
1840
+ //#region src/app/options.d.ts
1841
+ declare function normalizeAppOptions(input: AppOptionsInput): AppOptions;
1842
+ //#endregion
1843
+ export { AppOptions as $, HandlerSymbol as $t, sendFormat as A, fromNodeMiddleware as At, setResponseHeaderAttachment as B, ErrorHandlerOptions as Bt, getRequestAcceptableContentTypes as C, isHandlerOptions as Ct, setResponseContentTypeByFileName as D, WebHandler as Dt, toResponse as E, fromWebHandler as Et, SendFileStats as F, CoreHandlerOptions as Ft, EventStreamHandle as G, isPath as Gt, appendResponseHeader as H, HandlerBaseOptions as Ht, sendFile as I, Handler as It, EventStreamListener as J, Path as Jt, EventStreamOptions as K, PathMatcher as Kt, sendCreated as L, HandlerOptions as Lt, SendFileContentOptions as M, NodeMiddleware as Mt, SendFileDisposition as N, defineCoreHandler as Nt, sendStream as O, WebHandlerProvider as Ot, SendFileOptions as P, CoreHandler as Pt, AppContext as Q, createError as Qt, sendAccepted as R, defineErrorHandler as Rt, getRequestAcceptableContentType as S, isHandler as St, isRequestCacheable as T, isWebHandlerProvider as Tt, appendResponseHeaderDirective as U, HandlerBeforeListener as Ut, setResponseHeaderInline as V, HandlerAfterListener as Vt, serializeEventStreamMessage as W, HandlerErrorListener as Wt, ResponseCacheHeadersOptions as X, PathMatcherOptions as Xt, EventStreamMessage as Y, PathMatcherExecResult as Yt, setResponseCacheHeaders as Z, isError as Zt, getRequestAcceptableLanguages as _, PluginAlreadyInstalledError as _t, SmartRouter as a, AppEventCreateContext as an, ObjectLiteral as at, getRequestAcceptableCharset as b, PluginErrorCode as bt, RequestProtocolOptions as c, IAppEvent as cn, LruCache as ct, RequestIpOptions as d, MethodName as dn, isPlugin as dt, HandlerType as en, AppOptionsInput as et, getRequestIP as f, MethodNameLike as fn, Plugin as ft, getRequestAcceptableLanguage as g, PluginInstallError as gt, matchRequestContentType as h, HTTPErrorInput$1 as hn, PluginNotInstalledError as ht, TrieRouter as i, AppEvent as in, IRouter as it, SendFileContent as j, NodeHandler as jt, sendRedirect as k, fromNodeHandler as kt, getRequestProtocol as l, NextFn as ln, LruCacheOptions as lt, getRequestHostName as m, ErrorSymbol as mn, PluginInstallFn as mt, App as n, IDispatcher as nn, IApp as nt, SmartRouterOptions as o, AppRequest as on, Route as ot, RequestHostNameOptions as p, AppError as pn, PluginInstallContext as pt, createEventStream as q, IPathMatcher as qt, buildRoutePathMatcher as r, IDispatcherEvent as rn, BaseRouterOptions as rt, LinearRouter as s, AppResponse as sn, RouteMatch as st, normalizeAppOptions as t, DispatcherEvent as tn, AppPipelineContext as tt, useRequestNegotiator as u, HeaderName as un, ICache as ut, getRequestAcceptableEncoding as v, isPluginError as vt, getRequestHeader as w, isWebHandler as wt, getRequestAcceptableCharsets as x, matchHandlerMethod as xt, getRequestAcceptableEncodings as y, PluginError as yt, setResponseHeaderContentType as z, ErrorHandler as zt };
1844
+ //# sourceMappingURL=index-B80OpXdo.d.mts.map