@adonisjs/http-server 8.0.0-next.12 → 8.0.0-next.13

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