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.
- package/README.md +68 -47
- package/dist/bun.d.mts +3 -3
- package/dist/bun.mjs +4 -4
- package/dist/bun.mjs.map +1 -1
- package/dist/cloudflare.d.mts +3 -3
- package/dist/cloudflare.mjs +4 -4
- package/dist/cloudflare.mjs.map +1 -1
- package/dist/deno.d.mts +3 -3
- package/dist/deno.mjs +4 -4
- package/dist/deno.mjs.map +1 -1
- package/dist/generic.d.mts +3 -3
- package/dist/generic.mjs +4 -4
- package/dist/generic.mjs.map +1 -1
- package/dist/index-B80OpXdo.d.mts +1844 -0
- package/dist/node.d.mts +4 -4
- package/dist/node.mjs +6 -6
- package/dist/node.mjs.map +1 -1
- package/dist/service-worker.d.mts +3 -3
- package/dist/service-worker.mjs +4 -4
- package/dist/service-worker.mjs.map +1 -1
- package/dist/src-DaK6SZc0.mjs +2694 -0
- package/dist/src-DaK6SZc0.mjs.map +1 -0
- package/package.json +6 -4
- package/dist/index-DdsCL8RI.d.mts +0 -1159
- package/dist/src-DX0rndew.mjs +0 -1993
- package/dist/src-DX0rndew.mjs.map +0 -1
|
@@ -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
|