h3 2.0.1-rc.21 → 2.0.1-rc.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- import "./h3-DagAgogP.mjs";
2
1
  import { NullProtoObj as EmptyObject, addRoute, createRouter, findRoute, removeRoute, routeToRegExp } from "rou3";
3
2
  import { FastResponse, FastURL } from "srvx";
4
3
  function freezeApp(app) {
@@ -7,10 +6,6 @@ function freezeApp(app) {
7
6
  throw new Error("Cannot add routes after the server init.");
8
7
  };
9
8
  }
10
- function withLeadingSlash(path) {
11
- if (!path || path === "/") return "/";
12
- return path[0] === "/" ? path : `/${path}`;
13
- }
14
9
  function withoutTrailingSlash(path) {
15
10
  if (!path || path === "/") return "/";
16
11
  return path[path.length - 1] === "/" ? path.slice(0, -1) : path;
@@ -24,22 +19,11 @@ function withoutBase(input = "", base = "") {
24
19
  function decodePathname(pathname) {
25
20
  return decodeURI(pathname.includes("%25") ? pathname.replace(/%25/g, "%2525") : pathname);
26
21
  }
27
- function resolveDotSegments(path) {
28
- if (!path.includes(".") && !path.includes("%2")) return path;
29
- const segments = path.replaceAll("\\", "/").split("/");
30
- const resolved = [];
31
- for (const segment of segments) {
32
- const normalized = segment.replace(/%2e/gi, ".");
33
- if (normalized === "..") {
34
- if (resolved.length > 1) resolved.pop();
35
- } else if (normalized !== ".") resolved.push(segment);
36
- }
37
- return resolved.join("/") || "/";
38
- }
39
22
  const kEventNS = "h3.internal.event.";
40
23
  const kEventRes = /* @__PURE__ */ Symbol.for(`${kEventNS}res`);
41
24
  const kEventResHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.headers`);
42
25
  const kEventResErrHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.err.headers`);
26
+ const kMalformedURL = /* @__PURE__ */ Symbol.for(`${kEventNS}malformed`);
43
27
  var H3Event = class {
44
28
  app;
45
29
  req;
@@ -51,8 +35,13 @@ var H3Event = class {
51
35
  this.req = req;
52
36
  this.app = app;
53
37
  const _url = req._url;
54
- const url = _url && _url instanceof URL ? _url : new FastURL(req.url);
55
- if (url.pathname.includes("%")) url.pathname = decodePathname(url.pathname);
38
+ let url = _url && _url instanceof URL ? _url : new FastURL(req.url);
39
+ if (url.pathname.includes("%")) try {
40
+ const pathname = decodePathname(url.pathname);
41
+ if (pathname !== url.pathname) url = new FastURL(`${url.protocol}//${url.host}${pathname}${url.search}`);
42
+ } catch {
43
+ this[kMalformedURL] = true;
44
+ }
56
45
  this.url = url;
57
46
  }
58
47
  get res() {
@@ -100,7 +89,7 @@ function sanitizeStatusMessage(statusMessage = "") {
100
89
  function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
101
90
  if (!statusCode) return defaultStatusCode;
102
91
  if (typeof statusCode === "string") statusCode = +statusCode;
103
- if (statusCode < 100 || statusCode > 599) return defaultStatusCode;
92
+ if (Number.isNaN(statusCode) || statusCode < 100 || statusCode > 599) return defaultStatusCode;
104
93
  return statusCode;
105
94
  }
106
95
  var HTTPError = class HTTPError extends Error {
@@ -472,7 +461,7 @@ async function validateData(data, fn, options) {
472
461
  throw createValidationError(error);
473
462
  }
474
463
  }
475
- const reqBodyKeys = new Set([
464
+ const reqBodyKeys = /* @__PURE__ */ new Set([
476
465
  "body",
477
466
  "text",
478
467
  "formData",
@@ -485,21 +474,19 @@ function validatedRequest(req, validate) {
485
474
  }
486
475
  if (!validate.body) return req;
487
476
  return new Proxy(req, { get(_target, prop) {
488
- if (validate.body) {
489
- if (prop === "json") return function _validatedJson() {
490
- return req.json().then((data) => validate.body["~standard"].validate(data)).then((result) => {
491
- if (result.issues) throw createValidationError(validate.onError?.({
492
- _source: "body",
493
- ...result
494
- }) || {
495
- message: VALIDATION_FAILED,
496
- issues: result.issues
497
- });
498
- return result.value;
477
+ if (prop === "json") return function _validatedJson() {
478
+ return req.json().then((data) => validate.body["~standard"].validate(data)).then((result) => {
479
+ if (result.issues) throw createValidationError(validate.onError?.({
480
+ _source: "body",
481
+ ...result
482
+ }) || {
483
+ message: VALIDATION_FAILED,
484
+ issues: result.issues
499
485
  });
500
- };
501
- else if (reqBodyKeys.has(prop)) throw new TypeError(`Cannot access .${prop} on request with JSON validation enabled. Use .json() instead.`);
502
- }
486
+ return result.value;
487
+ });
488
+ };
489
+ else if (reqBodyKeys.has(prop)) throw new TypeError(`Cannot access .${prop} on request with JSON validation enabled. Use .json() instead.`);
503
490
  return Reflect.get(req, prop);
504
491
  } });
505
492
  }
@@ -556,7 +543,10 @@ function mockEvent(_request, options) {
556
543
  return new H3Event(request);
557
544
  }
558
545
  function requestWithURL(req, url) {
559
- const cache = { url };
546
+ const cache = {
547
+ url,
548
+ _url: void 0
549
+ };
560
550
  return new Proxy(req, { get(target, prop) {
561
551
  if (prop in cache) return cache[prop];
562
552
  const value = Reflect.get(target, prop);
@@ -566,7 +556,13 @@ function requestWithURL(req, url) {
566
556
  }
567
557
  function requestWithBaseURL(req, base) {
568
558
  const url = new URL(req.url);
569
- url.pathname = decodePathname(url.pathname).slice(base.length) || "/";
559
+ let pathname;
560
+ try {
561
+ pathname = decodePathname(url.pathname);
562
+ } catch {
563
+ pathname = url.pathname;
564
+ }
565
+ url.pathname = pathname.slice(base.length) || "/";
570
566
  return requestWithURL(req, url.href);
571
567
  }
572
568
  function toRequest(input, options) {
@@ -575,7 +571,7 @@ function toRequest(input, options) {
575
571
  if (url[0] === "/") {
576
572
  const headers = options?.headers ? new Headers(options.headers) : void 0;
577
573
  const host = headers?.get("host") || "localhost";
578
- url = `${headers?.get("x-forwarded-proto") === "https" ? "https" : "http"}://${host}${url}`;
574
+ url = `${(headers?.get("x-forwarded-proto") || "").split(",")[0].trim() === "https" ? "https" : "http"}://${host}${url}`;
579
575
  }
580
576
  return new Request(url, options);
581
577
  } else if (options || input instanceof URL) return new Request(input, options);
@@ -627,7 +623,7 @@ function getRequestHost(event, opts = {}) {
627
623
  }
628
624
  function getRequestProtocol(event, opts = {}) {
629
625
  if (opts.xForwardedProto !== false) {
630
- const forwardedProto = event.req.headers.get("x-forwarded-proto");
626
+ const forwardedProto = (event.req.headers.get("x-forwarded-proto") || "").split(",")[0].trim();
631
627
  if (forwardedProto === "https") return "https";
632
628
  if (forwardedProto === "http") return "http";
633
629
  }
@@ -709,13 +705,14 @@ function defineLazyEventHandler(loader) {
709
705
  }
710
706
  function toEventHandler(handler) {
711
707
  if (typeof handler === "function") return handler;
712
- if (typeof handler?.handler === "function") return handler.handler;
708
+ if (typeof handler?.handler === "function" && handler.constructor?.["~h3"]) return handler.handler;
713
709
  if (typeof handler?.fetch === "function") return function _fetchHandler(event) {
714
710
  return handler.fetch(event.req);
715
711
  };
716
712
  }
717
713
  const NoHandler = () => kNotFound;
718
714
  var H3Core = class {
715
+ static "~h3" = true;
719
716
  config;
720
717
  "~middleware";
721
718
  "~routes" = [];
@@ -742,6 +739,10 @@ var H3Core = class {
742
739
  const event = new H3Event(request, context, this);
743
740
  let handlerRes;
744
741
  try {
742
+ if (event[kMalformedURL] && !this.config.allowMalformedURL) throw new HTTPError({
743
+ status: 400,
744
+ message: "Bad Request"
745
+ });
745
746
  if (this.config.onRequest) {
746
747
  const hookRes = this.config.onRequest(event);
747
748
  handlerRes = typeof hookRes?.then === "function" ? hookRes.then(() => this.handler(event)) : this.handler(event);
@@ -783,10 +784,21 @@ const H3 = /* @__PURE__ */ (() => {
783
784
  const originalPathname = event.url.pathname;
784
785
  if (!originalPathname.startsWith(base) || originalPathname.length > base.length && originalPathname[base.length] !== "/") return next();
785
786
  event.url.pathname = event.url.pathname.slice(base.length) || "/";
786
- return callMiddleware(event, input["~middleware"], () => {
787
+ const restore = () => {
787
788
  event.url.pathname = originalPathname;
788
- return next();
789
- });
789
+ };
790
+ try {
791
+ const result = callMiddleware(event, input["~middleware"], () => {
792
+ restore();
793
+ return next();
794
+ });
795
+ if (typeof result?.then === "function") return Promise.resolve(result).finally(restore);
796
+ restore();
797
+ return result;
798
+ } catch (err) {
799
+ restore();
800
+ throw err;
801
+ }
790
802
  });
791
803
  for (const r of input["~routes"]) this["~addRoute"]({
792
804
  ...r,
@@ -860,6 +872,9 @@ const H3 = /* @__PURE__ */ (() => {
860
872
  };
861
873
  return H3;
862
874
  })();
875
+ function definePlugin(def) {
876
+ return ((opts) => (h3) => def(h3, opts));
877
+ }
863
878
  function toWebHandler(app) {
864
879
  return (request, context) => {
865
880
  return Promise.resolve(app.request(request, void 0, context || request.context));
@@ -873,8 +888,15 @@ function fromWebHandler(handler) {
873
888
  function fromNodeHandler(handler) {
874
889
  if (typeof handler !== "function") throw new TypeError(`Invalid handler. It should be a function: ${handler}`);
875
890
  return function _nodeHandler(event) {
876
- if (!event.runtime?.node?.res) throw new Error("[h3] Executing Node.js middleware is not supported in this server!");
877
- return callNodeHandler(handler, event.runtime?.node.req, event.runtime?.node.res);
891
+ const node = event.runtime?.node;
892
+ if (!node?.res) throw new Error("[h3] Executing Node.js middleware is not supported in this server!");
893
+ const url = event.url.pathname + event.url.search;
894
+ if (node.req.url === url) return callNodeHandler(handler, node.req, node.res);
895
+ const originalUrl = node.req.url;
896
+ node.req.url = url;
897
+ return callNodeHandler(handler, node.req, node.res).finally(() => {
898
+ node.req.url = originalUrl;
899
+ });
878
900
  };
879
901
  }
880
902
  function defineNodeHandler(handler) {
@@ -1036,7 +1058,7 @@ function serializeIterableValue(value) {
1036
1058
  if (value instanceof Uint8Array) return value;
1037
1059
  return textEncoder.encode(JSON.stringify(value));
1038
1060
  }
1039
- return new Uint8Array();
1061
+ return /* @__PURE__ */ new Uint8Array();
1040
1062
  }
1041
1063
  function coerceIterable(iterable) {
1042
1064
  if (typeof iterable === "function") iterable = iterable();
@@ -1079,14 +1101,19 @@ function redirectBack(event, opts = {}) {
1079
1101
  return redirect(location, opts.status);
1080
1102
  }
1081
1103
  function writeEarlyHints(event, hints) {
1082
- if (event.runtime?.node?.res?.writeEarlyHints) return new Promise((resolve) => {
1083
- event.runtime?.node?.res?.writeEarlyHints(hints, () => resolve());
1084
- });
1085
- for (const [name, value] of Object.entries(hints)) {
1086
- if (name.toLowerCase() !== "link") continue;
1087
- if (Array.isArray(value)) for (const v of value) event.res.headers.append("link", v);
1088
- else event.res.headers.append("link", value);
1104
+ const linkValues = [];
1105
+ for (const [name, value] of Object.entries(hints)) if (name.toLowerCase() === "link") {
1106
+ for (const v of Array.isArray(value) ? value : [value]) if (v) linkValues.push(v);
1107
+ }
1108
+ if (event.runtime?.node?.res?.writeEarlyHints) {
1109
+ if (linkValues.length === 0) return Promise.resolve();
1110
+ const normalizedHints = { link: linkValues };
1111
+ for (const [name, value] of Object.entries(hints)) if (name.toLowerCase() !== "link") normalizedHints[name] = value;
1112
+ return new Promise((resolve) => {
1113
+ event.runtime?.node?.res?.writeEarlyHints(normalizedHints, () => resolve());
1114
+ });
1089
1115
  }
1116
+ for (const v of linkValues) event.res.headers.append("link", v);
1090
1117
  }
1091
1118
  function iterable(iterable, options) {
1092
1119
  const serializer = options?.serializer ?? serializeIterableValue;
@@ -1109,18 +1136,39 @@ function html(first, ...values) {
1109
1136
  return new HTTPResponse(typeof first === "string" ? first : first.reduce((out, str, i) => out + str + (values[i] ?? ""), ""), { headers: { "content-type": "text/html; charset=utf-8" } });
1110
1137
  }
1111
1138
  function parseURLEncodedBody(body) {
1112
- const form = new URLSearchParams(body);
1113
- const parsedForm = new EmptyObject();
1114
- for (const [key, value] of form.entries()) if (hasProp(parsedForm, key)) {
1115
- if (!Array.isArray(parsedForm[key])) parsedForm[key] = [parsedForm[key]];
1116
- parsedForm[key].push(value);
1117
- } else parsedForm[key] = value;
1118
- return parsedForm;
1119
- }
1120
- async function readBody(event) {
1139
+ return collectEntries(new URLSearchParams(body).entries());
1140
+ }
1141
+ function parseFormData(form) {
1142
+ return collectEntries(form.entries());
1143
+ }
1144
+ function collectEntries(entries) {
1145
+ const parsed = new EmptyObject();
1146
+ for (const [key, value] of entries) if (hasProp(parsed, key)) {
1147
+ if (!Array.isArray(parsed[key])) parsed[key] = [parsed[key]];
1148
+ parsed[key].push(value);
1149
+ } else parsed[key] = value;
1150
+ return parsed;
1151
+ }
1152
+ async function readBody(event, options) {
1153
+ const contentType = event.req.headers.get("content-type") || "";
1154
+ const type = options?.type;
1155
+ if (type === "formData") {
1156
+ let form;
1157
+ try {
1158
+ form = await event.req.formData();
1159
+ } catch {
1160
+ throw new HTTPError({
1161
+ status: 400,
1162
+ statusText: "Bad Request",
1163
+ message: "Invalid form data body"
1164
+ });
1165
+ }
1166
+ return parseFormData(form);
1167
+ }
1121
1168
  const text = await event.req.text();
1169
+ if (type === "text") return text;
1122
1170
  if (!text) return;
1123
- if ((event.req.headers.get("content-type") || "").startsWith("application/x-www-form-urlencoded")) return parseURLEncodedBody(text);
1171
+ if (type === "urlencoded" || !type && contentType.startsWith("application/x-www-form-urlencoded")) return parseURLEncodedBody(text);
1124
1172
  try {
1125
1173
  return JSON.parse(text);
1126
1174
  } catch {
@@ -1196,14 +1244,15 @@ function bodyLimit(limit) {
1196
1244
  return next();
1197
1245
  };
1198
1246
  }
1199
- const PayloadMethods = new Set([
1247
+ const PayloadMethods = /* @__PURE__ */ new Set([
1200
1248
  "PATCH",
1201
1249
  "POST",
1202
1250
  "PUT",
1203
1251
  "DELETE"
1204
1252
  ]);
1205
- const ignoredHeaders = new Set([
1253
+ const ignoredHeaders = /* @__PURE__ */ new Set([
1206
1254
  "transfer-encoding",
1255
+ "accept-encoding",
1207
1256
  "connection",
1208
1257
  "keep-alive",
1209
1258
  "upgrade",
@@ -1251,14 +1300,23 @@ async function proxyRequest(event, target, opts = {}) {
1251
1300
  });
1252
1301
  }
1253
1302
  async function proxy(event, target, opts = {}) {
1303
+ const callerSignal = opts.fetchOptions?.signal;
1254
1304
  const fetchOptions = {
1255
1305
  headers: opts.headers,
1256
- ...opts.fetchOptions
1306
+ ...opts.fetchOptions,
1307
+ signal: callerSignal ? AbortSignal.any([event.req.signal, callerSignal]) : event.req.signal
1257
1308
  };
1258
1309
  let response;
1259
1310
  try {
1260
1311
  response = target[0] === "/" ? await event.app.fetch(createSubRequest(event, target, fetchOptions)) : await fetch(target, fetchOptions);
1261
1312
  } catch (error) {
1313
+ if (error?.name === "AbortError") {
1314
+ if (opts.propagateAbortError) throw error;
1315
+ if (event.req.signal.aborted) return new HTTPResponse(null, {
1316
+ status: 499,
1317
+ statusText: "Client Closed Request"
1318
+ });
1319
+ }
1262
1320
  throw new HTTPError({
1263
1321
  status: 502,
1264
1322
  cause: error
@@ -1683,6 +1741,9 @@ var EventStream = class {
1683
1741
  _unsentData;
1684
1742
  _disposed = false;
1685
1743
  _handled = false;
1744
+ get _isClosed() {
1745
+ return this._writerIsClosed || this._disposed;
1746
+ }
1686
1747
  constructor(event, opts = {}) {
1687
1748
  this._event = event;
1688
1749
  this._writer = this._transformStream.writable.getWriter();
@@ -1710,7 +1771,7 @@ var EventStream = class {
1710
1771
  await this._sendEvent(message);
1711
1772
  }
1712
1773
  async pushComment(comment) {
1713
- if (this._writerIsClosed) return;
1774
+ if (this._isClosed) return;
1714
1775
  if (this._paused && !this._unsentData) {
1715
1776
  this._unsentData = formatEventStreamComment(comment);
1716
1777
  return;
@@ -1724,7 +1785,7 @@ var EventStream = class {
1724
1785
  });
1725
1786
  }
1726
1787
  async _sendEvent(message) {
1727
- if (this._writerIsClosed) return;
1788
+ if (this._isClosed) return;
1728
1789
  if (this._paused && !this._unsentData) {
1729
1790
  this._unsentData = formatEventStreamMessage(message);
1730
1791
  return;
@@ -1738,7 +1799,7 @@ var EventStream = class {
1738
1799
  });
1739
1800
  }
1740
1801
  async _sendEvents(messages) {
1741
- if (this._writerIsClosed) return;
1802
+ if (this._isClosed) return;
1742
1803
  const payload = formatEventStreamMessages(messages);
1743
1804
  if (this._paused && !this._unsentData) {
1744
1805
  this._unsentData = payload;
@@ -1763,7 +1824,7 @@ var EventStream = class {
1763
1824
  await this.flush();
1764
1825
  }
1765
1826
  async flush() {
1766
- if (this._writerIsClosed) return;
1827
+ if (this._isClosed) return;
1767
1828
  if (this._unsentData?.length) {
1768
1829
  await this._writer.write(this._encoder.encode(this._unsentData)).catch(() => {
1769
1830
  this._writerIsClosed = true;
@@ -1773,7 +1834,7 @@ var EventStream = class {
1773
1834
  }
1774
1835
  async close() {
1775
1836
  if (this._disposed) return;
1776
- if (!this._writerIsClosed) try {
1837
+ if (!this._isClosed) try {
1777
1838
  await this._writer.close();
1778
1839
  } catch {}
1779
1840
  this._disposed = true;
@@ -1858,7 +1919,8 @@ function handleCacheHeaders(event, opts) {
1858
1919
  }
1859
1920
  if (opts.etag) {
1860
1921
  event.res.headers.set("etag", opts.etag);
1861
- if (event.req.headers.get("if-none-match") === opts.etag) cacheMatched = true;
1922
+ const ifNonMatch = event.req.headers.get("if-none-match");
1923
+ if (ifNonMatch && ifNonMatch.split(",").some((token) => token.trim() === opts.etag)) cacheMatched = true;
1862
1924
  }
1863
1925
  event.res.headers.set("cache-control", cacheControls.join(", "));
1864
1926
  if (cacheMatched) {
@@ -1867,6 +1929,28 @@ function handleCacheHeaders(event, opts) {
1867
1929
  }
1868
1930
  return false;
1869
1931
  }
1932
+ const DOT_SEGMENT_RE = /(?:^|\/)(?:\.|%(?:25)*2e){1,2}(?:\/|$)/i;
1933
+ const ENCODED_SEP_RE = /%(?:25)*(?:2f|5c)/i;
1934
+ const ENCODED_SEP_RE_G = new RegExp(ENCODED_SEP_RE.source, "gi");
1935
+ const ENCODED_DOT_RE_G = /%(?:25)*2e/gi;
1936
+ function resolveDotSegments(path, opts) {
1937
+ if (path[0] !== "/" || path[1] === "/" || path[1] === "\\") path = "/" + path.replace(/^[/\\]+/, "");
1938
+ const decodeSlashes = opts?.decodeSlashes;
1939
+ const hasBackslash = path.includes("\\");
1940
+ const hasEncodedSep = decodeSlashes && ENCODED_SEP_RE.test(path);
1941
+ if (!hasBackslash && !hasEncodedSep && !DOT_SEGMENT_RE.test(path)) return path;
1942
+ let normalized = hasBackslash ? path.replaceAll("\\", "/") : path;
1943
+ if (hasEncodedSep) normalized = normalized.replace(ENCODED_SEP_RE_G, "/");
1944
+ const segments = normalized.split("/");
1945
+ const resolved = [];
1946
+ for (const segment of segments) {
1947
+ const normalizedSegment = segment.includes("%") ? segment.replace(ENCODED_DOT_RE_G, ".") : segment;
1948
+ if (normalizedSegment === "..") {
1949
+ if (resolved.length > 1) resolved.pop();
1950
+ } else if (normalizedSegment !== ".") resolved.push(segment);
1951
+ }
1952
+ return (resolved.join("/") || "/").replace(/^\/+/, "/");
1953
+ }
1870
1954
  const COMMON_MIME_TYPES = {
1871
1955
  ".html": "text/html",
1872
1956
  ".htm": "text/html",
@@ -1908,7 +1992,13 @@ async function serveStatic(event, options) {
1908
1992
  event.res.headers.set("allow", "GET, HEAD");
1909
1993
  throw new HTTPError({ status: 405 });
1910
1994
  }
1911
- const originalId = resolveDotSegments(decodeURI(withLeadingSlash(withoutTrailingSlash(event.url.pathname))));
1995
+ const resolvedId = resolveDotSegments(withoutTrailingSlash(event.url.pathname));
1996
+ let originalId = resolvedId;
1997
+ if (resolvedId.includes("%")) try {
1998
+ originalId = decodeURI(resolvedId);
1999
+ } catch {
2000
+ originalId = resolvedId;
2001
+ }
1912
2002
  const acceptEncodings = parseAcceptEncoding(event.req.headers.get("accept-encoding") || "", options.encodings);
1913
2003
  if (acceptEncodings.length > 1) event.res.headers.set("vary", "accept-encoding");
1914
2004
  let id = originalId;
@@ -1928,6 +2018,7 @@ async function serveStatic(event, options) {
1928
2018
  }
1929
2019
  if (meta.mtime) {
1930
2020
  const mtimeDate = new Date(meta.mtime);
2021
+ mtimeDate.setMilliseconds(0);
1931
2022
  const ifModifiedSinceH = event.req.headers.get("if-modified-since");
1932
2023
  if (ifModifiedSinceH && new Date(ifModifiedSinceH) >= mtimeDate) return new HTTPResponse(null, {
1933
2024
  status: 304,
@@ -1947,7 +2038,7 @@ async function serveStatic(event, options) {
1947
2038
  if (type) event.res.headers.set("content-type", type);
1948
2039
  }
1949
2040
  if (meta.encoding && !event.res.headers.get("content-encoding")) event.res.headers.set("content-encoding", meta.encoding);
1950
- if (meta.size !== void 0 && meta.size > 0 && !event.req.headers.get("content-length")) event.res.headers.set("content-length", meta.size + "");
2041
+ if (meta.size !== void 0 && meta.size > 0 && !event.res.headers.get("content-length")) event.res.headers.set("content-length", meta.size + "");
1951
2042
  if (event.req.method === "HEAD") return new HTTPResponse(null, { status: 200 });
1952
2043
  return new HTTPResponse(await options.getContents(id) || null, { status: 200 });
1953
2044
  }
@@ -2365,13 +2456,17 @@ function isPreflightRequest(event) {
2365
2456
  return event.req.method === "OPTIONS" && !!origin && !!accessControlRequestMethod;
2366
2457
  }
2367
2458
  function appendCorsPreflightHeaders(event, options) {
2459
+ const originHeaders = createOriginHeaders(event, options);
2460
+ const allowHeaderHeaders = createAllowHeaderHeaders(event, options);
2368
2461
  const headers = {
2369
- ...createOriginHeaders(event, options),
2462
+ ...originHeaders,
2370
2463
  ...createCredentialsHeaders(options),
2371
2464
  ...createMethodsHeaders(options),
2372
- ...createAllowHeaderHeaders(event, options),
2465
+ ...allowHeaderHeaders,
2373
2466
  ...createMaxAgeHeader(options)
2374
2467
  };
2468
+ const varyValues = [originHeaders.vary, allowHeaderHeaders.vary].filter(Boolean);
2469
+ if (varyValues.length > 0) headers.vary = varyValues.join(", ");
2375
2470
  for (const [key, value] of Object.entries(headers)) {
2376
2471
  event.res.headers.append(key, value);
2377
2472
  event.res.errHeaders.append(key, value);
@@ -2409,7 +2504,7 @@ function timingSafeEqual(a, b) {
2409
2504
  return result === 0;
2410
2505
  }
2411
2506
  function randomJitter() {
2412
- const randomBuffer = new Uint32Array(1);
2507
+ const randomBuffer = /* @__PURE__ */ new Uint32Array(1);
2413
2508
  crypto.getRandomValues(randomBuffer);
2414
2509
  const jitter = randomBuffer[0] % 100;
2415
2510
  return new Promise((resolve) => setTimeout(resolve, jitter));
@@ -2430,6 +2525,7 @@ async function requireBasicAuth(event, opts) {
2430
2525
  throw authFailed(event, opts?.realm);
2431
2526
  }
2432
2527
  const colonIndex = authDecoded.indexOf(":");
2528
+ if (colonIndex === -1) throw authFailed(event, opts?.realm);
2433
2529
  const username = authDecoded.slice(0, colonIndex);
2434
2530
  const password = authDecoded.slice(colonIndex + 1);
2435
2531
  if (!username || !password) throw authFailed(event, opts?.realm);
@@ -2473,12 +2569,20 @@ async function getRequestFingerprint(event, opts = {}) {
2473
2569
  function defineWebSocket(hooks) {
2474
2570
  return hooks;
2475
2571
  }
2476
- function defineWebSocketHandler(hooks) {
2572
+ function defineWebSocketHandler(hooks, http) {
2477
2573
  return defineHandler(function _webSocketHandler(event) {
2574
+ if (http && !isWebSocketUpgrade(event)) return http(event);
2478
2575
  const crossws = typeof hooks === "function" ? hooks(event) : hooks;
2479
- return Object.assign(new Response("WebSocket upgrade is required.", { status: 426 }), { crossws });
2576
+ if (crossws instanceof Promise) return crossws.then(toUpgradeResponse);
2577
+ return toUpgradeResponse(crossws);
2480
2578
  });
2481
2579
  }
2580
+ function isWebSocketUpgrade(event) {
2581
+ return event.req.headers.get("upgrade")?.toLowerCase() === "websocket";
2582
+ }
2583
+ function toUpgradeResponse(crossws) {
2584
+ return Object.assign(new Response("WebSocket upgrade is required.", { status: 426 }), { crossws });
2585
+ }
2482
2586
  const PARSE_ERROR = -32700;
2483
2587
  const INVALID_REQUEST = -32600;
2484
2588
  const METHOD_NOT_FOUND = -32601;
@@ -2534,7 +2638,10 @@ async function processJsonRpcBody(body, methodMap, context) {
2534
2638
  async function processJsonRpcMethod(raw, methodMap, context) {
2535
2639
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return createJsonRpcError(null, INVALID_REQUEST, "Invalid Request");
2536
2640
  const req = raw;
2537
- if (req.jsonrpc !== "2.0" || typeof req.method !== "string" || "id" in req && !isValidId(req.id)) return createJsonRpcError("id" in req && isValidId(req.id) ? req.id : null, INVALID_REQUEST, "Invalid Request");
2641
+ if (req.jsonrpc !== "2.0" || typeof req.method !== "string" || "id" in req && !isValidId(req.id)) {
2642
+ const id = "id" in req && isValidId(req.id) ? req.id : null;
2643
+ return createJsonRpcError(id, INVALID_REQUEST, "Invalid Request");
2644
+ }
2538
2645
  if ("params" in req && req.params !== void 0 && (typeof req.params !== "object" || req.params === null)) return isNotification(req) ? void 0 : createJsonRpcError(req.id, INVALID_PARAMS, "Invalid params");
2539
2646
  if (req.method.startsWith("rpc.")) return isNotification(req) ? void 0 : createJsonRpcError(req.id, METHOD_NOT_FOUND, "Method not found");
2540
2647
  const method = req.method;
@@ -2565,7 +2672,8 @@ async function processJsonRpcMethod(raw, methodMap, context) {
2565
2672
  };
2566
2673
  const statusCode = h3Error.status;
2567
2674
  const statusMessage = h3Error.message;
2568
- return createJsonRpcError(id, mapHttpStatusToJsonRpcError(statusCode), statusMessage, h3Error.data);
2675
+ const errorCode = mapHttpStatusToJsonRpcError(statusCode);
2676
+ return createJsonRpcError(id, errorCode, statusMessage, h3Error.data);
2569
2677
  }
2570
2678
  }
2571
2679
  function mapHttpStatusToJsonRpcError(status) {
@@ -2721,4 +2829,4 @@ const toNodeListener = toNodeHandler;
2721
2829
  const createApp = (config) => new H3(config);
2722
2830
  const createRouter$1 = (config) => new H3(config);
2723
2831
  const useBase = withBase;
2724
- export { basicAuth as $, defineLazyEventHandler as $t, readFormDataBody as A, freezeApp as An, bodyLimit as At, setHeader as B, redirect as Bt, getResponseHeader as C, toMiddleware as Cn, parseCookies as Ct, isError as D, sanitizeStatusCode as Dn, getProxyRequestHeaders as Dt, getResponseStatusText as E, HTTPError as En, fetchWithEvent as Et, sendNoContent as F, readBody as Ft, toNodeHandler as G, defineNodeHandler as Gt, setResponseHeader as H, writeEarlyHints as Ht, sendProxy as I, readValidatedBody as It, defineJsonRpcHandler as J, fromWebHandler as Jt, toNodeListener as K, defineNodeMiddleware as Kt, sendRedirect as L, html as Lt, readRawBody as M, onRequest as Mt, removeResponseHeader as N, onResponse as Nt, lazyEventHandler as O, sanitizeStatusMessage as On, proxy as Ot, sendIterable as P, assertBodySize as Pt, getRequestFingerprint as Q, defineHandler as Qt, sendStream as R, iterable as Rt, getRequestWebStream as S, defineMiddleware as Sn, getValidatedCookies as St, getResponseStatus as T, toResponse as Tn, setCookie as Tt, setResponseHeaders as U, defineRoute as Ut, setHeaders as V, redirectBack as Vt, setResponseStatus as W, removeRoute$1 as Wt, defineWebSocket as X, H3 as Xt, defineJsonRpcWebSocketHandler as Y, toWebHandler as Yt, defineWebSocketHandler as Z, H3Core as Zt, getHeaders as _, getEventContext as _n, createEventStream as _t, appendResponseHeaders as a, getRequestHost as an, isCorsOriginAllowed as at, getRequestHeaders as b, mockEvent as bn, getChunkedCookie as bt, createError as c, getRequestURL as cn, sealSession as ct, defineEventHandler as d, getValidatedQuery as dn, useSession as dt, defineValidatedHandler as en, requireBasicAuth as et, defineNodeListener as f, getValidatedRouterParams as fn, withBase as ft, getHeader as g, toRequest as gn, withServerTiming as gt, getBodyStream as h, requestWithURL as hn, setServerTiming as ht, appendResponseHeader as i, getQuery as in, isPreflightRequest as it, readMultipartFormData as j, onError as jt, readFormData as k, H3Event as kn, proxyRequest as kt, createRouter$1 as l, getRouterParam as ln, unsealSession as lt, fromNodeMiddleware as m, requestWithBaseURL as mn, handleCacheHeaders as mt, appendHeader as n, toEventHandler as nn, appendCorsPreflightHeaders as nt, clearResponseHeaders as o, getRequestIP as on, clearSession as ot, eventHandler as p, isMethod as pn, serveStatic as pt, useBase as q, fromNodeHandler as qt, appendHeaders as r, assertMethod as rn, handleCors as rt, createApp as s, getRequestProtocol as sn, getSession as st, H3Error as t, dynamicEventHandler as tn, appendCorsHeaders as tt, defaultContentType as u, getRouterParams as un, updateSession as ut, getMethod as v, isEvent as vn, deleteChunkedCookie as vt, getResponseHeaders as w, HTTPResponse as wn, setChunkedCookie as wt, getRequestPath as x, callMiddleware as xn, getCookie as xt, getRequestHeader as y, isHTTPEvent as yn, deleteCookie as yt, sendWebResponse as z, noContent as zt };
2832
+ export { H3, H3Core, H3Error, H3Event, HTTPError, HTTPResponse, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertBodySize, assertMethod, basicAuth, bodyLimit, callMiddleware, clearResponseHeaders, clearSession, createApp, createError, createEventStream, createRouter$1 as createRouter, defaultContentType, defineEventHandler, defineHandler, defineJsonRpcHandler, defineJsonRpcWebSocketHandler, defineLazyEventHandler, defineMiddleware, defineNodeHandler, defineNodeListener, defineNodeMiddleware, definePlugin, defineRoute, defineValidatedHandler, defineWebSocket, defineWebSocketHandler, deleteChunkedCookie, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, freezeApp, fromNodeHandler, fromNodeMiddleware, fromWebHandler, getBodyStream, getChunkedCookie, getCookie, getEventContext, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedCookies, 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, redirectBack, removeResponseHeader, removeRoute$1 as removeRoute, requestWithBaseURL, requestWithURL, requireBasicAuth, resolveDotSegments, sanitizeStatusCode, sanitizeStatusMessage, sealSession, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setChunkedCookie, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, setServerTiming, toEventHandler, toMiddleware, toNodeHandler, toNodeListener, toRequest, toResponse, toWebHandler, unsealSession, updateSession, useBase, useSession, withBase, withServerTiming, writeEarlyHints };