h3 2.0.0-beta.2 → 2.0.0-beta.4

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/h3.mjs CHANGED
@@ -49,7 +49,7 @@ var H3Event = class {
49
49
  */
50
50
  _res;
51
51
  constructor(req, context, app) {
52
- this.context = context || new EmptyObject();
52
+ this.context = context || req.context || new EmptyObject();
53
53
  this.req = req;
54
54
  this.app = app;
55
55
  const _url = req._url;
@@ -297,9 +297,9 @@ function isJSONSerializable(value, _type) {
297
297
  const kNotFound = /* @__PURE__ */ Symbol.for("h3.notFound");
298
298
  const kHandled = /* @__PURE__ */ Symbol.for("h3.handled");
299
299
  function toResponse(val, event, config = {}) {
300
- if (val && val instanceof Promise) return val.catch((error) => error).then((resolvedVal) => toResponse(resolvedVal, event, config));
300
+ if (typeof val?.then === "function") return (val.catch?.((error) => error) || Promise.resolve(val)).then((resolvedVal) => toResponse(resolvedVal, event, config));
301
301
  const response = prepareResponse(val, event, config);
302
- if (response instanceof Promise) return toResponse(response, event, config);
302
+ if (typeof response?.then === "function") return toResponse(response, event, config);
303
303
  const { onResponse: onResponse$1 } = config;
304
304
  return onResponse$1 ? Promise.resolve(onResponse$1(response, event)).then(() => response) : response;
305
305
  }
@@ -440,190 +440,77 @@ function callMiddleware(event, middleware, handler, index = 0) {
440
440
  return nextResult;
441
441
  };
442
442
  const ret = fn(event, next);
443
- return ret === void 0 || ret === kNotFound ? next() : ret instanceof Promise ? ret.then((resolved) => resolved === void 0 || resolved === kNotFound ? next() : resolved) : ret;
443
+ return ret === void 0 || ret === kNotFound ? next() : typeof ret?.then === "function" ? ret.then((resolved) => resolved === void 0 || resolved === kNotFound ? next() : resolved) : ret;
444
444
  }
445
445
 
446
446
  //#endregion
447
- //#region src/h3.ts
448
- const H3Core = /* @__PURE__ */ (() => {
449
- const HTTPMethods = [
450
- "GET",
451
- "POST",
452
- "PUT",
453
- "DELETE",
454
- "PATCH",
455
- "HEAD",
456
- "OPTIONS",
457
- "CONNECT",
458
- "TRACE"
459
- ];
460
- class H3Core$1 {
461
- _middleware;
462
- _routes = [];
463
- config;
464
- constructor(config = {}) {
465
- this._middleware = [];
466
- this.config = config;
467
- this.fetch = this.fetch.bind(this);
468
- this._fetch = this._fetch.bind(this);
469
- this.handler = this.handler.bind(this);
470
- config.plugins?.forEach((plugin) => plugin(this));
471
- }
472
- fetch(request, options) {
473
- try {
474
- return Promise.resolve(this._fetch(request, options));
475
- } catch (error) {
476
- return Promise.reject(error);
477
- }
478
- }
479
- _fetch(_req, _init, context) {
480
- const request = toRequest(_req, _init);
481
- const event = new H3Event(request, context, this);
482
- let handlerRes;
483
- try {
484
- if (this.config.onRequest) {
485
- const hookRes = this.config.onRequest(event);
486
- handlerRes = hookRes instanceof Promise ? hookRes.then(() => this.handler(event)) : this.handler(event);
487
- } else handlerRes = this.handler(event);
488
- } catch (error) {
489
- handlerRes = Promise.reject(error);
447
+ //#region src/utils/internal/query.ts
448
+ const plusRegex = /\+/g;
449
+ function parseQuery(input) {
450
+ const params = new EmptyObject();
451
+ if (!input || input === "?") return params;
452
+ const inputLength = input.length;
453
+ let key = "";
454
+ let value = "";
455
+ let startingIndex = -1;
456
+ let equalityIndex = -1;
457
+ let shouldDecodeKey = false;
458
+ let shouldDecodeValue = false;
459
+ let keyHasPlus = false;
460
+ let valueHasPlus = false;
461
+ let hasBothKeyValuePair = false;
462
+ let c = 0;
463
+ for (let i = 0; i < inputLength + 1; i++) {
464
+ c = i === inputLength ? 38 : input.charCodeAt(i);
465
+ switch (c) {
466
+ case 38: {
467
+ hasBothKeyValuePair = equalityIndex > startingIndex;
468
+ if (!hasBothKeyValuePair) equalityIndex = i;
469
+ key = input.slice(startingIndex + 1, equalityIndex);
470
+ if (hasBothKeyValuePair || key.length > 0) {
471
+ if (keyHasPlus) key = key.replace(plusRegex, " ");
472
+ if (shouldDecodeKey) try {
473
+ key = decodeURIComponent(key);
474
+ } catch {}
475
+ if (hasBothKeyValuePair) {
476
+ value = input.slice(equalityIndex + 1, i);
477
+ if (valueHasPlus) value = value.replace(plusRegex, " ");
478
+ if (shouldDecodeValue) try {
479
+ value = decodeURIComponent(value);
480
+ } catch {}
481
+ }
482
+ const currentValue = params[key];
483
+ if (currentValue === void 0) params[key] = value;
484
+ else if (Array.isArray(currentValue)) currentValue.push(value);
485
+ else params[key] = [currentValue, value];
486
+ }
487
+ value = "";
488
+ startingIndex = i;
489
+ equalityIndex = i;
490
+ shouldDecodeKey = false;
491
+ shouldDecodeValue = false;
492
+ keyHasPlus = false;
493
+ valueHasPlus = false;
494
+ break;
490
495
  }
491
- return toResponse(handlerRes, event, this.config);
492
- }
493
- /**
494
- * Immediately register an H3 plugin.
495
- */
496
- register(plugin) {
497
- plugin(this);
498
- return this;
499
- }
500
- _findRoute(_event) {}
501
- _addRoute(_route) {
502
- this._routes.push(_route);
503
- }
504
- handler(event) {
505
- const route = this._findRoute(event);
506
- if (route) {
507
- event.context.params = route.params;
508
- event.context.matchedRoute = route.data;
496
+ case 61: {
497
+ if (equalityIndex <= startingIndex) equalityIndex = i;
498
+ else shouldDecodeValue = true;
499
+ break;
509
500
  }
510
- const middleware = route?.data.middleware ? [...this._middleware, ...route.data.middleware] : this._middleware;
511
- return callMiddleware(event, middleware, () => {
512
- return route ? route.data.handler(event) : kNotFound;
513
- });
514
- }
515
- mount(base, input) {
516
- if ("handler" in input) {
517
- if (input._middleware.length > 0) this._middleware.push((event, next) => {
518
- return event.url.pathname.startsWith(base) ? callMiddleware(event, input._middleware, next) : next();
519
- });
520
- for (const r of input._routes) this._addRoute({
521
- ...r,
522
- route: base + r.route
523
- });
524
- } else {
525
- const fetchHandler = "fetch" in input ? input.fetch : input;
526
- this.all(`${base}/**`, (event) => {
527
- const url = new URL(event.url);
528
- url.pathname = url.pathname.slice(base.length) || "/";
529
- return fetchHandler(new Request(url, event.req));
530
- });
501
+ case 43: {
502
+ if (equalityIndex > startingIndex) valueHasPlus = true;
503
+ else keyHasPlus = true;
504
+ break;
531
505
  }
532
- return this;
533
- }
534
- all(route, handler, opts) {
535
- return this.on("", route, handler, opts);
536
- }
537
- on(method, route, handler, opts) {
538
- const _method = (method || "").toUpperCase();
539
- route = new URL(route, "http://_").pathname;
540
- this._addRoute({
541
- method: _method,
542
- route,
543
- handler,
544
- middleware: opts?.middleware,
545
- meta: {
546
- ...handler.meta,
547
- ...opts?.meta
548
- }
549
- });
550
- return this;
551
- }
552
- use(arg1, arg2, arg3) {
553
- let route;
554
- let fn;
555
- let opts;
556
- if (typeof arg1 === "string") {
557
- route = arg1;
558
- fn = arg2;
559
- opts = arg3;
560
- } else {
561
- fn = arg1;
562
- opts = arg2;
506
+ case 37: {
507
+ if (equalityIndex > startingIndex) shouldDecodeValue = true;
508
+ else shouldDecodeKey = true;
509
+ break;
563
510
  }
564
- this._middleware.push(normalizeMiddleware(fn, route ? {
565
- ...opts,
566
- route
567
- } : opts));
568
- return this;
569
511
  }
570
512
  }
571
- for (const method of HTTPMethods) H3Core$1.prototype[method.toLowerCase()] = function(route, handler, opts) {
572
- return this.on(method, route, handler, opts);
573
- };
574
- return H3Core$1;
575
- })();
576
- var H3 = class extends H3Core {
577
- /**
578
- * @internal
579
- */
580
- _rou3;
581
- constructor(config = {}) {
582
- super(config);
583
- this._rou3 = createRouter();
584
- }
585
- _findRoute(_event) {
586
- return findRoute(this._rou3, _event.req.method, _event.url.pathname);
587
- }
588
- _addRoute(_route) {
589
- addRoute(this._rou3, _route.method, _route.route, _route);
590
- super._addRoute(_route);
591
- }
592
- };
593
- function toRequest(_request, _init) {
594
- if (typeof _request === "string") {
595
- let url = _request;
596
- if (url[0] === "/") {
597
- const headers = _init?.headers ? new Headers(_init.headers) : void 0;
598
- const host = headers?.get("host") || "localhost";
599
- const proto = headers?.get("x-forwarded-proto") === "https" ? "https" : "http";
600
- url = `${proto}://${host}${url}`;
601
- }
602
- return new Request(url, _init);
603
- } else if (_init || _request instanceof URL) return new Request(_request, _init);
604
- return _request;
605
- }
606
-
607
- //#endregion
608
- //#region src/utils/event.ts
609
- /**
610
- * Checks if the input is an H3Event object.
611
- * @param input - The input to check.
612
- * @returns True if the input is an H3Event object, false otherwise.
613
- * @see H3Event
614
- */
615
- function isEvent(input) {
616
- return input instanceof H3Event || input?.constructor?.__is_event__;
617
- }
618
- function mockEvent(_request, options) {
619
- let request;
620
- if (typeof _request === "string") {
621
- let url = _request;
622
- if (url[0] === "/") url = `http://localhost${url}`;
623
- request = new Request(url, options);
624
- } else if (options || _request instanceof URL) request = new Request(_request, options);
625
- else request = _request;
626
- return new H3Event(request);
513
+ return params;
627
514
  }
628
515
 
629
516
  //#endregion
@@ -689,226 +576,65 @@ function createValidationError(validateError) {
689
576
  }
690
577
 
691
578
  //#endregion
692
- //#region src/handler.ts
693
- function defineHandler(arg1) {
694
- if (typeof arg1 === "function") return handlerWithFetch(arg1);
695
- const { middleware, handler, meta } = arg1;
696
- const _handler = handlerWithFetch(middleware?.length ? (event) => callMiddleware(event, middleware, handler) : handler);
697
- _handler.meta = meta;
698
- return _handler;
579
+ //#region src/utils/event.ts
580
+ /**
581
+ * Checks if the input is an H3Event object.
582
+ * @param input - The input to check.
583
+ * @returns True if the input is an H3Event object, false otherwise.
584
+ * @see H3Event
585
+ */
586
+ function isEvent(input) {
587
+ return input instanceof H3Event || input?.constructor?.__is_event__;
699
588
  }
700
589
  /**
701
- * @experimental defineValidatedHandler is an experimental feature and API may change.
590
+ * Checks if the input is an object with `{ req: Request }` signature.
591
+ * @param input - The input to check.
592
+ * @returns True if the input is is `{ req: Request }`
702
593
  */
703
- function defineValidatedHandler(def) {
704
- if (!def.validate) return defineHandler(def);
705
- return defineHandler({
706
- ...def,
707
- handler: (event) => {
708
- event.req = validatedRequest(event.req, def.validate);
709
- event.url = validatedURL(event.url, def.validate);
710
- return def.handler(event);
711
- }
712
- });
594
+ function isHTTPEvent(input) {
595
+ return input?.req instanceof Request;
713
596
  }
714
- function handlerWithFetch(handler) {
715
- return Object.assign(handler, { fetch: (_req, _init) => {
716
- const req = toRequest(_req, _init);
717
- const event = new H3Event(req);
718
- try {
719
- return Promise.resolve(toResponse(handler(event), event));
720
- } catch (error) {
721
- return Promise.resolve(toResponse(error, event));
722
- }
723
- } });
724
- }
725
- function dynamicEventHandler(initial) {
726
- let current = initial;
727
- return Object.assign(defineHandler((event) => current?.(event)), { set: (handler) => {
728
- current = handler;
729
- } });
730
- }
731
- function defineLazyEventHandler(load) {
732
- let _promise;
733
- let _resolved;
734
- const resolveHandler = () => {
735
- if (_resolved) return Promise.resolve(_resolved);
736
- if (!_promise) _promise = Promise.resolve(load()).then((r) => {
737
- const handler = r.default || r;
738
- if (typeof handler !== "function") throw new TypeError("Invalid lazy handler result. It should be a function:", handler);
739
- _resolved = { handler: r.default || r };
740
- return _resolved;
741
- });
742
- return _promise;
743
- };
744
- return defineHandler((event) => {
745
- if (_resolved) return _resolved.handler(event);
746
- return resolveHandler().then((r) => r.handler(event));
747
- });
748
- }
749
-
750
- //#endregion
751
- //#region src/adapters.ts
752
597
  /**
753
- * @deprecated Since h3 v2 you can directly use `app.fetch(request, init?, context?)`
598
+ * Gets the context of the event, if it does not exists, initializes a new context on `req.context`.
754
599
  */
755
- function toWebHandler(app) {
756
- return (request, context) => {
757
- return Promise.resolve(app._fetch(request, void 0, context));
758
- };
600
+ function getEventContext(event) {
601
+ if (event.context) return event.context;
602
+ event.req.context ??= {};
603
+ return event.req.context;
759
604
  }
760
- function fromWebHandler(handler) {
761
- return (event) => handler(event.req, event.context);
762
- }
763
- function fromNodeHandler(handler) {
764
- if (typeof handler !== "function") throw new TypeError(`Invalid handler. It should be a function: ${handler}`);
765
- return (event) => {
766
- if (!event.runtime?.node?.res) throw new Error("[h3] Executing Node.js middleware is not supported in this server!");
767
- return callNodeHandler(handler, event.runtime?.node.req, event.runtime?.node.res);
768
- };
769
- }
770
- function defineNodeHandler(handler) {
771
- return handler;
772
- }
773
- function defineNodeMiddleware(handler) {
774
- return handler;
775
- }
776
- /**
777
- * Convert H3 app instance to a NodeHandler with (IncomingMessage, ServerResponse) => void signature.
778
- */
779
- function toNodeHandler$1(app) {
780
- return toNodeHandler(app.fetch);
781
- }
782
- function callNodeHandler(handler, req, res) {
783
- const isMiddleware = handler.length > 2;
784
- return new Promise((resolve, reject) => {
785
- res.once("close", () => resolve(kHandled));
786
- res.once("finish", () => resolve(kHandled));
787
- res.once("pipe", (stream) => resolve(stream));
788
- res.once("error", (error) => reject(error));
789
- try {
790
- if (isMiddleware) Promise.resolve(handler(req, res, (error) => error ? reject(new HTTPError({
791
- cause: error,
792
- unhandled: true
793
- })) : resolve(void 0))).catch((error) => reject(new HTTPError({
794
- cause: error,
795
- unhandled: true
796
- })));
797
- else return Promise.resolve(handler(req, res)).then(() => resolve(kHandled)).catch((error) => reject(new HTTPError({
798
- cause: error,
799
- unhandled: true
800
- })));
801
- } catch (error) {
802
- reject(new HTTPError({
803
- cause: error,
804
- unhandled: true
805
- }));
806
- }
807
- });
605
+ function mockEvent(_request, options) {
606
+ let request;
607
+ if (typeof _request === "string") {
608
+ let url = _request;
609
+ if (url[0] === "/") url = `http://localhost${url}`;
610
+ request = new Request(url, options);
611
+ } else if (options || _request instanceof URL) request = new Request(_request, options);
612
+ else request = _request;
613
+ return new H3Event(request);
808
614
  }
809
615
 
810
616
  //#endregion
811
- //#region src/utils/route.ts
617
+ //#region src/utils/request.ts
812
618
  /**
813
- * Define a route as a plugin that can be registered with app.register()
619
+ * Convert input into a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
814
620
  *
815
- * @example
816
- * ```js
817
- * import { z } from "zod";
621
+ * If input is a relative URL, it will be normalized into a full path based on headers.
818
622
  *
819
- * const userRoute = defineRoute({
820
- * method: 'POST',
821
- * validate: {
822
- * query: z.object({ id: z.string().uuid() }),
823
- * body: z.object({ name: z.string() }),
824
- * },
825
- * handler: (event) => {
826
- * return { success: true };
827
- * }
828
- * });
829
- *
830
- * app.register(userRoute);
831
- * ```
623
+ * If input is already a Request and no options are provided, it will be returned as-is.
832
624
  */
833
- function defineRoute(def) {
834
- const handler = defineValidatedHandler(def);
835
- return (h3) => {
836
- h3.on(def.method, def.route, handler);
837
- };
838
- }
839
-
840
- //#endregion
841
- //#region src/utils/internal/query.ts
842
- const plusRegex = /\+/g;
843
- function parseQuery(input) {
844
- const params = new EmptyObject();
845
- if (!input || input === "?") return params;
846
- const inputLength = input.length;
847
- let key = "";
848
- let value = "";
849
- let startingIndex = -1;
850
- let equalityIndex = -1;
851
- let shouldDecodeKey = false;
852
- let shouldDecodeValue = false;
853
- let keyHasPlus = false;
854
- let valueHasPlus = false;
855
- let hasBothKeyValuePair = false;
856
- let c = 0;
857
- for (let i = 0; i < inputLength + 1; i++) {
858
- c = i === inputLength ? 38 : input.charCodeAt(i);
859
- switch (c) {
860
- case 38: {
861
- hasBothKeyValuePair = equalityIndex > startingIndex;
862
- if (!hasBothKeyValuePair) equalityIndex = i;
863
- key = input.slice(startingIndex + 1, equalityIndex);
864
- if (hasBothKeyValuePair || key.length > 0) {
865
- if (keyHasPlus) key = key.replace(plusRegex, " ");
866
- if (shouldDecodeKey) try {
867
- key = decodeURIComponent(key);
868
- } catch {}
869
- if (hasBothKeyValuePair) {
870
- value = input.slice(equalityIndex + 1, i);
871
- if (valueHasPlus) value = value.replace(plusRegex, " ");
872
- if (shouldDecodeValue) try {
873
- value = decodeURIComponent(value);
874
- } catch {}
875
- }
876
- const currentValue = params[key];
877
- if (currentValue === void 0) params[key] = value;
878
- else if (Array.isArray(currentValue)) currentValue.push(value);
879
- else params[key] = [currentValue, value];
880
- }
881
- value = "";
882
- startingIndex = i;
883
- equalityIndex = i;
884
- shouldDecodeKey = false;
885
- shouldDecodeValue = false;
886
- keyHasPlus = false;
887
- valueHasPlus = false;
888
- break;
889
- }
890
- case 61: {
891
- if (equalityIndex <= startingIndex) equalityIndex = i;
892
- else shouldDecodeValue = true;
893
- break;
894
- }
895
- case 43: {
896
- if (equalityIndex > startingIndex) valueHasPlus = true;
897
- else keyHasPlus = true;
898
- break;
899
- }
900
- case 37: {
901
- if (equalityIndex > startingIndex) shouldDecodeValue = true;
902
- else shouldDecodeKey = true;
903
- break;
904
- }
625
+ function toRequest(input, options) {
626
+ if (typeof input === "string") {
627
+ let url = input;
628
+ if (url[0] === "/") {
629
+ const headers = options?.headers ? new Headers(options.headers) : void 0;
630
+ const host = headers?.get("host") || "localhost";
631
+ const proto = headers?.get("x-forwarded-proto") === "https" ? "https" : "http";
632
+ url = `${proto}://${host}${url}`;
905
633
  }
906
- }
907
- return params;
634
+ return new Request(url, options);
635
+ } else if (options || input instanceof URL) return new Request(input, options);
636
+ return input;
908
637
  }
909
-
910
- //#endregion
911
- //#region src/utils/request.ts
912
638
  /**
913
639
  * Get parsed query string object from the request URL.
914
640
  *
@@ -918,7 +644,8 @@ function parseQuery(input) {
918
644
  * });
919
645
  */
920
646
  function getQuery(event) {
921
- return parseQuery(event.url.search.slice(1));
647
+ const url = event.url || new URL(event.req.url);
648
+ return parseQuery(url.search.slice(1));
922
649
  }
923
650
  /**
924
651
  * Get the query param from the request URL validated with validate function.
@@ -958,7 +685,8 @@ function getValidatedQuery(event, validate) {
958
685
  * });
959
686
  */
960
687
  function getRouterParams(event, opts = {}) {
961
- let params = event.context.params || {};
688
+ const context = getEventContext(event);
689
+ let params = context.params || {};
962
690
  if (opts.decode) {
963
691
  params = { ...params };
964
692
  for (const key in params) params[key] = decodeURIComponent(params[key]);
@@ -1009,126 +737,423 @@ function getRouterParam(event, name, opts = {}) {
1009
737
  return params[name];
1010
738
  }
1011
739
  /**
1012
- *
1013
- * Checks if the incoming request method is of the expected type.
1014
- *
1015
- * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
1016
- *
1017
- * @example
1018
- * app.get("/", (event) => {
1019
- * if (isMethod(event, "GET")) {
1020
- * // Handle GET request
1021
- * } else if (isMethod(event, ["POST", "PUT"])) {
1022
- * // Handle POST or PUT request
1023
- * }
1024
- * });
740
+ *
741
+ * Checks if the incoming request method is of the expected type.
742
+ *
743
+ * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
744
+ *
745
+ * @example
746
+ * app.get("/", (event) => {
747
+ * if (isMethod(event, "GET")) {
748
+ * // Handle GET request
749
+ * } else if (isMethod(event, ["POST", "PUT"])) {
750
+ * // Handle POST or PUT request
751
+ * }
752
+ * });
753
+ */
754
+ function isMethod(event, expected, allowHead) {
755
+ if (allowHead && event.req.method === "HEAD") return true;
756
+ if (typeof expected === "string") {
757
+ if (event.req.method === expected) return true;
758
+ } else if (expected.includes(event.req.method)) return true;
759
+ return false;
760
+ }
761
+ /**
762
+ * Asserts that the incoming request method is of the expected type using `isMethod`.
763
+ *
764
+ * If the method is not allowed, it will throw a 405 error with the message "HTTP method is not allowed".
765
+ *
766
+ * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
767
+ *
768
+ * @example
769
+ * app.get("/", (event) => {
770
+ * assertMethod(event, "GET");
771
+ * // Handle GET request, otherwise throw 405 error
772
+ * });
773
+ */
774
+ function assertMethod(event, expected, allowHead) {
775
+ if (!isMethod(event, expected, allowHead)) throw new HTTPError({ status: 405 });
776
+ }
777
+ /**
778
+ * Get the request hostname.
779
+ *
780
+ * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
781
+ *
782
+ * If no host header is found, it will default to "localhost".
783
+ *
784
+ * @example
785
+ * app.get("/", (event) => {
786
+ * const host = getRequestHost(event); // "example.com"
787
+ * });
788
+ */
789
+ function getRequestHost(event, opts = {}) {
790
+ if (opts.xForwardedHost) {
791
+ const _header = event.req.headers.get("x-forwarded-host");
792
+ const xForwardedHost = (_header || "").split(",").shift()?.trim();
793
+ if (xForwardedHost) return xForwardedHost;
794
+ }
795
+ return event.req.headers.get("host") || "";
796
+ }
797
+ /**
798
+ * Get the request protocol.
799
+ *
800
+ * If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
801
+ *
802
+ * If protocol cannot be determined, it will default to "http".
803
+ *
804
+ * @example
805
+ * app.get("/", (event) => {
806
+ * const protocol = getRequestProtocol(event); // "https"
807
+ * });
808
+ */
809
+ function getRequestProtocol(event, opts = {}) {
810
+ if (opts.xForwardedProto !== false) {
811
+ const forwardedProto = event.req.headers.get("x-forwarded-proto");
812
+ if (forwardedProto === "https") return "https";
813
+ if (forwardedProto === "http") return "http";
814
+ }
815
+ const url = event.url || new URL(event.req.url);
816
+ return url.protocol.slice(0, -1);
817
+ }
818
+ /**
819
+ * Generated the full incoming request URL.
820
+ *
821
+ * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
822
+ *
823
+ * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
824
+ *
825
+ * @example
826
+ * app.get("/", (event) => {
827
+ * const url = getRequestURL(event); // "https://example.com/path"
828
+ * });
829
+ */
830
+ function getRequestURL(event, opts = {}) {
831
+ const url = new URL(event.url || event.req.url);
832
+ url.protocol = getRequestProtocol(event, opts);
833
+ if (opts.xForwardedHost) {
834
+ const host = getRequestHost(event, opts);
835
+ if (host) {
836
+ url.host = host;
837
+ if (!host.includes(":")) url.port = "";
838
+ }
839
+ }
840
+ return url;
841
+ }
842
+ /**
843
+ * Try to get the client IP address from the incoming request.
844
+ *
845
+ * If `xForwardedFor` is `true`, it will use the `x-forwarded-for` header if it exists.
846
+ *
847
+ * If IP cannot be determined, it will default to `undefined`.
848
+ *
849
+ * @example
850
+ * app.get("/", (event) => {
851
+ * const ip = getRequestIP(event); // "192.0.2.0"
852
+ * });
853
+ */
854
+ function getRequestIP(event, opts = {}) {
855
+ if (opts.xForwardedFor) {
856
+ const _header = event.req.headers.get("x-forwarded-for");
857
+ const xForwardedFor = (_header || "")?.split(",").shift()?.trim();
858
+ if (xForwardedFor) return xForwardedFor;
859
+ }
860
+ return event.req.context?.clientAddress || event.req.ip || void 0;
861
+ }
862
+
863
+ //#endregion
864
+ //#region src/h3.ts
865
+ const H3Core = /* @__PURE__ */ (() => {
866
+ const HTTPMethods = [
867
+ "GET",
868
+ "POST",
869
+ "PUT",
870
+ "DELETE",
871
+ "PATCH",
872
+ "HEAD",
873
+ "OPTIONS",
874
+ "CONNECT",
875
+ "TRACE"
876
+ ];
877
+ class H3Core$1 {
878
+ _middleware;
879
+ _routes = [];
880
+ config;
881
+ constructor(config = {}) {
882
+ this._middleware = [];
883
+ this.config = config;
884
+ this.fetch = this.fetch.bind(this);
885
+ this.request = this.request.bind(this);
886
+ this.handler = this.handler.bind(this);
887
+ config.plugins?.forEach((plugin) => plugin(this));
888
+ }
889
+ fetch(request) {
890
+ return this._request(request);
891
+ }
892
+ request(_req, _init, context) {
893
+ return this._request(toRequest(_req, _init), context);
894
+ }
895
+ _request(request, context) {
896
+ const event = new H3Event(request, context, this);
897
+ let handlerRes;
898
+ try {
899
+ if (this.config.onRequest) {
900
+ const hookRes = this.config.onRequest(event);
901
+ handlerRes = typeof hookRes?.then === "function" ? hookRes.then(() => this.handler(event)) : this.handler(event);
902
+ } else handlerRes = this.handler(event);
903
+ } catch (error) {
904
+ handlerRes = Promise.reject(error);
905
+ }
906
+ return toResponse(handlerRes, event, this.config);
907
+ }
908
+ /**
909
+ * Immediately register an H3 plugin.
910
+ */
911
+ register(plugin) {
912
+ plugin(this);
913
+ return this;
914
+ }
915
+ _findRoute(_event) {}
916
+ _addRoute(_route) {
917
+ this._routes.push(_route);
918
+ }
919
+ handler(event) {
920
+ const route = this._findRoute(event);
921
+ if (route) {
922
+ event.context.params = route.params;
923
+ event.context.matchedRoute = route.data;
924
+ }
925
+ const middleware = route?.data.middleware ? [...this._middleware, ...route.data.middleware] : this._middleware;
926
+ return callMiddleware(event, middleware, () => {
927
+ return route ? route.data.handler(event) : kNotFound;
928
+ });
929
+ }
930
+ mount(base, input) {
931
+ if ("handler" in input) {
932
+ if (input._middleware.length > 0) this._middleware.push((event, next) => {
933
+ return event.url.pathname.startsWith(base) ? callMiddleware(event, input._middleware, next) : next();
934
+ });
935
+ for (const r of input._routes) this._addRoute({
936
+ ...r,
937
+ route: base + r.route
938
+ });
939
+ } else {
940
+ const fetchHandler = "fetch" in input ? input.fetch : input;
941
+ this.all(`${base}/**`, (event) => {
942
+ const url = new URL(event.url);
943
+ url.pathname = url.pathname.slice(base.length) || "/";
944
+ return fetchHandler(new Request(url, event.req));
945
+ });
946
+ }
947
+ return this;
948
+ }
949
+ all(route, handler, opts) {
950
+ return this.on("", route, handler, opts);
951
+ }
952
+ on(method, route, handler, opts) {
953
+ const _method = (method || "").toUpperCase();
954
+ route = new URL(route, "http://_").pathname;
955
+ this._addRoute({
956
+ method: _method,
957
+ route,
958
+ handler,
959
+ middleware: opts?.middleware,
960
+ meta: {
961
+ ...handler.meta,
962
+ ...opts?.meta
963
+ }
964
+ });
965
+ return this;
966
+ }
967
+ use(arg1, arg2, arg3) {
968
+ let route;
969
+ let fn;
970
+ let opts;
971
+ if (typeof arg1 === "string") {
972
+ route = arg1;
973
+ fn = arg2;
974
+ opts = arg3;
975
+ } else {
976
+ fn = arg1;
977
+ opts = arg2;
978
+ }
979
+ this._middleware.push(normalizeMiddleware(fn, route ? {
980
+ ...opts,
981
+ route
982
+ } : opts));
983
+ return this;
984
+ }
985
+ }
986
+ for (const method of HTTPMethods) H3Core$1.prototype[method.toLowerCase()] = function(route, handler, opts) {
987
+ return this.on(method, route, handler, opts);
988
+ };
989
+ return H3Core$1;
990
+ })();
991
+ var H3 = class extends H3Core {
992
+ /**
993
+ * @internal
994
+ */
995
+ _rou3;
996
+ constructor(config = {}) {
997
+ super(config);
998
+ this._rou3 = createRouter();
999
+ }
1000
+ _findRoute(_event) {
1001
+ return findRoute(this._rou3, _event.req.method, _event.url.pathname);
1002
+ }
1003
+ _addRoute(_route) {
1004
+ addRoute(this._rou3, _route.method, _route.route, _route);
1005
+ super._addRoute(_route);
1006
+ }
1007
+ };
1008
+
1009
+ //#endregion
1010
+ //#region src/handler.ts
1011
+ function defineHandler(arg1) {
1012
+ if (typeof arg1 === "function") return handlerWithFetch(arg1);
1013
+ const { middleware, handler, meta } = arg1;
1014
+ const _handler = handlerWithFetch(middleware?.length ? (event) => callMiddleware(event, middleware, handler) : handler);
1015
+ _handler.meta = meta;
1016
+ return _handler;
1017
+ }
1018
+ /**
1019
+ * @experimental defineValidatedHandler is an experimental feature and API may change.
1025
1020
  */
1026
- function isMethod(event, expected, allowHead) {
1027
- if (allowHead && event.req.method === "HEAD") return true;
1028
- if (typeof expected === "string") {
1029
- if (event.req.method === expected) return true;
1030
- } else if (expected.includes(event.req.method)) return true;
1031
- return false;
1021
+ function defineValidatedHandler(def) {
1022
+ if (!def.validate) return defineHandler(def);
1023
+ return defineHandler({
1024
+ ...def,
1025
+ handler: (event) => {
1026
+ event.req = validatedRequest(event.req, def.validate);
1027
+ event.url = validatedURL(event.url, def.validate);
1028
+ return def.handler(event);
1029
+ }
1030
+ });
1032
1031
  }
1033
- /**
1034
- * Asserts that the incoming request method is of the expected type using `isMethod`.
1035
- *
1036
- * If the method is not allowed, it will throw a 405 error with the message "HTTP method is not allowed".
1037
- *
1038
- * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
1039
- *
1040
- * @example
1041
- * app.get("/", (event) => {
1042
- * assertMethod(event, "GET");
1043
- * // Handle GET request, otherwise throw 405 error
1044
- * });
1045
- */
1046
- function assertMethod(event, expected, allowHead) {
1047
- if (!isMethod(event, expected, allowHead)) throw new HTTPError({ status: 405 });
1032
+ function handlerWithFetch(handler) {
1033
+ return Object.assign(handler, { fetch: (req) => {
1034
+ if (typeof req === "string") req = new URL(req, "http://_");
1035
+ if (req instanceof URL) req = new Request(req);
1036
+ const event = new H3Event(req);
1037
+ try {
1038
+ return Promise.resolve(toResponse(handler(event), event));
1039
+ } catch (error) {
1040
+ return Promise.resolve(toResponse(error, event));
1041
+ }
1042
+ } });
1048
1043
  }
1049
- /**
1050
- * Get the request hostname.
1051
- *
1052
- * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
1053
- *
1054
- * If no host header is found, it will default to "localhost".
1055
- *
1056
- * @example
1057
- * app.get("/", (event) => {
1058
- * const host = getRequestHost(event); // "example.com"
1059
- * });
1060
- */
1061
- function getRequestHost(event, opts = {}) {
1062
- if (opts.xForwardedHost) {
1063
- const _header = event.req.headers.get("x-forwarded-host");
1064
- const xForwardedHost = (_header || "").split(",").shift()?.trim();
1065
- if (xForwardedHost) return xForwardedHost;
1066
- }
1067
- return event.req.headers.get("host") || "";
1044
+ function dynamicEventHandler(initial) {
1045
+ let current = initial;
1046
+ return Object.assign(defineHandler((event) => current?.(event)), { set: (handler) => {
1047
+ current = handler;
1048
+ } });
1049
+ }
1050
+ function defineLazyEventHandler(load) {
1051
+ let _promise;
1052
+ let _resolved;
1053
+ const resolveHandler = () => {
1054
+ if (_resolved) return Promise.resolve(_resolved);
1055
+ if (!_promise) _promise = Promise.resolve(load()).then((r) => {
1056
+ const handler = r.default || r;
1057
+ if (typeof handler !== "function") throw new TypeError("Invalid lazy handler result. It should be a function:", handler);
1058
+ _resolved = { handler: r.default || r };
1059
+ return _resolved;
1060
+ });
1061
+ return _promise;
1062
+ };
1063
+ return defineHandler((event) => {
1064
+ if (_resolved) return _resolved.handler(event);
1065
+ return resolveHandler().then((r) => r.handler(event));
1066
+ });
1068
1067
  }
1068
+
1069
+ //#endregion
1070
+ //#region src/adapters.ts
1069
1071
  /**
1070
- * Get the request protocol.
1071
- *
1072
- * If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
1073
- *
1074
- * If protocol cannot be determined, it will default to "http".
1075
- *
1076
- * @example
1077
- * app.get("/", (event) => {
1078
- * const protocol = getRequestProtocol(event); // "https"
1079
- * });
1072
+ * @deprecated Since h3 v2 you can directly use `app.fetch(request, init?, context?)`
1080
1073
  */
1081
- function getRequestProtocol(event, opts = {}) {
1082
- if (opts.xForwardedProto !== false) {
1083
- const forwardedProto = event.req.headers.get("x-forwarded-proto");
1084
- if (forwardedProto === "https") return "https";
1085
- if (forwardedProto === "http") return "http";
1086
- }
1087
- return event.url.protocol.slice(0, -1);
1074
+ function toWebHandler(app) {
1075
+ return (request, context) => {
1076
+ return Promise.resolve(app.request(request, void 0, context || request.context));
1077
+ };
1078
+ }
1079
+ function fromWebHandler(handler) {
1080
+ return (event) => handler(event.req, event.context);
1081
+ }
1082
+ function fromNodeHandler(handler) {
1083
+ if (typeof handler !== "function") throw new TypeError(`Invalid handler. It should be a function: ${handler}`);
1084
+ return (event) => {
1085
+ if (!event.runtime?.node?.res) throw new Error("[h3] Executing Node.js middleware is not supported in this server!");
1086
+ return callNodeHandler(handler, event.runtime?.node.req, event.runtime?.node.res);
1087
+ };
1088
+ }
1089
+ function defineNodeHandler(handler) {
1090
+ return handler;
1091
+ }
1092
+ function defineNodeMiddleware(handler) {
1093
+ return handler;
1088
1094
  }
1089
1095
  /**
1090
- * Generated the full incoming request URL.
1091
- *
1092
- * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
1093
- *
1094
- * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
1095
- *
1096
- * @example
1097
- * app.get("/", (event) => {
1098
- * const url = getRequestURL(event); // "https://example.com/path"
1099
- * });
1096
+ * Convert H3 app instance to a NodeHandler with (IncomingMessage, ServerResponse) => void signature.
1100
1097
  */
1101
- function getRequestURL(event, opts = {}) {
1102
- const url = new URL(event.url);
1103
- url.protocol = getRequestProtocol(event, opts);
1104
- if (opts.xForwardedHost) {
1105
- const host = getRequestHost(event, opts);
1106
- if (host) {
1107
- url.host = host;
1108
- if (!host.includes(":")) url.port = "";
1098
+ function toNodeHandler$1(app) {
1099
+ return toNodeHandler(app.fetch);
1100
+ }
1101
+ function callNodeHandler(handler, req, res) {
1102
+ const isMiddleware = handler.length > 2;
1103
+ return new Promise((resolve, reject) => {
1104
+ res.once("close", () => resolve(kHandled));
1105
+ res.once("finish", () => resolve(kHandled));
1106
+ res.once("pipe", (stream) => resolve(stream));
1107
+ res.once("error", (error) => reject(error));
1108
+ try {
1109
+ if (isMiddleware) Promise.resolve(handler(req, res, (error) => error ? reject(new HTTPError({
1110
+ cause: error,
1111
+ unhandled: true
1112
+ })) : resolve(void 0))).catch((error) => reject(new HTTPError({
1113
+ cause: error,
1114
+ unhandled: true
1115
+ })));
1116
+ else return Promise.resolve(handler(req, res)).then(() => resolve(kHandled)).catch((error) => reject(new HTTPError({
1117
+ cause: error,
1118
+ unhandled: true
1119
+ })));
1120
+ } catch (error) {
1121
+ reject(new HTTPError({
1122
+ cause: error,
1123
+ unhandled: true
1124
+ }));
1109
1125
  }
1110
- }
1111
- return url;
1126
+ });
1112
1127
  }
1128
+
1129
+ //#endregion
1130
+ //#region src/utils/route.ts
1113
1131
  /**
1114
- * Try to get the client IP address from the incoming request.
1115
- *
1116
- * If `xForwardedFor` is `true`, it will use the `x-forwarded-for` header if it exists.
1117
- *
1118
- * If IP cannot be determined, it will default to `undefined`.
1132
+ * Define a route as a plugin that can be registered with app.register()
1119
1133
  *
1120
1134
  * @example
1121
- * app.get("/", (event) => {
1122
- * const ip = getRequestIP(event); // "192.0.2.0"
1135
+ * ```js
1136
+ * import { z } from "zod";
1137
+ *
1138
+ * const userRoute = defineRoute({
1139
+ * method: 'POST',
1140
+ * validate: {
1141
+ * query: z.object({ id: z.string().uuid() }),
1142
+ * body: z.object({ name: z.string() }),
1143
+ * },
1144
+ * handler: (event) => {
1145
+ * return { success: true };
1146
+ * }
1123
1147
  * });
1148
+ *
1149
+ * app.register(userRoute);
1150
+ * ```
1124
1151
  */
1125
- function getRequestIP(event, opts = {}) {
1126
- if (opts.xForwardedFor) {
1127
- const _header = event.req.headers.get("x-forwarded-for");
1128
- const xForwardedFor = (_header || "")?.split(",").shift()?.trim();
1129
- if (xForwardedFor) return xForwardedFor;
1130
- }
1131
- return event.context.clientAddress || event.req.ip || void 0;
1152
+ function defineRoute(def) {
1153
+ const handler = defineValidatedHandler(def);
1154
+ return (h3) => {
1155
+ h3.on(def.method, def.route, handler);
1156
+ };
1132
1157
  }
1133
1158
 
1134
1159
  //#endregion
@@ -1282,7 +1307,7 @@ function noContent(event, code) {
1282
1307
  if (!code && currentStatus && currentStatus !== 200) code = event.res.status;
1283
1308
  event.res.status = sanitizeStatusCode(code, 204);
1284
1309
  if (event.res.status === 204) event.res.headers.delete("content-length");
1285
- return "";
1310
+ return new FastResponse(null, event.res);
1286
1311
  }
1287
1312
  /**
1288
1313
  * Send a redirect response to the client.
@@ -1494,7 +1519,7 @@ async function proxy(event, target, opts = {}) {
1494
1519
  };
1495
1520
  let response;
1496
1521
  try {
1497
- response = target[0] === "/" ? await event.app._fetch(createSubRequest(event, target, fetchOptions)) : await fetch(target, fetchOptions);
1522
+ response = target[0] === "/" ? await event.app.fetch(createSubRequest(event, target, fetchOptions)) : await fetch(target, fetchOptions);
1498
1523
  } catch (error) {
1499
1524
  throw new HTTPError({
1500
1525
  status: 502,
@@ -1528,7 +1553,17 @@ async function proxy(event, target, opts = {}) {
1528
1553
  */
1529
1554
  function getProxyRequestHeaders(event, opts) {
1530
1555
  const headers = new EmptyObject();
1531
- for (const [name, value] of event.req.headers.entries()) if (opts?.forwardHeaders?.includes(name) || opts?.filterHeaders && !opts.filterHeaders.includes(name) || !ignoredHeaders.has(name) || name === "host" && opts?.host) headers[name] = value;
1556
+ for (const [name, value] of event.req.headers.entries()) {
1557
+ if (opts?.filterHeaders?.includes(name)) continue;
1558
+ if (opts?.forwardHeaders?.includes(name)) {
1559
+ headers[name] = value;
1560
+ continue;
1561
+ }
1562
+ if (!ignoredHeaders.has(name) || name === "host" && opts?.host) {
1563
+ headers[name] = value;
1564
+ continue;
1565
+ }
1566
+ }
1532
1567
  return headers;
1533
1568
  }
1534
1569
  /**
@@ -1536,7 +1571,7 @@ function getProxyRequestHeaders(event, opts) {
1536
1571
  */
1537
1572
  async function fetchWithEvent(event, url, init) {
1538
1573
  if (url[0] !== "/") return fetch(url, init);
1539
- return event.app._fetch(createSubRequest(event, url, {
1574
+ return event.app.fetch(createSubRequest(event, url, {
1540
1575
  ...init,
1541
1576
  headers: mergeHeaders(getProxyRequestHeaders(event, { host: true }), init?.headers)
1542
1577
  }));
@@ -1614,7 +1649,7 @@ async function readBody(event) {
1614
1649
  * const body = await readValidatedBody(event, objectSchema);
1615
1650
  * });
1616
1651
  *
1617
- * @param event The H3Event passed by the handler.
1652
+ * @param event The HTTPEvent passed by the handler.
1618
1653
  * @param validate The function to use for body validation. It will be called passing the read request body. If the result is not false, the parsed body will be returned.
1619
1654
  * @throws If the validation function returns `false` or throws, a validation error will be thrown.
1620
1655
  * @return {*} The `Object`, `Array`, `String`, `Number`, `Boolean`, or `null` value corresponding to the request JSON body.
@@ -1627,9 +1662,11 @@ async function readValidatedBody(event, validate) {
1627
1662
 
1628
1663
  //#endregion
1629
1664
  //#region src/utils/cookie.ts
1665
+ const CHUNKED_COOKIE = "__chunked__";
1666
+ const CHUNKS_MAX_LENGTH = 4e3;
1630
1667
  /**
1631
1668
  * Parse the request to get HTTP Cookie header string and returning an object of all cookie name-value pairs.
1632
- * @param event {H3Event} H3 event or req passed by h3 handler
1669
+ * @param event {HTTPEvent} H3 event or req passed by h3 handler
1633
1670
  * @returns Object of cookie name-value pairs
1634
1671
  * ```ts
1635
1672
  * const cookies = parseCookies(event)
@@ -1640,7 +1677,7 @@ function parseCookies(event) {
1640
1677
  }
1641
1678
  /**
1642
1679
  * Get a cookie value by name.
1643
- * @param event {H3Event} H3 event or req passed by h3 handler
1680
+ * @param event {HTTPEvent} H3 event or req passed by h3 handler
1644
1681
  * @param name Name of the cookie to get
1645
1682
  * @returns {*} Value of the cookie (String or undefined)
1646
1683
  * ```ts
@@ -1695,6 +1732,59 @@ function deleteCookie(event, name, serializeOptions) {
1695
1732
  });
1696
1733
  }
1697
1734
  /**
1735
+ * Get a chunked cookie value by name. Will join chunks together.
1736
+ * @param event {HTTPEvent} { req: Request }
1737
+ * @param name Name of the cookie to get
1738
+ * @returns {*} Value of the cookie (String or undefined)
1739
+ * ```ts
1740
+ * const authorization = getCookie(request, 'Session')
1741
+ * ```
1742
+ */
1743
+ function getChunkedCookie(event, name) {
1744
+ const mainCookie = getCookie(event, name);
1745
+ if (!mainCookie || !mainCookie.startsWith(CHUNKED_COOKIE)) return mainCookie;
1746
+ const chunksCount = getChunkedCookieCount(mainCookie);
1747
+ if (chunksCount === 0) return void 0;
1748
+ const chunks = [];
1749
+ for (let i = 1; i <= chunksCount; i++) {
1750
+ const chunk = getCookie(event, chunkCookieName(name, i));
1751
+ if (!chunk) return void 0;
1752
+ chunks.push(chunk);
1753
+ }
1754
+ return chunks.join("");
1755
+ }
1756
+ /**
1757
+ * Set a cookie value by name. Chunked cookies will be created as needed.
1758
+ * @param event {H3Event} H3 event or res passed by h3 handler
1759
+ * @param name Name of the cookie to set
1760
+ * @param value Value of the cookie to set
1761
+ * @param options {CookieSerializeOptions} Options for serializing the cookie
1762
+ * ```ts
1763
+ * setCookie(res, 'Session', '<session data>')
1764
+ * ```
1765
+ */
1766
+ function setChunkedCookie(event, name, value, options) {
1767
+ const chunkMaxLength = options?.chunkMaxLength || CHUNKS_MAX_LENGTH;
1768
+ const chunkCount = Math.ceil(value.length / chunkMaxLength);
1769
+ const previousCookie = getCookie(event, name);
1770
+ if (previousCookie?.startsWith(CHUNKED_COOKIE)) {
1771
+ const previousChunkCount = getChunkedCookieCount(previousCookie);
1772
+ if (previousChunkCount > chunkCount) for (let i = chunkCount; i <= previousChunkCount; i++) deleteCookie(event, chunkCookieName(name, i), options);
1773
+ }
1774
+ if (chunkCount <= 1) {
1775
+ setCookie(event, name, value, options);
1776
+ return;
1777
+ }
1778
+ const mainCookieValue = `${CHUNKED_COOKIE}${chunkCount}`;
1779
+ setCookie(event, name, mainCookieValue, options);
1780
+ for (let i = 1; i <= chunkCount; i++) {
1781
+ const start = (i - 1) * chunkMaxLength;
1782
+ const end = start + chunkMaxLength;
1783
+ const chunkValue = value.slice(start, end);
1784
+ setCookie(event, chunkCookieName(name, i), chunkValue, options);
1785
+ }
1786
+ }
1787
+ /**
1698
1788
  * Cookies are unique by "cookie-name, domain-value, and path-value".
1699
1789
  *
1700
1790
  * @see https://httpwg.org/specs/rfc6265.html#rfc.section.4.1.2
@@ -1706,6 +1796,13 @@ function _getDistinctCookieKey(name, options) {
1706
1796
  options.path || "/"
1707
1797
  ].join(";");
1708
1798
  }
1799
+ function getChunkedCookieCount(cookie) {
1800
+ if (!cookie?.startsWith(CHUNKED_COOKIE)) return Number.NaN;
1801
+ return Number.parseInt(cookie.slice(CHUNKED_COOKIE.length));
1802
+ }
1803
+ function chunkCookieName(name, chunkNumber) {
1804
+ return `${name}.${chunkNumber}`;
1805
+ }
1709
1806
 
1710
1807
  //#endregion
1711
1808
  //#region src/utils/internal/event-stream.ts
@@ -1748,6 +1845,18 @@ var EventStream = class {
1748
1845
  }
1749
1846
  await this._sendEvent(message);
1750
1847
  }
1848
+ async pushComment(comment) {
1849
+ if (this._writerIsClosed) return;
1850
+ if (this._paused && !this._unsentData) {
1851
+ this._unsentData = formatEventStreamComment(comment);
1852
+ return;
1853
+ }
1854
+ if (this._paused) {
1855
+ this._unsentData += formatEventStreamComment(comment);
1856
+ return;
1857
+ }
1858
+ await this._writer.write(this._encoder.encode(formatEventStreamComment(comment))).catch();
1859
+ }
1751
1860
  async _sendEvent(message) {
1752
1861
  if (this._writerIsClosed) return;
1753
1862
  if (this._paused && !this._unsentData) {
@@ -1814,6 +1923,9 @@ var EventStream = class {
1814
1923
  return this._transformStream.readable;
1815
1924
  }
1816
1925
  };
1926
+ function formatEventStreamComment(comment) {
1927
+ return `: ${comment}\n\n`;
1928
+ }
1817
1929
  function formatEventStreamMessage(message) {
1818
1930
  let result = "";
1819
1931
  if (message.id) result += `id: ${message.id}\n`;
@@ -2294,10 +2406,12 @@ async function useSession(event, config) {
2294
2406
  await getSession(event, config);
2295
2407
  const sessionManager = {
2296
2408
  get id() {
2297
- return event.context.sessions?.[sessionName]?.id;
2409
+ const context = getEventContext(event);
2410
+ return context?.sessions?.[sessionName]?.id;
2298
2411
  },
2299
2412
  get data() {
2300
- return event.context.sessions?.[sessionName]?.data || {};
2413
+ const context = getEventContext(event);
2414
+ return context.sessions?.[sessionName]?.data || {};
2301
2415
  },
2302
2416
  update: async (update) => {
2303
2417
  await updateSession(event, config, update);
@@ -2315,29 +2429,30 @@ async function useSession(event, config) {
2315
2429
  */
2316
2430
  async function getSession(event, config) {
2317
2431
  const sessionName = config.name || DEFAULT_SESSION_NAME;
2318
- if (!event.context.sessions) event.context.sessions = new EmptyObject();
2319
- const existingSession = event.context.sessions[sessionName];
2432
+ const context = getEventContext(event);
2433
+ if (!context.sessions) context.sessions = new EmptyObject();
2434
+ const existingSession = context.sessions[sessionName];
2320
2435
  if (existingSession) return existingSession[kGetSession] || existingSession;
2321
2436
  const session = {
2322
2437
  id: "",
2323
2438
  createdAt: 0,
2324
2439
  data: new EmptyObject()
2325
2440
  };
2326
- event.context.sessions[sessionName] = session;
2441
+ context.sessions[sessionName] = session;
2327
2442
  let sealedSession;
2328
2443
  if (config.sessionHeader !== false) {
2329
2444
  const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
2330
2445
  const headerValue = event.req.headers.get(headerName);
2331
2446
  if (typeof headerValue === "string") sealedSession = headerValue;
2332
2447
  }
2333
- if (!sealedSession) sealedSession = getCookie(event, sessionName);
2448
+ if (!sealedSession) sealedSession = getChunkedCookie(event, sessionName);
2334
2449
  if (sealedSession) {
2335
2450
  const promise = unsealSession(event, config, sealedSession).catch(() => {}).then((unsealed) => {
2336
2451
  Object.assign(session, unsealed);
2337
- delete event.context.sessions[sessionName][kGetSession];
2452
+ delete context.sessions[sessionName][kGetSession];
2338
2453
  return session;
2339
2454
  });
2340
- event.context.sessions[sessionName][kGetSession] = promise;
2455
+ context.sessions[sessionName][kGetSession] = promise;
2341
2456
  await promise;
2342
2457
  }
2343
2458
  if (!session.id) {
@@ -2352,12 +2467,13 @@ async function getSession(event, config) {
2352
2467
  */
2353
2468
  async function updateSession(event, config, update) {
2354
2469
  const sessionName = config.name || DEFAULT_SESSION_NAME;
2355
- const session = event.context.sessions?.[sessionName] || await getSession(event, config);
2470
+ const context = getEventContext(event);
2471
+ const session = context.sessions?.[sessionName] || await getSession(event, config);
2356
2472
  if (typeof update === "function") update = update(session.data);
2357
2473
  if (update) Object.assign(session.data, update);
2358
- if (config.cookie !== false) {
2474
+ if (config.cookie !== false && event.res) {
2359
2475
  const sealed = await sealSession(event, config);
2360
- setCookie(event, sessionName, sealed, {
2476
+ setChunkedCookie(event, sessionName, sealed, {
2361
2477
  ...DEFAULT_SESSION_COOKIE,
2362
2478
  expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
2363
2479
  ...config.cookie
@@ -2370,7 +2486,8 @@ async function updateSession(event, config, update) {
2370
2486
  */
2371
2487
  async function sealSession(event, config) {
2372
2488
  const sessionName = config.name || DEFAULT_SESSION_NAME;
2373
- const session = event.context.sessions?.[sessionName] || await getSession(event, config);
2489
+ const context = getEventContext(event);
2490
+ const session = context.sessions?.[sessionName] || await getSession(event, config);
2374
2491
  const sealed = await seal(session, config.password, {
2375
2492
  ...defaults,
2376
2493
  ttl: config.maxAge ? config.maxAge * 1e3 : 0,
@@ -2397,9 +2514,10 @@ async function unsealSession(_event, config, sealed) {
2397
2514
  * Clear the session data for the current request.
2398
2515
  */
2399
2516
  function clearSession(event, config) {
2517
+ const context = getEventContext(event);
2400
2518
  const sessionName = config.name || DEFAULT_SESSION_NAME;
2401
- if (event.context.sessions?.[sessionName]) delete event.context.sessions[sessionName];
2402
- setCookie(event, sessionName, "", {
2519
+ if (context.sessions?.[sessionName]) delete context.sessions[sessionName];
2520
+ if (event.res && config.cookie !== false) setChunkedCookie(event, sessionName, "", {
2403
2521
  ...DEFAULT_SESSION_COOKIE,
2404
2522
  ...config.cookie
2405
2523
  });
@@ -2604,7 +2722,8 @@ async function requireBasicAuth(event, opts) {
2604
2722
  if (opts.username && username !== opts.username) throw autheFailed(event, opts?.realm);
2605
2723
  if (opts.password && password !== opts.password) throw autheFailed(event, opts?.realm);
2606
2724
  if (opts.validate && !await opts.validate(username, password)) throw autheFailed(event, opts?.realm);
2607
- event.context.basicAuth = {
2725
+ const context = getEventContext(event);
2726
+ context.basicAuth = {
2608
2727
  username,
2609
2728
  password,
2610
2729
  realm: opts.realm
@@ -2646,7 +2765,7 @@ async function getRequestFingerprint(event, opts = {}) {
2646
2765
  const fingerprint = [];
2647
2766
  if (opts.ip !== false) fingerprint.push(getRequestIP(event, { xForwardedFor: opts.xForwardedFor }));
2648
2767
  if (opts.method === true) fingerprint.push(event.req.method);
2649
- if (opts.url === true) fingerprint.push(event.url.href);
2768
+ if (opts.url === true) fingerprint.push(event.req.url);
2650
2769
  if (opts.userAgent === true) fingerprint.push(event.req.headers.get("user-agent"));
2651
2770
  const fingerprintString = fingerprint.filter(Boolean).join("|");
2652
2771
  if (!fingerprintString) return null;
@@ -2839,4 +2958,4 @@ const createRouter$1 = (config) => new H3(config);
2839
2958
  const useBase = withBase;
2840
2959
 
2841
2960
  //#endregion
2842
- export { H3, H3Core, H3Error, H3Event, HTTPError, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, basicAuth, clearResponseHeaders, clearSession, createApp, createError, createEventStream, createRouter$1 as createRouter, defaultContentType, defineEventHandler, defineHandler, defineLazyEventHandler, defineMiddleware, defineNodeHandler, defineNodeListener, defineNodeMiddleware, definePlugin, defineRoute, defineValidatedHandler, defineWebSocket, defineWebSocketHandler, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, freezeApp, fromNodeHandler, fromNodeMiddleware, fromWebHandler, getBodyStream, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, getValidatedRouterParams, handleCacheHeaders, handleCors, html, isCorsOriginAllowed, isError, isEvent, isMethod, isPreflightRequest, iterable, lazyEventHandler, mockEvent, noContent, onError, onRequest, onResponse, parseCookies, proxy, proxyRequest, readBody, readFormData, readFormDataBody, readMultipartFormData, readRawBody, readValidatedBody, redirect, removeResponseHeader, requireBasicAuth, sanitizeStatusCode, sanitizeStatusMessage, sealSession, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeHandler$1 as toNodeHandler, toNodeListener, toResponse, toWebHandler, unsealSession, updateSession, useBase, useSession, withBase, writeEarlyHints };
2961
+ export { H3, H3Core, H3Error, H3Event, HTTPError, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, basicAuth, clearResponseHeaders, clearSession, createApp, createError, createEventStream, createRouter$1 as createRouter, defaultContentType, defineEventHandler, defineHandler, defineLazyEventHandler, defineMiddleware, defineNodeHandler, defineNodeListener, defineNodeMiddleware, definePlugin, defineRoute, defineValidatedHandler, defineWebSocket, defineWebSocketHandler, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, freezeApp, fromNodeHandler, fromNodeMiddleware, fromWebHandler, getBodyStream, getCookie, getEventContext, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, getValidatedRouterParams, handleCacheHeaders, handleCors, html, isCorsOriginAllowed, isError, isEvent, isHTTPEvent, isMethod, isPreflightRequest, iterable, lazyEventHandler, mockEvent, noContent, onError, onRequest, onResponse, parseCookies, proxy, proxyRequest, readBody, readFormData, readFormDataBody, readMultipartFormData, readRawBody, readValidatedBody, redirect, removeResponseHeader, requireBasicAuth, sanitizeStatusCode, sanitizeStatusMessage, sealSession, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeHandler$1 as toNodeHandler, toNodeListener, toRequest, toResponse, toWebHandler, unsealSession, updateSession, useBase, useSession, withBase, writeEarlyHints };