@wooksjs/event-http 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -9,6 +9,7 @@ import { WooksAdapterBase } from "wooks";
9
9
  import { Readable as Readable$1 } from "stream";
10
10
 
11
11
  //#region packages/event-http/src/event-http.ts
12
+ /** Creates an async event context for an incoming HTTP request/response pair. */
12
13
  function createHttpContext(data, options) {
13
14
  return createAsyncEventContext({
14
15
  event: {
@@ -34,7 +35,7 @@ function escapeRegex(s) {
34
35
  function safeDecode(f, v) {
35
36
  try {
36
37
  return f(v);
37
- } catch (error) {
38
+ } catch {
38
39
  return v;
39
40
  }
40
41
  }
@@ -66,7 +67,12 @@ const units = {
66
67
 
67
68
  //#endregion
68
69
  //#region packages/event-http/src/utils/set-cookie.ts
70
+ const COOKIE_NAME_RE = /^[\w!#$%&'*+\-.^`|~]+$/;
71
+ function sanitizeCookieAttrValue(v) {
72
+ return v.replace(/[;\r\n]/g, "");
73
+ }
69
74
  function renderCookie(key, data) {
75
+ if (!COOKIE_NAME_RE.test(key)) throw new TypeError(`Invalid cookie name "${key}"`);
70
76
  let attrs = "";
71
77
  for (const [a, v] of Object.entries(data.attrs)) {
72
78
  const func = cookieAttrFunc[a];
@@ -80,8 +86,8 @@ function renderCookie(key, data) {
80
86
  const cookieAttrFunc = {
81
87
  expires: (v) => `Expires=${typeof v === "string" || typeof v === "number" ? new Date(v).toUTCString() : v.toUTCString()}`,
82
88
  maxAge: (v) => `Max-Age=${convertTime(v, "s").toString()}`,
83
- domain: (v) => `Domain=${v}`,
84
- path: (v) => `Path=${v}`,
89
+ domain: (v) => `Domain=${sanitizeCookieAttrValue(String(v))}`,
90
+ path: (v) => `Path=${sanitizeCookieAttrValue(String(v))}`,
85
91
  secure: (v) => v ? "Secure" : "",
86
92
  httpOnly: (v) => v ? "HttpOnly" : "",
87
93
  sameSite: (v) => v ? `SameSite=${typeof v === "string" ? v : "Strict"}` : ""
@@ -102,7 +108,7 @@ function encodingSupportsStream(encodings) {
102
108
  }
103
109
  async function uncompressBody(encodings, compressed) {
104
110
  let buf = compressed;
105
- for (const enc of encodings.slice().reverse()) {
111
+ for (const enc of encodings.slice().toReversed()) {
106
112
  const c = compressors[enc];
107
113
  if (!c) throw new Error(`Unsupported compression type "${enc}".`);
108
114
  buf = await c.uncompress(buf);
@@ -112,7 +118,7 @@ async function uncompressBody(encodings, compressed) {
112
118
  async function uncompressBodyStream(encodings, src) {
113
119
  if (!encodingSupportsStream(encodings)) throw new Error("Some encodings lack a streaming decompressor");
114
120
  let out = src;
115
- for (const enc of Array.from(encodings).reverse()) out = await compressors[enc].stream.uncompress(out);
121
+ for (const enc of Array.from(encodings).toReversed()) out = await compressors[enc].stream.uncompress(out);
116
122
  return out;
117
123
  }
118
124
 
@@ -315,24 +321,395 @@ let EHttpStatusCode = /* @__PURE__ */ function(EHttpStatusCode$1) {
315
321
  return EHttpStatusCode$1;
316
322
  }({});
317
323
 
324
+ //#endregion
325
+ //#region packages/event-http/src/errors/403.tl.svg
326
+ function _403_tl_default(ctx) {
327
+ return `<svg height="64" viewBox="0 4 100 96" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#888888" stroke-width="2">
328
+ <path d="M50 90.625C64.4042 87.1875 83.5751 69.8667 83.5751 48.6937V24.0854L50 9.375L16.425 24.0833V48.6937C16.425 69.8667 35.5959 87.1875 50 90.625Z" fill="#ff000050">
329
+ <animate attributeName="fill" dur="2s" repeatCount="indefinite"
330
+ values="#ff000000;#ff000050;#ff000000" />
331
+ </path>
332
+
333
+ <path d="M61.5395 46.0812H38.4604C37.1061 46.0812 36.0083 47.1791 36.0083 48.5333V65.075C36.0083 66.4292 37.1061 67.5271 38.4604 67.5271H61.5395C62.8938 67.5271 63.9916 66.4292 63.9916 65.075V48.5333C63.9916 47.1791 62.8938 46.0812 61.5395 46.0812Z" />
334
+
335
+ <path d="M41.7834 46.0834V39.6813C41.7834 37.5021 42.6491 35.4121 44.1901 33.8712C45.731 32.3303 47.8209 31.4646 50.0001 31.4646C52.1793 31.4646 54.2693 32.3303 55.8102 33.8712C57.3511 35.4121 58.2168 37.5021 58.2168 39.6813V46.0813" />
336
+ </svg>
337
+ `;
338
+ }
339
+
340
+ //#endregion
341
+ //#region packages/event-http/src/errors/404.tl.svg
342
+ function _404_tl_default(ctx) {
343
+ return `<svg height="64" viewBox="0 20 100 64" fill="none" xmlns="http://www.w3.org/2000/svg">
344
+ <defs>
345
+ <path id="sheet" d="M86.5 36.5V47.5H97.5V92.5H52.5V36.5H86.5ZM63 68.5H87V67.5H63V68.5ZM63 63.5H87V62.5H63V63.5ZM63 58.5H87V57.5H63V58.5ZM96.793 46.5H87.5V37.207L96.793 46.5Z" fill="#88888833" stroke="#888888aa"/>
346
+ </defs>
347
+
348
+ <g id="queue" transform="translate(-5 -10)">
349
+ <use href="#sheet" opacity="0">
350
+ <animateTransform attributeName="transform" type="translate"
351
+ dur="3s" repeatCount="indefinite"
352
+ keyTimes="0;0.1;0.32;0.42;1"
353
+ values="30 0; -20 0; -20 0; -70 0; -70 0" />
354
+ <animate attributeName="opacity"
355
+ dur="3s" repeatCount="indefinite"
356
+ keyTimes="0;0.1;0.32;0.42;1"
357
+ values="0;1;1;0;0" />
358
+ </use>
359
+
360
+ <use href="#sheet" opacity="0">
361
+ <animateTransform attributeName="transform" type="translate"
362
+ dur="3s" begin="1s" repeatCount="indefinite"
363
+ keyTimes="0;0.1;0.32;0.42;1"
364
+ values="30 0; -20 0; -20 0; -70 0; -70 0" />
365
+ <animate attributeName="opacity"
366
+ dur="3s" begin="1s" repeatCount="indefinite"
367
+ keyTimes="0;0.1;0.32;0.42;1"
368
+ values="0;1;1;0;0" />
369
+ </use>
370
+
371
+ <use href="#sheet" opacity="0">
372
+ <animateTransform attributeName="transform" type="translate"
373
+ dur="3s" begin="2s" repeatCount="indefinite"
374
+ keyTimes="0;0.1;0.32;0.42;1"
375
+ values="30 0; -20 0; -20 0; -70 0; -70 0" />
376
+ <animate attributeName="opacity"
377
+ dur="3s" begin="2s" repeatCount="indefinite"
378
+ keyTimes="0;0.1;0.32;0.42;1"
379
+ values="0;1;1;0;0" />
380
+ </use>
381
+ </g>
382
+
383
+ <g>
384
+ <path d="M49.5 32.5C58.3366 32.5 65.5 39.6634 65.5 48.5C65.5 54.4781 62.222 59.6923 57.3584 62.4404C55.0386 63.7512 52.3591 64.5 49.5 64.5C40.6634 64.5 33.5 57.3366 33.5 48.5C33.5 39.6634 40.6634 32.5 49.5 32.5Z" fill="#ffffff50" stroke="#888888" stroke-width="3"/>
385
+
386
+ <path d="M62.7101 74.5691C63.117 75.2907 64.0318 75.5459 64.7534 75.139C65.4751 74.7321 65.7302 73.8173 65.3233 73.0957L62.7101 74.5691ZM58.05 63.25L56.7434 63.9867L62.7101 74.5691L64.0167 73.8324L65.3233 73.0957L59.3567 62.5133L58.05 63.25Z" fill="#888888"/>
387
+ </g>
388
+ </svg>
389
+
390
+ `;
391
+ }
392
+
393
+ //#endregion
394
+ //#region packages/event-http/src/errors/500.tl.svg
395
+ function _500_tl_default(ctx) {
396
+ return `<svg height="64" viewBox="0 0 120 100" xmlns="http://www.w3.org/2000/svg">
397
+
398
+ <g id="server">
399
+
400
+ <g fill="#88888888" stroke="#88888888" stroke-width="2" >
401
+ <path d="M18 90C13.5817 90 10 86.4182 10 82V38C10 33.5817 13.5817 30 18 30H50.5L58 43L52.5 53L56 68.5L49.0098 89.97L61.2141 71.4358L58.363 54.315L64.7769 43.5511L58.2763 30.2943L104.243 32.0434C108.658 32.2114 112.101 35.9267 111.933 40.3418L110.26 84.31C110.092 88.725 106.377 92.168 101.962 92L49 90H18Z" />
402
+ </g>
403
+ <circle cx="30" cy="60" r="6" fill="red">
404
+ <animate attributeName="fill" dur="0.8s"
405
+ values="red;#2d0000;red" repeatCount="indefinite"/>
406
+ </circle>
407
+
408
+ </g>
409
+
410
+ <g fill="lightgray" opacity="0.75">
411
+ <circle cx="50" cy="35" r="6">
412
+ <animate attributeName="cy" from="35" to="15" dur="2s"
413
+ repeatCount="indefinite"/>
414
+ <animate attributeName="opacity" values="0.75;0" dur="2s"
415
+ repeatCount="indefinite"/>
416
+ </circle>
417
+ <circle cx="60" cy="40" r="4">
418
+ <animate attributeName="cy" from="40" to="20" dur="2s"
419
+ begin="0.4s" repeatCount="indefinite"/>
420
+ <animate attributeName="opacity" values="0.75;0" dur="2s"
421
+ begin="0.4s" repeatCount="indefinite"/>
422
+ </circle>
423
+ </g>
424
+
425
+ </svg>
426
+ `;
427
+ }
428
+
429
+ //#endregion
430
+ //#region packages/event-http/src/errors/error.tl.html
431
+ function error_tl_default(ctx) {
432
+ const { statusCode, statusMessage, icon, message, details, link, image, version } = ctx;
433
+ return `<!doctype html>
434
+ <html lang="en">
435
+ <head>
436
+ <meta charset="UTF-8" />
437
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
438
+ <title>${statusCode} ${statusMessage}</title>
439
+ <style>
440
+ body {
441
+ font-family:
442
+ -apple-system, BlinkMacMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
443
+ 'Open Sans', 'Helvetica Neue', sans-serif;
444
+ display: flex;
445
+ justify-content: center;
446
+ align-items: flex-start;
447
+ min-height: 100vh;
448
+ margin: 0;
449
+ padding: 0 20px;
450
+ box-sizing: border-box;
451
+ transition:
452
+ background-color 0.3s ease,
453
+ color 0.3s ease;
454
+ }
455
+
456
+ .error-container {
457
+ padding: 48px;
458
+ padding-bottom: 12px !important;
459
+ background-color: #ffffff;
460
+ border-radius: 0 0 12px 12px;
461
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
462
+ text-align: center;
463
+ max-width: 650px;
464
+ width: 100%;
465
+ transition:
466
+ background-color 0.3s ease,
467
+ border-color 0.3s ease,
468
+ box-shadow 0.3s ease;
469
+ }
470
+
471
+ .status-code {
472
+ font-size: 5rem;
473
+ font-weight: 900;
474
+ margin-bottom: 5px;
475
+ line-height: 1;
476
+ transition: color 0.3s ease;
477
+ display: flex;
478
+ align-items: center;
479
+ justify-content: center;
480
+ gap: 1rem;
481
+ position: relative;
482
+ margin-right: 24px;
483
+ }
484
+
485
+ .status-text {
486
+ font-size: 2.25rem;
487
+ font-weight: 700;
488
+ margin-bottom: 25px;
489
+ transition: color 0.3s ease;
490
+ }
491
+
492
+ .error-message {
493
+ font-size: 1.25rem;
494
+ margin-bottom: 40px;
495
+ line-height: 1.7;
496
+ transition: color 0.3s ease;
497
+ }
498
+
499
+ .json-details-container {
500
+ padding: 20px;
501
+ border-radius: 8px;
502
+ text-align: left;
503
+ overflow-x: auto;
504
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
505
+ font-size: 0.9rem;
506
+ border: 1px solid;
507
+ transition:
508
+ background-color 0.3s ease,
509
+ color 0.3s ease,
510
+ border-color 0.3s ease;
511
+ }
512
+
513
+ .json-details-container pre {
514
+ margin: 0;
515
+ white-space: pre-wrap;
516
+ word-break: break-all;
517
+ }
518
+
519
+ .json-details-container code {
520
+ display: block;
521
+ }
522
+
523
+ body {
524
+ background-color: #f8fafc;
525
+ color: #1f2937;
526
+ }
527
+ .error-container {
528
+ background-color: #ffffff;
529
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
530
+ }
531
+ .status-code {
532
+ color: #dc2626;
533
+ }
534
+ .status-text {
535
+ color: #1f2937;
536
+ }
537
+ .error-message {
538
+ color: #4b5563;
539
+ }
540
+ .json-details-container {
541
+ background-color: #f0f4f8;
542
+ color: #374151;
543
+ border-color: #d1d5db;
544
+ }
545
+ .json-details-container p {
546
+ color: #6b7280;
547
+ }
548
+
549
+ @media (prefers-color-scheme: dark) {
550
+ body {
551
+ background-color: #2d3748;
552
+ color: #e2e8f0;
553
+ }
554
+ .error-container {
555
+ background-color: #1a202c;
556
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
557
+ }
558
+ .status-code {
559
+ color: #f56565;
560
+ }
561
+ .status-text {
562
+ color: #cbd5e1;
563
+ }
564
+ .error-message {
565
+ color: #a0aec0;
566
+ }
567
+ .json-details-container {
568
+ background-color: #2d3748;
569
+ color: #e2e8f0;
570
+ border-color: #4a5568;
571
+ }
572
+ .json-details-container p {
573
+ color: #a0aec0;
574
+ }
575
+ }
576
+
577
+ .footer {
578
+ display: flex;
579
+ gap: 0.25rem;
580
+ font-size: 0.6em;
581
+ justify-content: flex-end;
582
+ align-items: center;
583
+ margin-top: 12px;
584
+ opacity: 0.5;
585
+ transition: 0.25s ease-in-out;
586
+ }
587
+
588
+ .footer img {
589
+ filter: grayscale(0.5);
590
+ transition: 0.25s ease-in-out;
591
+ }
592
+
593
+ .footer:hover {
594
+ opacity: 1;
595
+ }
596
+ .footer:hover img {
597
+ filter: grayscale(0);
598
+ }
599
+
600
+ @media (max-width: 768px) {
601
+ body {
602
+ padding: 15px;
603
+ }
604
+ .error-container {
605
+ padding: 32px;
606
+ }
607
+ .status-code {
608
+ font-size: 4rem;
609
+ }
610
+ .status-text {
611
+ font-size: 1.8rem;
612
+ }
613
+ .error-message {
614
+ font-size: 1.1rem;
615
+ margin-bottom: 30px;
616
+ }
617
+ .json-details-container {
618
+ font-size: 0.85rem;
619
+ }
620
+ }
621
+
622
+ @media (max-width: 480px) {
623
+ body {
624
+ padding: 10px;
625
+ }
626
+ .error-container {
627
+ padding: 24px;
628
+ border-radius: 8px;
629
+ }
630
+ .status-code {
631
+ font-size: 3rem;
632
+ }
633
+ .status-text {
634
+ font-size: 1.5rem;
635
+ margin-bottom: 20px;
636
+ }
637
+ .error-message {
638
+ font-size: 1rem;
639
+ margin-bottom: 25px;
640
+ }
641
+ .json-details-container {
642
+ padding: 15px;
643
+ font-size: 0.8rem;
644
+ }
645
+ }
646
+ </style>
647
+ </head>
648
+ <body>
649
+ <div class="error-container">
650
+ <div id="statusCode" class="status-code">${icon} ${statusCode}</div>
651
+
652
+ <div id="statusText" class="status-text">${statusMessage}</div>
653
+
654
+ <p id="errorMessage" class="error-message">${message}</p>
655
+
656
+ <!-- prettier-ignore -->
657
+ <div class="json-details-container"style="display: ${details ? "block" : "none"};">
658
+ <p class="text-sm">Technical Details:</p>
659
+ <pre><code id="jsonDetails">${details}</code></pre>
660
+ </div>
661
+ <div class="footer">
662
+ Powered by
663
+ <a href="${link}" target="_blank">
664
+ <img height="20" alt="%{poweredBy}" src="${image}" />
665
+ </a>
666
+ v${version}
667
+ </div>
668
+ </div>
669
+ </body>
670
+ </html>
671
+ `;
672
+ }
673
+
318
674
  //#endregion
319
675
  //#region packages/event-http/src/errors/error-renderer.ts
320
- const preStyles = "font-family: monospace;width: 100%;max-width: 900px;padding: 10px;margin: 20px auto;border-radius: 8px;background-color: #494949;box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);";
676
+ let framework = {
677
+ version: "0.6.2",
678
+ poweredBy: `wooksjs`,
679
+ link: `https://wooks.moost.org/`,
680
+ image: `https://wooks.moost.org/wooks-full-logo.png`
681
+ };
682
+ /** Renders HTTP error responses in HTML, JSON, or plain text based on the Accept header. */
321
683
  var HttpErrorRenderer = class extends BaseHttpResponseRenderer {
684
+ constructor(opts) {
685
+ super();
686
+ this.opts = opts;
687
+ }
688
+ icons = {
689
+ 401: typeof _403_tl_default === "function" ? _403_tl_default({}) : "",
690
+ 403: typeof _403_tl_default === "function" ? _403_tl_default({}) : "",
691
+ 404: typeof _404_tl_default === "function" ? _404_tl_default({}) : "",
692
+ 500: typeof _500_tl_default === "function" ? _500_tl_default({}) : ""
693
+ };
694
+ static registerFramework(opts) {
695
+ framework = opts;
696
+ }
322
697
  renderHtml(response) {
323
698
  const data = response.body || {};
324
699
  response.setContentType("text/html");
325
- const keys = Object.keys(data).filter((key) => ![
326
- "statusCode",
327
- "error",
328
- "message"
329
- ].includes(key));
330
- return `<html style="background-color: #333; color: #bbb;"><head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head><body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center><center><h4>${data.message}</h1></center><hr color="#666"><center style="color: #666;"> Wooks v0.6.0 </center>${keys.length > 0 ? `<pre style="${preStyles}">${JSON.stringify({
331
- ...data,
332
- statusCode: void 0,
333
- message: void 0,
334
- error: void 0
335
- }, null, " ")}</pre>` : ""}</body></html>`;
700
+ const hasDetails = Object.keys(data).length > 3;
701
+ const icon = data.statusCode >= 500 ? this.icons[500] : this.icons[data.statusCode] || "";
702
+ return typeof error_tl_default === "function" ? error_tl_default({
703
+ icon,
704
+ statusCode: data.statusCode,
705
+ statusMessage: httpStatusCodes[data.statusCode],
706
+ message: data.message,
707
+ details: hasDetails ? JSON.stringify(data, null, " ") : "",
708
+ version: (this.opts || framework).version,
709
+ poweredBy: (this.opts || framework).poweredBy,
710
+ link: (this.opts || framework).link,
711
+ image: (this.opts || framework).image
712
+ }) : JSON.stringify(data, null, " ");
336
713
  }
337
714
  renderText(response) {
338
715
  const data = response.body || {};
@@ -374,6 +751,7 @@ function escapeQuotes(s) {
374
751
 
375
752
  //#endregion
376
753
  //#region packages/event-http/src/errors/http-error.ts
754
+ /** Represents an HTTP error with a status code and optional structured body. */
377
755
  var HttpError = class extends Error {
378
756
  name = "HttpError";
379
757
  constructor(code = 500, _body = "") {
@@ -405,15 +783,24 @@ var HttpError = class extends Error {
405
783
  //#endregion
406
784
  //#region packages/event-http/src/composables/request.ts
407
785
  const xForwardedFor = "x-forwarded-for";
786
+ /** Default safety limits for request body reading (size, ratio, timeout). */
408
787
  const DEFAULT_LIMITS = {
409
788
  maxCompressed: 1 * 1024 * 1024,
410
789
  maxInflated: 10 * 1024 * 1024,
411
790
  maxRatio: 100,
412
791
  readTimeoutMs: 1e4
413
792
  };
793
+ /**
794
+ * Provides access to the incoming HTTP request (method, url, headers, body, IP).
795
+ * @example
796
+ * ```ts
797
+ * const { method, url, rawBody, getIp } = useRequest()
798
+ * const body = await rawBody()
799
+ * ```
800
+ */
414
801
  function useRequest() {
415
802
  const { store } = useHttpContext();
416
- const { init, get, set } = store("request");
803
+ const { init } = store("request");
417
804
  const event = store("event");
418
805
  const req = event.get("req");
419
806
  const contentEncoding = req.headers["content-encoding"];
@@ -427,18 +814,33 @@ function useRequest() {
427
814
  ].includes(p)) return true;
428
815
  return false;
429
816
  });
430
- const getMaxCompressed = () => get("maxCompressed") ?? DEFAULT_LIMITS.maxCompressed;
431
- const setMaxCompressed = (limit) => set("maxCompressed", limit);
432
- const getReadTimeoutMs = () => get("readTimeoutMs") ?? DEFAULT_LIMITS.readTimeoutMs;
433
- const setReadTimeoutMs = (limit) => set("readTimeoutMs", limit);
434
- const getMaxInflated = () => get("maxInflated") ?? DEFAULT_LIMITS.maxInflated;
435
- const setMaxInflated = (limit) => set("maxInflated", limit);
817
+ const limits = () => event.get("requestLimits");
818
+ const setLimit = (key, value) => {
819
+ let obj = limits();
820
+ if (!obj?.perRequest) {
821
+ obj = {
822
+ ...obj,
823
+ perRequest: true
824
+ };
825
+ event.set("requestLimits", obj);
826
+ }
827
+ obj[key] = value;
828
+ };
829
+ const getMaxCompressed = () => limits()?.maxCompressed ?? DEFAULT_LIMITS.maxCompressed;
830
+ const setMaxCompressed = (limit) => setLimit("maxCompressed", limit);
831
+ const getMaxInflated = () => limits()?.maxInflated ?? DEFAULT_LIMITS.maxInflated;
832
+ const setMaxInflated = (limit) => setLimit("maxInflated", limit);
833
+ const getMaxRatio = () => limits()?.maxRatio ?? DEFAULT_LIMITS.maxRatio;
834
+ const setMaxRatio = (limit) => setLimit("maxRatio", limit);
835
+ const getReadTimeoutMs = () => limits()?.readTimeoutMs ?? DEFAULT_LIMITS.readTimeoutMs;
836
+ const setReadTimeoutMs = (limit) => setLimit("readTimeoutMs", limit);
436
837
  const rawBody = () => init("rawBody", async () => {
437
838
  const encs = contentEncodings();
438
839
  const isZip = isCompressed();
439
840
  const streamable = isZip && encodingSupportsStream(encs);
440
841
  const maxCompressed = getMaxCompressed();
441
842
  const maxInflated = getMaxInflated();
843
+ const maxRatio = getMaxRatio();
442
844
  const timeoutMs = getReadTimeoutMs();
443
845
  const cl = Number(req.headers["content-length"] ?? 0);
444
846
  const upfrontLimit = isZip ? maxCompressed : maxInflated;
@@ -496,6 +898,7 @@ function useRequest() {
496
898
  inflatedBytes = body.byteLength;
497
899
  if (inflatedBytes > maxInflated) throw new HttpError(413, "Inflated body too large");
498
900
  }
901
+ if (isZip && rawBytes > 0 && inflatedBytes / rawBytes > maxRatio) throw new HttpError(413, "Compression ratio too high");
499
902
  return body;
500
903
  });
501
904
  const reqId = useEventId().getId;
@@ -527,15 +930,32 @@ function useRequest() {
527
930
  getReadTimeoutMs,
528
931
  setReadTimeoutMs,
529
932
  getMaxInflated,
530
- setMaxInflated
933
+ setMaxInflated,
934
+ getMaxRatio,
935
+ setMaxRatio
531
936
  };
532
937
  }
533
938
 
534
939
  //#endregion
535
940
  //#region packages/event-http/src/composables/headers.ts
941
+ /**
942
+ * Returns the incoming request headers.
943
+ * @example
944
+ * ```ts
945
+ * const { host, authorization } = useHeaders()
946
+ * ```
947
+ */
536
948
  function useHeaders() {
537
949
  return useRequest().headers;
538
950
  }
951
+ /**
952
+ * Provides methods to set, get, and remove outgoing response headers.
953
+ * @example
954
+ * ```ts
955
+ * const { setHeader, setContentType, enableCors } = useSetHeaders()
956
+ * setHeader('x-request-id', '123')
957
+ * ```
958
+ */
539
959
  function useSetHeaders() {
540
960
  const { store } = useHttpContext();
541
961
  const setHeaderStore = store("setHeader");
@@ -557,6 +977,7 @@ function useSetHeaders() {
557
977
  enableCors
558
978
  };
559
979
  }
980
+ /** Returns a hookable accessor for a single outgoing response header by name. */
560
981
  function useSetHeader(name) {
561
982
  const { store } = useHttpContext();
562
983
  const { hook } = store("setHeader");
@@ -565,6 +986,14 @@ function useSetHeader(name) {
565
986
 
566
987
  //#endregion
567
988
  //#region packages/event-http/src/composables/cookies.ts
989
+ /**
990
+ * Provides access to parsed request cookies.
991
+ * @example
992
+ * ```ts
993
+ * const { getCookie, rawCookies } = useCookies()
994
+ * const sessionId = getCookie('session_id')
995
+ * ```
996
+ */
568
997
  function useCookies() {
569
998
  const { store } = useHttpContext();
570
999
  const { cookie } = useHeaders();
@@ -580,6 +1009,7 @@ function useCookies() {
580
1009
  getCookie
581
1010
  };
582
1011
  }
1012
+ /** Provides methods to set, get, remove, and clear outgoing response cookies. */
583
1013
  function useSetCookies() {
584
1014
  const { store } = useHttpContext();
585
1015
  const cookiesStore = store("setCookies");
@@ -600,6 +1030,7 @@ function useSetCookies() {
600
1030
  cookies
601
1031
  };
602
1032
  }
1033
+ /** Returns a hookable accessor for a single outgoing cookie by name. */
603
1034
  function useSetCookie(name) {
604
1035
  const { setCookie, getCookie } = useSetCookies();
605
1036
  const valueHook = attachHook({
@@ -621,6 +1052,7 @@ function useSetCookie(name) {
621
1052
 
622
1053
  //#endregion
623
1054
  //#region packages/event-http/src/composables/header-accept.ts
1055
+ /** Provides helpers to check the request's Accept header for supported MIME types. */
624
1056
  function useAccept() {
625
1057
  const { store } = useHttpContext();
626
1058
  const { accept } = useHeaders();
@@ -641,6 +1073,14 @@ function useAccept() {
641
1073
 
642
1074
  //#endregion
643
1075
  //#region packages/event-http/src/composables/header-authorization.ts
1076
+ /**
1077
+ * Provides parsed access to the Authorization header (type, credentials, Basic decoding).
1078
+ * @example
1079
+ * ```ts
1080
+ * const { isBearer, authRawCredentials, basicCredentials } = useAuthorization()
1081
+ * if (isBearer()) { const token = authRawCredentials() }
1082
+ * ```
1083
+ */
644
1084
  function useAuthorization() {
645
1085
  const { store } = useHttpContext();
646
1086
  const { authorization } = useHeaders();
@@ -713,6 +1153,7 @@ const cacheControlFunc = {
713
1153
  const renderAge = (v) => convertTime(v, "s").toString();
714
1154
  const renderExpires = (v) => typeof v === "string" || typeof v === "number" ? new Date(v).toUTCString() : v.toUTCString();
715
1155
  const renderPragmaNoCache = (v) => v ? "no-cache" : "";
1156
+ /** Provides helpers to set cache-related response headers (Cache-Control, Expires, Age, Pragma). */
716
1157
  function useSetCacheControl() {
717
1158
  const { setHeader } = useSetHeaders();
718
1159
  const setAge = (value) => {
@@ -737,6 +1178,14 @@ function useSetCacheControl() {
737
1178
 
738
1179
  //#endregion
739
1180
  //#region packages/event-http/src/composables/response.ts
1181
+ /**
1182
+ * Provides access to the raw HTTP response and status code management.
1183
+ * @example
1184
+ * ```ts
1185
+ * const { status, rawResponse, hasResponded } = useResponse()
1186
+ * status(200)
1187
+ * ```
1188
+ */
740
1189
  function useResponse() {
741
1190
  const { store } = useHttpContext();
742
1191
  const event = store("event");
@@ -759,6 +1208,7 @@ function useResponse() {
759
1208
  })
760
1209
  };
761
1210
  }
1211
+ /** Returns a hookable accessor for the response status code. */
762
1212
  function useStatus() {
763
1213
  const { store } = useHttpContext();
764
1214
  return store("status").hook("code");
@@ -766,6 +1216,11 @@ function useStatus() {
766
1216
 
767
1217
  //#endregion
768
1218
  //#region packages/event-http/src/utils/url-search-params.ts
1219
+ const ILLEGAL_KEYS = new Set([
1220
+ "__proto__",
1221
+ "constructor",
1222
+ "prototype"
1223
+ ]);
769
1224
  var WooksURLSearchParams = class extends URLSearchParams {
770
1225
  toJson() {
771
1226
  const json = Object.create(null);
@@ -773,7 +1228,7 @@ var WooksURLSearchParams = class extends URLSearchParams {
773
1228
  const a = json[key] = json[key] || [];
774
1229
  a.push(value);
775
1230
  } else {
776
- if (key === "__proto__") throw new HttpError(400, `Illegal key name "${key}"`);
1231
+ if (ILLEGAL_KEYS.has(key)) throw new HttpError(400, `Illegal key name "${key}"`);
777
1232
  if (key in json) throw new HttpError(400, `Duplicate key "${key}"`);
778
1233
  json[key] = value;
779
1234
  }
@@ -786,6 +1241,14 @@ function isArrayParam(name) {
786
1241
 
787
1242
  //#endregion
788
1243
  //#region packages/event-http/src/composables/search-params.ts
1244
+ /**
1245
+ * Provides access to URL search (query) parameters from the request.
1246
+ * @example
1247
+ * ```ts
1248
+ * const { urlSearchParams, jsonSearchParams } = useSearchParams()
1249
+ * const page = urlSearchParams().get('page')
1250
+ * ```
1251
+ */
789
1252
  function useSearchParams() {
790
1253
  const { store } = useHttpContext();
791
1254
  const url = useRequest().url || "";
@@ -960,7 +1423,7 @@ var BaseHttpResponse = class {
960
1423
  async function respondWithFetch(fetchBody, res) {
961
1424
  if (fetchBody) try {
962
1425
  for await (const chunk of fetchBody) res.write(chunk);
963
- } catch (error) {}
1426
+ } catch {}
964
1427
  res.end();
965
1428
  }
966
1429
 
@@ -988,6 +1451,7 @@ function createWooksResponder(renderer = new BaseHttpResponseRenderer(), errorRe
988
1451
 
989
1452
  //#endregion
990
1453
  //#region packages/event-http/src/http-adapter.ts
1454
+ /** HTTP adapter for Wooks that provides route registration, server lifecycle, and request handling. */
991
1455
  var WooksHttp = class extends WooksAdapterBase {
992
1456
  logger;
993
1457
  constructor(opts, wooks) {
@@ -995,27 +1459,35 @@ var WooksHttp = class extends WooksAdapterBase {
995
1459
  this.opts = opts;
996
1460
  this.logger = opts?.logger || this.getLogger(`[wooks-http]`);
997
1461
  }
1462
+ /** Registers a handler for all HTTP methods on the given path. */
998
1463
  all(path, handler) {
999
1464
  return this.on("*", path, handler);
1000
1465
  }
1466
+ /** Registers a GET route handler. */
1001
1467
  get(path, handler) {
1002
1468
  return this.on("GET", path, handler);
1003
1469
  }
1470
+ /** Registers a POST route handler. */
1004
1471
  post(path, handler) {
1005
1472
  return this.on("POST", path, handler);
1006
1473
  }
1474
+ /** Registers a PUT route handler. */
1007
1475
  put(path, handler) {
1008
1476
  return this.on("PUT", path, handler);
1009
1477
  }
1478
+ /** Registers a PATCH route handler. */
1010
1479
  patch(path, handler) {
1011
1480
  return this.on("PATCH", path, handler);
1012
1481
  }
1482
+ /** Registers a DELETE route handler. */
1013
1483
  delete(path, handler) {
1014
1484
  return this.on("DELETE", path, handler);
1015
1485
  }
1486
+ /** Registers a HEAD route handler. */
1016
1487
  head(path, handler) {
1017
1488
  return this.on("HEAD", path, handler);
1018
1489
  }
1490
+ /** Registers an OPTIONS route handler. */
1019
1491
  options(path, handler) {
1020
1492
  return this.on("OPTIONS", path, handler);
1021
1493
  }
@@ -1073,8 +1545,8 @@ var WooksHttp = class extends WooksAdapterBase {
1073
1545
  }
1074
1546
  responder = createWooksResponder();
1075
1547
  respond(data) {
1076
- this.responder.respond(data)?.catch((e) => {
1077
- this.logger.error("Uncaught response exception", e);
1548
+ this.responder.respond(data)?.catch((error) => {
1549
+ this.logger.error("Uncaught response exception", error);
1078
1550
  });
1079
1551
  }
1080
1552
  /**
@@ -1093,7 +1565,8 @@ var WooksHttp = class extends WooksAdapterBase {
1093
1565
  return (req, res) => {
1094
1566
  const runInContext = createHttpContext({
1095
1567
  req,
1096
- res
1568
+ res,
1569
+ requestLimits: this.opts?.requestLimits
1097
1570
  }, this.mergeEventOptions(this.opts?.eventOptions));
1098
1571
  runInContext(async () => {
1099
1572
  const { handlers } = this.wooks.lookup(req.method, req.url);
@@ -1133,10 +1606,13 @@ var WooksHttp = class extends WooksAdapterBase {
1133
1606
  }
1134
1607
  };
1135
1608
  /**
1136
- * Factory for WooksHttp App
1137
- * @param opts TWooksHttpOptions
1138
- * @param wooks Wooks | WooksAdapterBase
1139
- * @returns WooksHttp
1609
+ * Creates a new WooksHttp application instance.
1610
+ * @example
1611
+ * ```ts
1612
+ * const app = createHttpApp()
1613
+ * app.get('/hello', () => 'Hello World!')
1614
+ * app.listen(3000)
1615
+ * ```
1140
1616
  */
1141
1617
  function createHttpApp(opts, wooks) {
1142
1618
  return new WooksHttp(opts, wooks);