@bepalo/router 1.0.3

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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +557 -0
  3. package/dist/cjs/helpers.d.ts +290 -0
  4. package/dist/cjs/helpers.d.ts.map +1 -0
  5. package/dist/cjs/helpers.js +691 -0
  6. package/dist/cjs/helpers.js.map +1 -0
  7. package/dist/cjs/index.d.ts +5 -0
  8. package/dist/cjs/index.d.ts.map +1 -0
  9. package/dist/cjs/index.js +21 -0
  10. package/dist/cjs/index.js.map +1 -0
  11. package/dist/cjs/list.d.ts +166 -0
  12. package/dist/cjs/list.d.ts.map +1 -0
  13. package/dist/cjs/list.js +483 -0
  14. package/dist/cjs/list.js.map +1 -0
  15. package/dist/cjs/middlewares.d.ts +251 -0
  16. package/dist/cjs/middlewares.d.ts.map +1 -0
  17. package/dist/cjs/middlewares.js +359 -0
  18. package/dist/cjs/middlewares.js.map +1 -0
  19. package/dist/cjs/router.d.ts +333 -0
  20. package/dist/cjs/router.d.ts.map +1 -0
  21. package/dist/cjs/router.js +659 -0
  22. package/dist/cjs/router.js.map +1 -0
  23. package/dist/cjs/tree.d.ts +18 -0
  24. package/dist/cjs/tree.d.ts.map +1 -0
  25. package/dist/cjs/tree.js +162 -0
  26. package/dist/cjs/tree.js.map +1 -0
  27. package/dist/cjs/types.d.ts +127 -0
  28. package/dist/cjs/types.d.ts.map +1 -0
  29. package/dist/cjs/types.js +3 -0
  30. package/dist/cjs/types.js.map +1 -0
  31. package/dist/cjs/upload-stream.d.ts +105 -0
  32. package/dist/cjs/upload-stream.d.ts.map +1 -0
  33. package/dist/cjs/upload-stream.js +417 -0
  34. package/dist/cjs/upload-stream.js.map +1 -0
  35. package/dist/helpers.d.ts +290 -0
  36. package/dist/helpers.d.ts.map +1 -0
  37. package/dist/helpers.js +691 -0
  38. package/dist/helpers.js.map +1 -0
  39. package/dist/index.d.ts +5 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +21 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/list.d.ts +166 -0
  44. package/dist/list.d.ts.map +1 -0
  45. package/dist/list.js +483 -0
  46. package/dist/list.js.map +1 -0
  47. package/dist/middlewares.d.ts +251 -0
  48. package/dist/middlewares.d.ts.map +1 -0
  49. package/dist/middlewares.js +359 -0
  50. package/dist/middlewares.js.map +1 -0
  51. package/dist/router.d.ts +333 -0
  52. package/dist/router.d.ts.map +1 -0
  53. package/dist/router.js +659 -0
  54. package/dist/router.js.map +1 -0
  55. package/dist/tree.d.ts +18 -0
  56. package/dist/tree.d.ts.map +1 -0
  57. package/dist/tree.js +162 -0
  58. package/dist/tree.js.map +1 -0
  59. package/dist/types.d.ts +127 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +3 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/upload-stream.d.ts +105 -0
  64. package/dist/upload-stream.d.ts.map +1 -0
  65. package/dist/upload-stream.js +417 -0
  66. package/dist/upload-stream.js.map +1 -0
  67. package/package.json +51 -0
