@h3ravel/http 11.4.3 → 11.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { i as __toESM, r as __require, t as __commonJS } from "./chunk-BLWcukCW.js";
2
+ import { writeFile } from "fs/promises";
3
+ import { Arr, Obj, Str, data_get, data_set, safeDot } from "@h3ravel/support";
2
4
  import { Command } from "@h3ravel/musket";
3
5
  import { HttpContext, Logger } from "@h3ravel/shared";
4
6
  import { fileURLToPath } from "node:url";
@@ -16,9 +18,697 @@ import { appendFileSync, createReadStream, createWriteStream, readFileSync, stat
16
18
  import { finished } from "node:stream/promises";
17
19
  import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
18
20
  import { Buffer as Buffer$1 } from "node:buffer";
19
- import { H3, getQuery, getRouterParams, html, readBody, redirect, serve } from "h3";
20
- import { safeDot } from "@h3ravel/support";
21
+ import { H3, getQuery, getRequestIP, getRequestURL, getRouterParams, html, parseCookies, redirect, serve } from "h3";
21
22
 
23
+ //#region src/Exceptions/BadRequestException.ts
24
+ var BadRequestException = class extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "BadRequestException";
28
+ }
29
+ };
30
+
31
+ //#endregion
32
+ //#region src/Exceptions/UnexpectedValueException.ts
33
+ var UnexpectedValueException = class extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = "UnexpectedValueException";
37
+ }
38
+ };
39
+
40
+ //#endregion
41
+ //#region src/Bags/ParamBag.ts
42
+ /**
43
+ * ParamBag is a container for key/value pairs
44
+ * for Node/H3 environments.
45
+ */
46
+ var ParamBag = class {
47
+ constructor(parameters = {}, event) {
48
+ this.parameters = parameters;
49
+ this.event = event;
50
+ this.parameters = { ...parameters };
51
+ }
52
+ /**
53
+ * Returns the parameters.
54
+ * @
55
+ * @param key The name of the parameter to return or null to get them all
56
+ *
57
+ * @throws BadRequestException if the value is not an array
58
+ */
59
+ all(key) {
60
+ if (key === null) return { ...this.parameters };
61
+ const value = key ? this.parameters[key] : void 0;
62
+ if (value && typeof value !== "object") throw new BadRequestException(`Unexpected value for parameter "${key}": expected object, got ${typeof value}`);
63
+ return value || {};
64
+ }
65
+ get(key, defaultValue) {
66
+ return key in this.parameters ? this.parameters[key] : defaultValue;
67
+ }
68
+ set(key, value) {
69
+ this.parameters[key] = value;
70
+ }
71
+ /**
72
+ * Returns true if the parameter is defined.
73
+ *
74
+ * @param key
75
+ */
76
+ has(key) {
77
+ return Object.prototype.hasOwnProperty.call(this.parameters, key);
78
+ }
79
+ /**
80
+ * Removes a parameter.
81
+ *
82
+ * @param key
83
+ */
84
+ remove(key) {
85
+ delete this.parameters[key];
86
+ }
87
+ /**
88
+ *
89
+ * Returns the parameter as string.
90
+ *
91
+ * @param key
92
+ * @param defaultValue
93
+ * @throws UnexpectedValueException if the value cannot be converted to string
94
+ * @returns
95
+ */
96
+ getString(key, defaultValue = "") {
97
+ const value = this.get(key, defaultValue);
98
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
99
+ throw new UnexpectedValueException(`Parameter "${key}" cannot be converted to string`);
100
+ }
101
+ /**
102
+ * Returns the parameter value converted to integer.
103
+ *
104
+ * @param key
105
+ * @param defaultValue
106
+ * @throws UnexpectedValueException if the value cannot be converted to integer
107
+ */
108
+ getInt(key, defaultValue = 0) {
109
+ const value = parseInt(this.get(key, defaultValue), 10);
110
+ if (isNaN(value)) throw new Error(`Parameter "${key}" is not an integer`);
111
+ return value;
112
+ }
113
+ /**
114
+ * Returns the parameter value converted to boolean.
115
+ *
116
+ * @param key
117
+ * @param defaultValue
118
+ * @throws UnexpectedValueException if the value cannot be converted to a boolean
119
+ */
120
+ getBoolean(key, defaultValue = false) {
121
+ const value = this.get(key, defaultValue);
122
+ if (typeof value === "boolean") return value;
123
+ if ([
124
+ "1",
125
+ "true",
126
+ "yes"
127
+ ].includes(String(value).toLowerCase())) return true;
128
+ if ([
129
+ "0",
130
+ "false",
131
+ "no"
132
+ ].includes(String(value).toLowerCase())) return false;
133
+ throw new Error(`Parameter "${key}" cannot be converted to boolean`);
134
+ }
135
+ /**
136
+ * Returns the alphabetic characters of the parameter value.
137
+ *
138
+ * @param key
139
+ * @param defaultValue
140
+ * @throws UnexpectedValueException if the value cannot be converted to string
141
+ */
142
+ getAlpha(key, defaultValue = "") {
143
+ return this.getString(key, defaultValue).replace(/[^a-z]/gi, "");
144
+ }
145
+ /**
146
+ * Returns the alphabetic characters and digits of the parameter value.
147
+ *
148
+ * @param key
149
+ * @param defaultValue
150
+ * @throws UnexpectedValueException if the value cannot be converted to string
151
+ */
152
+ getAlnum(key, defaultValue = "") {
153
+ return this.getString(key, defaultValue).replace(/[^a-z0-9]/gi, "");
154
+ }
155
+ /**
156
+ * Returns the digits of the parameter value.
157
+ *
158
+ * @param key
159
+ * @param defaultValue
160
+ * @throws UnexpectedValueException if the value cannot be converted to string
161
+ * @returns
162
+ **/
163
+ getDigits(key, defaultValue = "") {
164
+ return this.getString(key, defaultValue).replace(/\D/g, "");
165
+ }
166
+ /**
167
+ * Returns the parameter keys.
168
+ */
169
+ keys() {
170
+ return Object.keys(this.parameters);
171
+ }
172
+ /**
173
+ * Replaces the current parameters by a new set.
174
+ */
175
+ replace(parameters = {}) {
176
+ this.parameters = { ...parameters };
177
+ }
178
+ /**
179
+ * Adds parameters.
180
+ */
181
+ add(parameters = {}) {
182
+ this.parameters = {
183
+ ...this.parameters,
184
+ ...parameters
185
+ };
186
+ }
187
+ /**
188
+ * Returns the number of parameters.
189
+ */
190
+ count() {
191
+ return Object.keys(this.parameters).length;
192
+ }
193
+ /**
194
+ * Returns an iterator for parameters.
195
+ *
196
+ * @returns
197
+ */
198
+ [Symbol.iterator]() {
199
+ return Object.entries(this.parameters)[Symbol.iterator]();
200
+ }
201
+ };
202
+
203
+ //#endregion
204
+ //#region src/UploadedFile.ts
205
+ var UploadedFile = class UploadedFile {
206
+ constructor(originalName, mimeType, size, content) {
207
+ this.originalName = originalName;
208
+ this.mimeType = mimeType;
209
+ this.size = size;
210
+ this.content = content;
211
+ }
212
+ static createFromBase(file) {
213
+ return new UploadedFile(file.name, file.type, file.size, file);
214
+ }
215
+ /**
216
+ * Save to disk (Node environment only)
217
+ */
218
+ async moveTo(destination) {
219
+ await writeFile(destination, Buffer.from(await this.content.arrayBuffer()));
220
+ }
221
+ };
222
+
223
+ //#endregion
224
+ //#region src/Bags/FileBag.ts
225
+ /**
226
+ * FileBag is a container for uploaded files
227
+ * for Node/H3 environments.
228
+ */
229
+ var FileBag = class extends ParamBag {
230
+ parameters = {};
231
+ constructor(parameters = {}, event) {
232
+ super(parameters, event);
233
+ this.replace(parameters);
234
+ }
235
+ /**
236
+ * Replace all stored files.
237
+ */
238
+ replace(files = {}) {
239
+ this.parameters = {};
240
+ this.add(files);
241
+ }
242
+ /**
243
+ * Set a file or array of files.
244
+ */
245
+ set(key, value) {
246
+ if (Array.isArray(value)) this.parameters[key] = value.map((v) => v ? this.convertFileInformation(v) : null).filter(Boolean);
247
+ else if (value) this.parameters[key] = this.convertFileInformation(value);
248
+ else this.parameters[key] = null;
249
+ }
250
+ /**
251
+ * Add multiple files.
252
+ */
253
+ add(files = {}) {
254
+ for (const [key, file] of Object.entries(files)) this.set(key, file);
255
+ }
256
+ /**
257
+ * Get all stored files.
258
+ */
259
+ all() {
260
+ return this.parameters;
261
+ }
262
+ /**
263
+ * Normalize file input into UploadedFile instances.
264
+ */
265
+ convertFileInformation(file) {
266
+ if (!file) return null;
267
+ if (file instanceof UploadedFile) return file;
268
+ if (file instanceof File) return UploadedFile.createFromBase(file);
269
+ throw new TypeError("Invalid file input — expected File or UploadedFile instance.");
270
+ }
271
+ };
272
+
273
+ //#endregion
274
+ //#region src/Bags/HeaderBag.ts
275
+ /**
276
+ * HeaderBag — A container for HTTP headers
277
+ * for Node/H3 environments.
278
+ */
279
+ var HeaderBag = class HeaderBag {
280
+ static UPPER = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
281
+ static LOWER = "-abcdefghijklmnopqrstuvwxyz";
282
+ headers = {};
283
+ cacheControl = {};
284
+ constructor(headers = {}) {
285
+ for (const [key, values] of Object.entries(headers)) this.set(key, values);
286
+ }
287
+ /**
288
+ * Returns all headers as string (for debugging / toString)
289
+ *
290
+ * @returns
291
+ */
292
+ toString() {
293
+ const headers = this.all();
294
+ if (!Object.keys(headers).length) return "";
295
+ const sortedKeys = Object.keys(headers).sort();
296
+ const max = Math.max(...sortedKeys.map((k) => k.length)) + 1;
297
+ let content = "";
298
+ for (const name of sortedKeys) {
299
+ const values = headers[name] ?? [];
300
+ const displayName = name.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("-");
301
+ for (const value of values) content += `${displayName + ":"}`.padEnd(max + 1, " ") + `${value ?? ""}\r\n`;
302
+ }
303
+ return content;
304
+ }
305
+ /**
306
+ * Returns all headers or specific header list
307
+ *
308
+ * @param key
309
+ * @returns
310
+ */
311
+ all(key) {
312
+ if (key !== void 0) return this.headers[this.normalizeKey(key)] ?? [];
313
+ return this.headers;
314
+ }
315
+ /**
316
+ * Returns header keys
317
+ *
318
+ * @returns
319
+ */
320
+ keys() {
321
+ return Object.keys(this.headers);
322
+ }
323
+ /**
324
+ * Replace all headers with new set
325
+ *
326
+ * @param headers
327
+ */
328
+ replace(headers = {}) {
329
+ this.headers = {};
330
+ this.add(headers);
331
+ }
332
+ /**
333
+ * Add multiple headers
334
+ *
335
+ * @param headers
336
+ */
337
+ add(headers) {
338
+ for (const [key, values] of Object.entries(headers)) this.set(key, values);
339
+ }
340
+ /**
341
+ * Returns first header value by name or default
342
+ *
343
+ * @param key
344
+ * @param defaultValue
345
+ * @returns
346
+ */
347
+ get(key, defaultValue = null) {
348
+ const headers = this.all(key);
349
+ if (!headers.length) return defaultValue;
350
+ return headers[0];
351
+ }
352
+ /**
353
+ * Sets a header by name.
354
+ *
355
+ * @param replace Whether to replace existing values (default true)
356
+ */
357
+ set(key, values, replace = true) {
358
+ const normalized = this.normalizeKey(key);
359
+ if (Array.isArray(values)) {
360
+ const valList = values.map((v) => v === void 0 ? null : v);
361
+ if (replace || !this.headers[normalized]) this.headers[normalized] = valList;
362
+ else this.headers[normalized].push(...valList);
363
+ } else {
364
+ const val = values === void 0 ? null : values;
365
+ if (replace || !this.headers[normalized]) this.headers[normalized] = [val];
366
+ else this.headers[normalized].push(val);
367
+ }
368
+ if (normalized === "cache-control") this.cacheControl = this.parseCacheControl((this.headers[normalized] ?? []).join(", "));
369
+ }
370
+ /**
371
+ * Returns true if header exists
372
+ *
373
+ * @param key
374
+ * @returns
375
+ */
376
+ has(key) {
377
+ return Object.prototype.hasOwnProperty.call(this.headers, this.normalizeKey(key));
378
+ }
379
+ /**
380
+ * Returns true if header contains value
381
+ *
382
+ * @param key
383
+ * @param value
384
+ * @returns
385
+ */
386
+ contains(key, value) {
387
+ return this.all(key).includes(value);
388
+ }
389
+ /**
390
+ * Removes a header
391
+ *
392
+ * @param key
393
+ */
394
+ remove(key) {
395
+ const normalized = this.normalizeKey(key);
396
+ delete this.headers[normalized];
397
+ if (normalized === "cache-control") this.cacheControl = {};
398
+ }
399
+ /**
400
+ * Returns parsed date from header
401
+ *
402
+ * @param key
403
+ * @param defaultValue
404
+ * @returns
405
+ */
406
+ getDate(key, defaultValue = null) {
407
+ const value = this.get(key);
408
+ if (!value) return defaultValue ? new Date(defaultValue) : null;
409
+ const parsed = new Date(value);
410
+ if (isNaN(parsed.getTime())) throw new Error(`The "${key}" HTTP header is not parseable (${value}).`);
411
+ return parsed;
412
+ }
413
+ /**
414
+ * Adds a Cache-Control directive
415
+ *
416
+ * @param key
417
+ * @param value
418
+ */
419
+ addCacheControlDirective(key, value = true) {
420
+ this.cacheControl[key] = value;
421
+ this.set("Cache-Control", this.getCacheControlHeader());
422
+ }
423
+ /**
424
+ * Returns true if Cache-Control directive is defined
425
+ *
426
+ * @param key
427
+ * @returns
428
+ */
429
+ hasCacheControlDirective(key) {
430
+ return Object.prototype.hasOwnProperty.call(this.cacheControl, key);
431
+ }
432
+ /**
433
+ * Returns a Cache-Control directive value by name
434
+ *
435
+ * @param key
436
+ * @returns
437
+ */
438
+ getCacheControlDirective(key) {
439
+ return this.cacheControl[key] ?? null;
440
+ }
441
+ /**
442
+ * Removes a Cache-Control directive
443
+ *
444
+ * @param key
445
+ * @returns
446
+ */
447
+ removeCacheControlDirective(key) {
448
+ delete this.cacheControl[key];
449
+ this.set("Cache-Control", this.getCacheControlHeader());
450
+ }
451
+ /**
452
+ * Number of headers
453
+ *
454
+ * @param key
455
+ * @returns
456
+ */
457
+ count() {
458
+ return Object.keys(this.headers).length;
459
+ }
460
+ /**
461
+ * Normalize header name to lowercase with hyphens
462
+ *
463
+ * @param key
464
+ * @returns
465
+ */
466
+ normalizeKey(key) {
467
+ return key.replace(/[A-Z_]/g, (ch) => {
468
+ const idx = HeaderBag.UPPER.indexOf(ch);
469
+ return idx === -1 ? ch : HeaderBag.LOWER[idx];
470
+ }).toLowerCase();
471
+ }
472
+ /**
473
+ * Generates Cache-Control header string
474
+ *
475
+ * @param header
476
+ * @returns
477
+ */
478
+ getCacheControlHeader() {
479
+ return Object.entries(this.cacheControl).sort(([a$1], [b]) => a$1.localeCompare(b)).map(([k, v]) => v === true ? k : v === false ? "" : `${k}=${v}`).filter(Boolean).join(", ");
480
+ }
481
+ /**
482
+ * Parses Cache-Control header
483
+ *
484
+ * @param header
485
+ * @returns
486
+ */
487
+ parseCacheControl(header) {
488
+ const directives = {};
489
+ const parts = header.split(",").map((p) => p.trim()).filter(Boolean);
490
+ for (const part of parts) {
491
+ const [key, val] = part.split("=", 2);
492
+ directives[key.trim()] = val !== void 0 ? val.trim() : true;
493
+ }
494
+ return directives;
495
+ }
496
+ /**
497
+ * Iterator support
498
+ * @returns
499
+ */
500
+ [Symbol.iterator]() {
501
+ return Object.entries(this.headers)[Symbol.iterator]();
502
+ }
503
+ };
504
+
505
+ //#endregion
506
+ //#region src/Bags/InputBag.ts
507
+ /**
508
+ * InputBag is a container for user input values
509
+ * (e.g., query params, body, cookies)
510
+ * for Node/H3 environments.
511
+ */
512
+ var InputBag = class extends ParamBag {
513
+ constructor(inputs = {}, event) {
514
+ super(inputs, event);
515
+ this.add(inputs);
516
+ }
517
+ /**
518
+ * Returns a scalar input value by name.
519
+ *
520
+ * @param key
521
+ * @param defaultValue
522
+ * @throws BadRequestException if the input contains a non-scalar value
523
+ * @returns
524
+ */
525
+ get(key, defaultValue = null) {
526
+ if (defaultValue !== null && typeof defaultValue !== "string" && typeof defaultValue !== "number" && typeof defaultValue !== "boolean") throw new TypeError(`Expected a scalar value as 2nd argument to get(), got ${typeof defaultValue}`);
527
+ const value = Obj.get(this.parameters, key, defaultValue);
528
+ if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") throw new BadRequestException(`Input value "${key}" contains a non-scalar value.`);
529
+ return value;
530
+ }
531
+ /**
532
+ * Replaces all current input values.
533
+ *
534
+ * @param inputs
535
+ * @returns
536
+ */
537
+ replace(inputs = {}) {
538
+ this.parameters = {};
539
+ this.add(inputs);
540
+ }
541
+ /**
542
+ * Adds multiple input values.
543
+ *
544
+ * @param inputs
545
+ * @returns
546
+ */
547
+ add(inputs = {}) {
548
+ Object.entries(inputs).forEach(([key, value]) => this.set(key, value));
549
+ }
550
+ /**
551
+ * Sets an input by name.
552
+ *
553
+ * @param key
554
+ * @param value
555
+ * @throws TypeError if value is not scalar or array
556
+ * @returns
557
+ */
558
+ set(key, value) {
559
+ if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && !Array.isArray(value) && typeof value !== "object") throw new TypeError(`Expected scalar, array, object, or null as value for set(), got ${typeof value}`);
560
+ this.parameters[key] = value;
561
+ }
562
+ /**
563
+ * Returns true if a key exists.
564
+ *
565
+ * @param key
566
+ * @returns
567
+ */
568
+ has(key) {
569
+ return Object.prototype.hasOwnProperty.call(this.parameters, key);
570
+ }
571
+ /**
572
+ * Returns all parameters.
573
+ *
574
+ * @returns
575
+ */
576
+ all() {
577
+ return { ...this.parameters };
578
+ }
579
+ /**
580
+ * Converts a parameter value to string.
581
+ *
582
+ * @param key
583
+ * @param defaultValue
584
+ * @throws BadRequestException if input contains a non-scalar value
585
+ * @returns
586
+ */
587
+ getString(key, defaultValue = "") {
588
+ const value = this.get(key, defaultValue);
589
+ return String(value ?? "");
590
+ }
591
+ /**
592
+ * Filters input value with a predicate.
593
+ * Mimics PHP’s filter_var() in spirit, but simpler.
594
+ *
595
+ * @param key
596
+ * @param defaultValue
597
+ * @param filterFn
598
+ * @throws BadRequestException if validation fails
599
+ * @returns
600
+ */
601
+ filter(key, defaultValue = null, filterFn) {
602
+ const value = this.has(key) ? this.parameters[key] : defaultValue;
603
+ if (Array.isArray(value)) throw new BadRequestException(`Input value "${key}" contains an array, but array filtering not allowed.`);
604
+ if (filterFn && !filterFn(value)) throw new BadRequestException(`Input value "${key}" is invalid and did not pass filter.`);
605
+ return value;
606
+ }
607
+ /**
608
+ * Returns an enum value by key.
609
+ *
610
+ * @param key
611
+ * @param EnumClass
612
+ * @param defaultValue
613
+ * @throws BadRequestException if conversion fails
614
+ * @returns
615
+ */
616
+ getEnum(key, EnumClass, defaultValue = null) {
617
+ const value = this.get(key, defaultValue);
618
+ if (value === null) return defaultValue;
619
+ if (!Object.values(EnumClass).includes(value)) throw new BadRequestException(`Input "${key}" cannot be converted to enum.`);
620
+ return value;
621
+ }
622
+ /**
623
+ * Removes a key.
624
+ *
625
+ * @param key
626
+ */
627
+ remove(key) {
628
+ delete this.parameters[key];
629
+ }
630
+ /**
631
+ * Returns all keys.
632
+ *
633
+ * @returns
634
+ */
635
+ keys() {
636
+ return Object.keys(this.parameters);
637
+ }
638
+ /**
639
+ * Returns number of parameters.
640
+ *
641
+ * @returns
642
+ */
643
+ count() {
644
+ return this.keys().length;
645
+ }
646
+ };
647
+
648
+ //#endregion
649
+ //#region src/Bags/ServerBag.ts
650
+ /**
651
+ * ServerBag — a simplified version of Symfony's ServerBag
652
+ * for Node/H3 environments.
653
+ *
654
+ * Responsible for extracting and normalizing HTTP headers
655
+ * from the incoming request.
656
+ */
657
+ var ServerBag = class extends ParamBag {
658
+ constructor(parameters = {}, event) {
659
+ super(Object.fromEntries(Object.entries(parameters).map(([k, v]) => [k.toLowerCase(), v])), event);
660
+ }
661
+ /**
662
+ * Returns all request headers, normalized to uppercase with underscores.
663
+ * Example: content-type → CONTENT_TYPE
664
+ */
665
+ getHeaders() {
666
+ const headers = {};
667
+ for (const [key, value] of Object.entries(this.parameters)) {
668
+ if (value === void 0 || value === "") continue;
669
+ switch (key) {
670
+ case "accept":
671
+ case "content-type":
672
+ case "content-length":
673
+ case "content-md5":
674
+ case "authorization":
675
+ headers[key.toUpperCase().replace(/-/g, "_")] = value;
676
+ break;
677
+ default: headers[`HTTP_${key.toUpperCase().replace(/-/g, "_")}`] = value;
678
+ }
679
+ }
680
+ if (headers["HTTP_AUTHORIZATION"] || headers["AUTHORIZATION"]) {
681
+ const auth = headers["HTTP_AUTHORIZATION"] || headers["AUTHORIZATION"] || "";
682
+ if (auth.toLowerCase().startsWith("basic ")) {
683
+ const [user, pass] = Buffer.from(auth.slice(6), "base64").toString().split(":", 2);
684
+ headers["AUTH_TYPE"] = "Basic";
685
+ headers["AUTH_USER"] = user;
686
+ headers["AUTH_PASS"] = pass;
687
+ } else if (auth.toLowerCase().startsWith("bearer ")) {
688
+ headers["AUTH_TYPE"] = "Bearer";
689
+ headers["AUTH_TOKEN"] = auth.slice(7);
690
+ } else if (auth.toLowerCase().startsWith("digest ")) {
691
+ headers["AUTH_TYPE"] = "Digest";
692
+ headers["AUTH_DIGEST"] = auth;
693
+ }
694
+ }
695
+ return headers;
696
+ }
697
+ /**
698
+ * Returns a specific header by name, case-insensitive.
699
+ */
700
+ get(name) {
701
+ return this.parameters[name.toLowerCase()];
702
+ }
703
+ /**
704
+ * Returns true if a header exists.
705
+ */
706
+ has(name) {
707
+ return name.toLowerCase() in this.parameters;
708
+ }
709
+ };
710
+
711
+ //#endregion
22
712
  //#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
