@adonisjs/http-server 8.0.0-next.9 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/build/chunk-B2GA45YG.js +35 -0
  2. package/build/define_config-t7GL92UR.js +5470 -0
  3. package/build/factories/http_context.d.ts +10 -4
  4. package/build/factories/main.d.ts +2 -2
  5. package/build/factories/main.js +328 -345
  6. package/build/factories/request.d.ts +4 -4
  7. package/build/factories/response.d.ts +4 -4
  8. package/build/factories/router.d.ts +1 -1
  9. package/build/factories/server_factory.d.ts +1 -1
  10. package/build/factories/url_builder_factory.d.ts +3 -3
  11. package/build/helpers-XD4_pfkD.js +108 -0
  12. package/build/helpers-aa47NQc6.js +1426 -0
  13. package/build/index.d.ts +2 -2
  14. package/build/index.js +330 -373
  15. package/build/src/client/helpers.d.ts +37 -0
  16. package/build/src/client/types.d.ts +194 -0
  17. package/build/src/client/url_builder.d.ts +15 -0
  18. package/build/src/client/url_builder.js +131 -0
  19. package/build/src/cookies/client.d.ts +28 -5
  20. package/build/src/cookies/drivers/encrypted.d.ts +1 -1
  21. package/build/src/cookies/drivers/signed.d.ts +1 -1
  22. package/build/src/cookies/parser.d.ts +1 -1
  23. package/build/src/cookies/serializer.d.ts +2 -2
  24. package/build/src/debug.d.ts +14 -1
  25. package/build/src/define_config.d.ts +19 -1
  26. package/build/src/define_middleware.d.ts +19 -3
  27. package/build/src/errors.d.ts +60 -5
  28. package/build/src/exception_handler.d.ts +28 -8
  29. package/build/src/helpers.d.ts +5 -17
  30. package/build/src/helpers.js +4 -24
  31. package/build/src/http_context/main.d.ts +67 -17
  32. package/build/src/qs.d.ts +17 -3
  33. package/build/src/redirect.d.ts +22 -3
  34. package/build/src/request.d.ts +12 -5
  35. package/build/src/response.d.ts +5 -5
  36. package/build/src/response_status.d.ts +14 -0
  37. package/build/src/router/main.d.ts +6 -2
  38. package/build/src/router/route.d.ts +130 -32
  39. package/build/src/router/signed_url_builder.d.ts +1 -1
  40. package/build/src/server/main.d.ts +6 -6
  41. package/build/src/types/main.js +2 -0
  42. package/build/src/types/request.d.ts +2 -1
  43. package/build/src/types/response.d.ts +6 -1
  44. package/build/src/types/route.d.ts +3 -27
  45. package/build/src/types/url_builder.d.ts +2 -140
  46. package/build/src/utils.d.ts +71 -6
  47. package/package.json +57 -48
  48. package/build/chunk-CVZAIRWJ.js +0 -1222
  49. package/build/chunk-JD6QW4NQ.js +0 -4428
  50. package/build/src/router/url_builder.d.ts +0 -9