package/dist/router.js ADDED
@@ -0,0 +1,659 @@
1
+ "use strict";
2
+ /**
3
+ * @file A fast radix-trie based router for JavaScript runtimes.
4
+ * @module @bepalo/router
5
+ * @author Natnael Eshetu
6
+ * @exports Router
7
+ */
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
23
+ if (kind === "m") throw new TypeError("Private method is not writable");
24
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
25
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
26
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
27
+ };
28
+ var _Router_trees, _Router_enable, _Router_defaultHeaders, _Router_defaultCatcher, _Router_defaultFallback, _Router_setters;
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.Router = exports.isValidHttpMethod = void 0;
31
+ const tree_js_1 = require("./tree.js");
32
+ /**
33
+ * Checks if a string is a valid HTTP method.
34
+ * @param {string} method - The method string to validate
35
+ * @returns {boolean} True if the method is valid, false otherwise
36
+ */
37
+ const isValidHttpMethod = (method) => {
38
+ switch (method) {
39
+ case "HEAD":
40
+ case "OPTIONS":
41
+ case "GET":
42
+ case "POST":
43
+ case "PUT":
44
+ case "PATCH":
45
+ case "DELETE":
46
+ return true;
47
+ default:
48
+ return false;
49
+ }
50
+ };
51
+ exports.isValidHttpMethod = isValidHttpMethod;
52
+ /**
53
+ * Initializes method trees for all HTTP methods.
54
+ * @returns {Record<HttpMethod, Tree<RouteNode<Context>>>} Trees for each HTTP method
55
+ * @template Context
56
+ */
57
+ function initMethodTrees() {
58
+ return {
59
+ HEAD: new tree_js_1.Tree(),
60
+ OPTIONS: new tree_js_1.Tree(),
61
+ GET: new tree_js_1.Tree(),
62
+ POST: new tree_js_1.Tree(),
63
+ PUT: new tree_js_1.Tree(),
64
+ PATCH: new tree_js_1.Tree(),
65
+ DELETE: new tree_js_1.Tree(),
66
+ };
67
+ }
68
+ /** @constant {Array} emptyArray - Empty array constant for optimization */
69
+ const emptyArray = [];
70
+ /**
71
+ * A fast radix-trie based router for JavaScript runtimes.
72
+ * Supports hooks, filters, handlers, fallbacks, catchers, and after handlers.
73
+ * @class
74
+ * @template Context
75
+ * @example
76
+ * const router = new Router();
77
+ *
78
+ * // Register a simple GET handler
79
+ * router.handle("GET /users/:id", async (req, ctx) => {
80
+ * const userId = ctx.params.id;
81
+ * return json({ userId });
82
+ * });
83
+ *
84
+ * // Register a hook that runs before all /api routes
85
+ * router.hook("* /api/**", (req, ctx) => {
86
+ * console.log(`API request: ${req.method} ${req.url}`);
87
+ * });
88
+ *
89
+ * // Register an error handler
90
+ * router.catch("* /**", (req, ctx) => {
91
+ * console.error(ctx.error);
92
+ * return json({ error: "Something went wrong" }, { status: 500 });
93
+ * });
94
+ *
95
+ * // Handle a request and get a response
96
+ * const response = await router.respond(new Request("http://localhost/"));
97
+ *
98
+ */
99
+ class Router {
100
+ /**
101
+ * Gets the routing trees for all handler types.
102
+ * @returns {Record<HandlerType, Record<HttpMethod, Tree<RouteNode<Context>>>>}
103
+ */
104
+ get trees() {
105
+ return __classPrivateFieldGet(this, _Router_trees, "f");
106
+ }
107
+ /**
108
+ * Gets the enabled handler types configuration.
109
+ * @returns {HandlerEnable}
110
+ */
111
+ get enabled() {
112
+ return __classPrivateFieldGet(this, _Router_enable, "f");
113
+ }
114
+ /**
115
+ * Gets the default headers configuration.
116
+ * @returns {Array<[string, string]>}
117
+ */
118
+ get defaultHeaders() {
119
+ return __classPrivateFieldGet(this, _Router_defaultHeaders, "f");
120
+ }
121
+ /**
122
+ * Gets the default catcher handler.
123
+ * @returns {Handler<Context>|undefined}
124
+ */
125
+ get defaultCatcher() {
126
+ return __classPrivateFieldGet(this, _Router_defaultCatcher, "f");
127
+ }
128
+ /**
129
+ * Gets the default fallback handler.
130
+ * @returns {Handler<Context>|undefined}
131
+ */
132
+ get defaultFallback() {
133
+ return __classPrivateFieldGet(this, _Router_defaultFallback, "f");
134
+ }
135
+ /**
136
+ * Gets the route registration history.
137
+ * @returns {Set<HandlerSetter<Context>>}
138
+ */
139
+ get setters() {
140
+ return __classPrivateFieldGet(this, _Router_setters, "f");
141
+ }
142
+ /**
143
+ * Creates a new Router instance.
144
+ * @param {RouterConfig<Context>} [config] - Configuration options
145
+ */
146
+ constructor(config) {
147
+ _Router_trees.set(this, {
148
+ filter: initMethodTrees(),
149
+ hook: initMethodTrees(),
150
+ handler: initMethodTrees(),
151
+ fallback: initMethodTrees(),
152
+ catcher: initMethodTrees(),
153
+ after: initMethodTrees(),
154
+ });
155
+ _Router_enable.set(this, {
156
+ hooks: true,
157
+ afters: true,
158
+ filters: true,
159
+ fallbacks: true,
160
+ catchers: true,
161
+ });
162
+ _Router_defaultHeaders.set(this, []);
163
+ _Router_defaultCatcher.set(this, void 0);
164
+ _Router_defaultFallback.set(this, void 0);
165
+ _Router_setters.set(this, new Set());
166
+ this.respond = this.respond.bind(this);
167
+ if (config === null || config === void 0 ? void 0 : config.defaultHeaders) {
168
+ __classPrivateFieldSet(this, _Router_defaultHeaders, config.defaultHeaders, "f");
169
+ }
170
+ if (config === null || config === void 0 ? void 0 : config.enable) {
171
+ __classPrivateFieldSet(this, _Router_enable, config.enable, "f");
172
+ }
173
+ if (config === null || config === void 0 ? void 0 : config.defaultCatcher) {
174
+ __classPrivateFieldSet(this, _Router_defaultCatcher, config.defaultCatcher, "f");
175
+ }
176
+ if (config === null || config === void 0 ? void 0 : config.defaultFallback) {
177
+ __classPrivateFieldSet(this, _Router_defaultFallback, config.defaultFallback, "f");
178
+ }
179
+ }
180
+ /**
181
+ * Registers a hook handler that runs before other handlers.
182
+ * Hooks cannot modify the response directly but can modify context.
183
+ * Their responses are ignored.
184
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
185
+ * @param {Handler<Context & XContext>|Pipeline<Context & XContext>} pipeline - Handler(s) to execute
186
+ * @param {HandlerOptions} [options] - Registration options
187
+ * @returns {Router<Context & XContext>} The router instance for chaining
188
+ * @template XContext
189
+ * @example
190
+ * router.hook("GET /api/**", (req, ctx) => {
191
+ * ctx.startTime = Date.now();
192
+ * });
193
+ */
194
+ hook(urls, pipeline, options) {
195
+ return this.setRoutes("hook", urls, pipeline, options);
196
+ }
197
+ /**
198
+ * Registers an after handler that runs after the response is created.
199
+ * After handlers can inspect and modify the response from the context.
200
+ * Their responses are ignored.
201
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
202
+ * @param {Handler<Context & XContext>|Pipeline<Context & XContext>} pipeline - Handler(s) to execute
203
+ * @param {HandlerOptions} [options] - Registration options
204
+ * @returns {Router<Context & XContext>} The router instance for chaining
205
+ * @template XContext
206
+ * @example
207
+ * router.after("GET /**", (req, ctx) => {
208
+ * console.log(`Request completed: ${req.method} ${req.url}`);
209
+ * });
210
+ */
211
+ after(urls, pipeline, options) {
212
+ return this.setRoutes("after", urls, pipeline, options);
213
+ }
214
+ /**
215
+ * Registers a filter handler that can intercept and modify requests.
216
+ * Filters run after hooks but before handlers and can return a response.
217
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
218
+ * @param {Handler<Context & XContext>|Pipeline<Context & XContext>} pipeline - Handler(s) to execute
219
+ * @param {HandlerOptions} [options] - Registration options
220
+ * @returns {Router<Context & XContext>} The router instance for chaining
221
+ * @template XContext
222
+ * @example
223
+ * router.filter("GET /admin/**", (req, ctx) => {
224
+ * if (!req.headers.get("x-admin-token")) {
225
+ * return json({ error: "Unauthorized" }, { status: 401 });
226
+ * }
227
+ * });
228
+ */
229
+ filter(urls, pipeline, options) {
230
+ return this.setRoutes("filter", urls, pipeline, options);
231
+ }
232
+ /**
233
+ * Registers a main request handler.
234
+ * Handlers are the primary way to respond to requests.
235
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
236
+ * @param {Handler<Context & XContext>|Pipeline<Context & XContext>} pipeline - Handler(s) to execute
237
+ * @param {HandlerOptions} [options] - Registration options
238
+ * @returns {Router<Context & XContext>} The router instance for chaining
239
+ * @template XContext
240
+ * @example
241
+ * router.handle("GET /users", async (req, ctx) => {
242
+ * const users = await getUsers();
243
+ * return json({ users });
244
+ * });
245
+ */
246
+ handle(urls, pipeline, options) {
247
+ return this.setRoutes("handler", urls, pipeline, options);
248
+ }
249
+ /**
250
+ * Registers a fallback handler that runs when no main handler matches.
251
+ * Fallbacks are useful for custom 404 pages or default behaviors.
252
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
253
+ * @param {Handler<Context & XContext>|Pipeline<Context & XContext>} pipeline - Handler(s) to execute
254
+ * @param {HandlerOptions} [options] - Registration options
255
+ * @returns {Router<Context & XContext>} The router instance for chaining
256
+ * @template XContext
257
+ * @example
258
+ * router.fallback("GET /**", (req, ctx) => {
259
+ * return json({ error: "Not found" }, { status: 404 });
260
+ * });
261
+ */
262
+ fallback(urls, pipeline, options) {
263
+ return this.setRoutes("fallback", urls, pipeline, options);
264
+ }
265
+ /**
266
+ * Registers an error handler for catching exceptions.
267
+ * Catchers receive the error in the context and can return a response.
268
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
269
+ * @param {Handler<Context & XContext & { error: Error }>|Pipeline<Context & XContext & { error: Error }>} pipeline - Handler(s) to execute
270
+ * @param {HandlerOptions} [options] - Registration options
271
+ * @returns {Router<Context & XContext & { error: Error }>} The router instance for chaining
272
+ * @template XContext
273
+ * @example
274
+ * router.catch("GET /**", (req, ctx) => {
275
+ * console.error(ctx.error);
276
+ * return json({ error: "Internal server error" }, { status: 500 });
277
+ * });
278
+ */
279
+ catch(urls, pipeline, options) {
280
+ return this.setRoutes("catcher", urls, pipeline, options);
281
+ }
282
+ /**
283
+ * Appends routes from another router under a base URL.
284
+ * Useful for mounting sub-routers or organizing routes by prefix.
285
+ * @param {`/${string}`} baseUrl - The base URL to mount the router under
286
+ * @param {Router<Context>} router - The router to append
287
+ * @param {HandlerOptions} [options] - Registration options
288
+ * @returns {Router<Context>} The router instance for chaining
289
+ * @example
290
+ * const apiRouter = new Router();
291
+ * apiRouter.handle("GET /users", getUsersHandler);
292
+ *
293
+ * const mainRouter = new Router();
294
+ * mainRouter.append("/api", apiRouter);
295
+ * // Now GET /api/users routes to getUsersHandler
296
+ */
297
+ append(baseUrl, router, options) {
298
+ baseUrl =
299
+ baseUrl.charAt(baseUrl.length - 1) === "/"
300
+ ? baseUrl.slice(0, baseUrl.length - 1)
301
+ : baseUrl;
302
+ for (const elem of __classPrivateFieldGet(router, _Router_setters, "f")) {
303
+ let urls;
304
+ if (typeof elem.urls === "string") {
305
+ const [method, path] = elem.urls.split(" ", 2);
306
+ urls = `${method} ${baseUrl}${path}`;
307
+ }
308
+ else {
309
+ urls = elem.urls.map((url) => {
310
+ const [method, path] = url.split(" ", 2);
311
+ return `${method} ${baseUrl}${path}`;
312
+ });
313
+ }
314
+ this.setRoutes(elem.handlerType, urls, elem.pipeline, Object.assign(Object.assign({}, elem.options), options));
315
+ }
316
+ return this;
317
+ }
318
+ /**
319
+ * Low-level method to register routes of any handler type.
320
+ * @param {HandlerType} handlerType - The type of handler to register
321
+ * @param {"*"|MethodPath|Array<MethodPath>} urls - URL patterns to match
322
+ * @param {Handler<Context>|Pipeline<Context>} pipeline_ - Handler(s) to execute
323
+ * @param {HandlerOptions} [options] - Registration options
324
+ * @returns {Router<Context>} The router instance for chaining
325
+ * @private
326
+ */
327
+ setRoutes(handlerType, urls, pipeline_, options) {
328
+ const pipeline = Array.isArray(pipeline_)
329
+ ? pipeline_
330
+ : [pipeline_];
331
+ const splitUrls = urls === "*" ? Router.ALL_METHOD_PATHS : splitUrl(urls);
332
+ for (const { method, nodes, params, pathname } of splitUrls) {
333
+ const treeNode = __classPrivateFieldGet(this, _Router_trees, "f")[handlerType][method];
334
+ const splitPaths = pathname.substring(1).split("/");
335
+ const splitPathsLength_1 = splitPaths.length - 1;
336
+ for (let i = 0; i < splitPathsLength_1; i++) {
337
+ switch (splitPaths[i]) {
338
+ case "**":
339
+ throw new Error(`Super-glob '**' in the middle of pathname '${pathname}'. Should only be at the end.`);
340
+ case ".**":
341
+ throw new Error(`Super-glob '.**' in the middle of pathname '${pathname}'. Should only be at the end.`);
342
+ case ".*":
343
+ throw new Error(`glob '.*' in the middle of pathname '${pathname}'. Should only be at the end.`);
344
+ }
345
+ }
346
+ if (!(options === null || options === void 0 ? void 0 : options.overwrite)) {
347
+ const node = treeNode.get(splitPaths);
348
+ if (node) {
349
+ const maxLen = Math.min(node.nodes.length, splitPaths.length);
350
+ let colliding = [];
351
+ for (let i = 0; i < maxLen; i++) {
352
+ if (node.nodes[i] === "*"
353
+ ? splitPaths[i].startsWith(":") || splitPaths[i] === "*"
354
+ : node.nodes[i] === splitPaths[i]) {
355
+ colliding.unshift(i);
356
+ }
357
+ }
358
+ if (colliding.length > 0 && colliding[0] === maxLen - 1) {
359
+ throw new Error(`Overriding route '${node.pathname}' with '${pathname}'`);
360
+ }
361
+ }
362
+ }
363
+ treeNode.set(nodes, {
364
+ method,
365
+ nodes,
366
+ pipeline,
367
+ pathname,
368
+ params,
369
+ });
370
+ }
371
+ // add to setters for later use
372
+ __classPrivateFieldGet(this, _Router_setters, "f").add({
373
+ handlerType,
374
+ urls,
375
+ pipeline: pipeline_,
376
+ options,
377
+ });
378
+ return this;
379
+ }
380
+ /**
381
+ * Handles an incoming HTTP request and returns a response.
382
+ * This is the main entry point for request processing.
383
+ * Handlers are only called if they are not disabled.
384
+ * @param {Request} req - The incoming HTTP request
385
+ * @param {Partial<Context>} [context] - Initial context object
386
+ * @returns {Promise<Response>} The HTTP response
387
+ */
388
+ respond(req, context) {
389
+ return __awaiter(this, void 0, void 0, function* () {
390
+ var _a, _b, _c, _d;
391
+ const method = req.method;
392
+ if (!(0, exports.isValidHttpMethod)(method)) {
393
+ return new Response("Method Not Allowed", {
394
+ status: 405,
395
+ statusText: "Method Not Allowed",
396
+ headers: (_a = context === null || context === void 0 ? void 0 : context.headers) !== null && _a !== void 0 ? _a : new Headers(__classPrivateFieldGet(this, _Router_defaultHeaders, "f")),
397
+ });
398
+ }
399
+ let response = undefined;
400
+ const url = new URL(req.url);
401
+ const key = url.pathname
402
+ .substring(1, url.pathname !== "/" && url.pathname.endsWith("/")
403
+ ? url.pathname.length - 1
404
+ : url.pathname.length)
405
+ .split("/");
406
+ const hookNodes = this.enabled.hooks
407
+ ? __classPrivateFieldGet(this, _Router_trees, "f").hook[method].getAll(key)
408
+ : emptyArray;
409
+ const afterNodes = this.enabled.afters
410
+ ? __classPrivateFieldGet(this, _Router_trees, "f").after[method].getAll(key)
411
+ : emptyArray;
412
+ const filterNodes = this.enabled.filters
413
+ ? __classPrivateFieldGet(this, _Router_trees, "f").filter[method].getAll(key)
414
+ : emptyArray;
415
+ const handlerNodes = __classPrivateFieldGet(this, _Router_trees, "f").handler[method].getAll(key);
416
+ const fallbackNodes = this.enabled.fallbacks
417
+ ? __classPrivateFieldGet(this, _Router_trees, "f").fallback[method].getAll(key)
418
+ : emptyArray;
419
+ const catcherNodes = this.enabled.catchers
420
+ ? __classPrivateFieldGet(this, _Router_trees, "f").catcher[method].getAll(key)
421
+ : emptyArray;
422
+ const found = {
423
+ hooks: hookNodes.length > 0,
424
+ afters: afterNodes.length > 0,
425
+ filters: filterNodes.length > 0,
426
+ handlers: handlerNodes.length > 0,
427
+ fallbacks: fallbackNodes.length > 0,
428
+ catchers: catcherNodes.length > 0,
429
+ };
430
+ const ctx = Object.assign({ params: (_b = context === null || context === void 0 ? void 0 : context.params) !== null && _b !== void 0 ? _b : {}, headers: (_c = context === null || context === void 0 ? void 0 : context.headers) !== null && _c !== void 0 ? _c : new Headers(__classPrivateFieldGet(this, _Router_defaultHeaders, "f")), found }, context);
431
+ try {
432
+ // hooks
433
+ if (found.hooks) {
434
+ const params = hookNodes[hookNodes.length - 1].params;
435
+ if (params) {
436
+ for (const [index, param] of params) {
437
+ ctx.params[param.name] = key[index];
438
+ }
439
+ }
440
+ let hookResponse = undefined;
441
+ for (const hookNode of hookNodes) {
442
+ for (const hook of hookNode.pipeline) {
443
+ hookResponse = yield hook(req, ctx);
444
+ if (hookResponse)
445
+ break;
446
+ }
447
+ if (hookResponse)
448
+ break;
449
+ }
450
+ }
451
+ // filters
452
+ if (found.filters) {
453
+ const params = filterNodes[filterNodes.length - 1].params;
454
+ if (params) {
455
+ for (const [index, param] of params) {
456
+ ctx.params[param.name] = key[index];
457
+ }
458
+ }
459
+ for (const filterNode of filterNodes) {
460
+ for (const filter of filterNode.pipeline) {
461
+ response = yield filter(req, ctx);
462
+ if (response)
463
+ break;
464
+ }
465
+ if (response)
466
+ break;
467
+ }
468
+ }
469
+ // handlers
470
+ if (found.handlers) {
471
+ const params = handlerNodes[handlerNodes.length - 1].params;
472
+ if (params) {
473
+ for (const [index, param] of params) {
474
+ ctx.params[param.name] = key[index];
475
+ }
476
+ }
477
+ if (!(response instanceof Response)) {
478
+ for (const handlerNode of handlerNodes) {
479
+ for (const handler of handlerNode.pipeline) {
480
+ response = yield handler(req, ctx);
481
+ if (response)
482
+ break;
483
+ }
484
+ if (response)
485
+ break;
486
+ }
487
+ }
488
+ }
489
+ // fallbacks
490
+ if (!(response instanceof Response)) {
491
+ if (found.fallbacks) {
492
+ const params = fallbackNodes[fallbackNodes.length - 1].params;
493
+ if (params) {
494
+ for (const [index, param] of params) {
495
+ ctx.params[param.name] = key[index];
496
+ }
497
+ }
498
+ for (const fallbackNode of fallbackNodes) {
499
+ for (const fallback of fallbackNode.pipeline) {
500
+ response = yield fallback(req, ctx);
501
+ if (response)
502
+ break;
503
+ }
504
+ if (response)
505
+ break;
506
+ }
507
+ }
508
+ }
509
+ if (!(response instanceof Response) && __classPrivateFieldGet(this, _Router_defaultFallback, "f")) {
510
+ response = yield __classPrivateFieldGet(this, _Router_defaultFallback, "f").call(this, req, ctx);
511
+ }
512
+ // append context headers to response
513
+ if (response instanceof Response) {
514
+ if (ctx.headers) {
515
+ for (const [key, value] of ctx.headers) {
516
+ response.headers.set(key, value);
517
+ }
518
+ }
519
+ }
520
+ if (response instanceof Response)
521
+ ctx.response = response;
522
+ response =
523
+ (_d = (typeof response === "boolean" ? null : response)) !== null && _d !== void 0 ? _d : (found.handlers || found.fallbacks
524
+ ? new Response(null, {
525
+ status: 204,
526
+ statusText: "No Content",
527
+ headers: ctx.headers,
528
+ })
529
+ : new Response("Not Found", {
530
+ status: 404,
531
+ statusText: "Not Found",
532
+ headers: ctx.headers,
533
+ }));
534
+ // after response handlers
535
+ if (found.afters) {
536
+ const params = afterNodes[afterNodes.length - 1].params;
537
+ if (params) {
538
+ for (const [index, param] of params) {
539
+ ctx.params[param.name] = key[index];
540
+ }
541
+ }
542
+ let afterResponse = undefined;
543
+ for (const afterNode of afterNodes) {
544
+ for (const after of afterNode.pipeline) {
545
+ afterResponse = yield after(req, ctx);
546
+ if (afterResponse)
547
+ break;
548
+ }
549
+ if (afterResponse)
550
+ break;
551
+ }
552
+ }
553
+ }
554
+ catch (error) {
555
+ // error handlers
556
+ ctx.error = error instanceof Error ? error : new Error(String(error));
557
+ if (found.catchers) {
558
+ const params = catcherNodes[catcherNodes.length - 1].params;
559
+ if (params) {
560
+ for (const [index, param] of params) {
561
+ ctx.params[param.name] = key[index];
562
+ }
563
+ }
564
+ for (const catcherNode of catcherNodes) {
565
+ for (const catcher of catcherNode.pipeline) {
566
+ response = yield catcher(req, ctx);
567
+ if (response)
568
+ break;
569
+ }
570
+ if (response)
571
+ break;
572
+ }
573
+ }
574
+ if (!(response instanceof Response) && __classPrivateFieldGet(this, _Router_defaultCatcher, "f")) {
575
+ response = yield __classPrivateFieldGet(this, _Router_defaultCatcher, "f").call(this, req, ctx);
576
+ }
577
+ if (!(response instanceof Response)) {
578
+ response = new Response("Internal Server Error", {
579
+ status: 500,
580
+ statusText: "Internal Server Error",
581
+ headers: ctx.headers,
582
+ });
583
+ }
584
+ }
585
+ return response;
586
+ });
587
+ }
588
+ }
589
+ exports.Router = Router;
590
+ _Router_trees = new WeakMap(), _Router_enable = new WeakMap(), _Router_defaultHeaders = new WeakMap(), _Router_defaultCatcher = new WeakMap(), _Router_defaultFallback = new WeakMap(), _Router_setters = new WeakMap();
591
+ /**
592
+ * Static property containing all HTTP methods with wildcard paths.
593
+ * @type {Array<SplitURL>}
594
+ * @readonly
595
+ */
596
+ Router.ALL_METHOD_PATHS = splitUrl([
597
+ "HEAD /.**",
598
+ "OPTIONS /.**",
599
+ "GET /.**",
600
+ "POST /.**",
601
+ "PUT /.**",
602
+ "PATCH /.**",
603
+ "DELETE /.**",
604
+ ]);
605
+ /**
606
+ * Splits URL patterns into their components for routing.
607
+ * Supports wildcards (*), super-globs (**), and parameters (:param).
608
+ * @param {MethodPath|Array<MethodPath>} urls - URL patterns to split
609
+ * @returns {Array<SplitURL>} Array of split URL components
610
+ * @private
611
+ * @example
612
+ * // Returns: [{ method: 'GET', pathname: '/users/:id', nodes: ['users', '*'], params: Map({1: {name: 'id', index: 1}}) }]
613
+ * splitUrl(["GET /users/:id"]);
614
+ */
615
+ function splitUrl(urls) {
616
+ urls = (Array.isArray(urls) ? urls : [urls]);
617
+ let splitUrls = [];
618
+ for (const mp of urls) {
619
+ const [method, pathname] = mp
620
+ .split(" ", 2)
621
+ .map((mu) => mu.trim());
622
+ const params = new Map();
623
+ const nodes = [];
624
+ const pathNodes = pathname.substring(1).split("/");
625
+ const lastPathNode = pathNodes.length > 0 && pathNodes[pathNodes.length - 1];
626
+ // check the last path node to match '***'
627
+ if (lastPathNode === ".**") {
628
+ const curNodes = pathNodes.slice(0, pathNodes.length - 1);
629
+ splitUrls.push({ method, pathname, nodes: [...curNodes, ""], params });
630
+ splitUrls.push({ method, pathname, nodes: [...curNodes, "**"], params });
631
+ }
632
+ else if (lastPathNode === ".*") {
633
+ const curNodes = pathNodes.splice(0, pathNodes.length - 1);
634
+ splitUrls.push({ method, pathname, nodes: [...curNodes, ""], params });
635
+ splitUrls.push({ method, pathname, nodes: [...curNodes, "*"], params });
636
+ }
637
+ else {
638
+ // process the path nodes
639
+ for (let index = 0; index < pathNodes.length; index++) {
640
+ const pathNode = pathNodes[index];
641
+ if (pathNode === ".**" && index < pathNodes.length - 1) {
642
+ throw new Error("Super-Glob not at the end of pathname");
643
+ }
644
+ if (pathNode.startsWith(":")) {
645
+ const name = pathNode.substring(1);
646
+ params.set(index, { name, index });
647
+ nodes.push("*");
648
+ }
649
+ else {
650
+ nodes.push(pathNode);
651
+ }
652
+ }
653
+ splitUrls.push({ method, pathname, nodes, params });
654
+ }
655
+ }
656
+ return splitUrls;
657
+ }
658
+ exports.default = Router;
659
+ //# sourceMappingURL=router.js.map