23
713
  function isPlainObject(value) {
24
714
  if (typeof value !== "object" || value === null) return false;
@@ -10227,8 +10917,8 @@ var require_graceful_fs = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm
10227
10917
  }
10228
10918
  }
10229
10919
  var fs$writeFile = fs$9.writeFile;
10230
- fs$9.writeFile = writeFile;
10231
- function writeFile(path$12, data, options, cb) {
10920
+ fs$9.writeFile = writeFile$1;
10921
+ function writeFile$1(path$12, data, options, cb) {
10232
10922
  if (typeof options === "function") cb = options, options = null;
10233
10923
  return go$writeFile(path$12, data, options, cb);
10234
10924
  function go$writeFile(path$13, data$1, options$1, cb$1, startTime) {
@@ -18039,7 +18729,7 @@ var require_dumper = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/js-y
18039
18729
  }
18040
18730
  }
18041
18731
  }
18042
- function dump(input, options) {
18732
+ function dump$1(input, options) {
18043
18733
  options = options || {};
18044
18734
  var state = new State(options);
18045
18735
  if (!state.noRefs) getDuplicateReferences(input, state);
@@ -18047,9 +18737,9 @@ var require_dumper = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/js-y
18047
18737
  return "";
18048
18738
  }
18049
18739
  function safeDump(input, options) {
18050
- return dump(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
18740
+ return dump$1(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));
18051
18741
  }
18052
- module.exports.dump = dump;
18742
+ module.exports.dump = dump$1;
18053
18743
  module.exports.safeDump = safeDump;
18054
18744
  }) });
