@adonisjs/http-server 7.0.0-1 → 7.0.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 (65) hide show
  1. package/build/chunk-NC6OWANS.js +4437 -0
  2. package/build/chunk-NC6OWANS.js.map +1 -0
  3. package/build/factories/http_server.d.ts +1 -0
  4. package/build/factories/main.js +332 -14
  5. package/build/factories/main.js.map +1 -0
  6. package/build/index.js +309 -22
  7. package/build/index.js.map +1 -0
  8. package/build/src/define_middleware.d.ts +2 -1
  9. package/build/src/router/lookup_store/main.d.ts +1 -3
  10. package/build/src/router/lookup_store/route_finder.d.ts +5 -1
  11. package/build/src/router/main.d.ts +5 -4
  12. package/build/src/router/resource.d.ts +19 -7
  13. package/build/src/types/main.js +1 -15
  14. package/build/src/types/main.js.map +1 -0
  15. package/build/src/types/middleware.d.ts +3 -1
  16. package/package.json +60 -59
  17. package/build/factories/http_context.js +0 -51
  18. package/build/factories/http_server.js +0 -26
  19. package/build/factories/qs_parser_factory.js +0 -44
  20. package/build/factories/request.js +0 -73
  21. package/build/factories/response.js +0 -77
  22. package/build/factories/router.js +0 -45
  23. package/build/factories/server_factory.js +0 -65
  24. package/build/src/cookies/client.js +0 -84
  25. package/build/src/cookies/drivers/encrypted.js +0 -36
  26. package/build/src/cookies/drivers/plain.js +0 -33
  27. package/build/src/cookies/drivers/signed.js +0 -36
  28. package/build/src/cookies/parser.js +0 -167
  29. package/build/src/cookies/serializer.js +0 -79
  30. package/build/src/debug.js +0 -10
  31. package/build/src/define_config.js +0 -68
  32. package/build/src/define_middleware.js +0 -35
  33. package/build/src/exception_handler.js +0 -306
  34. package/build/src/exceptions.js +0 -38
  35. package/build/src/helpers.js +0 -105
  36. package/build/src/http_context/local_storage.js +0 -39
  37. package/build/src/http_context/main.js +0 -105
  38. package/build/src/qs.js +0 -25
  39. package/build/src/redirect.js +0 -140
  40. package/build/src/request.js +0 -865
  41. package/build/src/response.js +0 -1208
  42. package/build/src/router/brisk.js +0 -85
  43. package/build/src/router/executor.js +0 -30
  44. package/build/src/router/factories/use_return_value.js +0 -22
  45. package/build/src/router/group.js +0 -207
  46. package/build/src/router/lookup_store/main.js +0 -86
  47. package/build/src/router/lookup_store/route_finder.js +0 -49
  48. package/build/src/router/lookup_store/url_builder.js +0 -209
  49. package/build/src/router/main.js +0 -316
  50. package/build/src/router/matchers.js +0 -36
  51. package/build/src/router/parser.js +0 -17
  52. package/build/src/router/resource.js +0 -216
  53. package/build/src/router/route.js +0 -293
  54. package/build/src/router/store.js +0 -195
  55. package/build/src/server/factories/final_handler.js +0 -30
  56. package/build/src/server/factories/middleware_handler.js +0 -16
  57. package/build/src/server/factories/write_response.js +0 -24
  58. package/build/src/server/main.js +0 -292
  59. package/build/src/types/base.js +0 -9
  60. package/build/src/types/middleware.js +0 -9
  61. package/build/src/types/qs.js +0 -9
  62. package/build/src/types/request.js +0 -9
  63. package/build/src/types/response.js +0 -9
  64. package/build/src/types/route.js +0 -9
  65. package/build/src/types/server.js +0 -9