@@ -0,0 +1,1426 @@
1
+ import { n as __exportAll } from "./chunk-B2GA45YG.js";
2
+ import { t as createURL } from "./helpers-XD4_pfkD.js";
3
+ import { InvalidArgumentsException, RuntimeException } from "@poppinss/utils/exception";
4
+ import { debuglog } from "node:util";
5
+ import { serialize } from "cookie-es";
6
+ import matchit from "@poppinss/matchit";
7
+ import string from "@poppinss/utils/string";
8
+ import { moduleCaller, moduleImporter, parseBindingReference } from "@adonisjs/fold";
9
+ import Cache from "tmp-cache";
10
+ import Macroable from "@poppinss/macroable";
11
+ import is from "@sindresorhus/is";
12
+ import Middleware from "@poppinss/middleware";
13
+ import diagnostics_channel from "node:diagnostics_channel";
14
+ import StringBuilder from "@poppinss/utils/string_builder";
15
+ import encodeUrl from "encodeurl";
16
+ import mime from "mime-types";
17
+ //#region src/debug.ts
18
+ /**
19
+ * Debug logger instance for the AdonisJS HTTP server package.
20
+ *
21
+ * This debug logger can be enabled by setting the NODE_DEBUG environment variable
22
+ * to include 'adonisjs:http'. When enabled, it will output detailed debugging
23
+ * information about HTTP server operations including route matching, middleware
24
+ * execution, and response generation.
25
+ *
26
+ * @example
27
+ * ```bash
28
+ * NODE_DEBUG=adonisjs:http node ace serve
29
+ * ```
30
+ */
31
+ var debug_default = debuglog("adonisjs:http");
32
+ //#endregion
33
+ //#region src/router/factories/use_return_value.ts
34
+ /**
35
+ * Check if the value can be used to write the response body. Returns
36
+ * false when the response body has already been set
37
+ * @param value - The value to check
38
+ * @param ctx - The HTTP context instance
39
+ * @returns True if value can be used for response body
40
+ */
41
+ function canWriteResponseBody(value, ctx) {
42
+ return value !== void 0 && !ctx.response.hasLazyBody && value !== ctx.response;
43
+ }
44
+ /**
45
+ * A factory function that uses the return value of the request
46
+ * pipeline as the response
47
+ * @param ctx - The HTTP context instance
48
+ * @returns Function that handles return values
49
+ */
50
+ function useReturnValue(ctx) {
51
+ return function(value) {
52
+ if (canWriteResponseBody(value, ctx)) ctx.response.send(value);
53
+ };
54
+ }
55
+ //#endregion
56
+ //#region src/tracing_channels.ts
57
+ var tracing_channels_exports = /* @__PURE__ */ __exportAll({
58
+ httpExceptionHandler: () => httpExceptionHandler,
59
+ httpMiddleware: () => httpMiddleware,
60
+ httpRequest: () => httpRequest,
61
+ httpResponseSerializer: () => httpResponseSerializer,
62
+ httpRouteHandler: () => httpRouteHandler
63
+ });
64
+ /**
65
+ * Traces every HTTP request handled by the {@link Server} class.
66
+ */
67
+ const httpRequest = diagnostics_channel.tracingChannel("adonisjs.http.request");
68
+ /**
69
+ * Traces middleware executed during the HTTP request
70
+ */
71
+ const httpMiddleware = diagnostics_channel.tracingChannel("adonisjs.http.middleware");
72
+ /**
73
+ * Traces the exception handler that converts errors into HTTP responses
74
+ */
75
+ const httpExceptionHandler = diagnostics_channel.tracingChannel("adonisjs.http.exception.handler");
76
+ /**
77
+ * Traces route handler executed during the HTTP request
78
+ */
79
+ const httpRouteHandler = diagnostics_channel.tracingChannel("adonisjs.http.route.handler");
80
+ /**
81
+ * Traces non-stream and non-file download responses written by the AdonisJS
82
+ * response class
83
+ */
84
+ const httpResponseSerializer = diagnostics_channel.tracingChannel("adonisjs.http.response.serializer");
85
+ //#endregion
86
+ //#region src/router/executor.ts
87
+ /**
88
+ * Executor to execute the route middleware pipeline the route
89
+ * handler
90
+ * @param route - The route JSON object containing route information
91
+ * @param resolver - Container resolver for dependency injection
92
+ * @param ctx - The HTTP context instance
93
+ * @param errorResponder - Error handler function for handling errors
94
+ */
95
+ function execute(route, resolver, ctx, errorResponder) {
96
+ return route.middleware.runner().errorHandler((error) => errorResponder(error, ctx)).finalHandler(() => {
97
+ if (typeof route.handler === "function") return httpRouteHandler.tracePromise(($ctx) => Promise.resolve(route.handler($ctx)), httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, ctx).then(useReturnValue(ctx));
98
+ return httpRouteHandler.tracePromise(route.handler.handle, httpRouteHandler.hasSubscribers ? { route } : void 0, void 0, resolver, ctx).then(useReturnValue(ctx));
99
+ }).run(async (middleware, next) => {
100
+ if (typeof middleware === "function") return httpMiddleware.tracePromise(middleware, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, ctx, next);
101
+ return httpMiddleware.tracePromise(middleware.handle, httpMiddleware.hasSubscribers ? { middleware } : void 0, void 0, resolver, ctx, next, middleware.args);
102
+ });
103
+ }
104
+ //#endregion
105
+ //#region src/router/route.ts
106
+ /**
107
+ * The Route class provides a fluent API for constructing and configuring HTTP routes.
108
+ *
109
+ * Routes define how HTTP requests are handled by mapping URL patterns and HTTP methods
110
+ * to controller actions or inline handlers. This class supports middleware application,
111
+ * parameter validation, naming, and various other route-specific configurations.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const route = new Route(app, middleware, {
116
+ * pattern: '/users/:id',
117
+ * methods: ['GET'],
118
+ * handler: 'UsersController.show'
119
+ * })
120
+ *
121
+ * route
122
+ * .where('id', /^[0-9]+$/)
123
+ * .middleware(['auth'])
124
+ * .as('users.show')
125
+ * ```
126
+ */
127
+ var Route = class extends Macroable {
128
+ /**
129
+ * The URL pattern for this route. May contain dynamic parameters
130
+ * prefixed with a colon (e.g., '/users/:id').
131
+ */
132
+ #pattern;
133
+ /**
134
+ * Array of HTTP methods this route responds to (e.g., ['GET', 'POST']).
135
+ */
136
+ #methods;
137
+ /**
138
+ * A unique identifier for the route, used for URL generation and
139
+ * route referencing.
140
+ */
141
+ #name;
142
+ /**
143
+ * Flag indicating whether the route should be excluded from registration
144
+ * in the route store. This must be set before calling Router.commit().
145
+ */
146
+ #isDeleted = false;
147
+ /**
148
+ * The handler function or controller method that processes requests
149
+ * matching this route.
150
+ */
151
+ #handler;
152
+ /**
153
+ * Route parameter matchers inherited from the global router configuration.
154
+ * These are applied to all routes unless overridden locally.
155
+ */
156
+ #globalMatchers;
157
+ /**
158
+ * Reference to the AdonisJS application instance, used for module
159
+ * resolution and dependency injection.
160
+ */
161
+ #app;
162
+ /**
163
+ * Global middleware registered on the router that applies to this route.
164
+ */
165
+ #routerMiddleware;
166
+ /**
167
+ * The domain this route belongs to. Defaults to 'root' when no specific
168
+ * domain is configured.
169
+ */
170
+ #routeDomain = "root";
171
+ /**
172
+ * Route-specific parameter matchers that validate dynamic segments.
173
+ * Populated via the where() method and takes precedence over global matchers.
174
+ */
175
+ #matchers = {};
176
+ /**
177
+ * Stack of URL prefixes applied to this route, typically inherited from
178
+ * route groups. Prefixes are applied in reverse order during pattern computation.
179
+ */
180
+ #prefixes = [];
181
+ /**
182
+ * Multi-dimensional array of middleware, where each nested array represents
183
+ * a layer in the middleware stack. This structure maintains the order of
184
+ * middleware application from groups and direct assignments.
185
+ */
186
+ #middleware = [];
187
+ /**
188
+ * Creates a new Route instance
189
+ * @param app - The AdonisJS application instance
190
+ * @param routerMiddleware - Array of global middleware registered on the router
191
+ * @param options - Configuration options for the route
192
+ */
193
+ constructor(app, routerMiddleware, options) {
194
+ super();
195
+ this.#app = app;
196
+ this.#routerMiddleware = routerMiddleware;
197
+ this.#pattern = options.pattern;
198
+ this.#methods = options.methods;
199
+ this.#globalMatchers = options.globalMatchers;
200
+ const { handler, routeName } = this.#resolveRouteHandle(options.handler);
201
+ this.#handler = handler;
202
+ this.#name = routeName;
203
+ }
204
+ /**
205
+ * Resolves the route handler from various input formats into a normalized
206
+ * StoreRouteHandler object. Supports string references, inline functions,
207
+ * class constructors, and lazy imports.
208
+ *
209
+ * @param handler - The handler in one of the supported formats:
210
+ * - String: 'Controller.method' or 'Controller' (defaults to 'handle')
211
+ * - Function: Inline route handler function
212
+ * - Tuple: [Controller class or lazy import, optional method name]
213
+ */
214
+ #resolveRouteHandle(handler) {
215
+ /**
216
+ * Convert magic string to handle method call
217
+ */
218
+ if (typeof handler === "string") {
219
+ const parts = handler.split(".");
220
+ const method = parts.length === 1 ? "handle" : parts.pop();
221
+ const moduleRefId = parts.join(".");
222
+ const controllerName = new StringBuilder(moduleRefId.split("/").pop()).removeSuffix("controller").snakeCase();
223
+ return {
224
+ handler: {
225
+ method,
226
+ reference: handler,
227
+ importExpression: moduleRefId,
228
+ ...moduleImporter(() => this.#app.import(moduleRefId), method).toHandleMethod(),
229
+ name: handler
230
+ },
231
+ routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
232
+ };
233
+ }
234
+ /**
235
+ * Using a lazily imported controller
236
+ */
237
+ if (Array.isArray(handler)) {
238
+ const controller = handler[0];
239
+ const method = handler[1] ?? "handle";
240
+ /**
241
+ * The first item of the tuple is a class constructor
242
+ */
243
+ if (is.class(controller)) {
244
+ const controllerName = new StringBuilder(controller.name).removeSuffix("controller").snakeCase();
245
+ return {
246
+ handler: {
247
+ method,
248
+ reference: handler,
249
+ importExpression: null,
250
+ ...moduleCaller(controller, method).toHandleMethod()
251
+ },
252
+ routeName: method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}`
253
+ };
254
+ }
255
+ const controllerName = controller.name ? new StringBuilder(controller.name).removeSuffix("controller").snakeCase() : void 0;
256
+ /**
257
+ * The first item of the tuple is a function that lazily
258
+ * loads the controller
259
+ */
260
+ return {
261
+ handler: {
262
+ method,
263
+ reference: handler,
264
+ importExpression: String(controller),
265
+ ...moduleImporter(controller, method).toHandleMethod()
266
+ },
267
+ routeName: controllerName ? method === "handle" ? controllerName.toString() : `${controllerName}.${string.snakeCase(method)}` : void 0
268
+ };
269
+ }
270
+ return { handler };
271
+ }
272
+ /**
273
+ * Merges global and route-specific parameter matchers into a single object.
274
+ * Local matchers take precedence over global matchers when conflicts occur.
275
+ */
276
+ #getMatchers() {
277
+ return {
278
+ ...this.#globalMatchers,
279
+ ...this.#matchers
280
+ };
281
+ }
282
+ /**
283
+ * Computes the final route pattern by applying all prefixes from route groups.
284
+ * Prefixes are applied in reverse order (innermost group first) and normalized
285
+ * to remove leading/trailing slashes.
286
+ */
287
+ #computePattern() {
288
+ const pattern = dropSlash(this.#pattern);
289
+ const prefix = this.#prefixes.slice().reverse().map((one) => dropSlash(one)).join("");
290
+ return prefix ? `${prefix}${pattern === "/" ? "" : pattern}` : pattern;
291
+ }
292
+ /**
293
+ * Returns the route's handler configuration object.
294
+ */
295
+ getHandler() {
296
+ return this.#handler;
297
+ }
298
+ /**
299
+ * Defines a validation matcher for a route parameter. Route-level matchers
300
+ * take precedence over group-level matchers to ensure routes can override
301
+ * group constraints.
302
+ *
303
+ * @param param - The name of the route parameter to validate
304
+ * @param matcher - The validation pattern as a string, RegExp, or RouteMatcher object
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * // Validate that 'id' is numeric
309
+ * route.where('id', /^[0-9]+$/)
310
+ *
311
+ * // Using a string pattern
312
+ * route.where('slug', '[a-z0-9-]+')
313
+ *
314
+ * // Route matcher takes precedence over group matcher
315
+ * Route.group(() => {
316
+ * Route.get('/:id', 'handler').where('id', /^[0-9]$/)
317
+ * }).where('id', /[^a-z$]/)
318
+ * // The route's /^[0-9]$/ wins over the group's matcher
319
+ * ```
320
+ */
321
+ where(param, matcher) {
322
+ if (this.#matchers[param]) return this;
323
+ if (typeof matcher === "string") this.#matchers[param] = { match: new RegExp(matcher) };
324
+ else if (is.regExp(matcher)) this.#matchers[param] = { match: matcher };
325
+ else this.#matchers[param] = matcher;
326
+ return this;
327
+ }
328
+ /**
329
+ * Adds a URL prefix to the route pattern. Multiple calls stack prefixes
330
+ * which are applied in reverse order during pattern computation.
331
+ *
332
+ * @param prefix - The URL prefix to prepend to the route pattern
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * route.prefix('/api').prefix('/v1')
337
+ * // Results in pattern: /v1/api/users (for original pattern /users)
338
+ * ```
339
+ */
340
+ prefix(prefix) {
341
+ this.#prefixes.push(prefix);
342
+ return this;
343
+ }
344
+ /**
345
+ * Assigns a custom domain to the route. By default, routes belong to the
346
+ * 'root' domain. Once set, the domain is not overwritten unless the
347
+ * overwrite flag is true.
348
+ *
349
+ * @param domain - The domain identifier for this route
350
+ * @param overwrite - Whether to overwrite an existing non-root domain
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * route.domain('api.example.com')
355
+ *
356
+ * // Overwrite existing domain
357
+ * route.domain('new.example.com', true)
358
+ * ```
359
+ */
360
+ domain(domain, overwrite = false) {
361
+ if (this.#routeDomain === "root" || overwrite) this.#routeDomain = domain;
362
+ return this;
363
+ }
364
+ /**
365
+ * Registers one or more middleware to execute before the route handler.
366
+ * Middleware can be inline functions or named middleware references registered
367
+ * with the router's middleware store.
368
+ *
369
+ * @param middleware - Single middleware or array of middleware to apply
370
+ *
371
+ * @example
372
+ * ```ts
373
+ * // Single middleware
374
+ * route.use(async (ctx, next) => {
375
+ * console.log('Before handler')
376
+ * await next()
377
+ * })
378
+ *
379
+ * // Multiple middleware
380
+ * route.use(['auth', 'admin'])
381
+ * ```
382
+ */
383
+ use(middleware) {
384
+ this.#middleware.push(Array.isArray(middleware) ? middleware : [middleware]);
385
+ return this;
386
+ }
387
+ /**
388
+ * Alias for the {@link Route.use} method.
389
+ *
390
+ * @param middleware - Single middleware or array of middleware to apply
391
+ */
392
+ middleware(middleware) {
393
+ return this.use(middleware);
394
+ }
395
+ /**
396
+ * Assigns a unique name to the route for use in URL generation and route
397
+ * referencing. Assigning a new name replaces any existing name unless
398
+ * prepend is true.
399
+ *
400
+ * @param name - The route name to assign
401
+ * @param prepend - If true, prepends the name to the existing name with a dot separator
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * // Set route name
406
+ * route.as('users.show')
407
+ *
408
+ * // Prepend to existing name (typically used by route groups)
409
+ * route.as('admin', true) // Results in 'admin.users.show'
410
+ * ```
411
+ */
412
+ as(name, prepend = false) {
413
+ if (prepend) {
414
+ if (!this.#name) throw new RuntimeException(`Routes inside a group must have names before calling "router.group.as"`);
415
+ this.#name = `${name}.${this.#name}`;
416
+ return this;
417
+ }
418
+ this.#name = name;
419
+ return this;
420
+ }
421
+ /**
422
+ * Checks whether the route has been marked for deletion. Deleted routes
423
+ * are excluded from the route store during registration.
424
+ */
425
+ isDeleted() {
426
+ return this.#isDeleted;
427
+ }
428
+ /**
429
+ * Marks the route for deletion. Deleted routes will not be registered
430
+ * with the route store when Router.commit() is called.
431
+ *
432
+ * @example
433
+ * ```ts
434
+ * const route = Route.get('/admin', 'handler')
435
+ * route.markAsDeleted()
436
+ * // This route will not be registered
437
+ * ```
438
+ */
439
+ markAsDeleted() {
440
+ this.#isDeleted = true;
441
+ }
442
+ /**
443
+ * Returns the unique name assigned to the route, if any.
444
+ */
445
+ getName() {
446
+ return this.#name;
447
+ }
448
+ /**
449
+ * Returns the route's URL pattern with dynamic parameters.
450
+ */
451
+ getPattern() {
452
+ return this.#pattern;
453
+ }
454
+ /**
455
+ * Updates the route's URL pattern.
456
+ *
457
+ * @param pattern - The new URL pattern to assign to the route
458
+ *
459
+ * @example
460
+ * ```ts
461
+ * route.setPattern('/users/:id/posts/:postId')
462
+ * ```
463
+ */
464
+ setPattern(pattern) {
465
+ this.#pattern = pattern;
466
+ return this;
467
+ }
468
+ /**
469
+ * Returns the multi-dimensional middleware stack registered on this route.
470
+ * The returned value is shared by reference, not a copy.
471
+ */
472
+ getMiddleware() {
473
+ return this.#middleware;
474
+ }
475
+ /**
476
+ * Constructs a frozen Middleware instance for storage. This combines global
477
+ * router middleware with route-specific middleware in the correct execution order.
478
+ * The middleware is frozen to prevent modifications after route registration.
479
+ */
480
+ #getMiddlewareForStore() {
481
+ const middleware = new Middleware();
482
+ this.#routerMiddleware.forEach((one) => {
483
+ debug_default("adding global middleware to route %s, %O", this.#pattern, one);
484
+ middleware.add(one);
485
+ });
486
+ this.#middleware.flat().forEach((one) => {
487
+ debug_default("adding named middleware to route %s, %O", this.#pattern, one);
488
+ middleware.add(one);
489
+ });
490
+ middleware.freeze();
491
+ return middleware;
492
+ }
493
+ /**
494
+ * Serializes the route into a JSON representation suitable for storage and
495
+ * execution. This includes the computed pattern with prefixes, merged matchers,
496
+ * parsed route tokens, and frozen middleware stack.
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * const json = route.toJSON()
501
+ * console.log(json.pattern) // '/api/users/:id'
502
+ * console.log(json.methods) // ['GET']
503
+ * console.log(json.name) // 'users.show'
504
+ * ```
505
+ */
506
+ toJSON() {
507
+ const pattern = this.#computePattern();
508
+ const matchers = this.#getMatchers();
509
+ return {
510
+ domain: this.#routeDomain,
511
+ pattern,
512
+ matchers,
513
+ tokens: parseRoute(pattern, matchers),
514
+ meta: {},
515
+ name: this.#name,
516
+ handler: this.#handler,
517
+ methods: this.#methods,
518
+ middleware: this.#getMiddlewareForStore(),
519
+ execute
520
+ };
521
+ }
522
+ };
523
+ //#endregion
524
+ //#region src/router/brisk.ts
525
+ /**
526
+ * Brisk routes exposes the API to configure the route handler by chaining
527
+ * one of the pre-defined methods.
528
+ *
529
+ * For example: Instead of defining the redirect logic as a callback, one can
530
+ * chain the `.redirect` method.
531
+ *
532
+ * Brisk routes are always registered under the `GET` HTTP method.
533
+ */
534
+ var BriskRoute = class extends Macroable {
535
+ /**
536
+ * Route pattern
537
+ */
538
+ #pattern;
539
+ /**
540
+ * Matchers inherited from the router
541
+ */
542
+ #globalMatchers;
543
+ /**
544
+ * Reference to the AdonisJS application
545
+ */
546
+ #app;
547
+ /**
548
+ * Middleware registered on the router
549
+ */
550
+ #routerMiddleware;
551
+ /**
552
+ * Reference to route instance. Set after `setHandler` is called
553
+ */
554
+ route = null;
555
+ /**
556
+ * Creates a new BriskRoute instance
557
+ * @param app - The AdonisJS application instance
558
+ * @param routerMiddleware - Array of global middleware registered on the router
559
+ * @param options - Configuration options for the brisk route
560
+ */
561
+ constructor(app, routerMiddleware, options) {
562
+ super();
563
+ this.#app = app;
564
+ this.#routerMiddleware = routerMiddleware;
565
+ this.#pattern = options.pattern;
566
+ this.#globalMatchers = options.globalMatchers;
567
+ }
568
+ /**
569
+ * Set handler for the brisk route
570
+ * @param handler - The route handler function
571
+ * @returns The created route instance
572
+ */
573
+ setHandler(handler) {
574
+ this.route = new Route(this.#app, this.#routerMiddleware, {
575
+ pattern: this.#pattern,
576
+ globalMatchers: this.#globalMatchers,
577
+ methods: ["GET", "HEAD"],
578
+ handler
579
+ });
580
+ return this.route;
581
+ }
582
+ /**
583
+ * Redirects to a given route. Params from the original request will
584
+ * be used when no custom params are defined.
585
+ * @param args - Route identifier, parameters, and options for building the redirect URL
586
+ * @returns The created route instance
587
+ */
588
+ redirect(...args) {
589
+ const [identifier, params, options] = args;
590
+ function redirectsToRoute(ctx) {
591
+ const redirector = ctx.response.redirect();
592
+ if (options?.status) redirector.status(options.status);
593
+ return redirector.toRoute(identifier, params || ctx.params, options);
594
+ }
595
+ Object.defineProperty(redirectsToRoute, "listArgs", {
596
+ value: identifier,
597
+ writable: false
598
+ });
599
+ return this.setHandler(redirectsToRoute);
600
+ }
601
+ /**
602
+ * Redirect request to a fixed URL
603
+ * @param url - The URL to redirect to
604
+ * @param options - Optional redirect options including HTTP status code
605
+ * @returns The created route instance
606
+ */
607
+ redirectToPath(url, options) {
608
+ function redirectsToPath(ctx) {
609
+ const redirector = ctx.response.redirect();
610
+ if (options?.status) redirector.status(options.status);
611
+ return redirector.toPath(url);
612
+ }
613
+ Object.defineProperty(redirectsToPath, "listArgs", {
614
+ value: url,
615
+ writable: false
616
+ });
617
+ return this.setHandler(redirectsToPath);
618
+ }
619
+ };
620
+ //#endregion
621
+ //#region src/router/resource.ts
622
+ /**
623
+ * Route resource exposes the API to register multiple routes for a resource.
624
+ */
625
+ var RouteResource = class extends Macroable {
626
+ /**
627
+ * Resource identifier. Nested resources are separated
628
+ * with a dot notation
629
+ */
630
+ #resource;
631
+ /**
632
+ * The controller to handle resource routing requests
633
+ */
634
+ #controller;
635
+ /**
636
+ * Is it a shallow resource? Shallow resources URLs do not have parent
637
+ * resource name and id once they can be identified with the id.
638
+ */
639
+ #shallow = false;
640
+ /**
641
+ * Matchers inherited from the router
642
+ */
643
+ #globalMatchers;
644
+ /**
645
+ * Reference to the AdonisJS application
646
+ */
647
+ #app;
648
+ /**
649
+ * Middleware registered on the router
650
+ */
651
+ #routerMiddleware;
652
+ /**
653
+ * Parameter names for the resources. Defaults to `id` for
654
+ * a singular resource and `resource_id` for nested
655
+ * resources.
656
+ */
657
+ #params = {};
658
+ /**
659
+ * Base name for the routes. We suffix action names
660
+ * on top of the base name
661
+ */
662
+ #routesBaseName;
663
+ /**
664
+ * A collection of routes instances that belongs to this resource
665
+ */
666
+ routes = [];
667
+ /**
668
+ * Creates a new RouteResource instance
669
+ * @param app - The AdonisJS application instance
670
+ * @param routerMiddleware - Array of global middleware registered on the router
671
+ * @param options - Configuration options for the route resource
672
+ */
673
+ constructor(app, routerMiddleware, options) {
674
+ super();
675
+ this.#validateResourceName(options.resource);
676
+ this.#app = app;
677
+ this.#shallow = options.shallow;
678
+ this.#routerMiddleware = routerMiddleware;
679
+ this.#controller = options.controller;
680
+ this.#globalMatchers = options.globalMatchers;
681
+ this.#resource = this.#normalizeResourceName(options.resource);
682
+ this.#routesBaseName = this.#getRoutesBaseName();
683
+ this.#buildRoutes();
684
+ }
685
+ /**
686
+ * Normalizes the resource name to dropping leading and trailing
687
+ * slashes.
688
+ */
689
+ #normalizeResourceName(resource) {
690
+ return resource.replace(/^\//, "").replace(/\/$/, "");
691
+ }
692
+ /**
693
+ * Ensure resource name is not an empty string
694
+ */
695
+ #validateResourceName(resource) {
696
+ if (!resource || resource === "/") throw new RuntimeException(`Invalid resource name "${resource}"`);
697
+ }
698
+ /**
699
+ * Converting segments of a resource to snake case to
700
+ * make the route name.
701
+ */
702
+ #getRoutesBaseName() {
703
+ return this.#resource.split(".").map((token) => string.snakeCase(token)).join(".");
704
+ }
705
+ /**
706
+ * Create a new route for the given pattern, methods and controller action
707
+ */
708
+ #createRoute(pattern, methods, action) {
709
+ const route = new Route(this.#app, this.#routerMiddleware, {
710
+ pattern,
711
+ methods,
712
+ handler: typeof this.#controller === "string" ? `${this.#controller}.${action}` : [this.#controller, action],
713
+ globalMatchers: this.#globalMatchers
714
+ });
715
+ route.as(`${this.#routesBaseName}.${action}`);
716
+ this.routes.push(route);
717
+ }
718
+ /**
719
+ * Returns the `resource_id` name for a given resource. The
720
+ * resource name is converted to singular form and
721
+ * transformed to snake case.
722
+ *
723
+ * photos becomes photo_id
724
+ * users becomes user_id
725
+ */
726
+ #getResourceId(resource) {
727
+ return `${string.snakeCase(string.singular(resource))}_id`;
728
+ }
729
+ /**
730
+ * Build routes for the given resource
731
+ */
732
+ #buildRoutes() {
733
+ const resources = this.#resource.split(".");
734
+ const mainResource = resources.pop();
735
+ this.#params[mainResource] = ":id";
736
+ const baseURI = `${resources.map((resource) => {
737
+ const paramName = `:${this.#getResourceId(resource)}`;
738
+ this.#params[resource] = paramName;
739
+ return `${resource}/${paramName}`;
740
+ }).join("/")}/${mainResource}`;
741
+ this.#createRoute(baseURI, ["GET", "HEAD"], "index");
742
+ this.#createRoute(`${baseURI}/create`, ["GET", "HEAD"], "create");
743
+ this.#createRoute(baseURI, ["POST"], "store");
744
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["GET", "HEAD"], "show");
745
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id/edit`, ["GET", "HEAD"], "edit");
746
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["PUT", "PATCH"], "update");
747
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["DELETE"], "destroy");
748
+ }
749
+ /**
750
+ * Filter the routes based on their partial names
751
+ */
752
+ #filter(names, inverse) {
753
+ const actions = Array.isArray(names) ? names : [names];
754
+ return this.routes.filter((route) => {
755
+ const match = actions.find((name) => route.getName().endsWith(name));
756
+ return inverse ? !match : match;
757
+ });
758
+ }
759
+ /**
760
+ * Register only given routes and remove others
761
+ * @param names - Array of action names to keep
762
+ * @returns Current RouteResource instance with filtered actions
763
+ */
764
+ only(names) {
765
+ this.#filter(names, true).forEach((route) => route.markAsDeleted());
766
+ return this;
767
+ }
768
+ /**
769
+ * Register all routes, except the one's defined
770
+ * @param names - Array of action names to exclude
771
+ * @returns Current RouteResource instance with filtered actions
772
+ */
773
+ except(names) {
774
+ this.#filter(names, false).forEach((route) => route.markAsDeleted());
775
+ return this;
776
+ }
777
+ /**
778
+ * Register api only routes. The `create` and `edit` routes, which
779
+ * are meant to show forms will not be registered
780
+ * @returns Current RouteResource instance without create and edit actions
781
+ */
782
+ apiOnly() {
783
+ return this.except(["create", "edit"]);
784
+ }
785
+ /**
786
+ * Define matcher for params inside the resource
787
+ * @param key - The parameter name to match
788
+ * @param matcher - The matcher pattern (RegExp, string, or RouteMatcher)
789
+ * @returns Current RouteResource instance for method chaining
790
+ */
791
+ where(key, matcher) {
792
+ this.routes.forEach((route) => {
793
+ route.where(key, matcher);
794
+ });
795
+ return this;
796
+ }
797
+ tap(actions, callback) {
798
+ if (typeof actions === "function") {
799
+ this.routes.forEach((route) => {
800
+ if (!route.isDeleted()) actions(route);
801
+ });
802
+ return this;
803
+ }
804
+ this.#filter(actions, false).forEach((route) => {
805
+ if (!route.isDeleted()) callback(route);
806
+ });
807
+ return this;
808
+ }
809
+ /**
810
+ * Set the param name for a given resource
811
+ * @param resources - Object mapping resource names to parameter names
812
+ * @returns Current RouteResource instance for method chaining
813
+ */
814
+ params(resources) {
815
+ Object.keys(resources).forEach((resource) => {
816
+ const param = resources[resource];
817
+ const existingParam = this.#params[resource];
818
+ this.#params[resource] = `:${param}`;
819
+ this.routes.forEach((route) => {
820
+ route.setPattern(route.getPattern().replace(`${resource}/${existingParam}`, `${resource}/:${param}`));
821
+ });
822
+ });
823
+ return this;
824
+ }
825
+ /**
826
+ * Define one or more middleware on the routes created by
827
+ * the resource.
828
+ *
829
+ * Calling this method multiple times will append middleware
830
+ * to existing list.
831
+ * @param actions - Action name(s) or '*' for all actions
832
+ * @param middleware - Middleware function(s) to apply
833
+ * @returns Current RouteResource instance for method chaining
834
+ */
835
+ use(actions, middleware) {
836
+ if (actions === "*") this.tap((route) => route.use(middleware));
837
+ else this.tap(actions, (route) => route.use(middleware));
838
+ return this;
839
+ }
840
+ /**
841
+ * Alias for {@link RouteResource.use}
842
+ * @param actions - Action name(s) or '*' for all actions
843
+ * @param middleware - Middleware function(s) to apply
844
+ * @returns Current RouteResource instance for method chaining
845
+ */
846
+ middleware(actions, middleware) {
847
+ return this.use(actions, middleware);
848
+ }
849
+ /**
850
+ * Prepend name to all the routes
851
+ * @param name - The name to prepend to all route names
852
+ * @param normalizeName - Whether to normalize the name to snake_case
853
+ * @returns Current RouteResource instance for method chaining
854
+ */
855
+ as(name, normalizeName = true) {
856
+ name = normalizeName ? string.snakeCase(name) : name;
857
+ this.routes.forEach((route) => {
858
+ route.as(route.getName().replace(this.#routesBaseName, name), false);
859
+ });
860
+ this.#routesBaseName = name;
861
+ return this;
862
+ }
863
+ };
864
+ //#endregion
865
+ //#region src/router/group.ts
866
+ /**
867
+ * Group class exposes the API to take action on a group of routes.
868
+ * The group routes must be pre-defined using the constructor.
869
+ */
870
+ var RouteGroup = class RouteGroup extends Macroable {
871
+ /**
872
+ * Array of middleware registered on the group.
873
+ */
874
+ #middleware = [];
875
+ /**
876
+ * Creates a new RouteGroup instance
877
+ * @param routes - Array of routes that belong to this group
878
+ */
879
+ constructor(routes) {
880
+ super();
881
+ this.routes = routes;
882
+ }
883
+ /**
884
+ * Shares midldeware stack with the routes. The method is invoked recursively
885
+ * to only register middleware with the route class and not with the
886
+ * resource or the child group
887
+ */
888
+ #shareMiddlewareStackWithRoutes(route) {
889
+ if (route instanceof RouteGroup) {
890
+ route.routes.forEach((child) => this.#shareMiddlewareStackWithRoutes(child));
891
+ return;
892
+ }
893
+ if (route instanceof RouteResource) {
894
+ route.routes.forEach((child) => child.getMiddleware().unshift(this.#middleware));
895
+ return;
896
+ }
897
+ if (route instanceof BriskRoute) {
898
+ route.route.getMiddleware().unshift(this.#middleware);
899
+ return;
900
+ }
901
+ route.getMiddleware().unshift(this.#middleware);
902
+ }
903
+ /**
904
+ * Updates the route name. The method is invoked recursively to only update
905
+ * the name with the route class and not with the resource or the child
906
+ * group.
907
+ */
908
+ #updateRouteName(route, name) {
909
+ if (route instanceof RouteGroup) {
910
+ route.routes.forEach((child) => this.#updateRouteName(child, name));
911
+ return;
912
+ }
913
+ if (route instanceof RouteResource) {
914
+ route.routes.forEach((child) => child.as(name, true));
915
+ return;
916
+ }
917
+ if (route instanceof BriskRoute) {
918
+ route.route.as(name, true);
919
+ return;
920
+ }
921
+ route.as(name, true);
922
+ }
923
+ /**
924
+ * Sets prefix on the route. The method is invoked recursively to only set
925
+ * the prefix with the route class and not with the resource or the
926
+ * child group.
927
+ */
928
+ #setRoutePrefix(route, prefix) {
929
+ if (route instanceof RouteGroup) {
930
+ route.routes.forEach((child) => this.#setRoutePrefix(child, prefix));
931
+ return;
932
+ }
933
+ if (route instanceof RouteResource) {
934
+ route.routes.forEach((child) => child.prefix(prefix));
935
+ return;
936
+ }
937
+ if (route instanceof BriskRoute) {
938
+ route.route.prefix(prefix);
939
+ return;
940
+ }
941
+ route.prefix(prefix);
942
+ }
943
+ /**
944
+ * Updates domain on the route. The method is invoked recursively to only update
945
+ * the domain with the route class and not with the resource or the child
946
+ * group.
947
+ */
948
+ #updateRouteDomain(route, domain) {
949
+ if (route instanceof RouteGroup) {
950
+ route.routes.forEach((child) => this.#updateRouteDomain(child, domain));
951
+ return;
952
+ }
953
+ if (route instanceof RouteResource) {
954
+ route.routes.forEach((child) => child.domain(domain));
955
+ return;
956
+ }
957
+ if (route instanceof BriskRoute) {
958
+ route.route.domain(domain, false);
959
+ return;
960
+ }
961
+ route.domain(domain, false);
962
+ }
963
+ /**
964
+ * Updates matchers on the route. The method is invoked recursively to only update
965
+ * the matchers with the route class and not with the resource or the child
966
+ * group.
967
+ */
968
+ #updateRouteMatchers(route, param, matcher) {
969
+ if (route instanceof RouteGroup) {
970
+ route.routes.forEach((child) => this.#updateRouteMatchers(child, param, matcher));
971
+ return;
972
+ }
973
+ if (route instanceof RouteResource) {
974
+ route.routes.forEach((child) => child.where(param, matcher));
975
+ return;
976
+ }
977
+ if (route instanceof BriskRoute) {
978
+ route.route.where(param, matcher);
979
+ return;
980
+ }
981
+ route.where(param, matcher);
982
+ }
983
+ /**
984
+ * Define route param matcher
985
+ *
986
+ * ```ts
987
+ * Route.group(() => {
988
+ * }).where('id', /^[0-9]+/)
989
+ * ```
990
+ * @param param - The parameter name to match
991
+ * @param matcher - The matcher pattern (RegExp, string, or RouteMatcher)
992
+ * @returns Current RouteGroup instance for method chaining
993
+ */
994
+ where(param, matcher) {
995
+ this.routes.forEach((route) => this.#updateRouteMatchers(route, param, matcher));
996
+ return this;
997
+ }
998
+ /**
999
+ * Define prefix all the routes in the group.
1000
+ *
1001
+ * ```ts
1002
+ * Route.group(() => {
1003
+ * }).prefix('v1')
1004
+ * ```
1005
+ * @param prefix - The prefix to add to all routes in the group
1006
+ * @returns Current RouteGroup instance for method chaining
1007
+ */
1008
+ prefix(prefix) {
1009
+ this.routes.forEach((route) => this.#setRoutePrefix(route, prefix));
1010
+ return this;
1011
+ }
1012
+ /**
1013
+ * Define domain for all the routes.
1014
+ *
1015
+ * ```ts
1016
+ * Route.group(() => {
1017
+ * }).domain(':name.adonisjs.com')
1018
+ * ```
1019
+ * @param domain - The domain pattern for all routes in the group
1020
+ * @returns Current RouteGroup instance for method chaining
1021
+ */
1022
+ domain(domain) {
1023
+ this.routes.forEach((route) => this.#updateRouteDomain(route, domain));
1024
+ return this;
1025
+ }
1026
+ /**
1027
+ * Prepend name to the routes name.
1028
+ *
1029
+ * ```ts
1030
+ * Route.group(() => {
1031
+ * }).as('version1')
1032
+ * ```
1033
+ * @param name - The name to prepend to all route names in the group
1034
+ * @returns Current RouteGroup instance for method chaining
1035
+ */
1036
+ as(name) {
1037
+ this.routes.forEach((route) => this.#updateRouteName(route, name));
1038
+ return this;
1039
+ }
1040
+ /**
1041
+ * Prepend an array of middleware to all routes middleware.
1042
+ *
1043
+ * ```ts
1044
+ * Route.group(() => {
1045
+ * }).use(middleware.auth())
1046
+ * ```
1047
+ * @param middleware - Middleware function(s) to apply to all routes in the group
1048
+ * @returns Current RouteGroup instance for method chaining
1049
+ */
1050
+ use(middleware) {
1051
+ /**
1052
+ * Register middleware with children. We share the group middleware
1053
+ * array by reference, therefore have to register it only for the
1054
+ * first time.
1055
+ */
1056
+ if (!this.#middleware.length) this.routes.forEach((route) => this.#shareMiddlewareStackWithRoutes(route));
1057
+ if (Array.isArray(middleware)) for (let one of middleware) this.#middleware.push(one);
1058
+ else this.#middleware.push(middleware);
1059
+ return this;
1060
+ }
1061
+ /**
1062
+ * Alias for {@link RouteGroup.use}
1063
+ * @param middleware - Middleware function(s) to apply to all routes in the group
1064
+ * @returns Current RouteGroup instance for method chaining
1065
+ */
1066
+ middleware(middleware) {
1067
+ return this.use(middleware);
1068
+ }
1069
+ };
1070
+ //#endregion
1071
+ //#region src/utils.ts
1072
+ const proxyCache = new Cache({ max: 200 });
1073
+ /**
1074
+ * Makes input string consistent by having only the starting slash.
1075
+ *
1076
+ * Removes trailing slashes and ensures the path starts with a forward slash,
1077
+ * except for the root path '/' which remains unchanged.
1078
+ *
1079
+ * @param input - The input path string to normalize
1080
+ *
1081
+ * @example
1082
+ * ```ts
1083
+ * dropSlash('/users/') // '/users'
1084
+ * dropSlash('users') // '/users'
1085
+ * dropSlash('/') // '/'
1086
+ * ```
1087
+ */
1088
+ function dropSlash(input) {
1089
+ if (input === "/") return "/";
1090
+ return `/${input.replace(/^\//, "").replace(/\/$/, "")}`;
1091
+ }
1092
+ /**
1093
+ * Returns a flat list of routes from route groups, resources, and brisk routes.
1094
+ *
1095
+ * This function recursively processes route collections, extracting individual routes
1096
+ * from groups and resources while filtering out any deleted routes.
1097
+ *
1098
+ * @param routes - Array containing route groups, individual routes, resources, and brisk routes
1099
+ *
1100
+ * @example
1101
+ * ```ts
1102
+ * const flatRoutes = toRoutesJSON([
1103
+ * routeGroup,
1104
+ * singleRoute,
1105
+ * resourceRoutes
1106
+ * ])
1107
+ * ```
1108
+ */
1109
+ function toRoutesJSON(routes) {
1110
+ return routes.reduce((list, route) => {
1111
+ if (route instanceof RouteGroup) {
1112
+ list = list.concat(toRoutesJSON(route.routes));
1113
+ return list;
1114
+ }
1115
+ if (route instanceof RouteResource) {
1116
+ list = list.concat(toRoutesJSON(route.routes));
1117
+ return list;
1118
+ }
1119
+ if (route instanceof BriskRoute) {
1120
+ if (route.route && !route.route.isDeleted()) list.push(route.route.toJSON());
1121
+ return list;
1122
+ }
1123
+ if (!route.isDeleted()) list.push(route.toJSON());
1124
+ return list;
1125
+ }, []);
1126
+ }
1127
+ /**
1128
+ * Helper to determine if a remote address should be trusted.
1129
+ *
1130
+ * Uses caching to avoid repeated expensive proxy function calls for the same
1131
+ * remote address. The cache improves performance when the same addresses are
1132
+ * checked multiple times.
1133
+ *
1134
+ * @param remoteAddress - The remote IP address to check
1135
+ * @param proxyFn - Function that determines if an address should be trusted
1136
+ *
1137
+ * @example
1138
+ * ```ts
1139
+ * const isTrusted = trustProxy('192.168.1.1', proxyAddr.compile('loopback'))
1140
+ * ```
1141
+ */
1142
+ function trustProxy(remoteAddress, proxyFn) {
1143
+ if (proxyCache.has(remoteAddress)) return proxyCache.get(remoteAddress);
1144
+ const result = proxyFn(remoteAddress, 0);
1145
+ proxyCache.set(remoteAddress, result);
1146
+ return result;
1147
+ }
1148
+ /**
1149
+ * Parses a range expression (e.g., '200..299') into an object with numeric keys.
1150
+ *
1151
+ * Supports both single values and ranges. For ranges, all numbers between
1152
+ * the start and end (inclusive) are mapped to the provided value.
1153
+ *
1154
+ * @param range - Range expression as a string (e.g., '200', '200..299')
1155
+ * @param value - Value to assign to each number in the range
1156
+ *
1157
+ * @example
1158
+ * ```ts
1159
+ * parseRange('200', 'success') // { 200: 'success' }
1160
+ * parseRange('200..202', 'success') // { 200: 'success', 201: 'success', 202: 'success' }
1161
+ * ```
1162
+ */
1163
+ function parseRange(range, value) {
1164
+ const parts = range.split("..");
1165
+ const min = Number(parts[0]);
1166
+ const max = Number(parts[1]);
1167
+ /**
1168
+ * The ending status code does not exists
1169
+ */
1170
+ if (parts.length === 1 && !Number.isNaN(min)) return { [min]: value };
1171
+ /**
1172
+ * The starting status code is not a number
1173
+ */
1174
+ if (Number.isNaN(min) || Number.isNaN(max)) return {};
1175
+ /**
1176
+ * Min and max are same
1177
+ */
1178
+ if (min === max) return { [min]: value };
1179
+ /**
1180
+ * Max cannot be smaller than min
1181
+ */
1182
+ if (max < min) throw new InvalidArgumentsException(`Invalid range "${range}"`);
1183
+ /**
1184
+ * Loop over the range and create a collection
1185
+ * of status codes
1186
+ */
1187
+ return [...Array(max - min + 1).keys()].reduce((result, step) => {
1188
+ result[min + step] = value;
1189
+ return result;
1190
+ }, {});
1191
+ }
1192
+ /**
1193
+ * Decodes specific percent-encoded characters in URI components.
1194
+ *
1195
+ * This function handles decoding of specific character codes that are commonly
1196
+ * percent-encoded in URIs, such as %, #, $, &, +, etc.
1197
+ *
1198
+ * @param highCharCode - The high character code of the hex pair
1199
+ * @param lowCharCode - The low character code of the hex pair
1200
+ */
1201
+ function decodeComponentChar(highCharCode, lowCharCode) {
1202
+ if (highCharCode === 50) {
1203
+ if (lowCharCode === 53) return "%";
1204
+ if (lowCharCode === 51) return "#";
1205
+ if (lowCharCode === 52) return "$";
1206
+ if (lowCharCode === 54) return "&";
1207
+ if (lowCharCode === 66) return "+";
1208
+ if (lowCharCode === 98) return "+";
1209
+ if (lowCharCode === 67) return ",";
1210
+ if (lowCharCode === 99) return ",";
1211
+ if (lowCharCode === 70) return "/";
1212
+ if (lowCharCode === 102) return "/";
1213
+ return null;
1214
+ }
1215
+ if (highCharCode === 51) {
1216
+ if (lowCharCode === 65) return ":";
1217
+ if (lowCharCode === 97) return ":";
1218
+ if (lowCharCode === 66) return ";";
1219
+ if (lowCharCode === 98) return ";";
1220
+ if (lowCharCode === 68) return "=";
1221
+ if (lowCharCode === 100) return "=";
1222
+ if (lowCharCode === 70) return "?";
1223
+ if (lowCharCode === 102) return "?";
1224
+ return null;
1225
+ }
1226
+ if (highCharCode === 52 && lowCharCode === 48) return "@";
1227
+ return null;
1228
+ }
1229
+ /**
1230
+ * Safely decodes a URI path while handling special characters and query strings.
1231
+ *
1232
+ * This function carefully parses and decodes URI components, handling edge cases
1233
+ * like double-encoded characters and non-standard query string delimiters.
1234
+ * It separates the pathname from query parameters and determines whether
1235
+ * route parameters should be decoded.
1236
+ *
1237
+ * @param path - The URI path to decode
1238
+ * @param useSemicolonDelimiter - Whether to treat semicolons as query string delimiters
1239
+ *
1240
+ * @example
1241
+ * ```ts
1242
+ * const result = safeDecodeURI('/users/123?name=john', false)
1243
+ * // Returns: { pathname: '/users/123', query: 'name=john', shouldDecodeParam: false }
1244
+ * ```
1245
+ */
1246
+ function safeDecodeURI(path, useSemicolonDelimiter) {
1247
+ let shouldDecode = false;
1248
+ let shouldDecodeParam = false;
1249
+ let querystring = "";
1250
+ for (let i = 1; i < path.length; i++) {
1251
+ const charCode = path.charCodeAt(i);
1252
+ if (charCode === 37) {
1253
+ const highCharCode = path.charCodeAt(i + 1);
1254
+ const lowCharCode = path.charCodeAt(i + 2);
1255
+ if (decodeComponentChar(highCharCode, lowCharCode) === null) shouldDecode = true;
1256
+ else {
1257
+ shouldDecodeParam = true;
1258
+ if (highCharCode === 50 && lowCharCode === 53) {
1259
+ shouldDecode = true;
1260
+ path = path.slice(0, i + 1) + "25" + path.slice(i + 1);
1261
+ i += 2;
1262
+ }
1263
+ i += 2;
1264
+ }
1265
+ } else if (charCode === 63 || charCode === 35 || charCode === 59 && useSemicolonDelimiter) {
1266
+ querystring = path.slice(i + 1);
1267
+ path = path.slice(0, i);
1268
+ break;
1269
+ }
1270
+ }
1271
+ return {
1272
+ pathname: shouldDecode ? decodeURI(path) : path,
1273
+ query: querystring,
1274
+ shouldDecodeParam
1275
+ };
1276
+ }
1277
+ //#endregion
1278
+ //#region src/helpers.ts
1279
+ /**
1280
+ * Parse a route pattern into an array of tokens. These tokes can be used
1281
+ * to match routes, or print them with semantic information.
1282
+ *
1283
+ * Token types
1284
+ *
1285
+ * - 0: (static) segment
1286
+ * - 1: (parameter) segment
1287
+ * - 2: (wildcard) segment
1288
+ * - 3: (optional parameter) segment
1289
+ *
1290
+ * Value (val) refers to the segment value
1291
+ *
1292
+ * end refers to be the suffix or the segment (if any)
1293
+ *
1294
+ * @param pattern - The route pattern to parse
1295
+ * @param matchers - Optional route matchers
1296
+ * @returns {MatchItRouteToken[]} Array of parsed route tokens
1297
+ */
1298
+ function parseRoute(pattern, matchers) {
1299
+ return matchit.parse(pattern, matchers);
1300
+ }
1301
+ /**
1302
+ * Makes signed URL for a given route pattern using its parsed tokens. The
1303
+ * tokens could be generated using the "parseRoute" method.
1304
+ *
1305
+ * @param identifier - Route identifier
1306
+ * @param tokens - Array of parsed route tokens
1307
+ * @param searchParamsStringifier - Function to stringify query parameters
1308
+ * @param encryption - Encryption instance for signing
1309
+ * @param params - Route parameters as array or object
1310
+ * @param options - Signed URL options
1311
+ * @returns {string} The generated signed URL
1312
+ */
1313
+ function createSignedURL(identifier, tokens, searchParamsStringifier, encryption, params, options) {
1314
+ const signature = encryption.getMessageVerifier().sign(createURL(identifier, tokens, searchParamsStringifier, params, {
1315
+ ...options,
1316
+ prefixUrl: void 0
1317
+ }), options?.expiresIn, options?.purpose);
1318
+ return createURL(identifier, tokens, searchParamsStringifier, params, {
1319
+ ...options,
1320
+ qs: {
1321
+ ...options?.qs,
1322
+ signature
1323
+ }
1324
+ });
1325
+ }
1326
+ /**
1327
+ * Match a given URI with an array of patterns and extract the params
1328
+ * from the URL. Null value is returned in case of no match
1329
+ *
1330
+ * @param url - The URL to match
1331
+ * @param patterns - Array of route patterns to match against
1332
+ * @returns {null | Record<string, string>} Extracted parameters or null if no match
1333
+ */
1334
+ function matchRoute(url, patterns) {
1335
+ const tokensBucket = patterns.map((pattern) => parseRoute(pattern));
1336
+ const match = matchit.match(url, tokensBucket);
1337
+ if (!match.length) return null;
1338
+ return matchit.exec(url, match);
1339
+ }
1340
+ /**
1341
+ * Serialize the value of a cookie to a string you can send via
1342
+ * set-cookie response header.
1343
+ *
1344
+ * @param key - Cookie name
1345
+ * @param value - Cookie value
1346
+ * @param options - Cookie options
1347
+ * @returns {string} Serialized cookie string
1348
+ */
1349
+ function serializeCookie(key, value, options) {
1350
+ let expires;
1351
+ let maxAge;
1352
+ if (options) {
1353
+ expires = typeof options.expires === "function" ? options.expires() : options.expires;
1354
+ maxAge = options.maxAge ? string.seconds.parse(options.maxAge) : void 0;
1355
+ }
1356
+ return serialize(key, value, {
1357
+ ...options,
1358
+ maxAge,
1359
+ expires
1360
+ });
1361
+ }
1362
+ /**
1363
+ * Returns the info about a middleware handler. In case of lazy imports, the method
1364
+ * will return the import path
1365
+ *
1366
+ * @param middleware - The middleware function or parsed middleware
1367
+ * @returns {Promise<MiddlewareHandlerInfo>} Promise resolving to middleware handler information
1368
+ */
1369
+ async function middlewareInfo(middleware) {
1370
+ if (typeof middleware === "function") return {
1371
+ type: "closure",
1372
+ name: middleware.name || "closure"
1373
+ };
1374
+ if ("args" in middleware) return {
1375
+ type: "named",
1376
+ name: middleware.name,
1377
+ args: middleware.args,
1378
+ ...await parseBindingReference([middleware.reference])
1379
+ };
1380
+ return {
1381
+ type: "global",
1382
+ name: middleware.name,
1383
+ ...await parseBindingReference([middleware.reference])
1384
+ };
1385
+ }
1386
+ /**
1387
+ * Returns the info about a route handler. In case of lazy imports, the method
1388
+ * will return the import path.
1389
+ *
1390
+ * @param route - The route JSON object
1391
+ * @returns {Promise<RouteHandlerInfo>} Promise resolving to route handler information
1392
+ */
1393
+ async function routeInfo(route) {
1394
+ return "reference" in route.handler ? {
1395
+ type: "controller",
1396
+ ...await parseBindingReference(route.handler.reference)
1397
+ } : {
1398
+ type: "closure",
1399
+ name: route.handler.name || "closure",
1400
+ args: "listArgs" in route.handler ? String(route.handler.listArgs) : void 0
1401
+ };
1402
+ }
1403
+ /**
1404
+ * Appends query string parameters to a URI. Existing query parameters
1405
+ * in the URI are merged with the new ones.
1406
+ *
1407
+ * @param uri - The base URI to append query string to
1408
+ * @param queryString - Object containing query parameters to append
1409
+ * @param qsParser - Query string parser instance for stringify/parse operations
1410
+ *
1411
+ * @example
1412
+ * ```ts
1413
+ * const result = appendQueryString('/users', { page: 1, limit: 10 }, qsParser)
1414
+ * // Returns: '/users?page=1&limit=10'
1415
+ *
1416
+ * const result2 = appendQueryString('/users?sort=name', { page: 1 }, qsParser)
1417
+ * // Returns: '/users?sort=name&page=1'
1418
+ * ```
1419
+ */
1420
+ function appendQueryString(uri, queryString, qsParser) {
1421
+ const { query, pathname } = safeDecodeURI(uri, false);
1422
+ const mergedQueryString = qsParser.stringify(Object.assign(qsParser.parse(query), queryString));
1423
+ return mergedQueryString ? `${pathname}?${mergedQueryString}` : pathname;
1424
+ }
1425
+ //#endregion
1426
+ export { canWriteResponseBody as C, tracing_channels_exports as S, Route as _, middlewareInfo as a, httpRequest as b, routeInfo as c, safeDecodeURI as d, toRoutesJSON as f, BriskRoute as g, RouteResource as h, matchRoute as i, serializeCookie as l, RouteGroup as m, createSignedURL as n, mime as o, trustProxy as p, encodeUrl as r, parseRoute as s, appendQueryString as t, parseRange as u, httpExceptionHandler as v, debug_default as w, httpResponseSerializer as x, httpMiddleware as y };