18055
18745
 
@@ -18260,6 +18950,68 @@ var FireCommand = class extends Command {
18260
18950
  }
18261
18951
  };
18262
18952
 
18953
+ //#endregion
18954
+ //#region src/Exceptions/SuspiciousOperationException.ts
18955
+ var SuspiciousOperationException = class extends Error {
18956
+ constructor(message) {
18957
+ super(message);
18958
+ this.name = "SuspiciousOperationException";
18959
+ }
18960
+ };
18961
+
18962
+ //#endregion
18963
+ //#region src/FormRequest.ts
18964
+ var FormRequest = class {
18965
+ dataset;
18966
+ constructor(data) {
18967
+ this.initialize(data);
18968
+ }
18969
+ /**
18970
+ * Initialize the data
18971
+ * @param data
18972
+ */
18973
+ initialize(data) {
18974
+ this.dataset = {
18975
+ files: {},
18976
+ input: {}
18977
+ };
18978
+ for (const [rawKey, value] of data.entries()) {
18979
+ const key = rawKey.endsWith("[]") ? rawKey.slice(0, -2) : rawKey;
18980
+ if (value instanceof File) {
18981
+ const uploaded = value instanceof UploadedFile ? value : UploadedFile.createFromBase(value);
18982
+ if (this.dataset.files[key]) {
18983
+ const existing = this.dataset.files[key];
18984
+ if (Array.isArray(existing)) existing.push(uploaded);
18985
+ else this.dataset.files[key] = [existing, uploaded];
18986
+ } else this.dataset.files[key] = uploaded;
18987
+ } else if (this.dataset.input[key]) {
18988
+ const existing = this.dataset.input[key];
18989
+ if (Array.isArray(existing)) existing.push(value);
18990
+ else this.dataset.input[key] = [existing, value];
18991
+ } else this.dataset.input[key] = value;
18992
+ }
18993
+ }
18994
+ /**
18995
+ * Get all uploaded files
18996
+ */
18997
+ files() {
18998
+ return this.dataset.files;
18999
+ }
19000
+ /**
19001
+ * Get all input fields
19002
+ */
19003
+ input() {
19004
+ return this.dataset.input;
19005
+ }
19006
+ /**
19007
+ * Get combined input and files
19008
+ * File entries take precedence if names overlap.
19009
+ */
19010
+ all() {
19011
+ return Object.assign({}, this.dataset.input, this.dataset.files);
19012
+ }
19013
+ };
19014
+
18263
19015
  //#endregion