@@ -0,0 +1,4437 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/router/route.ts
8
+ import is from "@sindresorhus/is";
9
+ import Macroable4 from "@poppinss/macroable";
10
+ import Middleware from "@poppinss/middleware";
11
+ import { RuntimeException as RuntimeException2 } from "@poppinss/utils";
12
+ import { moduleCaller, moduleExpression, moduleImporter } from "@adonisjs/fold";
13
+
14
+ // src/router/factories/use_return_value.ts
15
+ function useReturnValue(ctx) {
16
+ return function(value) {
17
+ if (value !== void 0 && // Return value is explicitly defined
18
+ !ctx.response.hasLazyBody && // Lazy body is not set
19
+ value !== ctx.response) {
20
+ ctx.response.send(value);
21
+ }
22
+ };
23
+ }
24
+
25
+ // src/router/executor.ts
26
+ function execute(route, resolver, ctx, errorResponder) {
27
+ return route.middleware.runner().errorHandler((error) => errorResponder(error, ctx)).finalHandler(async () => {
28
+ if (typeof route.handler === "function") {
29
+ return Promise.resolve(route.handler(ctx)).then(useReturnValue(ctx));
30
+ }
31
+ return route.handler.handle(resolver, ctx).then(useReturnValue(ctx));
32
+ }).run(async (middleware, next) => {
33
+ if (typeof middleware === "function") {
34
+ return middleware(ctx, next);
35
+ }
36
+ return middleware.handle(resolver, ctx, next, middleware.args);
37
+ });
38
+ }
39
+
40
+ // src/helpers.ts
41
+ import Cache from "tmp-cache";
42
+ import { InvalidArgumentsException } from "@poppinss/utils";
43
+
44
+ // src/router/brisk.ts
45
+ import Macroable from "@poppinss/macroable";
46
+ var BriskRoute = class extends Macroable {
47
+ /**
48
+ * Route pattern
49
+ */
50
+ #pattern;
51
+ /**
52
+ * Matchers inherited from the router
53
+ */
54
+ #globalMatchers;
55
+ /**
56
+ * Reference to the AdonisJS application
57
+ */
58
+ #app;
59
+ /**
60
+ * Middleware registered on the router
61
+ */
62
+ #routerMiddleware;
63
+ /**
64
+ * Reference to route instance. Set after `setHandler` is called
65
+ */
66
+ route = null;
67
+ constructor(app, routerMiddleware, options) {
68
+ super();
69
+ this.#app = app;
70
+ this.#routerMiddleware = routerMiddleware;
71
+ this.#pattern = options.pattern;
72
+ this.#globalMatchers = options.globalMatchers;
73
+ }
74
+ /**
75
+ * Set handler for the brisk route
76
+ */
77
+ setHandler(handler) {
78
+ this.route = new Route(this.#app, this.#routerMiddleware, {
79
+ pattern: this.#pattern,
80
+ globalMatchers: this.#globalMatchers,
81
+ methods: ["GET", "HEAD"],
82
+ handler
83
+ });
84
+ return this.route;
85
+ }
86
+ /**
87
+ * Redirects to a given route. Params from the original request will
88
+ * be used when no custom params are defined.
89
+ */
90
+ redirect(identifier, params, options) {
91
+ return this.setHandler(async function redirectsToRoute(ctx) {
92
+ const redirector = ctx.response.redirect();
93
+ if (options?.status) {
94
+ redirector.status(options.status);
95
+ }
96
+ return redirector.toRoute(identifier, params || ctx.params, options);
97
+ });
98
+ }
99
+ /**
100
+ * Redirect request to a fixed URL
101
+ */
102
+ redirectToPath(url, options) {
103
+ return this.setHandler(async function redirectsToPath(ctx) {
104
+ const redirector = ctx.response.redirect();
105
+ if (options?.status) {
106
+ redirector.status(options.status);
107
+ }
108
+ return redirector.toPath(url);
109
+ });
110
+ }
111
+ };
112
+
113
+ // src/router/group.ts
114
+ import Macroable3 from "@poppinss/macroable";
115
+
116
+ // src/router/resource.ts
117
+ import string from "@poppinss/utils/string";
118
+ import Macroable2 from "@poppinss/macroable";
119
+ import { RuntimeException } from "@poppinss/utils";
120
+ var RouteResource = class extends Macroable2 {
121
+ /**
122
+ * Resource identifier. Nested resources are separated
123
+ * with a dot notation
124
+ */
125
+ #resource;
126
+ /**
127
+ * The controller to handle resource routing requests
128
+ */
129
+ #controller;
130
+ /**
131
+ * Is it a shallow resource? Shallow resources URLs do not have parent
132
+ * resource name and id once they can be identified with the id.
133
+ */
134
+ #shallow = false;
135
+ /**
136
+ * Matchers inherited from the router
137
+ */
138
+ #globalMatchers;
139
+ /**
140
+ * Reference to the AdonisJS application
141
+ */
142
+ #app;
143
+ /**
144
+ * Middleware registered on the router
145
+ */
146
+ #routerMiddleware;
147
+ /**
148
+ * Parameter names for the resources. Defaults to `id` for
149
+ * a singular resource and `resource_id` for nested
150
+ * resources.
151
+ */
152
+ #params = {};
153
+ /**
154
+ * Base name for the routes. We suffix action names
155
+ * on top of the base name
156
+ */
157
+ #routesBaseName;
158
+ /**
159
+ * A collection of routes instances that belongs to this resource
160
+ */
161
+ routes = [];
162
+ constructor(app, routerMiddleware, options) {
163
+ super();
164
+ this.#validateResourceName(options.resource);
165
+ this.#app = app;
166
+ this.#shallow = options.shallow;
167
+ this.#routerMiddleware = routerMiddleware;
168
+ this.#controller = options.controller;
169
+ this.#globalMatchers = options.globalMatchers;
170
+ this.#resource = this.#normalizeResourceName(options.resource);
171
+ this.#routesBaseName = this.#getRoutesBaseName();
172
+ this.#buildRoutes();
173
+ }
174
+ /**
175
+ * Normalizes the resource name to dropping leading and trailing
176
+ * slashes.
177
+ */
178
+ #normalizeResourceName(resource) {
179
+ return resource.replace(/^\//, "").replace(/\/$/, "");
180
+ }
181
+ /**
182
+ * Ensure resource name is not an empty string
183
+ */
184
+ #validateResourceName(resource) {
185
+ if (!resource || resource === "/") {
186
+ throw new RuntimeException(`Invalid resource name "${resource}"`);
187
+ }
188
+ }
189
+ /**
190
+ * Converting segments of a resource to snake case to
191
+ * make the route name.
192
+ */
193
+ #getRoutesBaseName() {
194
+ return this.#resource.split(".").map((token) => string.snakeCase(token)).join(".");
195
+ }
196
+ /**
197
+ * Create a new route for the given pattern, methods and controller action
198
+ */
199
+ #createRoute(pattern, methods, action) {
200
+ const route = new Route(this.#app, this.#routerMiddleware, {
201
+ pattern,
202
+ methods,
203
+ handler: typeof this.#controller === "string" ? `${this.#controller}.${action}` : [this.#controller, action],
204
+ globalMatchers: this.#globalMatchers
205
+ });
206
+ route.as(`${this.#routesBaseName}.${action}`);
207
+ this.routes.push(route);
208
+ }
209
+ /**
210
+ * Returns the `resource_id` name for a given resource. The
211
+ * resource name is converted to singular form and
212
+ * transformed to snake case.
213
+ *
214
+ * photos becomes photo_id
215
+ * users becomes user_id
216
+ */
217
+ #getResourceId(resource) {
218
+ return `${string.snakeCase(string.singular(resource))}_id`;
219
+ }
220
+ /**
221
+ * Build routes for the given resource
222
+ */
223
+ #buildRoutes() {
224
+ const resources = this.#resource.split(".");
225
+ const mainResource = resources.pop();
226
+ this.#params[mainResource] = ":id";
227
+ const baseURI = `${resources.map((resource) => {
228
+ const paramName = `:${this.#getResourceId(resource)}`;
229
+ this.#params[resource] = paramName;
230
+ return `${resource}/${paramName}`;
231
+ }).join("/")}/${mainResource}`;
232
+ this.#createRoute(baseURI, ["GET", "HEAD"], "index");
233
+ this.#createRoute(`${baseURI}/create`, ["GET", "HEAD"], "create");
234
+ this.#createRoute(baseURI, ["POST"], "store");
235
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["GET", "HEAD"], "show");
236
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id/edit`, ["GET", "HEAD"], "edit");
237
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["PUT", "PATCH"], "update");
238
+ this.#createRoute(`${this.#shallow ? mainResource : baseURI}/:id`, ["DELETE"], "destroy");
239
+ }
240
+ /**
241
+ * Filter the routes based on their partial names
242
+ */
243
+ #filter(names, inverse) {
244
+ const actions = Array.isArray(names) ? names : [names];
245
+ return this.routes.filter((route) => {
246
+ const match = actions.find((name) => route.getName().endsWith(name));
247
+ return inverse ? !match : match;
248
+ });
249
+ }
250
+ /**
251
+ * Register only given routes and remove others
252
+ */
253
+ only(names) {
254
+ this.#filter(names, true).forEach((route) => route.markAsDeleted());
255
+ return this;
256
+ }
257
+ /**
258
+ * Register all routes, except the one's defined
259
+ */
260
+ except(names) {
261
+ this.#filter(names, false).forEach((route) => route.markAsDeleted());
262
+ return this;
263
+ }
264
+ /**
265
+ * Register api only routes. The `create` and `edit` routes, which
266
+ * are meant to show forms will not be registered
267
+ */
268
+ apiOnly() {
269
+ return this.except(["create", "edit"]);
270
+ }
271
+ /**
272
+ * Define matcher for params inside the resource
273
+ */
274
+ where(key, matcher) {
275
+ this.routes.forEach((route) => {
276
+ route.where(key, matcher);
277
+ });
278
+ return this;
279
+ }
280
+ tap(actions, callback) {
281
+ if (typeof actions === "function") {
282
+ this.routes.forEach((route) => {
283
+ if (!route.isDeleted()) {
284
+ actions(route);
285
+ }
286
+ });
287
+ return this;
288
+ }
289
+ this.#filter(actions, false).forEach((route) => {
290
+ if (!route.isDeleted()) {
291
+ callback(route);
292
+ }
293
+ });
294
+ return this;
295
+ }
296
+ /**
297
+ * Set the param name for a given resource
298
+ */
299
+ params(resources) {
300
+ Object.keys(resources).forEach((resource) => {
301
+ const param = resources[resource];
302
+ const existingParam = this.#params[resource];
303
+ this.#params[resource] = `:${param}`;
304
+ this.routes.forEach((route) => {
305
+ route.setPattern(
306
+ route.getPattern().replace(`${resource}/${existingParam}`, `${resource}/:${param}`)
307
+ );
308
+ });
309
+ });
310
+ return this;
311
+ }
312
+ /**
313
+ * Define one or more middleware on the routes created by
314
+ * the resource.
315
+ *
316
+ * Calling this method multiple times will append middleware
317
+ * to existing list.
318
+ */
319
+ use(actions, middleware) {
320
+ if (actions === "*") {
321
+ this.tap((route) => route.use(middleware));
322
+ } else {
323
+ this.tap(actions, (route) => route.use(middleware));
324
+ }
325
+ return this;
326
+ }
327
+ /**
328
+ * @alias use
329
+ */
330
+ middleware(actions, middleware) {
331
+ return this.use(actions, middleware);
332
+ }
333
+ /**
334
+ * Prepend name to all the routes
335
+ */
336
+ as(name, normalizeName = true) {
337
+ name = normalizeName ? string.snakeCase(name) : name;
338
+ this.routes.forEach((route) => {
339
+ route.as(route.getName().replace(this.#routesBaseName, name), false);
340
+ });
341
+ this.#routesBaseName = name;
342
+ return this;
343
+ }
344
+ };
345
+
346
+ // src/router/group.ts
347
+ var RouteGroup = class _RouteGroup extends Macroable3 {
348
+ constructor(routes) {
349
+ super();
350
+ this.routes = routes;
351
+ }
352
+ /**
353
+ * Array of middleware registered on the group.
354
+ */
355
+ #middleware = [];
356
+ /**
357
+ * Shares midldeware stack with the routes. The method is invoked recursively
358
+ * to only register middleware with the route class and not with the
359
+ * resource or the child group
360
+ */
361
+ #shareMiddlewareStackWithRoutes(route) {
362
+ if (route instanceof _RouteGroup) {
363
+ route.routes.forEach((child) => this.#shareMiddlewareStackWithRoutes(child));
364
+ return;
365
+ }
366
+ if (route instanceof RouteResource) {
367
+ route.routes.forEach((child) => child.getMiddleware().unshift(this.#middleware));
368
+ return;
369
+ }
370
+ if (route instanceof BriskRoute) {
371
+ route.route.getMiddleware().unshift(this.#middleware);
372
+ return;
373
+ }
374
+ route.getMiddleware().unshift(this.#middleware);
375
+ }
376
+ /**
377
+ * Updates the route name. The method is invoked recursively to only update
378
+ * the name with the route class and not with the resource or the child
379
+ * group.
380
+ */
381
+ #updateRouteName(route, name) {
382
+ if (route instanceof _RouteGroup) {
383
+ route.routes.forEach((child) => this.#updateRouteName(child, name));
384
+ return;
385
+ }
386
+ if (route instanceof RouteResource) {
387
+ route.routes.forEach((child) => child.as(name, true));
388
+ return;
389
+ }
390
+ if (route instanceof BriskRoute) {
391
+ route.route.as(name, true);
392
+ return;
393
+ }
394
+ route.as(name, true);
395
+ }
396
+ /**
397
+ * Sets prefix on the route. The method is invoked recursively to only set
398
+ * the prefix with the route class and not with the resource or the
399
+ * child group.
400
+ */
401
+ #setRoutePrefix(route, prefix) {
402
+ if (route instanceof _RouteGroup) {
403
+ route.routes.forEach((child) => this.#setRoutePrefix(child, prefix));
404
+ return;
405
+ }
406
+ if (route instanceof RouteResource) {
407
+ route.routes.forEach((child) => child.prefix(prefix));
408
+ return;
409
+ }
410
+ if (route instanceof BriskRoute) {
411
+ route.route.prefix(prefix);
412
+ return;
413
+ }
414
+ route.prefix(prefix);
415
+ }
416
+ /**
417
+ * Updates domain on the route. The method is invoked recursively to only update
418
+ * the domain with the route class and not with the resource or the child
419
+ * group.
420
+ */
421
+ #updateRouteDomain(route, domain) {
422
+ if (route instanceof _RouteGroup) {
423
+ route.routes.forEach((child) => this.#updateRouteDomain(child, domain));
424
+ return;
425
+ }
426
+ if (route instanceof RouteResource) {
427
+ route.routes.forEach((child) => child.domain(domain));
428
+ return;
429
+ }
430
+ if (route instanceof BriskRoute) {
431
+ route.route.domain(domain, false);
432
+ return;
433
+ }
434
+ route.domain(domain, false);
435
+ }
436
+ /**
437
+ * Updates matchers on the route. The method is invoked recursively to only update
438
+ * the matchers with the route class and not with the resource or the child
439
+ * group.
440
+ */
441
+ #updateRouteMatchers(route, param, matcher) {
442
+ if (route instanceof _RouteGroup) {
443
+ route.routes.forEach((child) => this.#updateRouteMatchers(child, param, matcher));
444
+ return;
445
+ }
446
+ if (route instanceof RouteResource) {
447
+ route.routes.forEach((child) => child.where(param, matcher));
448
+ return;
449
+ }
450
+ if (route instanceof BriskRoute) {
451
+ route.route.where(param, matcher);
452
+ return;
453
+ }
454
+ route.where(param, matcher);
455
+ }
456
+ /**
457
+ * Define route param matcher
458
+ *
459
+ * ```ts
460
+ * Route.group(() => {
461
+ * }).where('id', /^[0-9]+/)
462
+ * ```
463
+ */
464
+ where(param, matcher) {
465
+ this.routes.forEach((route) => this.#updateRouteMatchers(route, param, matcher));
466
+ return this;
467
+ }
468
+ /**
469
+ * Define prefix all the routes in the group.
470
+ *
471
+ * ```ts
472
+ * Route.group(() => {
473
+ * }).prefix('v1')
474
+ * ```
475
+ */
476
+ prefix(prefix) {
477
+ this.routes.forEach((route) => this.#setRoutePrefix(route, prefix));
478
+ return this;
479
+ }
480
+ /**
481
+ * Define domain for all the routes.
482
+ *
483
+ * ```ts
484
+ * Route.group(() => {
485
+ * }).domain(':name.adonisjs.com')
486
+ * ```
487
+ */
488
+ domain(domain) {
489
+ this.routes.forEach((route) => this.#updateRouteDomain(route, domain));
490
+ return this;
491
+ }
492
+ /**
493
+ * Prepend name to the routes name.
494
+ *
495
+ * ```ts
496
+ * Route.group(() => {
497
+ * }).as('version1')
498
+ * ```
499
+ */
500
+ as(name) {
501
+ this.routes.forEach((route) => this.#updateRouteName(route, name));
502
+ return this;
503
+ }
504
+ /**
505
+ * Prepend an array of middleware to all routes middleware.
506
+ *
507
+ * ```ts
508
+ * Route.group(() => {
509
+ * }).use(middleware.auth())
510
+ * ```
511
+ */
512
+ use(middleware) {
513
+ if (!this.#middleware.length) {
514
+ this.routes.forEach((route) => this.#shareMiddlewareStackWithRoutes(route));
515
+ }
516
+ if (Array.isArray(middleware)) {
517
+ for (let one of middleware) {
518
+ this.#middleware.push(one);
519
+ }
520
+ } else {
521
+ this.#middleware.push(middleware);
522
+ }
523
+ return this;
524
+ }
525
+ /**
526
+ * @alias use
527
+ */
528
+ middleware(middleware) {
529
+ return this.use(middleware);
530
+ }
531
+ };
532
+
533
+ // src/helpers.ts
534
+ var proxyCache = new Cache({ max: 200 });
535
+ function dropSlash(input) {
536
+ if (input === "/") {
537
+ return "/";
538
+ }
539
+ return `/${input.replace(/^\//, "").replace(/\/$/, "")}`;
540
+ }
541
+ function toRoutesJSON(routes) {
542
+ return routes.reduce((list, route) => {
543
+ if (route instanceof RouteGroup) {
544
+ list = list.concat(toRoutesJSON(route.routes));
545
+ return list;
546
+ }
547
+ if (route instanceof RouteResource) {
548
+ list = list.concat(toRoutesJSON(route.routes));
549
+ return list;
550
+ }
551
+ if (route instanceof BriskRoute) {
552
+ if (route.route && !route.route.isDeleted()) {
553
+ list.push(route.route.toJSON());
554
+ }
555
+ return list;
556
+ }
557
+ if (!route.isDeleted()) {
558
+ list.push(route.toJSON());
559
+ }
560
+ return list;
561
+ }, []);
562
+ }
563
+ function trustProxy(remoteAddress, proxyFn) {
564
+ if (proxyCache.has(remoteAddress)) {
565
+ return proxyCache.get(remoteAddress);
566
+ }
567
+ const result = proxyFn(remoteAddress, 0);
568
+ proxyCache.set(remoteAddress, result);
569
+ return result;
570
+ }
571
+ function parseRange(range, value) {
572
+ const parts = range.split("..");
573
+ const min = Number(parts[0]);
574
+ const max = Number(parts[1]);
575
+ if (parts.length === 1 && !Number.isNaN(min)) {
576
+ return {
577
+ [min]: value
578
+ };
579
+ }
580
+ if (Number.isNaN(min) || Number.isNaN(max)) {
581
+ return {};
582
+ }
583
+ if (min === max) {
584
+ return {
585
+ [min]: value
586
+ };
587
+ }
588
+ if (max < min) {
589
+ throw new InvalidArgumentsException(`Invalid range "${range}"`);
590
+ }
591
+ return [...Array(max - min + 1).keys()].reduce(
592
+ (result, step) => {
593
+ result[min + step] = value;
594
+ return result;
595
+ },
596
+ {}
597
+ );
598
+ }
599
+
600
+ // src/debug.ts
601
+ import { debuglog } from "node:util";
602
+ var debug_default = debuglog("adonisjs:http");
603
+
604
+ // src/router/route.ts
605
+ var Route = class extends Macroable4 {
606
+ /**
607
+ * Route pattern
608
+ */
609
+ #pattern;
610
+ /**
611
+ * HTTP Methods for the route
612
+ */
613
+ #methods;
614
+ /**
615
+ * A unique name for the route
616
+ */
617
+ #name;
618
+ /**
619
+ * A boolean to prevent route from getting registered within
620
+ * the store.
621
+ *
622
+ * This flag must be set before "Router.commit" method
623
+ */
624
+ #isDeleted = false;
625
+ /**
626
+ * Route handler
627
+ */
628
+ #handler;
629
+ /**
630
+ * Matchers inherited from the router
631
+ */
632
+ #globalMatchers;
633
+ /**
634
+ * Reference to the AdonisJS application
635
+ */
636
+ #app;
637
+ /**
638
+ * Middleware registered on the router
639
+ */
640
+ #routerMiddleware;
641
+ /**
642
+ * By default the route is part of the `root` domain. Root domain is used
643
+ * when no domain is defined
644
+ */
645
+ #routeDomain = "root";
646
+ /**
647
+ * An object of matchers to be forwarded to the store. The matchers
648
+ * list is populated by calling `where` method
649
+ */
650
+ #matchers = {};
651
+ /**
652
+ * Custom prefixes defined on the route or the route parent
653
+ * groups
654
+ */
655
+ #prefixes = [];
656
+ /**
657
+ * Middleware defined directly on the route or the route parent
658
+ * routes. We mantain an array for each layer of the stack
659
+ */
660
+ #middleware = [];
661
+ constructor(app, routerMiddleware, options) {
662
+ super();
663
+ this.#app = app;
664
+ this.#routerMiddleware = routerMiddleware;
665
+ this.#pattern = options.pattern;
666
+ this.#methods = options.methods;
667
+ this.#handler = this.#resolveRouteHandle(options.handler);
668
+ this.#globalMatchers = options.globalMatchers;
669
+ }
670
+ /**
671
+ * Resolves the route handler string expression to a
672
+ * handler method object
673
+ */
674
+ #resolveRouteHandle(handler) {
675
+ if (typeof handler === "string") {
676
+ return {
677
+ reference: handler,
678
+ ...moduleExpression(handler, this.#app.appRoot).toHandleMethod()
679
+ };
680
+ }
681
+ if (Array.isArray(handler)) {
682
+ if (is.class(handler[0])) {
683
+ return {
684
+ reference: handler,
685
+ ...moduleCaller(handler[0], handler[1] || "handle").toHandleMethod()
686
+ };
687
+ }
688
+ return {
689
+ reference: handler,
690
+ ...moduleImporter(handler[0], handler[1] || "handle").toHandleMethod()
691
+ };
692
+ }
693
+ return handler;
694
+ }
695
+ /**
696
+ * Returns an object of param matchers by merging global and local
697
+ * matchers. The local copy is given preference over the global
698
+ * one's
699
+ */
700
+ #getMatchers() {
701
+ return { ...this.#globalMatchers, ...this.#matchers };
702
+ }
703
+ /**
704
+ * Returns a normalized pattern string by prefixing the `prefix` (if defined).
705
+ */
706
+ #computePattern() {
707
+ const pattern = dropSlash(this.#pattern);
708
+ const prefix = this.#prefixes.slice().reverse().map((one) => dropSlash(one)).join("");
709
+ return prefix ? `${prefix}${pattern === "/" ? "" : pattern}` : pattern;
710
+ }
711
+ /**
712
+ * Define matcher for a given param. If a matcher exists, then we do not
713
+ * override that, since the routes inside a group will set matchers
714
+ * before the group, so they should have priority over the group
715
+ * matchers.
716
+ *
717
+ * ```ts
718
+ * Route.group(() => {
719
+ * Route.get('/:id', 'handler').where('id', /^[0-9]$/)
720
+ * }).where('id', /[^a-z$]/)
721
+ * ```
722
+ *
723
+ * The `/^[0-9]$/` will win over the matcher defined by the group
724
+ */
725
+ where(param, matcher) {
726
+ if (this.#matchers[param]) {
727
+ return this;
728
+ }
729
+ if (typeof matcher === "string") {
730
+ this.#matchers[param] = { match: new RegExp(matcher) };
731
+ } else if (is.regExp(matcher)) {
732
+ this.#matchers[param] = { match: matcher };
733
+ } else {
734
+ this.#matchers[param] = matcher;
735
+ }
736
+ return this;
737
+ }
738
+ /**
739
+ * Define prefix for the route. Calling this method multiple times
740
+ * applies multiple prefixes in the reverse order.
741
+ */
742
+ prefix(prefix) {
743
+ this.#prefixes.push(prefix);
744
+ return this;
745
+ }
746
+ /**
747
+ * Define a custom domain for the route. We do not overwrite the domain
748
+ * unless `overwrite` flag is set to true.
749
+ */
750
+ domain(domain, overwrite = false) {
751
+ if (this.#routeDomain === "root" || overwrite) {
752
+ this.#routeDomain = domain;
753
+ }
754
+ return this;
755
+ }
756
+ /**
757
+ * Define one or more middleware to be executed before the route
758
+ * handler.
759
+ *
760
+ * Named middleware can be referenced using the name registered with
761
+ * the router middleware store.
762
+ */
763
+ use(middleware) {
764
+ this.#middleware.push(Array.isArray(middleware) ? middleware : [middleware]);
765
+ return this;
766
+ }
767
+ /**
768
+ * @alias use
769
+ */
770
+ middleware(middleware) {
771
+ return this.use(middleware);
772
+ }
773
+ /**
774
+ * Give a unique name to the route. Assinging a new unique removes the
775
+ * existing name of the route.
776
+ *
777
+ * Setting prepends to true prefixes the name to the existing name.
778
+ */
779
+ as(name, prepend = false) {
780
+ if (prepend) {
781
+ if (!this.#name) {
782
+ throw new RuntimeException2(
783
+ `Routes inside a group must have names before calling "router.group.as"`
784
+ );
785
+ }
786
+ this.#name = `${name}.${this.#name}`;
787
+ return this;
788
+ }
789
+ this.#name = name;
790
+ return this;
791
+ }
792
+ /**
793
+ * Check if the route was marked to be deleted
794
+ */
795
+ isDeleted() {
796
+ return this.#isDeleted;
797
+ }
798
+ /**
799
+ * Mark route as deleted. Deleted routes are not registered
800
+ * with the route store
801
+ */
802
+ markAsDeleted() {
803
+ this.#isDeleted = true;
804
+ }
805
+ /**
806
+ * Get the route name
807
+ */
808
+ getName() {
809
+ return this.#name;
810
+ }
811
+ /**
812
+ * Get the route pattern
813
+ */
814
+ getPattern() {
815
+ return this.#pattern;
816
+ }
817
+ /**
818
+ * Set the route pattern
819
+ */
820
+ setPattern(pattern) {
821
+ this.#pattern = pattern;
822
+ return this;
823
+ }
824
+ /**
825
+ * Returns the stack of middleware registered on the route.
826
+ * The value is shared by reference.
827
+ */
828
+ getMiddleware() {
829
+ return this.#middleware;
830
+ }
831
+ /**
832
+ * Returns the middleware instance for persistence inside the
833
+ * store
834
+ */
835
+ #getMiddlewareForStore() {
836
+ const middleware = new Middleware();
837
+ this.#routerMiddleware.forEach((one) => {
838
+ debug_default("adding global middleware to route %s, %O", this.#pattern, one);
839
+ middleware.add(one);
840
+ });
841
+ this.#middleware.flat().forEach((one) => {
842
+ debug_default("adding named middleware to route %s, %O", this.#pattern, one);
843
+ middleware.add(one);
844
+ });
845
+ return middleware;
846
+ }
847
+ /**
848
+ * Returns JSON representation of the route
849
+ */
850
+ toJSON() {
851
+ return {
852
+ domain: this.#routeDomain,
853
+ pattern: this.#computePattern(),
854
+ matchers: this.#getMatchers(),
855
+ meta: {},
856
+ name: this.#name,
857
+ handler: this.#handler,
858
+ methods: this.#methods,
859
+ middleware: this.#getMiddlewareForStore(),
860
+ execute
861
+ };
862
+ }
863
+ };
864
+
865
+ // src/cookies/drivers/plain.ts
866
+ import { base64, MessageBuilder } from "@poppinss/utils";
867
+ function pack(value) {
868
+ if (value === void 0 || value === null) {
869
+ return null;
870
+ }
871
+ return base64.urlEncode(new MessageBuilder().build(value));
872
+ }
873
+ function canUnpack(encodedValue) {
874
+ return typeof encodedValue === "string";
875
+ }
876
+ function unpack(encodedValue) {
877
+ return new MessageBuilder().verify(base64.urlDecode(encodedValue, "utf-8", false));
878
+ }
879
+
880
+ // src/cookies/drivers/signed.ts
881
+ function pack2(key, value, encryption) {
882
+ if (value === void 0 || value === null) {
883
+ return null;
884
+ }
885
+ return `s:${encryption.verifier.sign(value, void 0, key)}`;
886
+ }
887
+ function canUnpack2(signedValue) {
888
+ return typeof signedValue === "string" && signedValue.substring(0, 2) === "s:";
889
+ }
890
+ function unpack2(key, signedValue, encryption) {
891
+ const value = signedValue.slice(2);
892
+ if (!value) {
893
+ return null;
894
+ }
895
+ return encryption.verifier.unsign(value, key);
896
+ }
897
+
898
+ // src/cookies/drivers/encrypted.ts
899
+ function pack3(key, value, encryption) {
900
+ if (value === void 0 || value === null) {
901
+ return null;
902
+ }
903
+ return `e:${encryption.encrypt(value, void 0, key)}`;
904
+ }
905
+ function canUnpack3(encryptedValue) {
906
+ return typeof encryptedValue === "string" && encryptedValue.substring(0, 2) === "e:";
907
+ }
908
+ function unpack3(key, encryptedValue, encryption) {
909
+ const value = encryptedValue.slice(2);
910
+ if (!value) {
911
+ return null;
912
+ }
913
+ return encryption.decrypt(value, key);
914
+ }
915
+
916
+ // src/cookies/client.ts
917
+ var CookieClient = class {
918
+ #encryption;
919
+ constructor(encryption) {
920
+ this.#encryption = encryption;
921
+ }
922
+ /**
923
+ * Encrypt a key value pair to be sent in the cookie header
924
+ */
925
+ encrypt(key, value) {
926
+ return pack3(key, value, this.#encryption);
927
+ }
928
+ /**
929
+ * Sign a key value pair to be sent in the cookie header
930
+ */
931
+ sign(key, value) {
932
+ return pack2(key, value, this.#encryption);
933
+ }
934
+ /**
935
+ * Encode a key value pair to be sent in the cookie header
936
+ */
937
+ encode(_, value) {
938
+ return pack(value);
939
+ }
940
+ /**
941
+ * Unsign a signed cookie value
942
+ */
943
+ unsign(key, value) {
944
+ return canUnpack2(value) ? unpack2(key, value, this.#encryption) : null;
945
+ }
946
+ /**
947
+ * Decrypt an encrypted cookie value
948
+ */
949
+ decrypt(key, value) {
950
+ return canUnpack3(value) ? unpack3(key, value, this.#encryption) : null;
951
+ }
952
+ /**
953
+ * Decode an encoded cookie value
954
+ */
955
+ decode(_, value) {
956
+ return canUnpack(value) ? unpack(value) : null;
957
+ }
958
+ /**
959
+ * Parse response cookie
960
+ */
961
+ parse(key, value) {
962
+ if (canUnpack2(value)) {
963
+ return unpack2(key, value, this.#encryption);
964
+ }
965
+ if (canUnpack3(value)) {
966
+ return unpack3(key, value, this.#encryption);
967
+ }
968
+ if (canUnpack(value)) {
969
+ return unpack(value);
970
+ }
971
+ }
972
+ };
973
+
974
+ // src/request.ts
975
+ import fresh from "fresh";
976
+ import typeIs from "type-is";
977
+ import accepts from "accepts";
978
+ import { isIP } from "node:net";
979
+ import is2 from "@sindresorhus/is";
980
+ import proxyaddr from "proxy-addr";
981
+ import { safeEqual } from "@poppinss/utils";
982
+ import Macroable5 from "@poppinss/macroable";
983
+ import lodash from "@poppinss/utils/lodash";
984
+ import { createId } from "@paralleldrive/cuid2";
985
+ import { parse } from "node:url";
986
+
987
+ // src/cookies/parser.ts
988
+ import cookie from "cookie";
989
+ var CookieParser = class {
990
+ #client;
991
+ /**
992
+ * A copy of cached cookies, they are cached during a request after
993
+ * initial decoding, unsigning or decrypting.
994
+ */
995
+ #cachedCookies = {
996
+ signedCookies: {},
997
+ plainCookies: {},
998
+ encryptedCookies: {}
999
+ };
1000
+ /**
1001
+ * An object of key-value pair collected by parsing
1002
+ * the request cookie header.
1003
+ */
1004
+ #cookies;
1005
+ constructor(cookieHeader, encryption) {
1006
+ this.#client = new CookieClient(encryption);
1007
+ this.#cookies = this.#parse(cookieHeader);
1008
+ }
1009
+ /**
1010
+ * Parses the request `cookie` header
1011
+ */
1012
+ #parse(cookieHeader) {
1013
+ if (!cookieHeader) {
1014
+ return {};
1015
+ }
1016
+ return cookie.parse(cookieHeader);
1017
+ }
1018
+ /**
1019
+ * Attempts to decode a cookie by the name. When calling this method,
1020
+ * you are assuming that the cookie was just encoded in the first
1021
+ * place and not signed or encrypted.
1022
+ */
1023
+ decode(key, encoded = true) {
1024
+ const value = this.#cookies[key];
1025
+ if (value === null || value === void 0) {
1026
+ return null;
1027
+ }
1028
+ const cache = this.#cachedCookies.plainCookies;
1029
+ if (cache[key] !== void 0) {
1030
+ return cache[key];
1031
+ }
1032
+ const parsed = encoded ? this.#client.decode(key, value) : value;
1033
+ if (parsed !== null) {
1034
+ cache[key] = parsed;
1035
+ }
1036
+ return parsed;
1037
+ }
1038
+ /**
1039
+ * Attempts to unsign a cookie by the name. When calling this method,
1040
+ * you are assuming that the cookie was signed in the first place.
1041
+ */
1042
+ unsign(key) {
1043
+ const value = this.#cookies[key];
1044
+ if (value === null || value === void 0) {
1045
+ return null;
1046
+ }
1047
+ const cache = this.#cachedCookies.signedCookies;
1048
+ if (cache[key] !== void 0) {
1049
+ return cache[key];
1050
+ }
1051
+ const parsed = this.#client.unsign(key, value);
1052
+ if (parsed !== null) {
1053
+ cache[key] = parsed;
1054
+ }
1055
+ return parsed;
1056
+ }
1057
+ /**
1058
+ * Attempts to decrypt a cookie by the name. When calling this method,
1059
+ * you are assuming that the cookie was encrypted in the first place.
1060
+ */
1061
+ decrypt(key) {
1062
+ const value = this.#cookies[key];
1063
+ if (value === null || value === void 0) {
1064
+ return null;
1065
+ }
1066
+ const cache = this.#cachedCookies.encryptedCookies;
1067
+ if (cache[key] !== void 0) {
1068
+ return cache[key];
1069
+ }
1070
+ const parsed = this.#client.decrypt(key, value);
1071
+ if (parsed !== null) {
1072
+ cache[key] = parsed;
1073
+ }
1074
+ return parsed;
1075
+ }
1076
+ /**
1077
+ * Returns an object of cookies key-value pair. Do note, the
1078
+ * cookies are not decoded, unsigned or decrypted inside this
1079
+ * list.
1080
+ */
1081
+ list() {
1082
+ return this.#cookies;
1083
+ }
1084
+ };
1085
+
1086
+ // src/request.ts
1087
+ var Request = class extends Macroable5 {
1088
+ constructor(request, response, encryption, config, qsParser) {
1089
+ super();
1090
+ this.request = request;
1091
+ this.response = response;
1092
+ this.#qsParser = qsParser;
1093
+ this.#config = config;
1094
+ this.#encryption = encryption;
1095
+ this.parsedUrl = parse(this.request.url, false);
1096
+ this.#parseQueryString();
1097
+ }
1098
+ /**
1099
+ * Query string parser
1100
+ */
1101
+ #qsParser;
1102
+ /**
1103
+ * Encryption module to verify signed URLs and unsign/decrypt
1104
+ * cookies
1105
+ */
1106
+ #encryption;
1107
+ /**
1108
+ * Request config
1109
+ */
1110
+ #config;
1111
+ /**
1112
+ * Request body set using `setBody` method
1113
+ */
1114
+ #requestBody = {};
1115
+ /**
1116
+ * A merged copy of `request body` and `querystring`
1117
+ */
1118
+ #requestData = {};
1119
+ /**
1120
+ * Original merged copy of `request body` and `querystring`.
1121
+ * Further mutation to this object are not allowed
1122
+ */
1123
+ #originalRequestData = {};
1124
+ /**
1125
+ * Parsed query string
1126
+ */
1127
+ #requestQs = {};
1128
+ /**
1129
+ * Raw request body as text
1130
+ */
1131
+ #rawRequestBody;
1132
+ /**
1133
+ * Cached copy of `accepts` fn to do content
1134
+ * negotiation.
1135
+ */
1136
+ #lazyAccepts;
1137
+ /**
1138
+ * Copy of lazily parsed signed and plain cookies.
1139
+ */
1140
+ #cookieParser;
1141
+ /**
1142
+ * Parses copy of the URL with query string as a string and not
1143
+ * object. This is done to build URL's with query string without
1144
+ * stringifying the object
1145
+ */
1146
+ parsedUrl;
1147
+ /**
1148
+ * The ctx will be set by the context itself. It creates a circular
1149
+ * reference
1150
+ */
1151
+ ctx;
1152
+ /**
1153
+ * Parses the query string
1154
+ */
1155
+ #parseQueryString() {
1156
+ if (this.parsedUrl.query) {
1157
+ this.updateQs(this.#qsParser.parse(this.parsedUrl.query));
1158
+ this.#originalRequestData = { ...this.#requestData };
1159
+ }
1160
+ }
1161
+ /**
1162
+ * Initiates the cookie parser lazily
1163
+ */
1164
+ #initiateCookieParser() {
1165
+ if (!this.#cookieParser) {
1166
+ this.#cookieParser = new CookieParser(this.header("cookie"), this.#encryption);
1167
+ }
1168
+ }
1169
+ /**
1170
+ * Lazily initiates the `accepts` module to make sure to parse
1171
+ * the request headers only when one of the content-negotiation
1172
+ * methods are used.
1173
+ */
1174
+ #initiateAccepts() {
1175
+ this.#lazyAccepts = this.#lazyAccepts || accepts(this.request);
1176
+ }
1177
+ /**
1178
+ * Returns the request id from the `x-request-id` header. The
1179
+ * header is untouched, if it already exists.
1180
+ */
1181
+ id() {
1182
+ let requestId = this.header("x-request-id");
1183
+ if (!requestId && this.#config.generateRequestId) {
1184
+ requestId = createId();
1185
+ this.request.headers["x-request-id"] = requestId;
1186
+ }
1187
+ return requestId;
1188
+ }
1189
+ /**
1190
+ * Set initial request body. A copy of the input will be maintained as the original
1191
+ * request body. Since the request body and query string is subject to mutations, we
1192
+ * keep one original reference to flash old data (whenever required).
1193
+ *
1194
+ * This method is supposed to be invoked by the body parser and must be called only
1195
+ * once. For further mutations make use of `updateBody` method.
1196
+ */
1197
+ setInitialBody(body) {
1198
+ if (this.#originalRequestData && Object.isFrozen(this.#originalRequestData)) {
1199
+ throw new Error('Cannot re-set initial body. Use "request.updateBody" instead');
1200
+ }
1201
+ this.updateBody(body);
1202
+ this.#originalRequestData = Object.freeze(lodash.cloneDeep(this.#requestData));
1203
+ }
1204
+ /**
1205
+ * Update the request body with new data object. The `all` property
1206
+ * will be re-computed by merging the query string and request
1207
+ * body.
1208
+ */
1209
+ updateBody(body) {
1210
+ this.#requestBody = body;
1211
+ this.#requestData = { ...this.#requestBody, ...this.#requestQs };
1212
+ }
1213
+ /**
1214
+ * Update the request raw body. Bodyparser sets this when unable to parse
1215
+ * the request body or when request is multipart/form-data.
1216
+ */
1217
+ updateRawBody(rawBody) {
1218
+ this.#rawRequestBody = rawBody;
1219
+ }
1220
+ /**
1221
+ * Update the query string with the new data object. The `all` property
1222
+ * will be re-computed by merging the query and the request body.
1223
+ */
1224
+ updateQs(data) {
1225
+ this.#requestQs = data;
1226
+ this.#requestData = { ...this.#requestBody, ...this.#requestQs };
1227
+ }
1228
+ /**
1229
+ * Returns route params
1230
+ */
1231
+ params() {
1232
+ return this.ctx?.params || {};
1233
+ }
1234
+ /**
1235
+ * Returns the query string object by reference
1236
+ */
1237
+ qs() {
1238
+ return this.#requestQs;
1239
+ }
1240
+ /**
1241
+ * Returns reference to the request body
1242
+ */
1243
+ body() {
1244
+ return this.#requestBody;
1245
+ }
1246
+ /**
1247
+ * Returns reference to the merged copy of request body
1248
+ * and query string
1249
+ */
1250
+ all() {
1251
+ return this.#requestData;
1252
+ }
1253
+ /**
1254
+ * Returns reference to the merged copy of original request
1255
+ * query string and body
1256
+ */
1257
+ original() {
1258
+ return this.#originalRequestData;
1259
+ }
1260
+ /**
1261
+ * Returns the request raw body (if exists), or returns `null`.
1262
+ *
1263
+ * Ideally you must be dealing with the parsed body accessed using [[input]], [[all]] or
1264
+ * [[post]] methods. The `raw` body is always a string.
1265
+ */
1266
+ raw() {
1267
+ return this.#rawRequestBody || null;
1268
+ }
1269
+ /**
1270
+ * Returns value for a given key from the request body or query string.
1271
+ * The `defaultValue` is used when original value is `undefined`.
1272
+ *
1273
+ * @example
1274
+ * ```js
1275
+ * request.input('username')
1276
+ *
1277
+ * // with default value
1278
+ * request.input('username', 'virk')
1279
+ * ```
1280
+ */
1281
+ input(key, defaultValue) {
1282
+ return lodash.get(this.#requestData, key, defaultValue);
1283
+ }
1284
+ /**
1285
+ * Returns value for a given key from route params
1286
+ *
1287
+ * @example
1288
+ * ```js
1289
+ * request.param('id')
1290
+ *
1291
+ * // with default value
1292
+ * request.param('id', 1)
1293
+ * ```
1294
+ */
1295
+ param(key, defaultValue) {
1296
+ return lodash.get(this.params(), key, defaultValue);
1297
+ }
1298
+ /**
1299
+ * Get everything from the request body except the given keys.
1300
+ *
1301
+ * @example
1302
+ * ```js
1303
+ * request.except(['_csrf'])
1304
+ * ```
1305
+ */
1306
+ except(keys) {
1307
+ return lodash.omit(this.#requestData, keys);
1308
+ }
1309
+ /**
1310
+ * Get value for specified keys.
1311
+ *
1312
+ * @example
1313
+ * ```js
1314
+ * request.only(['username', 'age'])
1315
+ * ```
1316
+ */
1317
+ only(keys) {
1318
+ return lodash.pick(this.#requestData, keys);
1319
+ }
1320
+ /**
1321
+ * Returns the HTTP request method. This is the original
1322
+ * request method. For spoofed request method, make
1323
+ * use of [[method]].
1324
+ *
1325
+ * @example
1326
+ * ```js
1327
+ * request.intended()
1328
+ * ```
1329
+ */
1330
+ intended() {
1331
+ return this.request.method;
1332
+ }
1333
+ /**
1334
+ * Returns the request HTTP method by taking method spoofing into account.
1335
+ *
1336
+ * Method spoofing works when all of the following are true.
1337
+ *
1338
+ * 1. `app.http.allowMethodSpoofing` config value is true.
1339
+ * 2. request query string has `_method`.
1340
+ * 3. The [[intended]] request method is `POST`.
1341
+ *
1342
+ * @example
1343
+ * ```js
1344
+ * request.method()
1345
+ * ```
1346
+ */
1347
+ method() {
1348
+ if (this.#config.allowMethodSpoofing && this.intended() === "POST") {
1349
+ return this.input("_method", this.intended()).toUpperCase();
1350
+ }
1351
+ return this.intended();
1352
+ }
1353
+ /**
1354
+ * Returns a copy of headers as an object
1355
+ */
1356
+ headers() {
1357
+ return this.request.headers;
1358
+ }
1359
+ /**
1360
+ * Returns value for a given header key. The default value is
1361
+ * used when original value is `undefined`.
1362
+ */
1363
+ header(key, defaultValue) {
1364
+ key = key.toLowerCase();
1365
+ const headers = this.headers();
1366
+ switch (key) {
1367
+ case "referer":
1368
+ case "referrer":
1369
+ return headers.referrer || headers.referer || defaultValue;
1370
+ default:
1371
+ return headers[key] || defaultValue;
1372
+ }
1373
+ }
1374
+ /**
1375
+ * Returns the ip address of the user. This method is optimize to fetch
1376
+ * ip address even when running your AdonisJs app behind a proxy.
1377
+ *
1378
+ * You can also define your own custom function to compute the ip address by
1379
+ * defining `app.http.getIp` as a function inside the config file.
1380
+ *
1381
+ * ```js
1382
+ * {
1383
+ * http: {
1384
+ * getIp (request) {
1385
+ * // I am using nginx as a proxy server and want to trust 'x-real-ip'
1386
+ * return request.header('x-real-ip')
1387
+ * }
1388
+ * }
1389
+ * }
1390
+ * ```
1391
+ *
1392
+ * You can control the behavior of trusting the proxy values by defining it
1393
+ * inside the `config/app.js` file.
1394
+ *
1395
+ * ```js
1396
+ * {
1397
+ * http: {
1398
+ * trustProxy: '127.0.0.1'
1399
+ * }
1400
+ * }
1401
+ * ```
1402
+ *
1403
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
1404
+ */
1405
+ ip() {
1406
+ const ipFn = this.#config.getIp;
1407
+ if (typeof ipFn === "function") {
1408
+ return ipFn(this);
1409
+ }
1410
+ return proxyaddr(this.request, this.#config.trustProxy);
1411
+ }
1412
+ /**
1413
+ * Returns an array of ip addresses from most to least trusted one.
1414
+ * This method is optimize to fetch ip address even when running
1415
+ * your AdonisJs app behind a proxy.
1416
+ *
1417
+ * You can control the behavior of trusting the proxy values by defining it
1418
+ * inside the `config/app.js` file.
1419
+ *
1420
+ * ```js
1421
+ * {
1422
+ * http: {
1423
+ * trustProxy: '127.0.0.1'
1424
+ * }
1425
+ * }
1426
+ * ```
1427
+ *
1428
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
1429
+ */
1430
+ ips() {
1431
+ return proxyaddr.all(this.request, this.#config.trustProxy);
1432
+ }
1433
+ /**
1434
+ * Returns the request protocol by checking for the URL protocol or
1435
+ * `X-Forwarded-Proto` header.
1436
+ *
1437
+ * If the `trust` is evaluated to `false`, then URL protocol is returned,
1438
+ * otherwise `X-Forwarded-Proto` header is used (if exists).
1439
+ *
1440
+ * You can control the behavior of trusting the proxy values by defining it
1441
+ * inside the `config/app.js` file.
1442
+ *
1443
+ * ```js
1444
+ * {
1445
+ * http: {
1446
+ * trustProxy: '127.0.0.1'
1447
+ * }
1448
+ * }
1449
+ * ```
1450
+ *
1451
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
1452
+ */
1453
+ protocol() {
1454
+ if ("encrypted" in this.request.socket) {
1455
+ return "https";
1456
+ }
1457
+ if (!trustProxy(this.request.socket.remoteAddress, this.#config.trustProxy)) {
1458
+ return this.parsedUrl.protocol || "http";
1459
+ }
1460
+ const forwardedProtocol = this.header("X-Forwarded-Proto");
1461
+ return forwardedProtocol ? forwardedProtocol.split(/\s*,\s*/)[0] : "http";
1462
+ }
1463
+ /**
1464
+ * Returns a boolean telling if request is served over `https`
1465
+ * or not. Check [[protocol]] method to know how protocol is
1466
+ * fetched.
1467
+ */
1468
+ secure() {
1469
+ return this.protocol() === "https";
1470
+ }
1471
+ /**
1472
+ * Returns the request host. If proxy headers are trusted, then
1473
+ * `X-Forwarded-Host` is given priority over the `Host` header.
1474
+ *
1475
+ * You can control the behavior of trusting the proxy values by defining it
1476
+ * inside the `config/app.js` file.
1477
+ *
1478
+ * ```js
1479
+ * {
1480
+ * http: {
1481
+ * trustProxy: '127.0.0.1'
1482
+ * }
1483
+ * }
1484
+ * ```
1485
+ *
1486
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
1487
+ */
1488
+ host() {
1489
+ let host = this.header("host");
1490
+ if (trustProxy(this.request.socket.remoteAddress, this.#config.trustProxy)) {
1491
+ host = this.header("X-Forwarded-Host") || host;
1492
+ }
1493
+ if (!host) {
1494
+ return null;
1495
+ }
1496
+ return host;
1497
+ }
1498
+ /**
1499
+ * Returns the request hostname. If proxy headers are trusted, then
1500
+ * `X-Forwarded-Host` is given priority over the `Host` header.
1501
+ *
1502
+ * You can control the behavior of trusting the proxy values by defining it
1503
+ * inside the `config/app.js` file.
1504
+ *
1505
+ * ```js
1506
+ * {
1507
+ * http: {
1508
+ * trustProxy: '127.0.0.1'
1509
+ * }
1510
+ * }
1511
+ * ```
1512
+ *
1513
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
1514
+ */
1515
+ hostname() {
1516
+ const host = this.host();
1517
+ if (!host) {
1518
+ return null;
1519
+ }
1520
+ const offset = host[0] === "[" ? host.indexOf("]") + 1 : 0;
1521
+ const index = host.indexOf(":", offset);
1522
+ return index !== -1 ? host.substring(0, index) : host;
1523
+ }
1524
+ /**
1525
+ * Returns an array of subdomains for the given host. An empty array is
1526
+ * returned if [[hostname]] is `null` or is an IP address.
1527
+ *
1528
+ * Also `www` is not considered as a subdomain
1529
+ */
1530
+ subdomains() {
1531
+ const hostname = this.hostname();
1532
+ if (!hostname || isIP(hostname)) {
1533
+ return [];
1534
+ }
1535
+ const offset = this.#config.subdomainOffset;
1536
+ const subdomains = hostname.split(".").reverse().slice(offset);
1537
+ if (subdomains[subdomains.length - 1] === "www") {
1538
+ subdomains.splice(subdomains.length - 1, 1);
1539
+ }
1540
+ return subdomains;
1541
+ }
1542
+ /**
1543
+ * Returns a boolean telling, if request `X-Requested-With === 'xmlhttprequest'`
1544
+ * or not.
1545
+ */
1546
+ ajax() {
1547
+ const xRequestedWith = this.header("X-Requested-With", "");
1548
+ return xRequestedWith.toLowerCase() === "xmlhttprequest";
1549
+ }
1550
+ /**
1551
+ * Returns a boolean telling, if request has `X-Pjax` header
1552
+ * set or not
1553
+ */
1554
+ pjax() {
1555
+ return !!this.header("X-Pjax");
1556
+ }
1557
+ /**
1558
+ * Returns the request relative URL.
1559
+ *
1560
+ * @example
1561
+ * ```js
1562
+ * request.url()
1563
+ *
1564
+ * // include query string
1565
+ * request.url(true)
1566
+ * ```
1567
+ */
1568
+ url(includeQueryString) {
1569
+ const pathname = this.parsedUrl.pathname;
1570
+ return includeQueryString && this.parsedUrl.query ? `${pathname}?${this.parsedUrl.query}` : pathname;
1571
+ }
1572
+ /**
1573
+ * Returns the complete HTTP url by combining
1574
+ * [[protocol]]://[[hostname]]/[[url]]
1575
+ *
1576
+ * @example
1577
+ * ```js
1578
+ * request.completeUrl()
1579
+ *
1580
+ * // include query string
1581
+ * request.completeUrl(true)
1582
+ * ```
1583
+ */
1584
+ completeUrl(includeQueryString) {
1585
+ const protocol = this.protocol();
1586
+ const hostname = this.host();
1587
+ return `${protocol}://${hostname}${this.url(includeQueryString)}`;
1588
+ }
1589
+ /**
1590
+ * Find if the current HTTP request is for the given route or the routes
1591
+ */
1592
+ matchesRoute(routeIdentifier) {
1593
+ if (!this.ctx || !this.ctx.route) {
1594
+ return false;
1595
+ }
1596
+ const route = this.ctx.route;
1597
+ return !!(Array.isArray(routeIdentifier) ? routeIdentifier : [routeIdentifier]).find(
1598
+ (identifier) => {
1599
+ if (route.pattern === identifier || route.name === identifier) {
1600
+ return true;
1601
+ }
1602
+ if (typeof route.handler === "function") {
1603
+ return false;
1604
+ }
1605
+ return route.handler.reference === identifier;
1606
+ }
1607
+ );
1608
+ }
1609
+ /**
1610
+ * Returns the best matching content type of the request by
1611
+ * matching against the given types.
1612
+ *
1613
+ * The content type is picked from the `content-type` header and request
1614
+ * must have body.
1615
+ *
1616
+ * The method response highly depends upon the types array values. Described below:
1617
+ *
1618
+ * | Type(s) | Return value |
1619
+ * |----------|---------------|
1620
+ * | ['json'] | json |
1621
+ * | ['application/*'] | application/json |
1622
+ * | ['vnd+json'] | application/json |
1623
+ *
1624
+ * @example
1625
+ * ```js
1626
+ * const bodyType = request.is(['json', 'xml'])
1627
+ *
1628
+ * if (bodyType === 'json') {
1629
+ * // process JSON
1630
+ * }
1631
+ *
1632
+ * if (bodyType === 'xml') {
1633
+ * // process XML
1634
+ * }
1635
+ * ```
1636
+ */
1637
+ is(types) {
1638
+ return typeIs(this.request, types) || null;
1639
+ }
1640
+ /**
1641
+ * Returns the best type using `Accept` header and
1642
+ * by matching it against the given types.
1643
+ *
1644
+ * If nothing is matched, then `null` will be returned
1645
+ *
1646
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1647
+ * docs too.
1648
+ *
1649
+ * @example
1650
+ * ```js
1651
+ * switch (request.accepts(['json', 'html'])) {
1652
+ * case 'json':
1653
+ * return response.json(user)
1654
+ * case 'html':
1655
+ * return view.render('user', { user })
1656
+ * default:
1657
+ * // decide yourself
1658
+ * }
1659
+ * ```
1660
+ */
1661
+ accepts(types) {
1662
+ this.#initiateAccepts();
1663
+ return this.#lazyAccepts.type(types) || null;
1664
+ }
1665
+ /**
1666
+ * Return the types that the request accepts, in the order of the
1667
+ * client's preference (most preferred first).
1668
+ *
1669
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1670
+ * docs too.
1671
+ */
1672
+ types() {
1673
+ this.#initiateAccepts();
1674
+ return this.#lazyAccepts.types();
1675
+ }
1676
+ /**
1677
+ * Returns the best language using `Accept-language` header
1678
+ * and by matching it against the given languages.
1679
+ *
1680
+ * If nothing is matched, then `null` will be returned
1681
+ *
1682
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1683
+ * docs too.
1684
+ *
1685
+ * @example
1686
+ * ```js
1687
+ * switch (request.language(['fr', 'de'])) {
1688
+ * case 'fr':
1689
+ * return view.render('about', { lang: 'fr' })
1690
+ * case 'de':
1691
+ * return view.render('about', { lang: 'de' })
1692
+ * default:
1693
+ * return view.render('about', { lang: 'en' })
1694
+ * }
1695
+ * ```
1696
+ */
1697
+ language(languages) {
1698
+ this.#initiateAccepts();
1699
+ return this.#lazyAccepts.language(languages) || null;
1700
+ }
1701
+ /**
1702
+ * Return the languages that the request accepts, in the order of the
1703
+ * client's preference (most preferred first).
1704
+ *
1705
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1706
+ * docs too.
1707
+ */
1708
+ languages() {
1709
+ this.#initiateAccepts();
1710
+ return this.#lazyAccepts.languages();
1711
+ }
1712
+ /**
1713
+ * Returns the best charset using `Accept-charset` header
1714
+ * and by matching it against the given charsets.
1715
+ *
1716
+ * If nothing is matched, then `null` will be returned
1717
+ *
1718
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1719
+ * docs too.
1720
+ *
1721
+ * @example
1722
+ * ```js
1723
+ * switch (request.charset(['utf-8', 'ISO-8859-1'])) {
1724
+ * case 'utf-8':
1725
+ * // make utf-8 friendly response
1726
+ * case 'ISO-8859-1':
1727
+ * // make ISO-8859-1 friendly response
1728
+ * }
1729
+ * ```
1730
+ */
1731
+ charset(charsets) {
1732
+ this.#initiateAccepts();
1733
+ return this.#lazyAccepts.charset(charsets) || null;
1734
+ }
1735
+ /**
1736
+ * Return the charsets that the request accepts, in the order of the
1737
+ * client's preference (most preferred first).
1738
+ *
1739
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1740
+ * docs too.
1741
+ */
1742
+ charsets() {
1743
+ this.#initiateAccepts();
1744
+ return this.#lazyAccepts.charsets();
1745
+ }
1746
+ /**
1747
+ * Returns the best encoding using `Accept-encoding` header
1748
+ * and by matching it against the given encodings.
1749
+ *
1750
+ * If nothing is matched, then `null` will be returned
1751
+ *
1752
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1753
+ * docs too.
1754
+ */
1755
+ encoding(encodings) {
1756
+ this.#initiateAccepts();
1757
+ return this.#lazyAccepts.encoding(encodings) || null;
1758
+ }
1759
+ /**
1760
+ * Return the charsets that the request accepts, in the order of the
1761
+ * client's preference (most preferred first).
1762
+ *
1763
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
1764
+ * docs too.
1765
+ */
1766
+ encodings() {
1767
+ this.#initiateAccepts();
1768
+ return this.#lazyAccepts.encodings();
1769
+ }
1770
+ /**
1771
+ * Returns a boolean telling if request has body
1772
+ */
1773
+ hasBody() {
1774
+ return typeIs.hasBody(this.request);
1775
+ }
1776
+ /**
1777
+ * Returns a boolean telling if the new response etag evaluates same
1778
+ * as the request header `if-none-match`. In case of `true`, the
1779
+ * server must return `304` response, telling the browser to
1780
+ * use the client cache.
1781
+ *
1782
+ * You won't have to deal with this method directly, since AdonisJs will
1783
+ * handle this for you when `http.etag = true` inside `config/app.js` file.
1784
+ *
1785
+ * However, this is how you can use it manually.
1786
+ *
1787
+ * ```js
1788
+ * const responseBody = view.render('some-view')
1789
+ *
1790
+ * // sets the HTTP etag header for response
1791
+ * response.setEtag(responseBody)
1792
+ *
1793
+ * if (request.fresh()) {
1794
+ * response.sendStatus(304)
1795
+ * } else {
1796
+ * response.send(responseBody)
1797
+ * }
1798
+ * ```
1799
+ */
1800
+ fresh() {
1801
+ if (["GET", "HEAD"].indexOf(this.intended()) === -1) {
1802
+ return false;
1803
+ }
1804
+ const status = this.response.statusCode;
1805
+ if (status >= 200 && status < 300 || status === 304) {
1806
+ return fresh(this.headers(), this.response.getHeaders());
1807
+ }
1808
+ return false;
1809
+ }
1810
+ /**
1811
+ * Opposite of [[fresh]]
1812
+ */
1813
+ stale() {
1814
+ return !this.fresh();
1815
+ }
1816
+ /**
1817
+ * Returns all parsed and signed cookies. Signed cookies ensures
1818
+ * that their value isn't tampered.
1819
+ */
1820
+ cookiesList() {
1821
+ this.#initiateCookieParser();
1822
+ return this.#cookieParser.list();
1823
+ }
1824
+ /**
1825
+ * Returns value for a given key from signed cookies. Optional
1826
+ * defaultValue is returned when actual value is undefined.
1827
+ */
1828
+ cookie(key, defaultValue) {
1829
+ this.#initiateCookieParser();
1830
+ return this.#cookieParser.unsign(key) || defaultValue;
1831
+ }
1832
+ /**
1833
+ * Returns value for a given key from signed cookies. Optional
1834
+ * defaultValue is returned when actual value is undefined.
1835
+ */
1836
+ encryptedCookie(key, defaultValue) {
1837
+ this.#initiateCookieParser();
1838
+ return this.#cookieParser.decrypt(key) || defaultValue;
1839
+ }
1840
+ plainCookie(key, defaultValueOrOptions, encoded) {
1841
+ this.#initiateCookieParser();
1842
+ if (is2.object(defaultValueOrOptions)) {
1843
+ return this.#cookieParser.decode(key, defaultValueOrOptions?.encoded) || defaultValueOrOptions.defaultValue;
1844
+ }
1845
+ return this.#cookieParser.decode(key, encoded) || defaultValueOrOptions;
1846
+ }
1847
+ /**
1848
+ * Returns a boolean telling if a signed url as a valid signature
1849
+ * or not.
1850
+ */
1851
+ hasValidSignature(purpose) {
1852
+ const { signature, ...rest } = this.qs();
1853
+ if (!signature) {
1854
+ return false;
1855
+ }
1856
+ const signedUrl = this.#encryption.verifier.unsign(signature, purpose);
1857
+ if (!signedUrl) {
1858
+ return false;
1859
+ }
1860
+ const queryString = this.#qsParser.stringify(rest);
1861
+ return queryString ? safeEqual(signedUrl, `${this.url()}?${queryString}`) : safeEqual(signedUrl, this.url());
1862
+ }
1863
+ /**
1864
+ * Serializes request to JSON format
1865
+ */
1866
+ serialize() {
1867
+ return {
1868
+ id: this.id(),
1869
+ url: this.url(),
1870
+ query: this.parsedUrl.query,
1871
+ body: this.all(),
1872
+ params: this.params(),
1873
+ headers: this.headers(),
1874
+ method: this.method(),
1875
+ protocol: this.protocol(),
1876
+ cookies: this.cookiesList(),
1877
+ hostname: this.hostname(),
1878
+ ip: this.ip(),
1879
+ subdomains: this.ctx?.subdomains || {}
1880
+ };
1881
+ }
1882
+ /**
1883
+ * toJSON copy of the request
1884
+ */
1885
+ toJSON() {
1886
+ return this.serialize();
1887
+ }
1888
+ };
1889
+
1890
+ // src/redirect.ts
1891
+ import { parse as parse2 } from "node:url";
1892
+ import encodeUrl from "encodeurl";
1893
+ var Redirect = class {
1894
+ /**
1895
+ * A boolean to forward the existing query string
1896
+ */
1897
+ #forwardQueryString = false;
1898
+ /**
1899
+ * The status code for the redirect
1900
+ */
1901
+ #statusCode = 302;
1902
+ /**
1903
+ * A custom query string to forward
1904
+ */
1905
+ #queryString = {};
1906
+ #request;
1907
+ #response;
1908
+ #router;
1909
+ #qs;
1910
+ constructor(request, response, router, qs) {
1911
+ this.#request = request;
1912
+ this.#response = response;
1913
+ this.#router = router;
1914
+ this.#qs = qs;
1915
+ }
1916
+ /**
1917
+ * Sends response by setting require headers
1918
+ */
1919
+ #sendResponse(url, query) {
1920
+ const stringified = this.#qs.stringify(query);
1921
+ url = stringified ? `${url}?${stringified}` : url;
1922
+ debug_default('redirecting to url "%s"', url);
1923
+ this.#response.location(encodeUrl(url));
1924
+ this.#response.safeStatus(this.#statusCode);
1925
+ this.#response.type("text/plain; charset=utf-8");
1926
+ this.#response.send(`Redirecting to ${url}`);
1927
+ }
1928
+ /**
1929
+ * Returns the referrer url
1930
+ */
1931
+ #getReferrerUrl() {
1932
+ let url = this.#request.headers["referer"] || this.#request.headers["referrer"] || "/";
1933
+ return Array.isArray(url) ? url[0] : url;
1934
+ }
1935
+ /**
1936
+ * Set a custom status code.
1937
+ */
1938
+ status(statusCode) {
1939
+ this.#statusCode = statusCode;
1940
+ return this;
1941
+ }
1942
+ /**
1943
+ * Clearing query string values added using the
1944
+ * "withQs" method
1945
+ */
1946
+ clearQs() {
1947
+ this.#forwardQueryString = false;
1948
+ this.#queryString = {};
1949
+ return this;
1950
+ }
1951
+ withQs(name, value) {
1952
+ if (typeof name === "undefined") {
1953
+ this.#forwardQueryString = true;
1954
+ return this;
1955
+ }
1956
+ if (typeof name === "string") {
1957
+ this.#queryString[name] = value;
1958
+ return this;
1959
+ }
1960
+ Object.assign(this.#queryString, name);
1961
+ return this;
1962
+ }
1963
+ /**
1964
+ * Redirect to the previous path.
1965
+ */
1966
+ back() {
1967
+ let query = {};
1968
+ const referrerUrl = this.#getReferrerUrl();
1969
+ const url = parse2(referrerUrl);
1970
+ debug_default('referrer url "%s"', referrerUrl);
1971
+ debug_default('referrer base url "%s"', url.pathname);
1972
+ if (this.#forwardQueryString) {
1973
+ query = this.#qs.parse(url.query || "");
1974
+ }
1975
+ Object.assign(query, this.#queryString);
1976
+ this.#sendResponse(url.pathname || "", query);
1977
+ }
1978
+ /**
1979
+ * Redirect the request using a route identifier.
1980
+ */
1981
+ toRoute(routeIdentifier, params, options) {
1982
+ if (options && options.qs) {
1983
+ this.withQs(options.qs);
1984
+ options.qs = void 0;
1985
+ }
1986
+ const url = this.#router.makeUrl(routeIdentifier, params, options);
1987
+ return this.toPath(url);
1988
+ }
1989
+ /**
1990
+ * Redirect the request using a path.
1991
+ */
1992
+ toPath(url) {
1993
+ let query = {};
1994
+ if (this.#forwardQueryString) {
1995
+ query = this.#qs.parse(parse2(this.#request.url).query || "");
1996
+ }
1997
+ Object.assign(query, this.#queryString);
1998
+ this.#sendResponse(url, query);
1999
+ }
2000
+ };
2001
+
2002
+ // src/exceptions.ts
2003
+ var exceptions_exports = {};
2004
+ __export(exceptions_exports, {
2005
+ E_CANNOT_LOOKUP_ROUTE: () => E_CANNOT_LOOKUP_ROUTE,
2006
+ E_HTTP_EXCEPTION: () => E_HTTP_EXCEPTION,
2007
+ E_HTTP_REQUEST_ABORTED: () => E_HTTP_REQUEST_ABORTED,
2008
+ E_ROUTE_NOT_FOUND: () => E_ROUTE_NOT_FOUND
2009
+ });
2010
+ import { createError, Exception } from "@poppinss/utils";
2011
+ var E_ROUTE_NOT_FOUND = createError(
2012
+ "Cannot %s:%s",
2013
+ "E_ROUTE_NOT_FOUND",
2014
+ 404
2015
+ );
2016
+ var E_CANNOT_LOOKUP_ROUTE = createError(
2017
+ 'Cannot lookup route "%s"',
2018
+ "E_CANNOT_LOOKUP_ROUTE",
2019
+ 500
2020
+ );
2021
+ var E_HTTP_EXCEPTION = class HttpException extends Exception {
2022
+ body;
2023
+ static code = "E_HTTP_EXCEPTION";
2024
+ /**
2025
+ * This method returns an instance of the exception class
2026
+ */
2027
+ static invoke(body, status, code = "E_HTTP_EXCEPTION") {
2028
+ if (body === null || body === void 0) {
2029
+ const error2 = new this("HTTP Exception", { status, code });
2030
+ error2.body = "Internal server error";
2031
+ return error2;
2032
+ }
2033
+ if (typeof body === "object") {
2034
+ const error2 = new this(body.message || "HTTP Exception", { status, code });
2035
+ error2.body = body;
2036
+ return error2;
2037
+ }
2038
+ const error = new this(body, { status, code });
2039
+ error.body = body;
2040
+ return error;
2041
+ }
2042
+ };
2043
+ var E_HTTP_REQUEST_ABORTED = class AbortException extends E_HTTP_EXCEPTION {
2044
+ handle(error, ctx) {
2045
+ ctx.response.status(error.status).send(error.body);
2046
+ }
2047
+ };
2048
+
2049
+ // src/response.ts
2050
+ import etag from "etag";
2051
+ import vary from "vary";
2052
+ import fresh2 from "fresh";
2053
+ import mime from "mime-types";
2054
+ import destroy from "destroy";
2055
+ import { extname } from "node:path";
2056
+ import onFinished from "on-finished";
2057
+ import json from "@poppinss/utils/json";
2058
+ import Macroable6 from "@poppinss/macroable";
2059
+ import { createReadStream } from "node:fs";
2060
+ import { stat } from "node:fs/promises";
2061
+ import { RuntimeException as RuntimeException3 } from "@poppinss/utils";
2062
+ import contentDisposition from "content-disposition";
2063
+
2064
+ // src/cookies/serializer.ts
2065
+ import cookie2 from "cookie";
2066
+ import string2 from "@poppinss/utils/string";
2067
+ var CookieSerializer = class {
2068
+ #client;
2069
+ constructor(encryption) {
2070
+ this.#client = new CookieClient(encryption);
2071
+ }
2072
+ /**
2073
+ * Serializes the key-value pair to a string, that can be set on the
2074
+ * `Set-Cookie` header.
2075
+ */
2076
+ #serializeAsCookie(key, value, options) {
2077
+ let expires = options?.expires;
2078
+ if (typeof expires === "function") {
2079
+ expires = expires();
2080
+ }
2081
+ let maxAge = options?.maxAge ? string2.seconds.parse(options?.maxAge) : void 0;
2082
+ const parsedOptions = Object.assign({}, options, { maxAge, expires });
2083
+ return cookie2.serialize(key, value, parsedOptions);
2084
+ }
2085
+ /**
2086
+ * Encodes value as a plain cookie. By default, the plain value will be converted
2087
+ * to a string using "JSON.stringify" method and then encoded as a base64 string.
2088
+ *
2089
+ * You can disable encoding of the cookie by setting `options.encoded = false`.
2090
+ *
2091
+ * ```ts
2092
+ * serializer.encode('name', 'virk')
2093
+ * ```
2094
+ */
2095
+ encode(key, value, options) {
2096
+ const packedValue = options?.encode === false ? value : this.#client.encode(key, value);
2097
+ if (packedValue === null || packedValue === void 0) {
2098
+ return null;
2099
+ }
2100
+ return this.#serializeAsCookie(key, packedValue, options);
2101
+ }
2102
+ /**
2103
+ * Sign a key-value pair to a signed cookie. The signed value has a
2104
+ * verification hash attached to it to detect data tampering.
2105
+ */
2106
+ sign(key, value, options) {
2107
+ const packedValue = this.#client.sign(key, value);
2108
+ if (packedValue === null) {
2109
+ return null;
2110
+ }
2111
+ return this.#serializeAsCookie(key, packedValue, options);
2112
+ }
2113
+ /**
2114
+ * Encrypts a key-value pair to an encrypted cookie.
2115
+ */
2116
+ encrypt(key, value, options) {
2117
+ const packedValue = this.#client.encrypt(key, value);
2118
+ if (packedValue === null) {
2119
+ return null;
2120
+ }
2121
+ return this.#serializeAsCookie(key, packedValue, options);
2122
+ }
2123
+ };
2124
+
2125
+ // src/response.ts
2126
+ var CACHEABLE_HTTP_METHODS = ["GET", "HEAD"];
2127
+ var Response = class extends Macroable6 {
2128
+ constructor(request, response, encryption, config, router, qs) {
2129
+ super();
2130
+ this.request = request;
2131
+ this.response = response;
2132
+ this.#qs = qs;
2133
+ this.#config = config;
2134
+ this.#router = router;
2135
+ this.#cookieSerializer = new CookieSerializer(encryption);
2136
+ }
2137
+ /**
2138
+ * Query string parser
2139
+ */
2140
+ #qs;
2141
+ /**
2142
+ * Outgoing headers
2143
+ */
2144
+ #headers = {};
2145
+ /**
2146
+ * Has explicit status been set
2147
+ */
2148
+ #hasExplicitStatus = false;
2149
+ /**
2150
+ * Cookies serializer to serialize the outgoing cookies
2151
+ */
2152
+ #cookieSerializer;
2153
+ /**
2154
+ * Router is used to make the redirect URLs from routes
2155
+ */
2156
+ #router;
2157
+ /**
2158
+ * Response config
2159
+ */
2160
+ #config;
2161
+ /**
2162
+ * Does response has body set that will written to the
2163
+ * response socket at the end of the request
2164
+ */
2165
+ get hasLazyBody() {
2166
+ return !!(this.lazyBody.content || this.lazyBody.fileToStream || this.lazyBody.stream);
2167
+ }
2168
+ /**
2169
+ * Find if the response has non-stream content
2170
+ */
2171
+ get hasContent() {
2172
+ return !!this.lazyBody.content;
2173
+ }
2174
+ /**
2175
+ * Returns true when response body is set using "response.stream"
2176
+ * method
2177
+ */
2178
+ get hasStream() {
2179
+ return !!this.lazyBody.stream;
2180
+ }
2181
+ /**
2182
+ * Returns true when response body is set using "response.download"
2183
+ * or "response.attachment" methods
2184
+ */
2185
+ get hasFileToStream() {
2186
+ return !!this.lazyBody.fileToStream;
2187
+ }
2188
+ /**
2189
+ * Returns the response content. Check if the response
2190
+ * has content using the "hasContent" method
2191
+ */
2192
+ get content() {
2193
+ return this.lazyBody.content;
2194
+ }
2195
+ /**
2196
+ * Returns reference to the stream set using "response.stream"
2197
+ * method
2198
+ */
2199
+ get outgoingStream() {
2200
+ return this.lazyBody.stream?.[0];
2201
+ }
2202
+ /**
2203
+ * Returns reference to the file path set using "response.stream"
2204
+ * method.
2205
+ */
2206
+ get fileToStream() {
2207
+ return this.lazyBody.fileToStream ? {
2208
+ path: this.lazyBody.fileToStream[0],
2209
+ generateEtag: this.lazyBody.fileToStream[1]
2210
+ } : void 0;
2211
+ }
2212
+ /**
2213
+ * Lazy body is used to set the response body. However, do not
2214
+ * write it on the socket immediately unless `response.finish`
2215
+ * is called.
2216
+ */
2217
+ lazyBody = {};
2218
+ /**
2219
+ * The ctx will be set by the context itself. It creates a circular
2220
+ * reference
2221
+ */
2222
+ ctx;
2223
+ /**
2224
+ * Returns a boolean telling if response is finished or not.
2225
+ * Any more attempts to update headers or body will result
2226
+ * in raised exceptions.
2227
+ */
2228
+ get finished() {
2229
+ return this.response.writableFinished;
2230
+ }
2231
+ /**
2232
+ * Returns a boolean telling if response headers has been sent or not.
2233
+ * Any more attempts to update headers will result in raised
2234
+ * exceptions.
2235
+ */
2236
+ get headersSent() {
2237
+ return this.response.headersSent;
2238
+ }
2239
+ /**
2240
+ * Returns a boolean telling if response headers and body is written
2241
+ * or not. When value is `true`, you can feel free to write headers
2242
+ * and body.
2243
+ */
2244
+ get isPending() {
2245
+ return !this.headersSent && !this.finished;
2246
+ }
2247
+ /**
2248
+ * Normalizes header value to a string or an array of string
2249
+ */
2250
+ #castHeaderValue(value) {
2251
+ return Array.isArray(value) ? value.map(String) : String(value);
2252
+ }
2253
+ /**
2254
+ * Ends the response by flushing headers and writing body
2255
+ */
2256
+ #endResponse(body, statusCode) {
2257
+ this.writeHead(statusCode);
2258
+ const res = this.response;
2259
+ res.end(body, null, null);
2260
+ }
2261
+ /**
2262
+ * Returns type for the content body. Only following types are allowed
2263
+ *
2264
+ * - Dates
2265
+ * - Arrays
2266
+ * - Booleans
2267
+ * - Objects
2268
+ * - Strings
2269
+ * - Buffer
2270
+ */
2271
+ #getDataType(content) {
2272
+ if (Buffer.isBuffer(content)) {
2273
+ return "buffer";
2274
+ }
2275
+ if (content instanceof Date) {
2276
+ return "date";
2277
+ }
2278
+ if (content instanceof RegExp) {
2279
+ return "regexp";
2280
+ }
2281
+ const dataType = typeof content;
2282
+ if (dataType === "number" || dataType === "boolean" || dataType === "string" || dataType === "bigint") {
2283
+ return dataType;
2284
+ }
2285
+ if (dataType === "object") {
2286
+ return "object";
2287
+ }
2288
+ throw new RuntimeException3(`Cannot serialize "${dataType}" to HTTP response`);
2289
+ }
2290
+ /**
2291
+ * Writes the body with appropriate response headers. Etag header is set
2292
+ * when `generateEtag` is set to `true`.
2293
+ *
2294
+ * Empty body results in `204`.
2295
+ */
2296
+ writeBody(content, generateEtag, jsonpCallbackName) {
2297
+ const hasEmptyBody = content === null || content === void 0 || content === "";
2298
+ if (hasEmptyBody) {
2299
+ this.safeStatus(204);
2300
+ }
2301
+ const statusCode = this.response.statusCode;
2302
+ if (statusCode && (statusCode < 200 || statusCode === 204 || statusCode === 304)) {
2303
+ this.removeHeader("Content-Type");
2304
+ this.removeHeader("Content-Length");
2305
+ this.removeHeader("Transfer-Encoding");
2306
+ this.#endResponse();
2307
+ return;
2308
+ }
2309
+ if (hasEmptyBody) {
2310
+ this.removeHeader("Content-Length");
2311
+ this.#endResponse();
2312
+ return;
2313
+ }
2314
+ const dataType = this.#getDataType(content);
2315
+ if (dataType === "object") {
2316
+ content = json.safeStringify(content);
2317
+ } else if (dataType === "number" || dataType === "boolean" || dataType === "bigint" || dataType === "regexp") {
2318
+ content = String(content);
2319
+ } else if (dataType === "date") {
2320
+ content = content.toISOString();
2321
+ }
2322
+ if (jsonpCallbackName) {
2323
+ content = content.replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
2324
+ content = `/**/ typeof ${jsonpCallbackName} === 'function' && ${jsonpCallbackName}(${content});`;
2325
+ }
2326
+ if (generateEtag) {
2327
+ this.setEtag(content);
2328
+ }
2329
+ if (generateEtag && this.fresh()) {
2330
+ this.removeHeader("Content-Type");
2331
+ this.removeHeader("Content-Length");
2332
+ this.removeHeader("Transfer-Encoding");
2333
+ this.#endResponse(null, 304);
2334
+ return;
2335
+ }
2336
+ this.header("Content-Length", Buffer.byteLength(content));
2337
+ if (jsonpCallbackName) {
2338
+ this.header("X-Content-Type-Options", "nosniff");
2339
+ this.safeHeader("Content-Type", "text/javascript; charset=utf-8");
2340
+ } else {
2341
+ switch (dataType) {
2342
+ case "string":
2343
+ const type = /^\s*</.test(content) ? "text/html" : "text/plain";
2344
+ this.safeHeader("Content-Type", `${type}; charset=utf-8`);
2345
+ break;
2346
+ case "number":
2347
+ case "boolean":
2348
+ case "date":
2349
+ case "bigint":
2350
+ case "regexp":
2351
+ this.safeHeader("Content-Type", "text/plain; charset=utf-8");
2352
+ break;
2353
+ case "buffer":
2354
+ this.safeHeader("Content-Type", "application/octet-stream; charset=utf-8");
2355
+ break;
2356
+ case "object":
2357
+ this.safeHeader("Content-Type", "application/json; charset=utf-8");
2358
+ break;
2359
+ }
2360
+ }
2361
+ this.#endResponse(content);
2362
+ }
2363
+ /**
2364
+ * Stream the body to the response and handles cleaning up the stream
2365
+ */
2366
+ streamBody(body, errorCallback) {
2367
+ return new Promise((resolve) => {
2368
+ let finished = false;
2369
+ body.on("error", (error) => {
2370
+ if (finished) {
2371
+ return;
2372
+ }
2373
+ finished = true;
2374
+ destroy(body);
2375
+ this.type("text");
2376
+ if (!this.headersSent) {
2377
+ if (typeof errorCallback === "function") {
2378
+ this.#endResponse(...errorCallback(error));
2379
+ } else {
2380
+ this.#endResponse(
2381
+ error.code === "ENOENT" ? "File not found" : "Cannot process file",
2382
+ error.code === "ENOENT" ? 404 : 500
2383
+ );
2384
+ }
2385
+ } else {
2386
+ this.response.destroy();
2387
+ }
2388
+ resolve();
2389
+ });
2390
+ body.on("end", () => {
2391
+ if (!this.headersSent) {
2392
+ this.#endResponse();
2393
+ }
2394
+ resolve();
2395
+ });
2396
+ onFinished(this.response, () => {
2397
+ finished = true;
2398
+ destroy(body);
2399
+ });
2400
+ this.relayHeaders();
2401
+ body.pipe(this.response);
2402
+ });
2403
+ }
2404
+ /**
2405
+ * Downloads a file by streaming it to the response
2406
+ */
2407
+ async streamFileForDownload(filePath, generateEtag, errorCallback) {
2408
+ try {
2409
+ const stats = await stat(filePath);
2410
+ if (!stats || !stats.isFile()) {
2411
+ throw new TypeError("response.download only accepts path to a file");
2412
+ }
2413
+ this.header("Last-Modified", stats.mtime.toUTCString());
2414
+ this.type(extname(filePath));
2415
+ if (generateEtag) {
2416
+ this.setEtag(stats, true);
2417
+ }
2418
+ if (this.request.method === "HEAD") {
2419
+ this.#endResponse(null, generateEtag && this.fresh() ? 304 : 200);
2420
+ return;
2421
+ }
2422
+ if (generateEtag && this.fresh()) {
2423
+ this.#endResponse(null, 304);
2424
+ return;
2425
+ }
2426
+ this.header("Content-length", stats.size);
2427
+ return this.streamBody(createReadStream(filePath), errorCallback);
2428
+ } catch (error) {
2429
+ this.type("text");
2430
+ this.removeHeader("Etag");
2431
+ if (typeof errorCallback === "function") {
2432
+ this.#endResponse(...errorCallback(error));
2433
+ } else {
2434
+ this.#endResponse(
2435
+ error.code === "ENOENT" ? "File not found" : "Cannot process file",
2436
+ error.code === "ENOENT" ? 404 : 500
2437
+ );
2438
+ }
2439
+ }
2440
+ }
2441
+ /**
2442
+ * Writes headers with the Node.js res object using the
2443
+ * response.setHeader method
2444
+ */
2445
+ relayHeaders() {
2446
+ if (!this.headersSent) {
2447
+ for (let key in this.#headers) {
2448
+ const value = this.#headers[key];
2449
+ if (value) {
2450
+ this.response.setHeader(key, value);
2451
+ }
2452
+ }
2453
+ }
2454
+ }
2455
+ /**
2456
+ * Calls res.writeHead on the Node.js res object.
2457
+ */
2458
+ writeHead(statusCode) {
2459
+ this.response.writeHead(statusCode || this.response.statusCode, this.#headers);
2460
+ return this;
2461
+ }
2462
+ /**
2463
+ * Returns the existing value for a given HTTP response
2464
+ * header.
2465
+ */
2466
+ getHeader(key) {
2467
+ const value = this.#headers[key.toLowerCase()];
2468
+ return value === void 0 ? this.response.getHeader(key) : value;
2469
+ }
2470
+ /**
2471
+ * Get response headers
2472
+ */
2473
+ getHeaders() {
2474
+ return {
2475
+ ...this.response.getHeaders(),
2476
+ ...this.#headers
2477
+ };
2478
+ }
2479
+ /**
2480
+ * Set header on the response. To `append` values to the existing header, we suggest
2481
+ * using [[append]] method.
2482
+ *
2483
+ * If `value` is non existy, then header won't be set.
2484
+ *
2485
+ * @example
2486
+ * ```js
2487
+ * response.header('content-type', 'application/json')
2488
+ * ```
2489
+ */
2490
+ header(key, value) {
2491
+ if (value === null || value === void 0) {
2492
+ return this;
2493
+ }
2494
+ this.#headers[key.toLowerCase()] = this.#castHeaderValue(value);
2495
+ return this;
2496
+ }
2497
+ /**
2498
+ * Append value to an existing header. To replace the value, we suggest using
2499
+ * [[header]] method.
2500
+ *
2501
+ * If `value` is not existy, then header won't be set.
2502
+ *
2503
+ * @example
2504
+ * ```js
2505
+ * response.append('set-cookie', 'username=virk')
2506
+ * ```
2507
+ */
2508
+ append(key, value) {
2509
+ if (value === null || value === void 0) {
2510
+ return this;
2511
+ }
2512
+ key = key.toLowerCase();
2513
+ let existingHeader = this.getHeader(key);
2514
+ let casted = this.#castHeaderValue(value);
2515
+ if (!existingHeader) {
2516
+ this.#headers[key] = casted;
2517
+ return this;
2518
+ }
2519
+ existingHeader = this.#castHeaderValue(existingHeader);
2520
+ casted = Array.isArray(existingHeader) ? existingHeader.concat(casted) : [existingHeader].concat(casted);
2521
+ this.#headers[key] = casted;
2522
+ return this;
2523
+ }
2524
+ /**
2525
+ * Adds HTTP response header, when it doesn't exists already.
2526
+ */
2527
+ safeHeader(key, value) {
2528
+ if (!this.getHeader(key)) {
2529
+ this.header(key, value);
2530
+ }
2531
+ return this;
2532
+ }
2533
+ /**
2534
+ * Removes the existing response header from being sent.
2535
+ */
2536
+ removeHeader(key) {
2537
+ key = key.toLowerCase();
2538
+ this.response.removeHeader(key);
2539
+ if (this.#headers[key]) {
2540
+ delete this.#headers[key.toLowerCase()];
2541
+ }
2542
+ return this;
2543
+ }
2544
+ /**
2545
+ * Returns the status code for the response
2546
+ */
2547
+ getStatus() {
2548
+ return this.response.statusCode;
2549
+ }
2550
+ /**
2551
+ * Set HTTP status code
2552
+ */
2553
+ status(code) {
2554
+ this.#hasExplicitStatus = true;
2555
+ this.response.statusCode = code;
2556
+ return this;
2557
+ }
2558
+ /**
2559
+ * Set's status code only when it's not explictly
2560
+ * set
2561
+ */
2562
+ safeStatus(code) {
2563
+ if (this.#hasExplicitStatus) {
2564
+ return this;
2565
+ }
2566
+ this.response.statusCode = code;
2567
+ return this;
2568
+ }
2569
+ /**
2570
+ * Set response type by looking up for the mime-type using
2571
+ * partial types like file extensions.
2572
+ *
2573
+ * Make sure to read [mime-types](https://www.npmjs.com/package/mime-types) docs
2574
+ * too.
2575
+ *
2576
+ * @example
2577
+ * ```js
2578
+ * response.type('.json') // Content-type: application/json
2579
+ * ```
2580
+ */
2581
+ type(type, charset) {
2582
+ type = charset ? `${type}; charset=${charset}` : type;
2583
+ this.header("Content-Type", mime.contentType(type));
2584
+ return this;
2585
+ }
2586
+ /**
2587
+ * Set the Vary HTTP header
2588
+ */
2589
+ vary(field) {
2590
+ vary(this.response, field);
2591
+ return this;
2592
+ }
2593
+ /**
2594
+ * Set etag by computing hash from the body. This class will set the etag automatically
2595
+ * when `etag = true` in the defined config object.
2596
+ *
2597
+ * Use this function, when you want to compute etag manually for some other resons.
2598
+ */
2599
+ setEtag(body, weak = false) {
2600
+ this.header("Etag", etag(body, { weak }));
2601
+ return this;
2602
+ }
2603
+ /**
2604
+ * Returns a boolean telling if the new response etag evaluates same
2605
+ * as the request header `if-none-match`. In case of `true`, the
2606
+ * server must return `304` response, telling the browser to
2607
+ * use the client cache.
2608
+ *
2609
+ * You won't have to deal with this method directly, since AdonisJs will
2610
+ * handle this for you when `http.etag = true` inside `config/app.js` file.
2611
+ *
2612
+ * However, this is how you can use it manually.
2613
+ *
2614
+ * @example
2615
+ * ```js
2616
+ * const responseBody = view.render('some-view')
2617
+ *
2618
+ * // sets the HTTP etag header for response
2619
+ * response.setEtag(responseBody)
2620
+ *
2621
+ * if (response.fresh()) {
2622
+ * response.sendStatus(304)
2623
+ * } else {
2624
+ * response.send(responseBody)
2625
+ * }
2626
+ * ```
2627
+ */
2628
+ fresh() {
2629
+ if (this.request.method && !CACHEABLE_HTTP_METHODS.includes(this.request.method)) {
2630
+ return false;
2631
+ }
2632
+ const status = this.response.statusCode;
2633
+ if (status >= 200 && status < 300 || status === 304) {
2634
+ return fresh2(this.request.headers, this.#headers);
2635
+ }
2636
+ return false;
2637
+ }
2638
+ /**
2639
+ * Returns the response body. Returns null when response
2640
+ * body is a stream
2641
+ */
2642
+ getBody() {
2643
+ if (this.lazyBody.content) {
2644
+ return this.lazyBody.content[0];
2645
+ }
2646
+ return null;
2647
+ }
2648
+ /**
2649
+ * Send the body as response and optionally generate etag. The default value
2650
+ * is read from `config/app.js` file, using `http.etag` property.
2651
+ *
2652
+ * This method buffers the body if `explicitEnd = true`, which is the default
2653
+ * behavior and do not change, unless you know what you are doing.
2654
+ */
2655
+ send(body, generateEtag = this.#config.etag) {
2656
+ this.lazyBody.content = [body, generateEtag];
2657
+ }
2658
+ /**
2659
+ * Alias of [[send]]
2660
+ */
2661
+ json(body, generateEtag = this.#config.etag) {
2662
+ return this.send(body, generateEtag);
2663
+ }
2664
+ /**
2665
+ * Writes response as JSONP. The callback name is resolved as follows, with priority
2666
+ * from top to bottom.
2667
+ *
2668
+ * 1. Explicitly defined as 2nd Param.
2669
+ * 2. Fetch from request query string.
2670
+ * 3. Use the config value `http.jsonpCallbackName` from `config/app.js`.
2671
+ * 4. Fallback to `callback`.
2672
+ *
2673
+ * This method buffers the body if `explicitEnd = true`, which is the default
2674
+ * behavior and do not change, unless you know what you are doing.
2675
+ */
2676
+ jsonp(body, callbackName = this.#config.jsonpCallbackName, generateEtag = this.#config.etag) {
2677
+ this.lazyBody.content = [body, generateEtag, callbackName];
2678
+ }
2679
+ /**
2680
+ * Pipe stream to the response. This method will gracefully destroy
2681
+ * the stream, avoiding memory leaks.
2682
+ *
2683
+ * If `raiseErrors=false`, then this method will self handle all the exceptions by
2684
+ * writing a generic HTTP response. To have more control over the error, it is
2685
+ * recommended to set `raiseErrors=true` and wrap this function inside a
2686
+ * `try/catch` statement.
2687
+ *
2688
+ * Streaming a file from the disk and showing 404 when file is missing.
2689
+ *
2690
+ * @example
2691
+ * ```js
2692
+ * // Errors handled automatically with generic HTTP response
2693
+ * response.stream(fs.createReadStream('file.txt'))
2694
+ *
2695
+ * // Manually handle (note the await call)
2696
+ * try {
2697
+ * await response.stream(fs.createReadStream('file.txt'))
2698
+ * } catch () {
2699
+ * response.status(404).send('File not found')
2700
+ * }
2701
+ * ```
2702
+ */
2703
+ stream(body, errorCallback) {
2704
+ if (typeof body.pipe !== "function" || !body.readable || typeof body.read !== "function") {
2705
+ throw new TypeError("response.stream accepts a readable stream only");
2706
+ }
2707
+ this.lazyBody.stream = [body, errorCallback];
2708
+ }
2709
+ /**
2710
+ * Download file by streaming it from the file path. This method will setup
2711
+ * appropriate `Content-type`, `Content-type` and `Last-modified` headers.
2712
+ *
2713
+ * Unexpected stream errors are handled gracefully to avoid memory leaks.
2714
+ *
2715
+ * If `raiseErrors=false`, then this method will self handle all the exceptions by
2716
+ * writing a generic HTTP response. To have more control over the error, it is
2717
+ * recommended to set `raiseErrors=true` and wrap this function inside a
2718
+ * `try/catch` statement.
2719
+ *
2720
+ * @example
2721
+ * ```js
2722
+ * // Errors handled automatically with generic HTTP response
2723
+ * response.download('somefile.jpg')
2724
+ *
2725
+ * // Manually handle (note the await call)
2726
+ * try {
2727
+ * await response.download('somefile.jpg')
2728
+ * } catch (error) {
2729
+ * response.status(error.code === 'ENOENT' ? 404 : 500)
2730
+ * response.send('Cannot process file')
2731
+ * }
2732
+ * ```
2733
+ */
2734
+ download(filePath, generateEtag = this.#config.etag, errorCallback) {
2735
+ this.lazyBody.fileToStream = [filePath, generateEtag, errorCallback];
2736
+ }
2737
+ /**
2738
+ * Download the file by forcing the user to save the file vs displaying it
2739
+ * within the browser.
2740
+ *
2741
+ * Internally calls [[download]]
2742
+ */
2743
+ attachment(filePath, name, disposition, generateEtag, errorCallback) {
2744
+ name = name || filePath;
2745
+ this.header("Content-Disposition", contentDisposition(name, { type: disposition }));
2746
+ return this.download(filePath, generateEtag, errorCallback);
2747
+ }
2748
+ /**
2749
+ * Set the location header.
2750
+ *
2751
+ * @example
2752
+ * ```js
2753
+ * response.location('/login')
2754
+ * ```
2755
+ */
2756
+ location(url) {
2757
+ this.header("Location", url);
2758
+ return this;
2759
+ }
2760
+ redirect(path, forwardQueryString = false, statusCode = 302) {
2761
+ const handler = new Redirect(this.request, this, this.#router, this.#qs);
2762
+ if (forwardQueryString) {
2763
+ handler.withQs();
2764
+ }
2765
+ if (path === "back") {
2766
+ return handler.status(statusCode).back();
2767
+ }
2768
+ if (path) {
2769
+ return handler.status(statusCode).toPath(path);
2770
+ }
2771
+ return handler;
2772
+ }
2773
+ /**
2774
+ * Abort the request with custom body and a status code. 400 is
2775
+ * used when status is not defined
2776
+ */
2777
+ abort(body, status) {
2778
+ throw E_HTTP_REQUEST_ABORTED.invoke(body, status || 400);
2779
+ }
2780
+ /**
2781
+ * Abort the request with custom body and a status code when
2782
+ * passed condition returns `true`
2783
+ */
2784
+ abortIf(condition, body, status) {
2785
+ if (condition) {
2786
+ this.abort(body, status);
2787
+ }
2788
+ }
2789
+ /**
2790
+ * Abort the request with custom body and a status code when
2791
+ * passed condition returns `false`
2792
+ */
2793
+ abortUnless(condition, body, status) {
2794
+ if (!condition) {
2795
+ this.abort(body, status);
2796
+ }
2797
+ }
2798
+ /**
2799
+ * Set signed cookie as the response header. The inline options overrides
2800
+ * all options from the config.
2801
+ */
2802
+ cookie(key, value, options) {
2803
+ options = Object.assign({}, this.#config.cookie, options);
2804
+ const serialized = this.#cookieSerializer.sign(key, value, options);
2805
+ if (!serialized) {
2806
+ return this;
2807
+ }
2808
+ this.append("set-cookie", serialized);
2809
+ return this;
2810
+ }
2811
+ /**
2812
+ * Set encrypted cookie as the response header. The inline options overrides
2813
+ * all options from the config.
2814
+ */
2815
+ encryptedCookie(key, value, options) {
2816
+ options = Object.assign({}, this.#config.cookie, options);
2817
+ const serialized = this.#cookieSerializer.encrypt(key, value, options);
2818
+ if (!serialized) {
2819
+ return this;
2820
+ }
2821
+ this.append("set-cookie", serialized);
2822
+ return this;
2823
+ }
2824
+ /**
2825
+ * Set unsigned cookie as the response header. The inline options overrides
2826
+ * all options from the config.
2827
+ */
2828
+ plainCookie(key, value, options) {
2829
+ options = Object.assign({}, this.#config.cookie, options);
2830
+ const serialized = this.#cookieSerializer.encode(key, value, options);
2831
+ if (!serialized) {
2832
+ return this;
2833
+ }
2834
+ this.append("set-cookie", serialized);
2835
+ return this;
2836
+ }
2837
+ /**
2838
+ * Clear existing cookie.
2839
+ */
2840
+ clearCookie(key, options) {
2841
+ options = Object.assign({}, this.#config.cookie, options);
2842
+ options.expires = /* @__PURE__ */ new Date(1);
2843
+ options.maxAge = -1;
2844
+ const serialized = this.#cookieSerializer.encode(key, "", { ...options, encode: false });
2845
+ this.append("set-cookie", serialized);
2846
+ return this;
2847
+ }
2848
+ /**
2849
+ * Finishes the response by writing the lazy body, when `explicitEnd = true`
2850
+ * and response is already pending.
2851
+ *
2852
+ * Calling this method twice or when `explicitEnd = false` is noop.
2853
+ */
2854
+ finish() {
2855
+ if (!this.isPending) {
2856
+ return;
2857
+ }
2858
+ if (this.content) {
2859
+ this.writeBody(...this.content);
2860
+ return;
2861
+ }
2862
+ if (this.lazyBody.stream) {
2863
+ this.streamBody(...this.lazyBody.stream);
2864
+ return;
2865
+ }
2866
+ if (this.lazyBody.fileToStream) {
2867
+ this.streamFileForDownload(...this.lazyBody.fileToStream);
2868
+ return;
2869
+ }
2870
+ this.#endResponse();
2871
+ }
2872
+ /**
2873
+ * Shorthand method to finish request with "100" status code
2874
+ */
2875
+ continue() {
2876
+ this.status(100);
2877
+ return this.send(null, false);
2878
+ }
2879
+ /**
2880
+ * Shorthand method to finish request with "101" status code
2881
+ */
2882
+ switchingProtocols() {
2883
+ this.status(101);
2884
+ return this.send(null, false);
2885
+ }
2886
+ /**
2887
+ * Shorthand method to finish request with "200" status code
2888
+ */
2889
+ ok(body, generateEtag) {
2890
+ this.status(200);
2891
+ return this.send(body, generateEtag);
2892
+ }
2893
+ /**
2894
+ * Shorthand method to finish request with "201" status code
2895
+ */
2896
+ created(body, generateEtag) {
2897
+ this.status(201);
2898
+ return this.send(body, generateEtag);
2899
+ }
2900
+ /**
2901
+ * Shorthand method to finish request with "202" status code
2902
+ */
2903
+ accepted(body, generateEtag) {
2904
+ this.status(202);
2905
+ return this.send(body, generateEtag);
2906
+ }
2907
+ /**
2908
+ * Shorthand method to finish request with "203" status code
2909
+ */
2910
+ nonAuthoritativeInformation(body, generateEtag) {
2911
+ this.status(203);
2912
+ return this.send(body, generateEtag);
2913
+ }
2914
+ /**
2915
+ * Shorthand method to finish request with "204" status code
2916
+ */
2917
+ noContent() {
2918
+ this.status(204);
2919
+ return this.send(null, false);
2920
+ }
2921
+ /**
2922
+ * Shorthand method to finish request with "205" status code
2923
+ */
2924
+ resetContent() {
2925
+ this.status(205);
2926
+ return this.send(null, false);
2927
+ }
2928
+ /**
2929
+ * Shorthand method to finish request with "206" status code
2930
+ */
2931
+ partialContent(body, generateEtag) {
2932
+ this.status(206);
2933
+ return this.send(body, generateEtag);
2934
+ }
2935
+ /**
2936
+ * Shorthand method to finish request with "300" status code
2937
+ */
2938
+ multipleChoices(body, generateEtag) {
2939
+ this.status(300);
2940
+ return this.send(body, generateEtag);
2941
+ }
2942
+ /**
2943
+ * Shorthand method to finish request with "301" status code
2944
+ */
2945
+ movedPermanently(body, generateEtag) {
2946
+ this.status(301);
2947
+ return this.send(body, generateEtag);
2948
+ }
2949
+ /**
2950
+ * Shorthand method to finish request with "302" status code
2951
+ */
2952
+ movedTemporarily(body, generateEtag) {
2953
+ this.status(302);
2954
+ return this.send(body, generateEtag);
2955
+ }
2956
+ /**
2957
+ * Shorthand method to finish request with "303" status code
2958
+ */
2959
+ seeOther(body, generateEtag) {
2960
+ this.status(303);
2961
+ return this.send(body, generateEtag);
2962
+ }
2963
+ /**
2964
+ * Shorthand method to finish request with "304" status code
2965
+ */
2966
+ notModified(body, generateEtag) {
2967
+ this.status(304);
2968
+ return this.send(body, generateEtag);
2969
+ }
2970
+ /**
2971
+ * Shorthand method to finish request with "305" status code
2972
+ */
2973
+ useProxy(body, generateEtag) {
2974
+ this.status(305);
2975
+ return this.send(body, generateEtag);
2976
+ }
2977
+ /**
2978
+ * Shorthand method to finish request with "307" status code
2979
+ */
2980
+ temporaryRedirect(body, generateEtag) {
2981
+ this.status(307);
2982
+ return this.send(body, generateEtag);
2983
+ }
2984
+ /**
2985
+ * Shorthand method to finish request with "400" status code
2986
+ */
2987
+ badRequest(body, generateEtag) {
2988
+ this.status(400);
2989
+ return this.send(body, generateEtag);
2990
+ }
2991
+ /**
2992
+ * Shorthand method to finish request with "401" status code
2993
+ */
2994
+ unauthorized(body, generateEtag) {
2995
+ this.status(401);
2996
+ return this.send(body, generateEtag);
2997
+ }
2998
+ /**
2999
+ * Shorthand method to finish request with "402" status code
3000
+ */
3001
+ paymentRequired(body, generateEtag) {
3002
+ this.status(402);
3003
+ return this.send(body, generateEtag);
3004
+ }
3005
+ /**
3006
+ * Shorthand method to finish request with "403" status code
3007
+ */
3008
+ forbidden(body, generateEtag) {
3009
+ this.status(403);
3010
+ return this.send(body, generateEtag);
3011
+ }
3012
+ /**
3013
+ * Shorthand method to finish request with "404" status code
3014
+ */
3015
+ notFound(body, generateEtag) {
3016
+ this.status(404);
3017
+ return this.send(body, generateEtag);
3018
+ }
3019
+ /**
3020
+ * Shorthand method to finish request with "405" status code
3021
+ */
3022
+ methodNotAllowed(body, generateEtag) {
3023
+ this.status(405);
3024
+ return this.send(body, generateEtag);
3025
+ }
3026
+ /**
3027
+ * Shorthand method to finish request with "406" status code
3028
+ */
3029
+ notAcceptable(body, generateEtag) {
3030
+ this.status(406);
3031
+ return this.send(body, generateEtag);
3032
+ }
3033
+ /**
3034
+ * Shorthand method to finish request with "407" status code
3035
+ */
3036
+ proxyAuthenticationRequired(body, generateEtag) {
3037
+ this.status(407);
3038
+ return this.send(body, generateEtag);
3039
+ }
3040
+ /**
3041
+ * Shorthand method to finish request with "408" status code
3042
+ */
3043
+ requestTimeout(body, generateEtag) {
3044
+ this.status(408);
3045
+ return this.send(body, generateEtag);
3046
+ }
3047
+ /**
3048
+ * Shorthand method to finish request with "409" status code
3049
+ */
3050
+ conflict(body, generateEtag) {
3051
+ this.status(409);
3052
+ return this.send(body, generateEtag);
3053
+ }
3054
+ /**
3055
+ * Shorthand method to finish request with "401" status code
3056
+ */
3057
+ gone(body, generateEtag) {
3058
+ this.status(410);
3059
+ return this.send(body, generateEtag);
3060
+ }
3061
+ /**
3062
+ * Shorthand method to finish request with "411" status code
3063
+ */
3064
+ lengthRequired(body, generateEtag) {
3065
+ this.status(411);
3066
+ return this.send(body, generateEtag);
3067
+ }
3068
+ /**
3069
+ * Shorthand method to finish request with "412" status code
3070
+ */
3071
+ preconditionFailed(body, generateEtag) {
3072
+ this.status(412);
3073
+ return this.send(body, generateEtag);
3074
+ }
3075
+ /**
3076
+ * Shorthand method to finish request with "413" status code
3077
+ */
3078
+ requestEntityTooLarge(body, generateEtag) {
3079
+ this.status(413);
3080
+ return this.send(body, generateEtag);
3081
+ }
3082
+ /**
3083
+ * Shorthand method to finish request with "414" status code
3084
+ */
3085
+ requestUriTooLong(body, generateEtag) {
3086
+ this.status(414);
3087
+ return this.send(body, generateEtag);
3088
+ }
3089
+ /**
3090
+ * Shorthand method to finish request with "415" status code
3091
+ */
3092
+ unsupportedMediaType(body, generateEtag) {
3093
+ this.status(415);
3094
+ return this.send(body, generateEtag);
3095
+ }
3096
+ /**
3097
+ * Shorthand method to finish request with "416" status code
3098
+ */
3099
+ requestedRangeNotSatisfiable(body, generateEtag) {
3100
+ this.status(416);
3101
+ return this.send(body, generateEtag);
3102
+ }
3103
+ /**
3104
+ * Shorthand method to finish request with "417" status code
3105
+ */
3106
+ expectationFailed(body, generateEtag) {
3107
+ this.status(417);
3108
+ return this.send(body, generateEtag);
3109
+ }
3110
+ /**
3111
+ * Shorthand method to finish request with "422" status code
3112
+ */
3113
+ unprocessableEntity(body, generateEtag) {
3114
+ this.status(422);
3115
+ return this.send(body, generateEtag);
3116
+ }
3117
+ /**
3118
+ * Shorthand method to finish request with "429" status code
3119
+ */
3120
+ tooManyRequests(body, generateEtag) {
3121
+ this.status(429);
3122
+ return this.send(body, generateEtag);
3123
+ }
3124
+ /**
3125
+ * Shorthand method to finish request with "500" status code
3126
+ */
3127
+ internalServerError(body, generateEtag) {
3128
+ this.status(500);
3129
+ return this.send(body, generateEtag);
3130
+ }
3131
+ /**
3132
+ * Shorthand method to finish request with "501" status code
3133
+ */
3134
+ notImplemented(body, generateEtag) {
3135
+ this.status(501);
3136
+ return this.send(body, generateEtag);
3137
+ }
3138
+ /**
3139
+ * Shorthand method to finish request with "502" status code
3140
+ */
3141
+ badGateway(body, generateEtag) {
3142
+ this.status(502);
3143
+ return this.send(body, generateEtag);
3144
+ }
3145
+ /**
3146
+ * Shorthand method to finish request with "503" status code
3147
+ */
3148
+ serviceUnavailable(body, generateEtag) {
3149
+ this.status(503);
3150
+ return this.send(body, generateEtag);
3151
+ }
3152
+ /**
3153
+ * Shorthand method to finish request with "504" status code
3154
+ */
3155
+ gatewayTimeout(body, generateEtag) {
3156
+ this.status(504);
3157
+ return this.send(body, generateEtag);
3158
+ }
3159
+ /**
3160
+ * Shorthand method to finish request with "505" status code
3161
+ */
3162
+ httpVersionNotSupported(body, generateEtag) {
3163
+ this.status(505);
3164
+ return this.send(body, generateEtag);
3165
+ }
3166
+ };
3167
+
3168
+ // src/router/main.ts
3169
+ import is3 from "@sindresorhus/is";
3170
+ import { moduleImporter as moduleImporter3 } from "@adonisjs/fold";
3171
+ import { RuntimeException as RuntimeException6 } from "@poppinss/utils";
3172
+
3173
+ // src/router/store.ts
3174
+ import matchit2 from "@poppinss/matchit";
3175
+ import lodash2 from "@poppinss/utils/lodash";
3176
+ import { RuntimeException as RuntimeException4 } from "@poppinss/utils";
3177
+
3178
+ // src/router/parser.ts
3179
+ import matchit from "@poppinss/matchit";
3180
+ function parseRoutePattern(pattern, matchers) {
3181
+ const tokens = matchit.parse(pattern, matchers);
3182
+ return tokens;
3183
+ }
3184
+
3185
+ // src/router/store.ts
3186
+ var RoutesStore = class {
3187
+ /**
3188
+ * A flag to know if routes for explicit domains
3189
+ * have been registered
3190
+ */
3191
+ usingDomains = false;
3192
+ /**
3193
+ * Tree of registered routes and their matchit tokens
3194
+ */
3195
+ tree = { tokens: [], domains: {} };
3196
+ /**
3197
+ * Returns the domain node for a given domain.
3198
+ */
3199
+ #getDomainNode(domain) {
3200
+ if (!this.tree.domains[domain]) {
3201
+ this.tree.tokens.push(parseRoutePattern(domain));
3202
+ this.tree.domains[domain] = {};
3203
+ }
3204
+ return this.tree.domains[domain];
3205
+ }
3206
+ /**
3207
+ * Returns the method node for a given domain and method.
3208
+ */
3209
+ #getMethodNode(domain, method) {
3210
+ const domainNode = this.#getDomainNode(domain);
3211
+ if (!domainNode[method]) {
3212
+ domainNode[method] = { tokens: [], routes: {}, routeKeys: {} };
3213
+ }
3214
+ return domainNode[method];
3215
+ }
3216
+ /**
3217
+ * Collects route params
3218
+ */
3219
+ #collectRouteParams(route, tokens) {
3220
+ const collectedParams = /* @__PURE__ */ new Set();
3221
+ for (let token of tokens) {
3222
+ if ([1, 3].includes(token.type)) {
3223
+ if (collectedParams.has(token.val)) {
3224
+ throw new RuntimeException4(`Duplicate param "${token.val}" found in "${route.pattern}"`);
3225
+ } else {
3226
+ collectedParams.add(token.val);
3227
+ }
3228
+ }
3229
+ }
3230
+ const params = [...collectedParams];
3231
+ collectedParams.clear();
3232
+ return params;
3233
+ }
3234
+ /**
3235
+ * Register route for a given domain and method
3236
+ */
3237
+ #registerRoute(domain, method, tokens, route) {
3238
+ const methodRoutes = this.#getMethodNode(domain, method);
3239
+ if (methodRoutes.routes[route.pattern]) {
3240
+ throw new RuntimeException4(
3241
+ `Duplicate route found. "${method}: ${route.pattern}" route already exists`
3242
+ );
3243
+ }
3244
+ if (debug_default.enabled) {
3245
+ debug_default("registering route to the store %O", route);
3246
+ debug_default("route middleware %O", route.middleware.all().entries());
3247
+ }
3248
+ methodRoutes.tokens.push(tokens);
3249
+ methodRoutes.routes[route.pattern] = route;
3250
+ methodRoutes.routeKeys[route.pattern] = domain !== "root" ? `${domain}-${method}-${route.pattern}` : `${method}-${route.pattern}`;
3251
+ }
3252
+ /**
3253
+ * Add a route to the store
3254
+ *
3255
+ * ```ts
3256
+ * store.add({
3257
+ * pattern: 'post/:id',
3258
+ * methods: ['GET'],
3259
+ * matchers: {},
3260
+ * meta: {},
3261
+ * handler: function handler () {
3262
+ * }
3263
+ * })
3264
+ * ```
3265
+ */
3266
+ add(route) {
3267
+ if (route.domain !== "root") {
3268
+ this.usingDomains = true;
3269
+ }
3270
+ const tokens = parseRoutePattern(route.pattern, route.matchers);
3271
+ const routeNode = lodash2.merge(
3272
+ { meta: {} },
3273
+ lodash2.pick(route, ["pattern", "handler", "meta", "middleware", "name", "execute"])
3274
+ );
3275
+ routeNode.meta.params = this.#collectRouteParams(routeNode, tokens);
3276
+ route.methods.forEach((method) => {
3277
+ this.#registerRoute(route.domain, method, tokens, routeNode);
3278
+ });
3279
+ return this;
3280
+ }
3281
+ /**
3282
+ * Matches the url, method and optionally domain to pull the matching
3283
+ * route. `null` is returned when unable to match the URL against
3284
+ * registered routes.
3285
+ *
3286
+ * The domain parameter has to be a registered pattern and not the fully
3287
+ * qualified runtime domain. You must call `matchDomain` first to fetch
3288
+ * the pattern for qualified domain
3289
+ */
3290
+ match(url, method, domain) {
3291
+ const domainName = domain?.tokens[0]?.old || "root";
3292
+ const matchedDomain = this.tree.domains[domainName];
3293
+ if (!matchedDomain) {
3294
+ return null;
3295
+ }
3296
+ const matchedMethod = this.tree.domains[domainName][method];
3297
+ if (!matchedMethod) {
3298
+ return null;
3299
+ }
3300
+ const matchedRoute = matchit2.match(url, matchedMethod.tokens);
3301
+ if (!matchedRoute.length) {
3302
+ return null;
3303
+ }
3304
+ const route = matchedMethod.routes[matchedRoute[0].old];
3305
+ return {
3306
+ route,
3307
+ routeKey: matchedMethod.routeKeys[route.pattern],
3308
+ params: matchit2.exec(url, matchedRoute),
3309
+ subdomains: domain?.hostname ? matchit2.exec(domain.hostname, domain.tokens) : {}
3310
+ };
3311
+ }
3312
+ /**
3313
+ * Match hostname against registered domains.
3314
+ */
3315
+ matchDomain(hostname) {
3316
+ if (!hostname || !this.usingDomains) {
3317
+ return [];
3318
+ }
3319
+ return matchit2.match(hostname, this.tree.tokens);
3320
+ }
3321
+ };
3322
+
3323
+ // src/router/lookup_store/url_builder.ts
3324
+ import { RuntimeException as RuntimeException5 } from "@poppinss/utils";
3325
+ var UrlBuilder = class {
3326
+ /**
3327
+ * Query string parser
3328
+ */
3329
+ #qsParser;
3330
+ /**
3331
+ * The parameters to apply on the route
3332
+ */
3333
+ #params = {};
3334
+ /**
3335
+ * Query string to append to the route
3336
+ */
3337
+ #qs = {};
3338
+ /**
3339
+ * Should we perform the route lookup or just build the
3340
+ * given pattern as it is.
3341
+ */
3342
+ #shouldPerformLookup = true;
3343
+ /**
3344
+ * BaseURL to append to the constructored URL
3345
+ */
3346
+ #baseUrl;
3347
+ /**
3348
+ * Encryption class for making signed URLs
3349
+ */
3350
+ #encryption;
3351
+ /**
3352
+ * Route finder for finding route pattern
3353
+ */
3354
+ #routeFinder;
3355
+ constructor(encryption, routeFinder, qsParser) {
3356
+ this.#qsParser = qsParser;
3357
+ this.#encryption = encryption;
3358
+ this.#routeFinder = routeFinder;
3359
+ }
3360
+ /**
3361
+ * Raises exception when wildcard values array is missing or
3362
+ * has length of zero.
3363
+ */
3364
+ #ensureHasWildCardValues(pattern, values) {
3365
+ if (!values || !Array.isArray(values) || !values.length) {
3366
+ throw new RuntimeException5(
3367
+ `Cannot make URL for "${pattern}" route. Invalid value provided for wildcard param`
3368
+ );
3369
+ }
3370
+ }
3371
+ /*
3372
+ * Raises exception when value is not defined
3373
+ */
3374
+ #ensureHasParamValue(pattern, param, value) {
3375
+ if (value === void 0 || value === null) {
3376
+ throw new RuntimeException5(
3377
+ `Cannot make URL for "${pattern}" route. Missing value for "${param}" param`
3378
+ );
3379
+ }
3380
+ }
3381
+ /**
3382
+ * Processes the pattern against the params
3383
+ */
3384
+ #processPattern(pattern) {
3385
+ const uriSegments = [];
3386
+ const paramsArray = Array.isArray(this.#params) ? this.#params : null;
3387
+ const paramsObject = !Array.isArray(this.#params) ? this.#params : {};
3388
+ let paramsIndex = 0;
3389
+ const tokens = parseRoutePattern(pattern);
3390
+ for (const token of tokens) {
3391
+ if (token.type === 0) {
3392
+ uriSegments.push(token.val === "/" ? "" : `${token.val}${token.end}`);
3393
+ } else if (token.type === 2) {
3394
+ const values = paramsArray ? paramsArray.slice(paramsIndex) : paramsObject["*"];
3395
+ this.#ensureHasWildCardValues(pattern, values);
3396
+ uriSegments.push(`${values.join("/")}${token.end}`);
3397
+ break;
3398
+ } else {
3399
+ const paramName = token.val;
3400
+ const value = paramsArray ? paramsArray[paramsIndex] : paramsObject[paramName];
3401
+ if (token.type === 1) {
3402
+ this.#ensureHasParamValue(pattern, paramName, value);
3403
+ }
3404
+ paramsIndex++;
3405
+ if (value !== void 0 && value !== null) {
3406
+ uriSegments.push(`${value}${token.end}`);
3407
+ }
3408
+ }
3409
+ }
3410
+ return `/${uriSegments.join("/")}`;
3411
+ }
3412
+ /**
3413
+ * Suffix the query string to the URL
3414
+ */
3415
+ #suffixQueryString(url, qs) {
3416
+ if (qs) {
3417
+ const queryString = this.#qsParser.stringify(qs);
3418
+ url = queryString ? `${url}?${queryString}` : url;
3419
+ }
3420
+ return url;
3421
+ }
3422
+ /**
3423
+ * Prefixes base URL to the uri string
3424
+ */
3425
+ #prefixBaseUrl(uri) {
3426
+ return this.#baseUrl ? `${this.#baseUrl}${uri}` : uri;
3427
+ }
3428
+ /**
3429
+ * Prefix a custom base URL to the final URI
3430
+ */
3431
+ prefixUrl(url) {
3432
+ this.#baseUrl = url;
3433
+ return this;
3434
+ }
3435
+ /**
3436
+ * Disable route lookup. Calling this method considers
3437
+ * the "identifier" as the route pattern
3438
+ */
3439
+ disableRouteLookup() {
3440
+ this.#shouldPerformLookup = false;
3441
+ return this;
3442
+ }
3443
+ /**
3444
+ * Append query string to the final URI
3445
+ */
3446
+ qs(queryString) {
3447
+ if (!queryString) {
3448
+ return this;
3449
+ }
3450
+ this.#qs = queryString;
3451
+ return this;
3452
+ }
3453
+ /**
3454
+ * Specify params to apply to the route pattern
3455
+ */
3456
+ params(params) {
3457
+ if (!params) {
3458
+ return this;
3459
+ }
3460
+ this.#params = params;
3461
+ return this;
3462
+ }
3463
+ /**
3464
+ * Generate URL for the given route identifier. The identifier can be the
3465
+ * route name, controller.method name or the route pattern
3466
+ * itself.
3467
+ */
3468
+ make(identifier) {
3469
+ let url;
3470
+ if (this.#shouldPerformLookup) {
3471
+ const route = this.#routeFinder.findOrFail(identifier);
3472
+ url = this.#processPattern(route.pattern);
3473
+ } else {
3474
+ url = this.#processPattern(identifier);
3475
+ }
3476
+ return this.#suffixQueryString(this.#prefixBaseUrl(url), this.#qs);
3477
+ }
3478
+ /**
3479
+ * Generate a signed URL for the given route identifier. The identifier can be the
3480
+ * route name, controller.method name or the route pattern
3481
+ * itself.
3482
+ */
3483
+ makeSigned(identifier, options) {
3484
+ let url;
3485
+ if (this.#shouldPerformLookup) {
3486
+ const route = this.#routeFinder.findOrFail(identifier);
3487
+ url = this.#processPattern(route.pattern);
3488
+ } else {
3489
+ url = this.#processPattern(identifier);
3490
+ }
3491
+ const signature = this.#encryption.verifier.sign(
3492
+ this.#suffixQueryString(url, this.#qs),
3493
+ options?.expiresIn,
3494
+ options?.purpose
3495
+ );
3496
+ const qs = Object.assign({}, this.#qs, { signature });
3497
+ return this.#suffixQueryString(this.#prefixBaseUrl(url), qs);
3498
+ }
3499
+ };
3500
+
3501
+ // src/router/lookup_store/route_finder.ts
3502
+ var RouteFinder = class {
3503
+ #routes = [];
3504
+ register(route) {
3505
+ this.#routes.push(route);
3506
+ }
3507
+ /**
3508
+ * Find a route by indentifier
3509
+ */
3510
+ find(routeIdentifier) {
3511
+ return this.#routes.find(({ name, pattern, handler }) => {
3512
+ if (name === routeIdentifier || pattern === routeIdentifier) {
3513
+ return true;
3514
+ }
3515
+ if (typeof handler === "function") {
3516
+ return false;
3517
+ }
3518
+ return handler.reference === routeIdentifier;
3519
+ }) || null;
3520
+ }
3521
+ /**
3522
+ * Find a route by indentifier or fail
3523
+ */
3524
+ findOrFail(routeIdentifier) {
3525
+ const route = this.find(routeIdentifier);
3526
+ if (!route) {
3527
+ throw new E_CANNOT_LOOKUP_ROUTE([routeIdentifier]);
3528
+ }
3529
+ return route;
3530
+ }
3531
+ /**
3532
+ * Find if a route exists
3533
+ */
3534
+ has(routeIdentifier) {
3535
+ return !!this.find(routeIdentifier);
3536
+ }
3537
+ /**
3538
+ * Returns an array of registered routes
3539
+ */
3540
+ toJSON() {
3541
+ return this.#routes;
3542
+ }
3543
+ };
3544
+
3545
+ // src/router/lookup_store/main.ts
3546
+ var LookupStore = class {
3547
+ /**
3548
+ * List of route finders grouped by domains
3549
+ */
3550
+ #routes = {};
3551
+ /**
3552
+ * Encryption for making URLs
3553
+ */
3554
+ #encryption;
3555
+ /**
3556
+ * Query string parser for making URLs
3557
+ */
3558
+ #qsParser;
3559
+ constructor(encryption, qsParser) {
3560
+ this.#encryption = encryption;
3561
+ this.#qsParser = qsParser;
3562
+ }
3563
+ /**
3564
+ * Register route JSON payload
3565
+ */
3566
+ register(route) {
3567
+ this.#routes[route.domain] = this.#routes[route.domain] || new RouteFinder();
3568
+ this.#routes[route.domain].register(route);
3569
+ }
3570
+ /**
3571
+ * Returns an instance of the URL builder for making
3572
+ * route URIs
3573
+ */
3574
+ builder() {
3575
+ return this.builderForDomain("root");
3576
+ }
3577
+ /**
3578
+ * Returns an instance of the URL builder for a specific
3579
+ * domain.
3580
+ */
3581
+ builderForDomain(domain) {
3582
+ const finder = this.#routes[domain];
3583
+ return new UrlBuilder(this.#encryption, finder || new RouteFinder(), this.#qsParser);
3584
+ }
3585
+ /**
3586
+ * Finds a route by its identifier. The identifier can be the
3587
+ * route name, controller.method name or the route pattern
3588
+ * itself.
3589
+ */
3590
+ find(routeIdentifier, domain) {
3591
+ const finder = this.#routes[domain || "root"];
3592
+ if (!finder) {
3593
+ return null;
3594
+ }
3595
+ return finder.find(routeIdentifier);
3596
+ }
3597
+ /**
3598
+ * Finds a route by its identifier. The identifier can be the
3599
+ * route name, controller.method name or the route pattern
3600
+ * itself.
3601
+ *
3602
+ * An error is raised when unable to find the route.
3603
+ */
3604
+ findOrFail(routeIdentifier, domain) {
3605
+ const finder = this.#routes[domain || "root"];
3606
+ if (!finder) {
3607
+ throw new E_CANNOT_LOOKUP_ROUTE([routeIdentifier]);
3608
+ }
3609
+ return finder.findOrFail(routeIdentifier);
3610
+ }
3611
+ /**
3612
+ * Check if a route exists. The identifier can be the
3613
+ * route name, controller.method name or the route pattern
3614
+ * itself.
3615
+ */
3616
+ has(routeIdentifier, domain) {
3617
+ const finder = this.#routes[domain || "root"];
3618
+ if (!finder) {
3619
+ return false;
3620
+ }
3621
+ return finder.has(routeIdentifier);
3622
+ }
3623
+ toJSON() {
3624
+ return Object.keys(this.#routes).reduce((result, domain) => {
3625
+ result[domain] = this.#routes[domain].toJSON();
3626
+ return result;
3627
+ }, {});
3628
+ }
3629
+ };
3630
+
3631
+ // src/router/matchers.ts
3632
+ import Macroable7 from "@poppinss/macroable";
3633
+ var RouteMatchers = class extends Macroable7 {
3634
+ /**
3635
+ * Enforce value to be a number and also casts it to number data
3636
+ * type
3637
+ */
3638
+ number() {
3639
+ return { match: /^[0-9]+$/, cast: (value) => Number(value) };
3640
+ }
3641
+ /**
3642
+ * Enforce value to be formatted as uuid
3643
+ */
3644
+ uuid() {
3645
+ return {
3646
+ match: /^[0-9a-zA-F]{8}-[0-9a-zA-F]{4}-[0-9a-zA-F]{4}-[0-9a-zA-F]{4}-[0-9a-zA-F]{12}$/,
3647
+ cast: (value) => value.toLowerCase()
3648
+ };
3649
+ }
3650
+ /**
3651
+ * Enforce value to be formatted as slug
3652
+ */
3653
+ slug() {
3654
+ return { match: /^[^\s-_](?!.*?[-_]{2,})([a-z0-9-\\]{1,})[^\s]*[^-_\s]$/ };
3655
+ }
3656
+ };
3657
+
3658
+ // src/define_middleware.ts
3659
+ import { moduleImporter as moduleImporter2 } from "@adonisjs/fold";
3660
+ function middlewareReferenceBuilder(name, middleware) {
3661
+ const handler = moduleImporter2(middleware, "handle").toHandleMethod();
3662
+ return function(...args) {
3663
+ return {
3664
+ ...handler,
3665
+ name,
3666
+ args: args[0]
3667
+ };
3668
+ };
3669
+ }
3670
+ function defineNamedMiddleware(collection) {
3671
+ return Object.keys(collection).reduce(
3672
+ (result, key) => {
3673
+ result[key] = middlewareReferenceBuilder(key, collection[key]);
3674
+ return result;
3675
+ },
3676
+ {}
3677
+ );
3678
+ }
3679
+
3680
+ // src/router/main.ts
3681
+ var Router = class extends LookupStore {
3682
+ /**
3683
+ * Application is needed to resolve string based controller expressions
3684
+ */
3685
+ #app;
3686
+ /**
3687
+ * Store with tokenized routes
3688
+ */
3689
+ #store = new RoutesStore();
3690
+ /**
3691
+ * Global matchers to test route params against regular expressions.
3692
+ */
3693
+ #globalMatchers = {};
3694
+ /**
3695
+ * Middleware store to be shared with the routes
3696
+ */
3697
+ #middleware = [];
3698
+ /**
3699
+ * A boolean to tell the router that a group is in
3700
+ * open state right now
3701
+ */
3702
+ #openedGroups = [];
3703
+ /**
3704
+ * Collection of routes, including route resource and route
3705
+ * group. To get a flat list of routes, call `router.toJSON()`
3706
+ */
3707
+ routes = [];
3708
+ /**
3709
+ * A flag to know if routes for explicit domains have been registered.
3710
+ * The boolean is computed after calling the "commit" method.
3711
+ */
3712
+ usingDomains = false;
3713
+ /**
3714
+ * Shortcut methods for commonly used route matchers
3715
+ */
3716
+ matchers = new RouteMatchers();
3717
+ constructor(app, encryption, qsParser) {
3718
+ super(encryption, qsParser);
3719
+ this.#app = app;
3720
+ }
3721
+ /**
3722
+ * Push a give router entity to the list of routes or the
3723
+ * recently opened group.
3724
+ */
3725
+ #pushToRoutes(entity) {
3726
+ const openedGroup = this.#openedGroups[this.#openedGroups.length - 1];
3727
+ if (openedGroup) {
3728
+ openedGroup.routes.push(entity);
3729
+ return;
3730
+ }
3731
+ this.routes.push(entity);
3732
+ }
3733
+ /**
3734
+ * Parses the route pattern
3735
+ */
3736
+ parsePattern(pattern, matchers) {
3737
+ return parseRoutePattern(pattern, matchers);
3738
+ }
3739
+ /**
3740
+ * Define an array of middleware to use on all the routes.
3741
+ * Calling this method multiple times pushes to the
3742
+ * existing list of middleware
3743
+ */
3744
+ use(middleware) {
3745
+ middleware.forEach(
3746
+ (one) => this.#middleware.push(moduleImporter3(one, "handle").toHandleMethod())
3747
+ );
3748
+ return this;
3749
+ }
3750
+ /**
3751
+ * Define a collection of named middleware. The defined collection is
3752
+ * not registered anywhere, but instead converted in a new collection
3753
+ * of functions you can apply on the routes, or router groups.
3754
+ */
3755
+ named(collection) {
3756
+ return defineNamedMiddleware(collection);
3757
+ }
3758
+ /**
3759
+ * Add route for a given pattern and methods
3760
+ */
3761
+ route(pattern, methods, handler) {
3762
+ const route = new Route(this.#app, this.#middleware, {
3763
+ pattern,
3764
+ methods,
3765
+ handler,
3766
+ globalMatchers: this.#globalMatchers
3767
+ });
3768
+ this.#pushToRoutes(route);
3769
+ return route;
3770
+ }
3771
+ /**
3772
+ * Define a route that handles all common HTTP methods
3773
+ */
3774
+ any(pattern, handler) {
3775
+ return this.route(
3776
+ pattern,
3777
+ ["HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE"],
3778
+ handler
3779
+ );
3780
+ }
3781
+ /**
3782
+ * Define `GET` route
3783
+ */
3784
+ get(pattern, handler) {
3785
+ return this.route(pattern, ["GET", "HEAD"], handler);
3786
+ }
3787
+ /**
3788
+ * Define `POST` route
3789
+ */
3790
+ post(pattern, handler) {
3791
+ return this.route(pattern, ["POST"], handler);
3792
+ }
3793
+ /**
3794
+ * Define `PUT` route
3795
+ */
3796
+ put(pattern, handler) {
3797
+ return this.route(pattern, ["PUT"], handler);
3798
+ }
3799
+ /**
3800
+ * Define `PATCH` route
3801
+ */
3802
+ patch(pattern, handler) {
3803
+ return this.route(pattern, ["PATCH"], handler);
3804
+ }
3805
+ /**
3806
+ * Define `DELETE` route
3807
+ */
3808
+ delete(pattern, handler) {
3809
+ return this.route(pattern, ["DELETE"], handler);
3810
+ }
3811
+ /**
3812
+ * Creates a group of routes. A route group can apply transforms
3813
+ * to routes in bulk
3814
+ */
3815
+ group(callback) {
3816
+ const group = new RouteGroup([]);
3817
+ this.#pushToRoutes(group);
3818
+ this.#openedGroups.push(group);
3819
+ callback();
3820
+ this.#openedGroups.pop();
3821
+ return group;
3822
+ }
3823
+ /**
3824
+ * Registers a route resource with conventional set of routes
3825
+ */
3826
+ resource(resource, controller) {
3827
+ const resourceInstance = new RouteResource(this.#app, this.#middleware, {
3828
+ resource,
3829
+ controller,
3830
+ shallow: false,
3831
+ globalMatchers: this.#globalMatchers
3832
+ });
3833
+ this.#pushToRoutes(resourceInstance);
3834
+ return resourceInstance;
3835
+ }
3836
+ /**
3837
+ * Register a route resource with shallow nested routes.
3838
+ */
3839
+ shallowResource(resource, controller) {
3840
+ const resourceInstance = new RouteResource(this.#app, this.#middleware, {
3841
+ resource,
3842
+ controller,
3843
+ shallow: true,
3844
+ globalMatchers: this.#globalMatchers
3845
+ });
3846
+ this.#pushToRoutes(resourceInstance);
3847
+ return resourceInstance;
3848
+ }
3849
+ /**
3850
+ * Returns a brisk route instance for a given URL pattern
3851
+ */
3852
+ on(pattern) {
3853
+ const briskRoute = new BriskRoute(this.#app, this.#middleware, {
3854
+ pattern,
3855
+ globalMatchers: this.#globalMatchers
3856
+ });
3857
+ this.#pushToRoutes(briskRoute);
3858
+ return briskRoute;
3859
+ }
3860
+ /**
3861
+ * Define matcher for a given param. The global params are applied
3862
+ * on all the routes (unless overridden at the route level).
3863
+ */
3864
+ where(param, matcher) {
3865
+ if (typeof matcher === "string") {
3866
+ this.#globalMatchers[param] = { match: new RegExp(matcher) };
3867
+ } else if (is3.regExp(matcher)) {
3868
+ this.#globalMatchers[param] = { match: matcher };
3869
+ } else {
3870
+ this.#globalMatchers[param] = matcher;
3871
+ }
3872
+ return this;
3873
+ }
3874
+ /**
3875
+ * Commit routes to the store. The router is freezed after the
3876
+ * commit method is called.
3877
+ */
3878
+ commit() {
3879
+ const routeNamesByDomain = /* @__PURE__ */ new Map();
3880
+ toRoutesJSON(this.routes).forEach((route) => {
3881
+ if (!routeNamesByDomain.has(route.domain)) {
3882
+ routeNamesByDomain.set(route.domain, /* @__PURE__ */ new Set());
3883
+ }
3884
+ const routeNames = routeNamesByDomain.get(route.domain);
3885
+ if (route.name && routeNames.has(route.name)) {
3886
+ throw new RuntimeException6(
3887
+ `Route with duplicate name found. A route with name "${route.name}" already exists`
3888
+ );
3889
+ }
3890
+ if (route.name) {
3891
+ routeNames.add(route.name);
3892
+ }
3893
+ this.register(route);
3894
+ this.#store.add(route);
3895
+ });
3896
+ routeNamesByDomain.clear();
3897
+ this.usingDomains = this.#store.usingDomains;
3898
+ this.routes = [];
3899
+ this.#globalMatchers = {};
3900
+ this.#middleware = [];
3901
+ }
3902
+ /**
3903
+ * Find route for a given URL, method and optionally domain
3904
+ */
3905
+ match(url, method, hostname) {
3906
+ const matchingDomain = this.#store.matchDomain(hostname);
3907
+ return matchingDomain.length ? this.#store.match(url, method, {
3908
+ tokens: matchingDomain,
3909
+ hostname
3910
+ }) : this.#store.match(url, method);
3911
+ }
3912
+ /**
3913
+ * Make URL to a pre-registered route
3914
+ */
3915
+ makeUrl(routeIdentifier, params, options) {
3916
+ const normalizedOptions = Object.assign({}, options);
3917
+ const builder = normalizedOptions.domain ? this.builderForDomain(normalizedOptions.domain) : this.builder();
3918
+ builder.params(params);
3919
+ builder.qs(normalizedOptions.qs);
3920
+ normalizedOptions.prefixUrl && builder.prefixUrl(normalizedOptions.prefixUrl);
3921
+ normalizedOptions.disableRouteLookup && builder.disableRouteLookup();
3922
+ return builder.make(routeIdentifier);
3923
+ }
3924
+ /**
3925
+ * Makes a signed URL to a pre-registered route.
3926
+ */
3927
+ makeSignedUrl(routeIdentifier, params, options) {
3928
+ const normalizedOptions = Object.assign({}, options);
3929
+ const builder = normalizedOptions.domain ? this.builderForDomain(normalizedOptions.domain) : this.builder();
3930
+ builder.params(params);
3931
+ builder.qs(normalizedOptions.qs);
3932
+ normalizedOptions.prefixUrl && builder.prefixUrl(normalizedOptions.prefixUrl);
3933
+ normalizedOptions.disableRouteLookup && builder.disableRouteLookup();
3934
+ return builder.makeSigned(routeIdentifier, normalizedOptions);
3935
+ }
3936
+ };
3937
+
3938
+ // src/http_context/main.ts
3939
+ import { inspect } from "node:util";
3940
+ import Macroable8 from "@poppinss/macroable";
3941
+ import { RuntimeException as RuntimeException7 } from "@poppinss/utils";
3942
+
3943
+ // src/http_context/local_storage.ts
3944
+ import { AsyncLocalStorage } from "node:async_hooks";
3945
+ var asyncLocalStorage = {
3946
+ /**
3947
+ * Check if the async local storage for the HTTP
3948
+ * context is enabled or not
3949
+ */
3950
+ isEnabled: false,
3951
+ /**
3952
+ * HTTP context storage instance for the current scope
3953
+ */
3954
+ storage: null,
3955
+ /**
3956
+ * Create the storage instance. This method must be called only
3957
+ * once.
3958
+ */
3959
+ create() {
3960
+ this.isEnabled = true;
3961
+ this.storage = new AsyncLocalStorage();
3962
+ return this.storage;
3963
+ },
3964
+ /**
3965
+ * Destroy the create storage instance
3966
+ */
3967
+ destroy() {
3968
+ this.isEnabled = false;
3969
+ this.storage = null;
3970
+ }
3971
+ };
3972
+
3973
+ // src/http_context/main.ts
3974
+ var HttpContext = class extends Macroable8 {
3975
+ constructor(request, response, logger, containerResolver) {
3976
+ super();
3977
+ this.request = request;
3978
+ this.response = response;
3979
+ this.logger = logger;
3980
+ this.containerResolver = containerResolver;
3981
+ this.request.ctx = this;
3982
+ this.response.ctx = this;
3983
+ }
3984
+ /**
3985
+ * Find if async localstorage is enabled for HTTP requests
3986
+ * or not
3987
+ */
3988
+ static get usingAsyncLocalStorage() {
3989
+ return asyncLocalStorage.isEnabled;
3990
+ }
3991
+ /**
3992
+ * Get access to the HTTP context. Available only when
3993
+ * "usingAsyncLocalStorage" is true
3994
+ */
3995
+ static get() {
3996
+ if (!this.usingAsyncLocalStorage || !asyncLocalStorage.storage) {
3997
+ return null;
3998
+ }
3999
+ return asyncLocalStorage.storage.getStore() || null;
4000
+ }
4001
+ /**
4002
+ * Get the HttpContext instance or raise an exception if not
4003
+ * available
4004
+ */
4005
+ static getOrFail() {
4006
+ if (!this.usingAsyncLocalStorage || !asyncLocalStorage.storage) {
4007
+ throw new RuntimeException7(
4008
+ 'HTTP context is not available. Enable "useAsyncLocalStorage" inside "config/app.ts" file'
4009
+ );
4010
+ }
4011
+ const store = this.get();
4012
+ if (!store) {
4013
+ throw new RuntimeException7("Http context is not available outside of an HTTP request");
4014
+ }
4015
+ return store;
4016
+ }
4017
+ /**
4018
+ * Run a method that doesn't have access to HTTP context from
4019
+ * the async local storage.
4020
+ */
4021
+ static runOutsideContext(callback, ...args) {
4022
+ if (!asyncLocalStorage.storage) {
4023
+ return callback(...args);
4024
+ }
4025
+ return asyncLocalStorage.storage.exit(callback, ...args);
4026
+ }
4027
+ /**
4028
+ * Reference to the current route. Not available inside
4029
+ * server middleware
4030
+ */
4031
+ route;
4032
+ /**
4033
+ * A unique key for the current route
4034
+ */
4035
+ routeKey;
4036
+ /**
4037
+ * Route params
4038
+ */
4039
+ params = {};
4040
+ /**
4041
+ * Route subdomains
4042
+ */
4043
+ subdomains = {};
4044
+ /**
4045
+ * A helper to see top level properties on the context object
4046
+ */
4047
+ /* c8 ignore next 3 */
4048
+ inspect() {
4049
+ return inspect(this, false, 1, true);
4050
+ }
4051
+ };
4052
+
4053
+ // src/server/main.ts
4054
+ import onFinished2 from "on-finished";
4055
+ import Middleware2 from "@poppinss/middleware";
4056
+ import { moduleCaller as moduleCaller2, moduleImporter as moduleImporter4 } from "@adonisjs/fold";
4057
+
4058
+ // src/qs.ts
4059
+ import { parse as parse3, stringify } from "qs";
4060
+ var Qs = class {
4061
+ #config;
4062
+ constructor(config) {
4063
+ this.#config = config;
4064
+ }
4065
+ parse(value) {
4066
+ return parse3(value, this.#config.parse);
4067
+ }
4068
+ stringify(value) {
4069
+ return stringify(value, this.#config.stringify);
4070
+ }
4071
+ };
4072
+
4073
+ // src/server/factories/final_handler.ts
4074
+ function finalHandler(router, resolver, ctx, errorResponder) {
4075
+ return function() {
4076
+ const url = ctx.request.url();
4077
+ const method = ctx.request.method();
4078
+ const hostname = router.usingDomains ? ctx.request.hostname() : void 0;
4079
+ const route = router.match(url, method, hostname);
4080
+ if (route) {
4081
+ ctx.params = route.params;
4082
+ ctx.subdomains = route.subdomains;
4083
+ ctx.route = route.route;
4084
+ ctx.routeKey = route.routeKey;
4085
+ return route.route.execute(route.route, resolver, ctx, errorResponder);
4086
+ }
4087
+ return Promise.reject(new E_ROUTE_NOT_FOUND([method, url]));
4088
+ };
4089
+ }
4090
+
4091
+ // src/server/factories/write_response.ts
4092
+ function writeResponse(ctx) {
4093
+ return function() {
4094
+ try {
4095
+ ctx.response.finish();
4096
+ } catch (error) {
4097
+ ctx.logger.fatal({ err: error }, "Response serialization failed");
4098
+ ctx.response.internalServerError(error.message);
4099
+ ctx.response.finish();
4100
+ }
4101
+ };
4102
+ }
4103
+
4104
+ // src/server/factories/middleware_handler.ts
4105
+ function middlewareHandler(resolver, ctx) {
4106
+ return function(fn, next) {
4107
+ debug_default("executing middleware %s", fn.name);
4108
+ return fn.handle(resolver, ctx, next);
4109
+ };
4110
+ }
4111
+
4112
+ // src/server/main.ts
4113
+ var Server = class {
4114
+ /**
4115
+ * The default error handler to use
4116
+ */
4117
+ #defaultErrorHandler = {
4118
+ report() {
4119
+ },
4120
+ handle(error, ctx) {
4121
+ ctx.response.status(error.status || 500).send(error.message || "Internal server error");
4122
+ }
4123
+ };
4124
+ /**
4125
+ * Logger instance, a child logger is added
4126
+ * to the context to have request specific
4127
+ * logging capabilities.
4128
+ */
4129
+ #logger;
4130
+ /**
4131
+ * Registered error handler (if any)
4132
+ */
4133
+ #errorHandler;
4134
+ /**
4135
+ * Resolved error handler is an instance of the lazily imported error
4136
+ * handler class.
4137
+ */
4138
+ #resolvedErrorHandler = this.#defaultErrorHandler;
4139
+ /**
4140
+ * Emitter is required to notify when a request finishes
4141
+ */
4142
+ #emitter;
4143
+ /**
4144
+ * The application instance to be shared with the router
4145
+ */
4146
+ #app;
4147
+ /**
4148
+ * The encryption instance to be shared with the router
4149
+ */
4150
+ #encryption;
4151
+ /**
4152
+ * Server config
4153
+ */
4154
+ #config;
4155
+ /**
4156
+ * Query string parser used by the server
4157
+ */
4158
+ #qsParser;
4159
+ /**
4160
+ * Server middleware stack runs on every incoming HTTP request
4161
+ */
4162
+ #serverMiddlewareStack;
4163
+ /**
4164
+ * Reference to the router used by the server
4165
+ */
4166
+ #router;
4167
+ /**
4168
+ * Reference to the underlying Node HTTP server in use
4169
+ */
4170
+ #nodeHttpServer;
4171
+ /**
4172
+ * Middleware store to be shared with the routes
4173
+ */
4174
+ #middleware = [];
4175
+ /**
4176
+ * The request error response is attached to the middleware
4177
+ * pipeline to intercept errors and invoke the user
4178
+ * registered error handler.
4179
+ *
4180
+ * We share this with the route middleware pipeline as well,
4181
+ * so that it does not throw any exceptions
4182
+ */
4183
+ #requestErrorResponder = (error, ctx) => {
4184
+ this.#resolvedErrorHandler.report(error, ctx);
4185
+ return this.#resolvedErrorHandler.handle(error, ctx);
4186
+ };
4187
+ /**
4188
+ * Know if async local storage is enabled or not.
4189
+ */
4190
+ get usingAsyncLocalStorage() {
4191
+ return asyncLocalStorage.isEnabled;
4192
+ }
4193
+ constructor(app, encryption, emitter, logger, config) {
4194
+ this.#app = app;
4195
+ this.#emitter = emitter;
4196
+ this.#config = config;
4197
+ this.#logger = logger;
4198
+ this.#encryption = encryption;
4199
+ this.#qsParser = new Qs(this.#config.qs);
4200
+ this.#router = new Router(this.#app, this.#encryption, this.#qsParser);
4201
+ this.#createAsyncLocalStore();
4202
+ debug_default("server config: %O", this.#config);
4203
+ }
4204
+ /**
4205
+ * Create async local storage store when enabled
4206
+ */
4207
+ #createAsyncLocalStore() {
4208
+ if (this.#config.useAsyncLocalStorage) {
4209
+ debug_default("creating ALS store for HTTP context");
4210
+ asyncLocalStorage.create();
4211
+ } else {
4212
+ asyncLocalStorage.destroy();
4213
+ }
4214
+ }
4215
+ /**
4216
+ * Creates an instance of the server middleware stack
4217
+ */
4218
+ #createServerMiddlewareStack() {
4219
+ this.#serverMiddlewareStack = new Middleware2();
4220
+ this.#middleware.forEach((middleware) => this.#serverMiddlewareStack.add(middleware));
4221
+ this.#serverMiddlewareStack.freeze();
4222
+ this.#middleware = [];
4223
+ }
4224
+ /**
4225
+ * Handles the HTTP request
4226
+ */
4227
+ #handleRequest(ctx, resolver) {
4228
+ return this.#serverMiddlewareStack.runner().errorHandler((error) => this.#requestErrorResponder(error, ctx)).finalHandler(finalHandler(this.#router, resolver, ctx, this.#requestErrorResponder)).run(middlewareHandler(resolver, ctx)).catch((error) => {
4229
+ ctx.logger.fatal({ err: error }, "Exception raised by error handler");
4230
+ return this.#defaultErrorHandler.handle(error, ctx);
4231
+ }).finally(writeResponse(ctx));
4232
+ }
4233
+ /**
4234
+ * Creates a pipeline of middleware.
4235
+ */
4236
+ pipeline(middleware) {
4237
+ const middlewareStack = new Middleware2();
4238
+ middleware.forEach((one) => {
4239
+ middlewareStack.add(moduleCaller2(one, "handle").toHandleMethod());
4240
+ });
4241
+ middlewareStack.freeze();
4242
+ const stackRunner = middlewareStack.runner();
4243
+ return {
4244
+ finalHandler(handler) {
4245
+ stackRunner.finalHandler(handler);
4246
+ return this;
4247
+ },
4248
+ errorHandler(handler) {
4249
+ stackRunner.errorHandler(handler);
4250
+ return this;
4251
+ },
4252
+ run(ctx) {
4253
+ return stackRunner.run((handler, next) => {
4254
+ return handler.handle(ctx.containerResolver, ctx, next);
4255
+ });
4256
+ }
4257
+ };
4258
+ }
4259
+ /**
4260
+ * Define an array of middleware to use on all the incoming HTTP request.
4261
+ * Calling this method multiple times pushes to the existing list
4262
+ * of middleware
4263
+ */
4264
+ use(middleware) {
4265
+ middleware.forEach(
4266
+ (one) => this.#middleware.push(moduleImporter4(one, "handle").toHandleMethod())
4267
+ );
4268
+ return this;
4269
+ }
4270
+ /**
4271
+ * Register a custom error handler for HTTP requests.
4272
+ * All errors will be reported to this method
4273
+ */
4274
+ errorHandler(handler) {
4275
+ this.#errorHandler = handler;
4276
+ return this;
4277
+ }
4278
+ /**
4279
+ * Boot the server. Calling this method performs the following actions.
4280
+ *
4281
+ * - Register routes with the store.
4282
+ * - Resolve and construct the error handler.
4283
+ */
4284
+ async boot() {
4285
+ debug_default("booting HTTP server");
4286
+ this.#createServerMiddlewareStack();
4287
+ this.#router.commit();
4288
+ if (this.#errorHandler) {
4289
+ if (debug_default.enabled) {
4290
+ debug_default('using custom error handler "%s"', this.#errorHandler);
4291
+ }
4292
+ const moduleExports = await this.#errorHandler();
4293
+ this.#resolvedErrorHandler = await this.#app.container.make(moduleExports.default);
4294
+ }
4295
+ }
4296
+ /**
4297
+ * Set the HTTP server instance used to listen for requests.
4298
+ */
4299
+ setNodeServer(server) {
4300
+ this.#nodeHttpServer = server;
4301
+ }
4302
+ /**
4303
+ * Returns reference to the underlying HTTP server
4304
+ * in use
4305
+ */
4306
+ getNodeServer() {
4307
+ return this.#nodeHttpServer;
4308
+ }
4309
+ /**
4310
+ * Returns reference to the router instance used
4311
+ * by the server.
4312
+ */
4313
+ getRouter() {
4314
+ return this.#router;
4315
+ }
4316
+ /**
4317
+ * Creates an instance of the [[Request]] class
4318
+ */
4319
+ createRequest(req, res) {
4320
+ return new Request(req, res, this.#encryption, this.#config, this.#qsParser);
4321
+ }
4322
+ /**
4323
+ * Creates an instance of the [[Response]] class
4324
+ */
4325
+ createResponse(req, res) {
4326
+ return new Response(req, res, this.#encryption, this.#config, this.#router, this.#qsParser);
4327
+ }
4328
+ /**
4329
+ * Creates an instance of the [[HttpContext]] class
4330
+ */
4331
+ createHttpContext(request, response, resolver) {
4332
+ return new HttpContext(
4333
+ request,
4334
+ response,
4335
+ this.#logger.child({ request_id: request.id() }),
4336
+ resolver
4337
+ );
4338
+ }
4339
+ /**
4340
+ * Handle request
4341
+ */
4342
+ handle(req, res) {
4343
+ const hasRequestListener = this.#emitter.hasListeners("http:request_finished");
4344
+ const startTime = hasRequestListener ? process.hrtime() : null;
4345
+ const resolver = this.#app.container.createResolver();
4346
+ const ctx = this.createHttpContext(
4347
+ this.createRequest(req, res),
4348
+ this.createResponse(req, res),
4349
+ resolver
4350
+ );
4351
+ if (startTime) {
4352
+ onFinished2(res, () => {
4353
+ this.#emitter.emit("http:request_finished", {
4354
+ ctx,
4355
+ duration: process.hrtime(startTime)
4356
+ });
4357
+ });
4358
+ }
4359
+ if (this.usingAsyncLocalStorage) {
4360
+ return asyncLocalStorage.storage.run(ctx, () => this.#handleRequest(ctx, resolver));
4361
+ }
4362
+ return this.#handleRequest(ctx, resolver);
4363
+ }
4364
+ };
4365
+
4366
+ // src/define_config.ts
4367
+ import proxyAddr from "proxy-addr";
4368
+ import string3 from "@poppinss/utils/string";
4369
+ function defineConfig(config) {
4370
+ const { trustProxy: trustProxy2, ...rest } = config;
4371
+ const normalizedConfig = {
4372
+ allowMethodSpoofing: false,
4373
+ trustProxy: proxyAddr.compile("loopback"),
4374
+ subdomainOffset: 2,
4375
+ generateRequestId: false,
4376
+ useAsyncLocalStorage: false,
4377
+ etag: false,
4378
+ jsonpCallbackName: "callback",
4379
+ cookie: {
4380
+ maxAge: "2h",
4381
+ path: "/",
4382
+ httpOnly: true,
4383
+ secure: false,
4384
+ sameSite: false
4385
+ },
4386
+ qs: {
4387
+ parse: {
4388
+ depth: 5,
4389
+ parameterLimit: 1e3,
4390
+ allowSparse: false,
4391
+ arrayLimit: 20,
4392
+ comma: true
4393
+ },
4394
+ stringify: {
4395
+ encode: true,
4396
+ encodeValuesOnly: false,
4397
+ arrayFormat: "indices",
4398
+ skipNulls: false
4399
+ }
4400
+ },
4401
+ ...rest
4402
+ };
4403
+ if (normalizedConfig.cookie.maxAge) {
4404
+ normalizedConfig.cookie.maxAge = string3.seconds.parse(normalizedConfig.cookie.maxAge);
4405
+ }
4406
+ if (typeof trustProxy2 === "boolean") {
4407
+ const tpValue = trustProxy2;
4408
+ normalizedConfig.trustProxy = (_, __) => tpValue;
4409
+ } else if (typeof trustProxy2 === "string") {
4410
+ const tpValue = trustProxy2;
4411
+ normalizedConfig.trustProxy = proxyAddr.compile(tpValue);
4412
+ }
4413
+ return normalizedConfig;
4414
+ }
4415
+
4416
+ export {
4417
+ Route,
4418
+ BriskRoute,
4419
+ RouteResource,
4420
+ RouteGroup,
4421
+ parseRange,
4422
+ CookieClient,
4423
+ Request,
4424
+ Redirect,
4425
+ E_ROUTE_NOT_FOUND,
4426
+ E_CANNOT_LOOKUP_ROUTE,
4427
+ E_HTTP_EXCEPTION,
4428
+ E_HTTP_REQUEST_ABORTED,
4429
+ exceptions_exports,
4430
+ Response,
4431
+ Qs,
4432
+ Router,
4433
+ HttpContext,
4434
+ Server,
4435
+ defineConfig
4436
+ };
4437
+ //# sourceMappingURL=chunk-NC6OWANS.js.map