18264
19016
  //#region src/Middleware.ts
18265
19017
  var Middleware = class {};
@@ -18306,50 +19058,586 @@ var HttpServiceProvider = class {
18306
19058
 
18307
19059
  //#endregion
18308
19060
  //#region src/Request.ts
18309
- var Request = class {
19061
+ var Request = class Request {
19062
+ /**
19063
+ * Parsed request body
19064
+ */
19065
+ body;
19066
+ /**
19067
+ * The decoded JSON content for the request.
19068
+ */
19069
+ #json;
19070
+ #uri;
19071
+ #method = void 0;
18310
19072
  /**
18311
19073
  * Gets route parameters.
18312
19074
  * @returns An object containing route parameters.
18313
19075
  */
18314
19076
  params;
18315
19077
  /**
18316
- * Gets query parameters.
18317
- * @returns An object containing query parameters.
19078
+ * All of the converted files for the request.
19079
+ */
19080
+ convertedFiles;
19081
+ /**
19082
+ * Form data from incoming request.
19083
+ * @returns The FormRequest object.
19084
+ */
19085
+ formData;
19086
+ /**
19087
+ * Request body parameters (POST).
19088
+ *
19089
+ * @see getPayload() for portability between content types
19090
+ */
19091
+ request;
19092
+ /**
19093
+ * Query string parameters (GET).
18318
19094
  */
18319
19095
  query;
18320
19096
  /**
19097
+ * Uploaded files (FILES).
19098
+ */
19099
+ files;
19100
+ /**
19101
+ * Server and execution environment parameters
19102
+ */
19103
+ server;
19104
+ /**
19105
+ * Cookies
19106
+ */
19107
+ cookies;
19108
+ /**
19109
+ * The request attributes (parameters parsed from the PATH_INFO, ...).
19110
+ */
19111
+ attributes;
19112
+ /**
18321
19113
  * Gets the request headers.
18322
19114
  * @returns An object containing request headers.
18323
19115
  */
18324
19116
  headers;
19117
+ content = void 0;
19118
+ static formats = void 0;
19119
+ static httpMethodParameterOverride = false;
18325
19120
  /**
18326
- * The current H3 H3Event instance
19121
+ * List of Acceptable Content Types
18327
19122
  */
18328
- event;
19123
+ acceptableContentTypes = [];
18329
19124
  constructor(event, app) {
18330
- this.app = app;
18331
19125
  this.event = event;
18332
- this.query = getQuery(this.event);
19126
+ this.app = app;
19127
+ }
19128
+ /**
19129
+ * Factory method to create a Request instance from an H3Event.
19130
+ */
19131
+ static async create(event, app) {
19132
+ const instance = new Request(event, app);
19133
+ await instance.setBody();
19134
+ await instance.initialize();
19135
+ globalThis.request = () => instance;
19136
+ return instance;
19137
+ }
19138
+ /**
19139
+ * Sets the parameters for this request.
19140
+ *
19141
+ * This method also re-initializes all properties.
19142
+ *
19143
+ * @param attributes
19144
+ * @param cookies The COOKIE parameters
19145
+ * @param files The FILES parameters
19146
+ * @param server The SERVER parameters
19147
+ * @param content The raw body data
19148
+ */
19149
+ async initialize() {
18333
19150
  this.params = getRouterParams(this.event);
18334
- this.headers = this.event.req.headers;
19151
+ this.request = new InputBag(this.formData ? this.formData.input() : {}, this.event);
19152
+ this.query = new InputBag(getQuery(this.event), this.event);
19153
+ this.attributes = new ParamBag(getRouterParams(this.event), this.event);
19154
+ this.cookies = new InputBag(parseCookies(this.event), this.event);
19155
+ this.files = new FileBag(this.formData ? this.formData.files() : {}, this.event);
19156
+ this.server = new ServerBag(Object.fromEntries(this.event.req.headers.entries()), this.event);
19157
+ this.headers = new HeaderBag(this.server.getHeaders());
19158
+ this.acceptableContentTypes = [];
19159
+ this.#method = void 0;
19160
+ this.#uri = (await import(String("@h3ravel/url"))).Url.of(getRequestURL(this.event).toString(), this.app);
19161
+ }
19162
+ async setBody() {
19163
+ const type = this.event.req.headers.get("content-type") || "";
19164
+ if (this.body) return;
19165
+ if (type.includes("application/json")) {
19166
+ this.body = await this.event.req.json().catch(() => ({}));
19167
+ this.content = this.body;
19168
+ } else if (type.includes("form-data") || type.includes("x-www-form-urlencoded")) {
19169
+ this.formData = new FormRequest(await this.event.req.formData());
19170
+ this.body = this.formData.all();
19171
+ this.content = JSON.stringify(this.formData.input());
19172
+ } else if (type.startsWith("text/")) {
19173
+ this.body = await this.event.req.text();
19174
+ this.content = this.body;
19175
+ } else {
19176
+ const content = this.event.req.body;
19177
+ this.content = content;
19178
+ if (content instanceof ReadableStream) {
19179
+ const reader = content.getReader();
19180
+ const chunks = [];
19181
+ let done = false;
19182
+ while (!done) {
19183
+ const { value, done: isDone } = await reader.read();
19184
+ if (value) chunks.push(value);
19185
+ done = isDone;
19186
+ }
19187
+ this.body = new TextDecoder().decode(new Uint8Array(chunks.flatMap((chunk) => Array.from(chunk))));
19188
+ } else this.body = content;
19189
+ }
19190
+ }
19191
+ /**
19192
+ * Retrieve all data from the instance (query + body).
19193
+ */
19194
+ all(keys) {
19195
+ const input = Obj.deepMerge({}, this.input(), this.allFiles());
19196
+ if (!keys) return input;
19197
+ const results = {};
19198
+ const list = Array.isArray(keys) ? keys : [keys];
19199
+ for (const key of list) data_set(results, key, Obj.get(input, key));
19200
+ return results;
18335
19201
  }
18336
19202
  /**
18337
- * Get all input data (query + body).
19203
+ * Retrieve an input item from the request.
19204
+ *
19205
+ * @param key
19206
+ * @param defaultValue
19207
+ * @returns
18338
19208
  */
18339
- async all() {
18340
- let data = {
18341
- ...getRouterParams(this.event),
18342
- ...getQuery(this.event)
19209
+ input(key, defaultValue) {
19210
+ const source = {
19211
+ ...this.getInputSource().all(),
19212
+ ...this.query.all()
18343
19213
  };
18344
- if (this.event.req.method === "POST") data = Object.assign({}, data, Object.fromEntries((await this.event.req.formData()).entries()));
18345
- else if (this.event.req.method === "PUT") data = Object.fromEntries(Object.entries(await readBody(this.event)));
18346
- return data;
19214
+ return key ? data_get(source, key, defaultValue) : Arr.except(source, ["_method"]);
19215
+ }
19216
+ /**
19217
+ * Retrieve a file from the request.
19218
+ *
19219
+ * @param key
19220
+ * @param defaultValue
19221
+ * @returns
19222
+ */
19223
+ file(key, defaultValue) {
19224
+ const source = this.allFiles();
19225
+ return key ? data_get(source, key, defaultValue) : source;
19226
+ }
19227
+ /**
19228
+ * Get an array of all of the files on the request.
19229
+ */
19230
+ allFiles() {
19231
+ if (this.convertedFiles) return this.convertedFiles;
19232
+ const entries = Object.entries(this.files.all()).filter((e) => e[1] != null);
19233
+ const files = Object.fromEntries(entries);
19234
+ this.convertedFiles = this.convertUploadedFiles(files);
19235
+ return this.convertedFiles;
19236
+ }
19237
+ /**
19238
+ * Extract and convert uploaded files from FormData.
19239
+ */
19240
+ convertUploadedFiles(files) {
19241
+ if (!this.formData) return files;
19242
+ for (const [key, value] of Object.entries(this.formData.files())) {
19243
+ if (!(value instanceof File)) continue;
19244
+ if (key.endsWith("[]")) {
19245
+ const normalizedKey = key.slice(0, -2);
19246
+ if (!files[normalizedKey]) files[normalizedKey] = [];
19247
+ files[normalizedKey].push(UploadedFile.createFromBase(value));
19248
+ } else files[key] = UploadedFile.createFromBase(value);
19249
+ }
19250
+ return files;
19251
+ }
19252
+ /**
19253
+ * Determine if the data contains a given key.
19254
+ *
19255
+ * @param keys
19256
+ * @returns
19257
+ */
19258
+ has(keys) {
19259
+ return Obj.has(this.all(), keys);
19260
+ }
19261
+ /**
19262
+ * Determine if the instance is missing a given key.
19263
+ */
19264
+ missing(key) {
19265
+ const keys = Array.isArray(key) ? key : [key];
19266
+ return !this.has(keys);
19267
+ }
19268
+ /**
19269
+ * Get a subset containing the provided keys with values from the instance data.
19270
+ *
19271
+ * @param keys
19272
+ * @returns
19273
+ */
19274
+ only(keys) {
19275
+ const data = Object.entries(this.all()).filter(([key]) => keys.includes(key));
19276
+ return Object.fromEntries(data);
19277
+ }
19278
+ /**
19279
+ * Get all of the data except for a specified array of items.
19280
+ *
19281
+ * @param keys
19282
+ * @returns
19283
+ */
19284
+ except(keys) {
19285
+ const data = Object.entries(this.all()).filter(([key]) => !keys.includes(key));
19286
+ return Object.fromEntries(data);
18347
19287
  }
18348
19288
  /**
18349
- * Get a single input field from query or body.
19289
+ * Merges new input data into the current request's input source.
19290
+ *
19291
+ * @param input - An object containing key-value pairs to merge.
19292
+ * @returns this - For fluent chaining.
18350
19293
  */
18351
- async input(key, defaultValue) {
18352
- return (await this.all())[key] ?? defaultValue;
19294
+ merge(input) {
19295
+ const source = this.getInputSource();
19296
+ for (const [key, value] of Object.entries(input)) source.set(key, value);
19297
+ return this;
19298
+ }
19299
+ /**
19300
+ * Merge new input into the request's input, but only when that key is missing from the request.
19301
+ *
19302
+ * @param input
19303
+ */
19304
+ mergeIfMissing(input) {
19305
+ return this.merge(Object.fromEntries(Object.entries(input).filter(([key]) => this.missing(key))));
19306
+ }
19307
+ /**
19308
+ * Get the keys for all of the input and files.
19309
+ */
19310
+ keys() {
19311
+ return [...Object.keys(this.input()), ...this.files.keys()];
19312
+ }
19313
+ /**
19314
+ * Determine if the request is sending JSON.
19315
+ *
19316
+ * @return bool
19317
+ */
19318
+ isJson() {
19319
+ return Str.contains(this.getHeader("CONTENT_TYPE") ?? "", ["/json", "+json"]);
19320
+ }
19321
+ /**
19322
+ * Determine if the current request probably expects a JSON response.
19323
+ *
19324
+ * @returns
19325
+ */
19326
+ expectsJson() {
19327
+ return Str.contains(this.getHeader("Accept") ?? "", "application/json");
19328
+ }
19329
+ /**
19330
+ * Determine if the current request is asking for JSON.
19331
+ *
19332
+ * @returns
19333
+ */
19334
+ wantsJson() {
19335
+ const acceptable = this.getAcceptableContentTypes();
19336
+ return !!acceptable[0] && Str.contains(acceptable[0].toLowerCase(), ["/json", "+json"]);
19337
+ }
19338
+ /**
19339
+ * Gets a list of content types acceptable by the client browser in preferable order.
19340
+ * @returns {string[]}
19341
+ */
19342
+ getAcceptableContentTypes() {
19343
+ if (this.acceptableContentTypes.length > 0) return this.acceptableContentTypes;
19344
+ const accept = this.getHeader("accept");
19345
+ if (!accept) return [];
19346
+ return this.acceptableContentTypes = accept.split(",").map((type) => type.trim()).map((type) => type.split(";")[0]).filter(Boolean);
19347
+ }
19348
+ /**
19349
+ * Determine if the request is the result of a PJAX call.
19350
+ *
19351
+ * @return bool
19352
+ */
19353
+ pjax() {
19354
+ return this.headers.get("X-PJAX") == true;
19355
+ }
19356
+ /**
19357
+ * Returns true if the request is an XMLHttpRequest (AJAX).
19358
+ *
19359
+ * @alias isXmlHttpRequest()
19360
+ * @returns {boolean}
19361
+ */
19362
+ ajax() {
19363
+ return this.isXmlHttpRequest();
19364
+ }
19365
+ /**
19366
+ * Returns true if the request is an XMLHttpRequest (AJAX).
19367
+ */
19368
+ isXmlHttpRequest() {
19369
+ return "XMLHttpRequest" === this.getHeader("X-Requested-With");
19370
+ }
19371
+ /**
19372
+ * Returns the value of the requested header.
19373
+ */
19374
+ getHeader(name) {
19375
+ return this.headers.get(name);
19376
+ }
19377
+ /**
19378
+ * Checks if the request method is of specified type.
19379
+ *
19380
+ * @param method Uppercase request method (GET, POST etc)
19381
+ */
19382
+ isMethod(method) {
19383
+ return this.getMethod() === method.toUpperCase();
19384
+ }
19385
+ /**
19386
+ * Checks whether or not the method is safe.
19387
+ *
19388
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
19389
+ */
19390
+ isMethodSafe() {
19391
+ return [
19392
+ "GET",
19393
+ "HEAD",
19394
+ "OPTIONS",
19395
+ "TRACE"
19396
+ ].includes(this.getMethod());
19397
+ }
19398
+ /**
19399
+ * Checks whether or not the method is idempotent.
19400
+ */
19401
+ isMethodIdempotent() {
19402
+ return [
19403
+ "HEAD",
19404
+ "GET",
19405
+ "PUT",
19406
+ "DELETE",
19407
+ "TRACE",
19408
+ "OPTIONS",
19409
+ "PURGE"
19410
+ ].includes(this.getMethod());
19411
+ }
19412
+ /**
19413
+ * Checks whether the method is cacheable or not.
19414
+ *
19415
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
19416
+ */
19417
+ isMethodCacheable() {
19418
+ return ["GET", "HEAD"].includes(this.getMethod());
19419
+ }
19420
+ /**
19421
+ * Initializes HTTP request formats.
19422
+ */
19423
+ static initializeFormats() {
19424
+ this.formats = {
19425
+ html: ["text/html", "application/xhtml+xml"],
19426
+ txt: ["text/plain"],
19427
+ js: [
19428
+ "application/javascript",
19429
+ "application/x-javascript",
19430
+ "text/javascript"
19431
+ ],
19432
+ css: ["text/css"],
19433
+ json: ["application/json", "application/x-json"],
19434
+ jsonld: ["application/ld+json"],
19435
+ xml: [
19436
+ "text/xml",
19437
+ "application/xml",
19438
+ "application/x-xml"
19439
+ ],
19440
+ rdf: ["application/rdf+xml"],
19441
+ atom: ["application/atom+xml"],
19442
+ rss: ["application/rss+xml"],
19443
+ form: ["application/x-www-form-urlencoded", "multipart/form-data"]
19444
+ };
19445
+ }
19446
+ /**
19447
+ * Gets the request "intended" method.
19448
+ *
19449
+ * If the X-HTTP-Method-Override header is set, and if the method is a POST,
19450
+ * then it is used to determine the "real" intended HTTP method.
19451
+ *
19452
+ * The _method request parameter can also be used to determine the HTTP method,
19453
+ * but only if enableHttpMethodParameterOverride() has been called.
19454
+ *
19455
+ * The method is always an uppercased string.
19456
+ *
19457
+ * @see getRealMethod()
19458
+ */
19459
+ getMethod() {
19460
+ if (this.#method) return this.#method;
19461
+ this.#method = this.getRealMethod();
19462
+ if ("POST" !== this.#method) return this.#method;
19463
+ let method = this.event.req.headers.get("X-HTTP-METHOD-OVERRIDE");
19464
+ if (!method && Request.httpMethodParameterOverride) method = this.request.get("_method", this.query.get("_method", "POST"));
19465
+ if (typeof method !== "string") return this.#method;
19466
+ method = method.toUpperCase();
19467
+ if ([
19468
+ "GET",
19469
+ "HEAD",
19470
+ "POST",
19471
+ "PUT",
19472
+ "DELETE",
19473
+ "CONNECT",
19474
+ "OPTIONS",
19475
+ "PATCH",
19476
+ "PURGE",
19477
+ "TRACE"
19478
+ ].includes(method)) {
19479
+ this.#method = method;
19480
+ return this.#method;
19481
+ }
19482
+ if (!/^[A-Z]+$/.test(method)) throw new SuspiciousOperationException("Invalid HTTP method override.");
19483
+ this.#method = method;
19484
+ return this.#method;
19485
+ }
19486
+ /**
19487
+ * Gets the "real" request method.
19488
+ *
19489
+ * @see getMethod()
19490
+ */
19491
+ getRealMethod() {
19492
+ return this.event.req.method.toUpperCase();
19493
+ }
19494
+ /**
19495
+ * Get the client IP address.
19496
+ */
19497
+ ip() {
19498
+ return getRequestIP(this.event);
19499
+ }
19500
+ /**
19501
+ * Get a URI instance for the request.
19502
+ */
19503
+ uri() {
19504
+ return this.#uri;
19505
+ }
19506
+ /**
19507
+ * Get the full URL for the request.
19508
+ */
19509
+ fullUrl() {
19510
+ return this.event.req.url;
19511
+ }
19512
+ /**
19513
+ * Return the Request instance.
19514
+ */
19515
+ instance() {
19516
+ return this;
19517
+ }
19518
+ /**
19519
+ * Get the request method.
19520
+ */
19521
+ method() {
19522
+ return this.getMethod();
19523
+ }
19524
+ /**
19525
+ * Get the JSON payload for the request.
19526
+ *
19527
+ * @param key
19528
+ * @param defaultValue
19529
+ * @return {InputBag}
19530
+ */
19531
+ json(key, defaultValue) {
19532
+ if (!this.#json) {
19533
+ let json = this.getContent();
19534
+ if (typeof json == "string") json = JSON.parse(json || "{}");
19535
+ this.#json = new InputBag(json, this.event);
19536
+ }
19537
+ if (!key) return this.#json;
19538
+ return Obj.get(this.#json.all(), key, defaultValue);
19539
+ }
19540
+ /**
19541
+ * Get the input source for the request.
19542
+ *
19543
+ * @return {InputBag}
19544
+ */
19545
+ getInputSource() {
19546
+ if (this.isJson()) return this.json();
19547
+ return ["GET", "HEAD"].includes(this.getRealMethod()) ? this.query : this.request;
19548
+ }
19549
+ /**
19550
+ * Returns the request body content.
19551
+ *
19552
+ * @param asStream If true, returns a ReadableStream instead of the parsed string
19553
+ * @return {string | ReadableStream | Promise<string | ReadableStream>}
19554
+ */
19555
+ getContent(asStream = false) {
19556
+ let content = this.body;
19557
+ if (content !== void 0 && content !== null) {
19558
+ if (asStream) {
19559
+ if (content instanceof ReadableStream) return content;
19560
+ const encoder = new TextEncoder();
19561
+ return new ReadableStream({ start(controller) {
19562
+ controller.enqueue(encoder.encode(String(content)));
19563
+ controller.close();
19564
+ } });
19565
+ }
19566
+ if (typeof content === "string") return content;
19567
+ }
19568
+ if (asStream) return this.content;
19569
+ content = this.content;
19570
+ this.body = content;
19571
+ return content;
19572
+ }
19573
+ /**
19574
+ * Determine if the uploaded data contains a file.
19575
+ *
19576
+ * @param key
19577
+ * @return boolean
19578
+ */
19579
+ hasFile(key) {
19580
+ let files = this.file(key);
19581
+ if (!Array.isArray(files)) files = [files];
19582
+ return files.some((e) => this.isValidFile(e));
19583
+ }
19584
+ /**
19585
+ * Check that the given file is a valid file instance.
19586
+ *
19587
+ * @param file
19588
+ * @return boolean
19589
+ */
19590
+ isValidFile(file) {
19591
+ return file.content instanceof File && file.size > 0;
19592
+ }
19593
+ /**
19594
+ * Gets a "parameter" value from any bag.
19595
+ *
19596
+ * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
19597
+ * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
19598
+ * public property instead (attributes, query, request).
19599
+ *
19600
+ * Order of precedence: PATH (routing placeholders or custom attributes), GET, POST
19601
+ *
19602
+ * @internal use explicit input sources instead
19603
+ */
19604
+ get(key, defaultValue) {
19605
+ const result = this.attributes.get(key, this);
19606
+ if (this !== result) return result;
19607
+ if (this.query.has(key)) return this.query.all()[key];
19608
+ if (this.request.has(key)) return this.request.all()[key];
19609
+ return defaultValue;
19610
+ }
19611
+ /**
19612
+ * Enables support for the _method request parameter to determine the intended HTTP method.
19613
+ *
19614
+ * Be warned that enabling this feature might lead to CSRF issues in your code.
19615
+ * Check that you are using CSRF tokens when required.
19616
+ * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
19617
+ * and used to send a "PUT" or "DELETE" request via the _method request parameter.
19618
+ * If these methods are not protected against CSRF, this presents a possible vulnerability.
19619
+ *
19620
+ * The HTTP method can only be overridden when the real HTTP method is POST.
19621
+ */
19622
+ static enableHttpMethodParameterOverride() {
19623
+ this.httpMethodParameterOverride = true;
19624
+ }
19625
+ /**
19626
+ * Checks whether support for the _method request parameter is enabled.
19627
+ */
19628
+ static getHttpMethodParameterOverride() {
19629
+ return this.httpMethodParameterOverride;
19630
+ }
19631
+ /**
19632
+ * Dump the items.
19633
+ *
19634
+ * @param keys
19635
+ * @return this
19636
+ */
19637
+ dump(...keys) {
19638
+ if (keys.length > 0) this.only(keys).then(dump);
19639
+ else this.all().then(dump);
19640
+ return this;
18353
19641
  }
18354
19642
  getEvent(key) {
18355
19643
  return safeDot(this.event, key);
@@ -18534,7 +19822,7 @@ var Response = class {
18534
19822
  }
18535
19823
  html(content) {
18536
19824
  this.applyHeaders();
18537
- return html(this.event, content);
19825
+ return html(content);
18538
19826
  }
18539
19827
  /**
18540
19828
  * Send a JSON response.
@@ -18555,9 +19843,9 @@ var Response = class {
18555
19843
  /**
18556
19844
  * Redirect to another URL.
18557
19845
  */
18558
- redirect(url, status = 302) {
19846
+ redirect(location, status = 302, statusText) {
18559
19847
  this.setStatusCode(status);
18560
- return redirect(this.event, url, this.statusCode);
19848
+ return redirect(location, this.statusCode, statusText);
18561
19849
  }
18562
19850
  /**
18563
19851
  * Apply headers before sending response.
@@ -18573,4 +19861,4 @@ var Response = class {
18573
19861
  };
18574
19862
 
18575
19863
  //#endregion
18576
- export { ApiResource, FireCommand, HttpContext, HttpServiceProvider, JsonResource, LogRequests, Middleware, Request, Response };
19864
+ export { ApiResource, BadRequestException, FileBag, FireCommand, FormRequest, HeaderBag, HttpContext, HttpServiceProvider, InputBag, JsonResource, LogRequests, Middleware, ParamBag, Request, Response, ServerBag, SuspiciousOperationException, UnexpectedValueException, UploadedFile };