h3 2.0.1-rc.2 → 2.0.1-rc.21

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.
Files changed (51) hide show
  1. package/bin/h3.mjs +35 -0
  2. package/dist/THIRD-PARTY-LICENSES.md +70 -0
  3. package/dist/_entries/bun.d.mts +3 -8
  4. package/dist/_entries/bun.mjs +3 -9
  5. package/dist/_entries/cloudflare.d.mts +4 -9
  6. package/dist/_entries/cloudflare.mjs +3 -9
  7. package/dist/_entries/deno.d.mts +3 -8
  8. package/dist/_entries/deno.mjs +3 -9
  9. package/dist/_entries/generic.d.mts +3 -8
  10. package/dist/_entries/generic.mjs +3 -9
  11. package/dist/_entries/node.d.mts +3 -8
  12. package/dist/_entries/node.mjs +3 -12
  13. package/dist/_entries/service-worker.d.mts +3 -8
  14. package/dist/_entries/service-worker.mjs +3 -9
  15. package/dist/docs/0.guide/0.index/index.md +117 -0
  16. package/dist/docs/0.guide/1.basics/0.lifecycle.md +68 -0
  17. package/dist/docs/0.guide/1.basics/1.routing.md +92 -0
  18. package/dist/docs/0.guide/1.basics/2.middleware.md +97 -0
  19. package/dist/docs/0.guide/1.basics/3.handler.md +165 -0
  20. package/dist/docs/0.guide/1.basics/4.response.md +162 -0
  21. package/dist/docs/0.guide/1.basics/5.error.md +117 -0
  22. package/dist/docs/0.guide/1.basics/6.nested-apps.md +57 -0
  23. package/dist/docs/0.guide/2.api/0.h3.md +145 -0
  24. package/dist/docs/0.guide/2.api/1.h3event.md +113 -0
  25. package/dist/docs/0.guide/3.advanced/0.plugins.md +50 -0
  26. package/dist/docs/0.guide/3.advanced/1.websocket.md +124 -0
  27. package/dist/docs/0.guide/3.advanced/2.nightly.md +13 -0
  28. package/dist/docs/1.utils/0.index/index.md +46 -0
  29. package/dist/docs/1.utils/1.request.md +355 -0
  30. package/dist/docs/1.utils/2.response.md +144 -0
  31. package/dist/docs/1.utils/3.cookie.md +33 -0
  32. package/dist/docs/1.utils/4.security.md +109 -0
  33. package/dist/docs/1.utils/5.proxy.md +31 -0
  34. package/dist/docs/1.utils/6.mcp.md +71 -0
  35. package/dist/docs/1.utils/7.more.md +78 -0
  36. package/dist/docs/1.utils/8.community.md +42 -0
  37. package/dist/docs/2.examples/0.index/index.md +16 -0
  38. package/dist/docs/2.examples/1.handle-cookie.md +67 -0
  39. package/dist/docs/2.examples/2.handle-session.md +130 -0
  40. package/dist/docs/2.examples/3.serve-static-assets.md +66 -0
  41. package/dist/docs/2.examples/4.stream-response.md +76 -0
  42. package/dist/docs/2.examples/5.validate-data.md +193 -0
  43. package/dist/docs/3.migration/0.index/index.md +200 -0
  44. package/dist/docs/README.md +35 -0
  45. package/dist/{h3.mjs → h3-CRCltuUf.mjs} +915 -1218
  46. package/dist/h3-D76FUMrE.d.mts +833 -0
  47. package/dist/h3-DagAgogP.mjs +4 -0
  48. package/dist/{h3.d.mts → h3-DiSMXP1G.d.mts} +320 -656
  49. package/dist/tracing.d.mts +24 -0
  50. package/dist/tracing.mjs +76 -0
  51. package/package.json +56 -44
@@ -1,74 +1,66 @@
1
- import { NullProtoObj as EmptyObject, addRoute, createRouter, findRoute, routeToRegExp } from "rou3";
1
+ import "./h3-DagAgogP.mjs";
2
+ import { NullProtoObj as EmptyObject, addRoute, createRouter, findRoute, removeRoute, routeToRegExp } from "rou3";
2
3
  import { FastResponse, FastURL } from "srvx";
3
- import { parse, parseSetCookie, serialize, splitSetCookieString } from "cookie-es";
4
-
5
- //#region src/_entries/_common.ts
6
4
  function freezeApp(app) {
7
5
  app.config = Object.freeze(app.config);
8
- app._addRoute = () => {
6
+ app["~addRoute"] = () => {
9
7
  throw new Error("Cannot add routes after the server init.");
10
8
  };
11
9
  }
12
-
13
- //#endregion
14
- //#region src/types/h3.ts
15
- function definePlugin(def) {
16
- return (opts) => (h3) => def(h3, opts);
10
+ function withLeadingSlash(path) {
11
+ if (!path || path === "/") return "/";
12
+ return path[0] === "/" ? path : `/${path}`;
13
+ }
14
+ function withoutTrailingSlash(path) {
15
+ if (!path || path === "/") return "/";
16
+ return path[path.length - 1] === "/" ? path.slice(0, -1) : path;
17
+ }
18
+ function withoutBase(input = "", base = "") {
19
+ if (!base || base === "/") return input;
20
+ const _base = withoutTrailingSlash(base);
21
+ if (!input.startsWith(_base) || input.length > _base.length && input[_base.length] !== "/") return input;
22
+ return "/" + input.slice(_base.length).replace(/^\/+/, "");
23
+ }
24
+ function decodePathname(pathname) {
25
+ return decodeURI(pathname.includes("%25") ? pathname.replace(/%25/g, "%2525") : pathname);
26
+ }
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("/") || "/";
17
38
  }
18
-
19
- //#endregion
20
- //#region src/event.ts
21
39
  const kEventNS = "h3.internal.event.";
22
40
  const kEventRes = /* @__PURE__ */ Symbol.for(`${kEventNS}res`);
23
41
  const kEventResHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.headers`);
42
+ const kEventResErrHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.err.headers`);
24
43
  var H3Event = class {
25
- /**
26
- * Access to the H3 application instance.
27
- */
28
44
  app;
29
- /**
30
- * Incoming HTTP request info.
31
- *
32
- * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request)
33
- */
34
45
  req;
35
- /**
36
- * Access to the parsed request URL.
37
- *
38
- * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/URL)
39
- */
40
46
  url;
41
- /**
42
- * Event context.
43
- */
44
47
  context;
45
- /**
46
- * @internal
47
- */
48
48
  static __is_event__ = true;
49
49
  constructor(req, context, app) {
50
50
  this.context = context || req.context || new EmptyObject();
51
51
  this.req = req;
52
52
  this.app = app;
53
53
  const _url = req._url;
54
- this.url = _url && _url instanceof URL ? _url : new FastURL(req.url);
54
+ const url = _url && _url instanceof URL ? _url : new FastURL(req.url);
55
+ if (url.pathname.includes("%")) url.pathname = decodePathname(url.pathname);
56
+ this.url = url;
55
57
  }
56
- /**
57
- * Prepared HTTP response.
58
- */
59
58
  get res() {
60
59
  return this[kEventRes] ||= new H3EventResponse();
61
60
  }
62
- /**
63
- * Access to runtime specific additional context.
64
- *
65
- */
66
61
  get runtime() {
67
62
  return this.req.runtime;
68
63
  }
69
- /**
70
- * Tell the runtime about an ongoing operation that shouldn't close until the promise resolves.
71
- */
72
64
  waitUntil(promise) {
73
65
  this.req.waitUntil?.(promise);
74
66
  }
@@ -78,38 +70,15 @@ var H3Event = class {
78
70
  toJSON() {
79
71
  return this.toString();
80
72
  }
81
- /**
82
- * Access to the raw Node.js req/res objects.
83
- *
84
- * @deprecated Use `event.runtime.{node|deno|bun|...}.` instead.
85
- */
86
73
  get node() {
87
74
  return this.req.runtime?.node;
88
75
  }
89
- /**
90
- * Access to the incoming request headers.
91
- *
92
- * @deprecated Use `event.req.headers` instead.
93
- *
94
- */
95
76
  get headers() {
96
77
  return this.req.headers;
97
78
  }
98
- /**
99
- * Access to the incoming request url (pathname+search).
100
- *
101
- * @deprecated Use `event.url.pathname + event.url.search` instead.
102
- *
103
- * Example: `/api/hello?name=world`
104
- * */
105
79
  get path() {
106
80
  return this.url.pathname + this.url.search;
107
81
  }
108
- /**
109
- * Access to the incoming request method.
110
- *
111
- * @deprecated Use `event.req.method` instead.
112
- */
113
82
  get method() {
114
83
  return this.req.method;
115
84
  }
@@ -120,90 +89,34 @@ var H3EventResponse = class {
120
89
  get headers() {
121
90
  return this[kEventResHeaders] ||= new Headers();
122
91
  }
92
+ get errHeaders() {
93
+ return this[kEventResErrHeaders] ||= new Headers();
94
+ }
123
95
  };
124
-
125
- //#endregion
126
- //#region src/utils/sanitize.ts
127
96
  const DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
128
- /**
129
- * Make sure the status message is safe to use in a response.
130
- *
131
- * Allowed characters: horizontal tabs, spaces or visible ascii characters: https://www.rfc-editor.org/rfc/rfc7230#section-3.1.2
132
- */
133
97
  function sanitizeStatusMessage(statusMessage = "") {
134
98
  return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
135
99
  }
136
- /**
137
- * Make sure the status code is a valid HTTP status code.
138
- */
139
100
  function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
140
101
  if (!statusCode) return defaultStatusCode;
141
102
  if (typeof statusCode === "string") statusCode = +statusCode;
142
103
  if (statusCode < 100 || statusCode > 599) return defaultStatusCode;
143
104
  return statusCode;
144
105
  }
145
-
146
- //#endregion
147
- //#region src/error.ts
148
- /**
149
- * HTTPError
150
- */
151
106
  var HTTPError = class HTTPError extends Error {
152
107
  get name() {
153
108
  return "HTTPError";
154
109
  }
155
- /**
156
- * HTTP status code in range [200...599]
157
- */
158
110
  status;
159
- /**
160
- * HTTP status text
161
- *
162
- * **NOTE:** This should be short (max 512 to 1024 characters).
163
- * Allowed characters are tabs, spaces, visible ASCII characters, and extended characters (byte value 128–255).
164
- *
165
- * **TIP:** Use `message` for longer error descriptions in JSON body.
166
- */
167
111
  statusText;
168
- /**
169
- * Additional HTTP headers to be sent in error response.
170
- */
171
112
  headers;
172
- /**
173
- * Original error object that caused this error.
174
- */
175
113
  cause;
176
- /**
177
- * Additional data attached in the error JSON body under `data` key.
178
- */
179
114
  data;
180
- /**
181
- * Additional top level JSON body properties to attach in the error JSON body.
182
- */
183
115
  body;
184
- /**
185
- * Flag to indicate that the error was not handled by the application.
186
- *
187
- * Unhandled error stack trace, data and message are hidden in non debug mode for security reasons.
188
- */
189
116
  unhandled;
190
- /**
191
- * Check if the input is an instance of HTTPError using its constructor name.
192
- *
193
- * It is safer than using `instanceof` because it works across different contexts (e.g., if the error was thrown in a different module).
194
- */
195
117
  static isError(input) {
196
118
  return input instanceof Error && input?.name === "HTTPError";
197
119
  }
198
- /**
199
- * Create a new HTTPError with the given status code and optional status text and details.
200
- *
201
- * @example
202
- *
203
- * HTTPError.status(404)
204
- * HTTPError.status(418, "I'm a teapot")
205
- * HTTPError.status(403, "Forbidden", { message: "Not authenticated" })
206
- */
207
120
  static status(status, statusText, details) {
208
121
  return new HTTPError({
209
122
  ...details,
@@ -218,8 +131,8 @@ var HTTPError = class HTTPError extends Error {
218
131
  messageInput = arg1;
219
132
  details = arg2;
220
133
  } else details = arg1;
221
- const status = sanitizeStatusCode(details?.status || (details?.cause)?.status || details?.status || details?.statusCode, 500);
222
- const statusText = sanitizeStatusMessage(details?.statusText || (details?.cause)?.statusText || details?.statusText || details?.statusMessage);
134
+ const status = sanitizeStatusCode(details?.status || details?.statusCode || (details?.cause)?.status || (details?.cause)?.statusCode, 500);
135
+ const statusText = sanitizeStatusMessage(details?.statusText || details?.statusMessage || (details?.cause)?.statusText || (details?.cause)?.statusMessage);
223
136
  const message = messageInput || details?.message || (details?.cause)?.message || details?.statusText || details?.statusMessage || [
224
137
  "HTTPError",
225
138
  status,
@@ -227,7 +140,6 @@ var HTTPError = class HTTPError extends Error {
227
140
  ].filter(Boolean).join(" ");
228
141
  super(message, { cause: details });
229
142
  this.cause = details;
230
- Error.captureStackTrace?.(this, this.constructor);
231
143
  this.status = status;
232
144
  this.statusText = statusText || void 0;
233
145
  const rawHeaders = details?.headers || (details?.cause)?.headers;
@@ -236,15 +148,9 @@ var HTTPError = class HTTPError extends Error {
236
148
  this.data = details?.data;
237
149
  this.body = details?.body;
238
150
  }
239
- /**
240
- * @deprecated Use `status`
241
- */
242
151
  get statusCode() {
243
152
  return this.status;
244
153
  }
245
- /**
246
- * @deprecated Use `statusText`
247
- */
248
154
  get statusMessage() {
249
155
  return this.statusText;
250
156
  }
@@ -260,15 +166,6 @@ var HTTPError = class HTTPError extends Error {
260
166
  };
261
167
  }
262
168
  };
263
-
264
- //#endregion
265
- //#region src/utils/internal/object.ts
266
- /**
267
- * Checks if a certain input has a given property.
268
- * @param obj - The input to check.
269
- * @param prop - The property to check for.
270
- * @returns A boolean indicating whether the input is an object and has the property.
271
- */
272
169
  function hasProp(obj, prop) {
273
170
  try {
274
171
  return prop in obj;
@@ -286,17 +183,14 @@ function isJSONSerializable(value, _type) {
286
183
  const proto = Object.getPrototypeOf(value);
287
184
  return proto === Object.prototype || proto === null;
288
185
  }
289
-
290
- //#endregion
291
- //#region src/response.ts
292
186
  const kNotFound = /* @__PURE__ */ Symbol.for("h3.notFound");
293
187
  const kHandled = /* @__PURE__ */ Symbol.for("h3.handled");
294
188
  function toResponse(val, event, config = {}) {
295
- if (typeof val?.then === "function") return (val.catch?.((error) => error) || Promise.resolve(val)).then((resolvedVal) => toResponse(resolvedVal, event, config));
189
+ if (typeof val?.then === "function") return val.then((resolvedVal) => toResponse(resolvedVal, event, config), (r) => toResponse(typeof r === "number" ? new HTTPError({ status: r }) : r, event, config));
296
190
  const response = prepareResponse(val, event, config);
297
191
  if (typeof response?.then === "function") return toResponse(response, event, config);
298
- const { onResponse: onResponse$1 } = config;
299
- return onResponse$1 ? Promise.resolve(onResponse$1(response, event)).then(() => response) : response;
192
+ const { onResponse } = config;
193
+ return onResponse ? Promise.resolve(onResponse(response, event)).then(() => response) : response;
300
194
  }
301
195
  var HTTPResponse = class {
302
196
  #headers;
@@ -330,11 +224,13 @@ function prepareResponse(val, event, config, nested) {
330
224
  if (val?.stack) error.stack = val.stack;
331
225
  }
332
226
  if (error.unhandled && !config.silent) console.error(error);
333
- const { onError: onError$1 } = config;
334
- return onError$1 && !nested ? Promise.resolve(onError$1(error, event)).catch((error$1) => error$1).then((newVal) => prepareResponse(newVal ?? val, event, config, true)) : errorResponse(error, config.debug);
227
+ const { onError } = config;
228
+ const errHeaders = event[kEventRes]?.[kEventResErrHeaders];
229
+ return onError && !nested ? Promise.resolve(onError(error, event)).catch((error) => error).then((newVal) => prepareResponse(newVal ?? val, event, config, true)) : errorResponse(error, config.debug, errHeaders);
335
230
  }
336
231
  const preparedRes = event[kEventRes];
337
232
  const preparedHeaders = preparedRes?.[kEventResHeaders];
233
+ event[kEventRes] = void 0;
338
234
  if (!(val instanceof Response)) {
339
235
  const res = prepareResponseBody(val, event, config);
340
236
  const status = res.status || preparedRes?.status;
@@ -344,7 +240,7 @@ function prepareResponse(val, event, config, nested) {
344
240
  headers: res.headers && preparedHeaders ? mergeHeaders$1(res.headers, preparedHeaders) : res.headers || preparedHeaders
345
241
  });
346
242
  }
347
- if (!preparedHeaders) return val;
243
+ if (!preparedHeaders || nested || !val.ok) return val;
348
244
  try {
349
245
  mergeHeaders$1(val.headers, preparedHeaders, val.headers);
350
246
  return val;
@@ -361,8 +257,16 @@ function mergeHeaders$1(base, overrides, target = new Headers(base)) {
361
257
  else target.set(name, value);
362
258
  return target;
363
259
  }
364
- const emptyHeaders = /* @__PURE__ */ new Headers({ "content-length": "0" });
365
- const jsonHeaders = /* @__PURE__ */ new Headers({ "content-type": "application/json;charset=UTF-8" });
260
+ const frozen = (name) => (...args) => {
261
+ throw new Error(`Headers are frozen (${name} ${args.join(", ")})`);
262
+ };
263
+ var FrozenHeaders = class extends Headers {
264
+ set = frozen("set");
265
+ append = frozen("append");
266
+ delete = frozen("delete");
267
+ };
268
+ const emptyHeaders = /* @__PURE__ */ new FrozenHeaders({ "content-length": "0" });
269
+ const jsonHeaders = /* @__PURE__ */ new FrozenHeaders({ "content-type": "application/json;charset=UTF-8" });
366
270
  function prepareResponseBody(val, event, config) {
367
271
  if (val === null || val === void 0) return {
368
272
  body: "",
@@ -405,19 +309,18 @@ function prepareResponseBody(val, event, config) {
405
309
  function nullBody(method, status) {
406
310
  return method === "HEAD" || status === 100 || status === 101 || status === 102 || status === 204 || status === 205 || status === 304;
407
311
  }
408
- function errorResponse(error, debug) {
312
+ function errorResponse(error, debug, errHeaders) {
313
+ let headers = error.headers ? mergeHeaders$1(jsonHeaders, error.headers) : new Headers(jsonHeaders);
314
+ if (errHeaders) headers = mergeHeaders$1(headers, errHeaders);
409
315
  return new FastResponse(JSON.stringify({
410
316
  ...error.toJSON(),
411
317
  stack: debug && error.stack ? error.stack.split("\n").map((l) => l.trim()) : void 0
412
318
  }, void 0, debug ? 2 : void 0), {
413
319
  status: error.status,
414
320
  statusText: error.statusText,
415
- headers: error.headers ? mergeHeaders$1(jsonHeaders, error.headers) : jsonHeaders
321
+ headers
416
322
  });
417
323
  }
418
-
419
- //#endregion
420
- //#region src/middleware.ts
421
324
  function defineMiddleware(input) {
422
325
  return input;
423
326
  }
@@ -431,7 +334,7 @@ function normalizeMiddleware(input, opts = {}) {
431
334
  };
432
335
  }
433
336
  function createMatcher(opts) {
434
- if (!opts.route && !opts.method && !opts.match) return void 0;
337
+ if (!opts.route && !opts.method && !opts.match) return;
435
338
  const routeMatcher = opts.route ? routeToRegExp(opts.route) : void 0;
436
339
  const method = opts.method?.toUpperCase();
437
340
  return function _middlewareMatcher(event) {
@@ -459,14 +362,34 @@ function callMiddleware(event, middleware, handler, index = 0) {
459
362
  return nextResult;
460
363
  };
461
364
  const ret = fn(event, next);
462
- return is404(ret) ? next() : typeof ret?.then === "function" ? ret.then((resolved) => is404(resolved) ? next() : resolved) : ret;
365
+ return isUnhandledResponse(ret) ? next() : typeof ret?.then === "function" ? ret.then((resolved) => isUnhandledResponse(resolved) ? next() : resolved) : ret;
366
+ }
367
+ function isUnhandledResponse(val) {
368
+ return val === void 0 || val === kNotFound;
369
+ }
370
+ function toMiddleware(input) {
371
+ let h = input.handler || input;
372
+ let isFunction = typeof h === "function";
373
+ if (!isFunction && typeof input?.fetch === "function") {
374
+ isFunction = true;
375
+ h = function _fetchHandler(event) {
376
+ return input.fetch(event.req);
377
+ };
378
+ }
379
+ if (!isFunction) return function noopMiddleware(event, next) {
380
+ return next();
381
+ };
382
+ if (h.length === 2) return h;
383
+ return function _middlewareHandler(event, next) {
384
+ const res = h(event);
385
+ return typeof res?.then === "function" ? res.then((r) => {
386
+ return is404(r) ? next() : r;
387
+ }) : is404(res) ? next() : res;
388
+ };
463
389
  }
464
390
  function is404(val) {
465
- return val === void 0 || val === kNotFound || val?.status === 404 && val instanceof Response;
391
+ return isUnhandledResponse(val) || val?.status === 404 && val instanceof Response;
466
392
  }
467
-
468
- //#endregion
469
- //#region src/utils/internal/query.ts
470
393
  const plusRegex = /\+/g;
471
394
  function parseQuery(input) {
472
395
  const params = new EmptyObject();
@@ -485,7 +408,7 @@ function parseQuery(input) {
485
408
  for (let i = 0; i < inputLength + 1; i++) {
486
409
  c = i === inputLength ? 38 : input.charCodeAt(i);
487
410
  switch (c) {
488
- case 38: {
411
+ case 38:
489
412
  hasBothKeyValuePair = equalityIndex > startingIndex;
490
413
  if (!hasBothKeyValuePair) equalityIndex = i;
491
414
  key = input.slice(startingIndex + 1, equalityIndex);
@@ -514,41 +437,35 @@ function parseQuery(input) {
514
437
  keyHasPlus = false;
515
438
  valueHasPlus = false;
516
439
  break;
517
- }
518
- case 61: {
440
+ case 61:
519
441
  if (equalityIndex <= startingIndex) equalityIndex = i;
520
442
  else shouldDecodeValue = true;
521
443
  break;
522
- }
523
- case 43: {
444
+ case 43:
524
445
  if (equalityIndex > startingIndex) valueHasPlus = true;
525
446
  else keyHasPlus = true;
526
447
  break;
527
- }
528
- case 37: {
448
+ case 37:
529
449
  if (equalityIndex > startingIndex) shouldDecodeValue = true;
530
450
  else shouldDecodeKey = true;
531
451
  break;
532
- }
533
452
  }
534
453
  }
535
454
  return params;
536
455
  }
537
-
538
- //#endregion
539
- //#region src/utils/internal/validate.ts
540
- async function validateData(data, fn) {
456
+ const VALIDATION_FAILED = "Validation failed";
457
+ async function validateData(data, fn, options) {
541
458
  if ("~standard" in fn) {
542
459
  const result = await fn["~standard"].validate(data);
543
- if (result.issues) throw createValidationError({
544
- message: "Validation failed",
460
+ if (result.issues) throw createValidationError(options?.onError?.(result) || {
461
+ message: VALIDATION_FAILED,
545
462
  issues: result.issues
546
463
  });
547
464
  return result.value;
548
465
  }
549
466
  try {
550
467
  const res = await fn(data);
551
- if (res === false) throw createValidationError({ message: "Validation failed" });
468
+ if (res === false) throw createValidationError(options?.onError?.({ issues: [{ message: VALIDATION_FAILED }] }) || { message: VALIDATION_FAILED });
552
469
  if (res === true) return data;
553
470
  return res ?? data;
554
471
  } catch (error) {
@@ -563,14 +480,23 @@ const reqBodyKeys = new Set([
563
480
  ]);
564
481
  function validatedRequest(req, validate) {
565
482
  if (validate.headers) {
566
- const validatedheaders = syncValidate("headers", Object.fromEntries(req.headers.entries()), validate.headers);
483
+ const validatedheaders = syncValidate("headers", Object.fromEntries(req.headers.entries()), validate.headers, validate.onError);
567
484
  for (const [key, value] of Object.entries(validatedheaders)) req.headers.set(key, value);
568
485
  }
569
486
  if (!validate.body) return req;
570
487
  return new Proxy(req, { get(_target, prop) {
571
488
  if (validate.body) {
572
489
  if (prop === "json") return function _validatedJson() {
573
- return req.json().then((data) => validate.body["~standard"].validate(data)).then((result) => result.issues ? Promise.reject(createValidationError(result)) : result.value);
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;
499
+ });
574
500
  };
575
501
  else if (reqBodyKeys.has(prop)) throw new TypeError(`Cannot access .${prop} on request with JSON validation enabled. Use .json() instead.`);
576
502
  }
@@ -579,48 +505,40 @@ function validatedRequest(req, validate) {
579
505
  }
580
506
  function validatedURL(url, validate) {
581
507
  if (!validate.query) return url;
582
- const validatedQuery = syncValidate("query", Object.fromEntries(url.searchParams.entries()), validate.query);
508
+ const validatedQuery = syncValidate("query", Object.fromEntries(url.searchParams.entries()), validate.query, validate.onError);
583
509
  for (const [key, value] of Object.entries(validatedQuery)) url.searchParams.set(key, value);
584
510
  return url;
585
511
  }
586
- function syncValidate(type, data, fn) {
512
+ function syncValidate(source, data, fn, onError) {
587
513
  const result = fn["~standard"].validate(data);
588
- if (result instanceof Promise) throw new TypeError(`Asynchronous validation is not supported for ${type}`);
589
- if (result.issues) throw createValidationError({ issues: result.issues });
514
+ if (result instanceof Promise) throw new TypeError(`Asynchronous validation is not supported for ${source}`);
515
+ if (result.issues) throw createValidationError(onError?.({
516
+ _source: source,
517
+ ...result
518
+ }) || {
519
+ message: VALIDATION_FAILED,
520
+ issues: result.issues
521
+ });
590
522
  return result.value;
591
523
  }
592
- function createValidationError(validateError) {
593
- return new HTTPError({
594
- status: 400,
595
- statusText: "Validation failed",
596
- message: validateError?.message,
597
- data: validateError,
598
- cause: validateError
524
+ function createValidationError(cause) {
525
+ return HTTPError.isError(cause) ? cause : new HTTPError({
526
+ cause,
527
+ status: cause?.status || 400,
528
+ statusText: cause?.statusText || VALIDATION_FAILED,
529
+ message: cause?.message || VALIDATION_FAILED,
530
+ data: {
531
+ issues: cause?.issues,
532
+ message: cause instanceof Error ? VALIDATION_FAILED : cause?.message || VALIDATION_FAILED
533
+ }
599
534
  });
600
535
  }
601
-
602
- //#endregion
603
- //#region src/utils/event.ts
604
- /**
605
- * Checks if the input is an H3Event object.
606
- * @param input - The input to check.
607
- * @returns True if the input is an H3Event object, false otherwise.
608
- * @see H3Event
609
- */
610
536
  function isEvent(input) {
611
537
  return input instanceof H3Event || input?.constructor?.__is_event__;
612
538
  }
613
- /**
614
- * Checks if the input is an object with `{ req: Request }` signature.
615
- * @param input - The input to check.
616
- * @returns True if the input is is `{ req: Request }`
617
- */
618
539
  function isHTTPEvent(input) {
619
540
  return input?.req instanceof Request;
620
541
  }
621
- /**
622
- * Gets the context of the event, if it does not exists, initializes a new context on `req.context`.
623
- */
624
542
  function getEventContext(event) {
625
543
  if (event.context) return event.context;
626
544
  event.req.context ??= {};
@@ -628,6 +546,7 @@ function getEventContext(event) {
628
546
  }
629
547
  function mockEvent(_request, options) {
630
548
  let request;
549
+ if (options?.body && !options.duplex) options.duplex = "half";
631
550
  if (typeof _request === "string") {
632
551
  let url = _request;
633
552
  if (url[0] === "/") url = `http://localhost${url}`;
@@ -636,145 +555,53 @@ function mockEvent(_request, options) {
636
555
  else request = _request;
637
556
  return new H3Event(request);
638
557
  }
639
-
640
- //#endregion
641
- //#region src/utils/request.ts
642
- /**
643
- * Convert input into a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
644
- *
645
- * If input is a relative URL, it will be normalized into a full path based on headers.
646
- *
647
- * If input is already a Request and no options are provided, it will be returned as-is.
648
- */
558
+ function requestWithURL(req, url) {
559
+ const cache = { url };
560
+ return new Proxy(req, { get(target, prop) {
561
+ if (prop in cache) return cache[prop];
562
+ const value = Reflect.get(target, prop);
563
+ cache[prop] = typeof value === "function" ? value.bind(target) : value;
564
+ return cache[prop];
565
+ } });
566
+ }
567
+ function requestWithBaseURL(req, base) {
568
+ const url = new URL(req.url);
569
+ url.pathname = decodePathname(url.pathname).slice(base.length) || "/";
570
+ return requestWithURL(req, url.href);
571
+ }
649
572
  function toRequest(input, options) {
650
573
  if (typeof input === "string") {
651
574
  let url = input;
652
575
  if (url[0] === "/") {
653
576
  const headers = options?.headers ? new Headers(options.headers) : void 0;
654
577
  const host = headers?.get("host") || "localhost";
655
- const proto = headers?.get("x-forwarded-proto") === "https" ? "https" : "http";
656
- url = `${proto}://${host}${url}`;
578
+ url = `${headers?.get("x-forwarded-proto") === "https" ? "https" : "http"}://${host}${url}`;
657
579
  }
658
580
  return new Request(url, options);
659
581
  } else if (options || input instanceof URL) return new Request(input, options);
660
582
  return input;
661
583
  }
662
- /**
663
- * Get parsed query string object from the request URL.
664
- *
665
- * @example
666
- * app.get("/", (event) => {
667
- * const query = getQuery(event); // { key: "value", key2: ["value1", "value2"] }
668
- * });
669
- */
670
584
  function getQuery(event) {
671
- const url = event.url || new URL(event.req.url);
672
- return parseQuery(url.search.slice(1));
673
- }
674
- /**
675
- * Get the query param from the request URL validated with validate function.
676
- *
677
- * You can use a simple function to validate the query object or use a Standard-Schema compatible library like `zod` to define a schema.
678
- *
679
- * @example
680
- * app.get("/", async (event) => {
681
- * const query = await getValidatedQuery(event, (data) => {
682
- * return "key" in data && typeof data.key === "string";
683
- * });
684
- * });
685
- * @example
686
- * import { z } from "zod";
687
- *
688
- * app.get("/", async (event) => {
689
- * const query = await getValidatedQuery(
690
- * event,
691
- * z.object({
692
- * key: z.string(),
693
- * }),
694
- * );
695
- * });
696
- */
697
- function getValidatedQuery(event, validate) {
698
- const query = getQuery(event);
699
- return validateData(query, validate);
700
- }
701
- /**
702
- * Get matched route params.
703
- *
704
- * If `decode` option is `true`, it will decode the matched route params using `decodeURIComponent`.
705
- *
706
- * @example
707
- * app.get("/", (event) => {
708
- * const params = getRouterParams(event); // { key: "value" }
709
- * });
710
- */
585
+ return parseQuery((event.url || new URL(event.req.url)).search.slice(1));
586
+ }
587
+ function getValidatedQuery(event, validate, options) {
588
+ return validateData(getQuery(event), validate, options);
589
+ }
711
590
  function getRouterParams(event, opts = {}) {
712
- const context = getEventContext(event);
713
- let params = context.params || {};
591
+ let params = getEventContext(event).params || {};
714
592
  if (opts.decode) {
715
593
  params = { ...params };
716
594
  for (const key in params) params[key] = decodeURIComponent(params[key]);
717
595
  }
718
596
  return params;
719
597
  }
720
- /**
721
- * Get matched route params and validate with validate function.
722
- *
723
- * If `decode` option is `true`, it will decode the matched route params using `decodeURI`.
724
- *
725
- * You can use a simple function to validate the params object or use a Standard-Schema compatible library like `zod` to define a schema.
726
- *
727
- * @example
728
- * app.get("/", async (event) => {
729
- * const params = await getValidatedRouterParams(event, (data) => {
730
- * return "key" in data && typeof data.key === "string";
731
- * });
732
- * });
733
- * @example
734
- * import { z } from "zod";
735
- *
736
- * app.get("/", async (event) => {
737
- * const params = await getValidatedRouterParams(
738
- * event,
739
- * z.object({
740
- * key: z.string(),
741
- * }),
742
- * );
743
- * });
744
- */
745
- function getValidatedRouterParams(event, validate, opts = {}) {
746
- const routerParams = getRouterParams(event, opts);
747
- return validateData(routerParams, validate);
748
- }
749
- /**
750
- * Get a matched route param by name.
751
- *
752
- * If `decode` option is `true`, it will decode the matched route param using `decodeURI`.
753
- *
754
- * @example
755
- * app.get("/", (event) => {
756
- * const param = getRouterParam(event, "key");
757
- * });
758
- */
598
+ function getValidatedRouterParams(event, validate, options = {}) {
599
+ const { decode, ...opts } = options;
600
+ return validateData(getRouterParams(event, { decode }), validate, opts);
601
+ }
759
602
  function getRouterParam(event, name, opts = {}) {
760
- const params = getRouterParams(event, opts);
761
- return params[name];
762
- }
763
- /**
764
- *
765
- * Checks if the incoming request method is of the expected type.
766
- *
767
- * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
768
- *
769
- * @example
770
- * app.get("/", (event) => {
771
- * if (isMethod(event, "GET")) {
772
- * // Handle GET request
773
- * } else if (isMethod(event, ["POST", "PUT"])) {
774
- * // Handle POST or PUT request
775
- * }
776
- * });
777
- */
603
+ return getRouterParams(event, opts)[name];
604
+ }
778
605
  function isMethod(event, expected, allowHead) {
779
606
  if (allowHead && event.req.method === "HEAD") return true;
780
607
  if (typeof expected === "string") {
@@ -782,75 +609,30 @@ function isMethod(event, expected, allowHead) {
782
609
  } else if (expected.includes(event.req.method)) return true;
783
610
  return false;
784
611
  }
785
- /**
786
- * Asserts that the incoming request method is of the expected type using `isMethod`.
787
- *
788
- * If the method is not allowed, it will throw a 405 error with the message "HTTP method is not allowed".
789
- *
790
- * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
791
- *
792
- * @example
793
- * app.get("/", (event) => {
794
- * assertMethod(event, "GET");
795
- * // Handle GET request, otherwise throw 405 error
796
- * });
797
- */
798
612
  function assertMethod(event, expected, allowHead) {
799
- if (!isMethod(event, expected, allowHead)) throw new HTTPError({ status: 405 });
800
- }
801
- /**
802
- * Get the request hostname.
803
- *
804
- * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
805
- *
806
- * If no host header is found, it will default to "localhost".
807
- *
808
- * @example
809
- * app.get("/", (event) => {
810
- * const host = getRequestHost(event); // "example.com"
811
- * });
812
- */
613
+ if (!isMethod(event, expected, allowHead)) {
614
+ const allowed = Array.isArray(expected) ? expected : [expected];
615
+ throw new HTTPError({
616
+ status: 405,
617
+ headers: { Allow: allowHead ? [...allowed, "HEAD"].join(", ") : allowed.join(", ") }
618
+ });
619
+ }
620
+ }
813
621
  function getRequestHost(event, opts = {}) {
814
622
  if (opts.xForwardedHost) {
815
- const _header = event.req.headers.get("x-forwarded-host");
816
- const xForwardedHost = (_header || "").split(",").shift()?.trim();
623
+ const xForwardedHost = (event.req.headers.get("x-forwarded-host") || "").split(",").shift()?.trim();
817
624
  if (xForwardedHost) return xForwardedHost;
818
625
  }
819
626
  return event.req.headers.get("host") || "";
820
627
  }
821
- /**
822
- * Get the request protocol.
823
- *
824
- * If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
825
- *
826
- * If protocol cannot be determined, it will default to "http".
827
- *
828
- * @example
829
- * app.get("/", (event) => {
830
- * const protocol = getRequestProtocol(event); // "https"
831
- * });
832
- */
833
628
  function getRequestProtocol(event, opts = {}) {
834
629
  if (opts.xForwardedProto !== false) {
835
630
  const forwardedProto = event.req.headers.get("x-forwarded-proto");
836
631
  if (forwardedProto === "https") return "https";
837
632
  if (forwardedProto === "http") return "http";
838
633
  }
839
- const url = event.url || new URL(event.req.url);
840
- return url.protocol.slice(0, -1);
841
- }
842
- /**
843
- * Generated the full incoming request URL.
844
- *
845
- * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
846
- *
847
- * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
848
- *
849
- * @example
850
- * app.get("/", (event) => {
851
- * const url = getRequestURL(event); // "https://example.com/path"
852
- * });
853
- */
634
+ return (event.url || new URL(event.req.url)).protocol.slice(0, -1);
635
+ }
854
636
  function getRequestURL(event, opts = {}) {
855
637
  const url = new URL(event.url || event.req.url);
856
638
  url.protocol = getRequestProtocol(event, opts);
@@ -858,23 +640,11 @@ function getRequestURL(event, opts = {}) {
858
640
  const host = getRequestHost(event, opts);
859
641
  if (host) {
860
642
  url.host = host;
861
- if (!host.includes(":")) url.port = "";
643
+ if (!/:\d+$/.test(host)) url.port = "";
862
644
  }
863
645
  }
864
646
  return url;
865
647
  }
866
- /**
867
- * Try to get the client IP address from the incoming request.
868
- *
869
- * If `xForwardedFor` is `true`, it will use the `x-forwarded-for` header if it exists.
870
- *
871
- * If IP cannot be determined, it will default to `undefined`.
872
- *
873
- * @example
874
- * app.get("/", (event) => {
875
- * const ip = getRequestIP(event); // "192.0.2.0"
876
- * });
877
- */
878
648
  function getRequestIP(event, opts = {}) {
879
649
  if (opts.xForwardedFor) {
880
650
  const _header = event.req.headers.get("x-forwarded-for");
@@ -885,9 +655,6 @@ function getRequestIP(event, opts = {}) {
885
655
  }
886
656
  return event.req.context?.clientAddress || event.req.ip || void 0;
887
657
  }
888
-
889
- //#endregion
890
- //#region src/handler.ts
891
658
  function defineHandler(input) {
892
659
  if (typeof input === "function") return handlerWithFetch(input);
893
660
  const handler = input.handler || (input.fetch ? function _fetchHandler(event) {
@@ -897,9 +664,6 @@ function defineHandler(input) {
897
664
  return callMiddleware(event, input.middleware, handler);
898
665
  } : handler), input);
899
666
  }
900
- /**
901
- * @experimental defineValidatedHandler is an experimental feature and API may change.
902
- */
903
667
  function defineValidatedHandler(def) {
904
668
  if (!def.validate) return defineHandler(def);
905
669
  return defineHandler({
@@ -935,16 +699,12 @@ function dynamicEventHandler(initial) {
935
699
  function defineLazyEventHandler(loader) {
936
700
  let handler;
937
701
  let promise;
938
- const resolveLazyHandler = () => {
939
- if (handler) return Promise.resolve(handler);
940
- return promise ??= Promise.resolve(loader()).then((r) => {
702
+ return defineHandler(function lazyHandler(event) {
703
+ return handler ? handler(event) : (promise ??= Promise.resolve(loader()).then(function resolveLazyHandler(r) {
941
704
  handler = toEventHandler(r) || toEventHandler(r.default);
942
705
  if (typeof handler !== "function") throw new TypeError("Invalid lazy handler", { cause: { resolved: r } });
943
706
  return handler;
944
- });
945
- };
946
- return defineHandler(function lazyHandler(event) {
947
- return handler ? handler(event) : resolveLazyHandler().then((r) => r(event));
707
+ })).then((r) => r(event));
948
708
  });
949
709
  }
950
710
  function toEventHandler(handler) {
@@ -954,103 +714,96 @@ function toEventHandler(handler) {
954
714
  return handler.fetch(event.req);
955
715
  };
956
716
  }
957
-
958
- //#endregion
959
- //#region src/h3.ts
960
717
  const NoHandler = () => kNotFound;
961
- const H3Core = /* @__PURE__ */ (() => {
962
- const HTTPMethods = [
963
- "GET",
964
- "POST",
965
- "PUT",
966
- "DELETE",
967
- "PATCH",
968
- "HEAD",
969
- "OPTIONS",
970
- "CONNECT",
971
- "TRACE"
972
- ];
973
- class H3Core$1 {
974
- _middleware;
975
- _routes = [];
976
- config;
718
+ var H3Core = class {
719
+ config;
720
+ "~middleware";
721
+ "~routes" = [];
722
+ constructor(config = {}) {
723
+ this["~middleware"] = [];
724
+ this.config = config;
725
+ this.fetch = this.fetch.bind(this);
726
+ this.handler = this.handler.bind(this);
727
+ }
728
+ fetch(request) {
729
+ return this["~request"](request);
730
+ }
731
+ handler(event) {
732
+ const route = this["~findRoute"](event);
733
+ if (route) {
734
+ event.context.params = route.params;
735
+ event.context.matchedRoute = route.data;
736
+ }
737
+ const routeHandler = route?.data.handler || NoHandler;
738
+ const middleware = this["~getMiddleware"](event, route);
739
+ return middleware.length > 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);
740
+ }
741
+ "~request"(request, context) {
742
+ const event = new H3Event(request, context, this);
743
+ let handlerRes;
744
+ try {
745
+ if (this.config.onRequest) {
746
+ const hookRes = this.config.onRequest(event);
747
+ handlerRes = typeof hookRes?.then === "function" ? hookRes.then(() => this.handler(event)) : this.handler(event);
748
+ } else handlerRes = this.handler(event);
749
+ } catch (error) {
750
+ handlerRes = Promise.reject(error);
751
+ }
752
+ return toResponse(handlerRes, event, this.config);
753
+ }
754
+ "~findRoute"(_event) {}
755
+ "~addRoute"(_route) {
756
+ this["~routes"].push(_route);
757
+ }
758
+ "~getMiddleware"(_event, route) {
759
+ const routeMiddleware = route?.data.middleware;
760
+ const globalMiddleware = this["~middleware"];
761
+ return routeMiddleware ? [...globalMiddleware, ...routeMiddleware] : globalMiddleware;
762
+ }
763
+ };
764
+ const H3 = /* @__PURE__ */ (() => {
765
+ class H3 extends H3Core {
766
+ "~rou3";
977
767
  constructor(config = {}) {
978
- this._middleware = [];
979
- this.config = config;
980
- this.fetch = this.fetch.bind(this);
768
+ super(config);
769
+ this["~rou3"] = createRouter();
981
770
  this.request = this.request.bind(this);
982
- this.handler = this.handler.bind(this);
983
771
  config.plugins?.forEach((plugin) => plugin(this));
984
772
  }
985
- fetch(request) {
986
- return this._request(request);
987
- }
988
- request(_req, _init, context) {
989
- return this._request(toRequest(_req, _init), context);
990
- }
991
- _request(request, context) {
992
- const event = new H3Event(request, context, this);
993
- let handlerRes;
994
- try {
995
- if (this.config.onRequest) {
996
- const hookRes = this.config.onRequest(event);
997
- handlerRes = typeof hookRes?.then === "function" ? hookRes.then(() => this.handler(event)) : this.handler(event);
998
- } else handlerRes = this.handler(event);
999
- } catch (error) {
1000
- handlerRes = Promise.reject(error);
1001
- }
1002
- return toResponse(handlerRes, event, this.config);
1003
- }
1004
- /**
1005
- * Immediately register an H3 plugin.
1006
- */
1007
773
  register(plugin) {
1008
774
  plugin(this);
1009
775
  return this;
1010
776
  }
1011
- _findRoute(_event) {}
1012
- _addRoute(_route) {
1013
- this._routes.push(_route);
1014
- }
1015
- _getMiddleware(_event, route) {
1016
- return route?.data.middleware ? [...this._middleware, ...route.data.middleware] : this._middleware;
1017
- }
1018
- handler(event) {
1019
- const route = this._findRoute(event);
1020
- if (route) {
1021
- event.context.params = route.params;
1022
- event.context.matchedRoute = route.data;
1023
- }
1024
- const routeHandler = route?.data.handler || NoHandler;
1025
- const middleware = this._getMiddleware(event, route);
1026
- return middleware.length > 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);
777
+ request(_req, _init, context) {
778
+ return this["~request"](toRequest(_req, _init), context);
1027
779
  }
1028
780
  mount(base, input) {
1029
781
  if ("handler" in input) {
1030
- if (input._middleware.length > 0) this._middleware.push((event, next) => {
1031
- return event.url.pathname.startsWith(base) ? callMiddleware(event, input._middleware, next) : next();
782
+ if (input["~middleware"].length > 0) this["~middleware"].push((event, next) => {
783
+ const originalPathname = event.url.pathname;
784
+ if (!originalPathname.startsWith(base) || originalPathname.length > base.length && originalPathname[base.length] !== "/") return next();
785
+ event.url.pathname = event.url.pathname.slice(base.length) || "/";
786
+ return callMiddleware(event, input["~middleware"], () => {
787
+ event.url.pathname = originalPathname;
788
+ return next();
789
+ });
1032
790
  });
1033
- for (const r of input._routes) this._addRoute({
791
+ for (const r of input["~routes"]) this["~addRoute"]({
1034
792
  ...r,
1035
793
  route: base + r.route
1036
794
  });
1037
795
  } else {
1038
796
  const fetchHandler = "fetch" in input ? input.fetch : input;
1039
797
  this.all(`${base}/**`, function _mountedMiddleware(event) {
1040
- const url = new URL(event.url);
1041
- url.pathname = url.pathname.slice(base.length) || "/";
1042
- return fetchHandler(new Request(url, event.req));
798
+ return fetchHandler(requestWithBaseURL(event.req, base));
1043
799
  });
1044
800
  }
1045
801
  return this;
1046
802
  }
1047
- all(route, handler, opts) {
1048
- return this.on("", route, handler, opts);
1049
- }
1050
803
  on(method, route, handler, opts) {
1051
804
  const _method = (method || "").toUpperCase();
1052
805
  route = new URL(route, "http://_").pathname;
1053
- this._addRoute({
806
+ this["~addRoute"]({
1054
807
  method: _method,
1055
808
  route,
1056
809
  handler: toEventHandler(handler),
@@ -1062,8 +815,15 @@ const H3Core = /* @__PURE__ */ (() => {
1062
815
  });
1063
816
  return this;
1064
817
  }
1065
- _normalizeMiddleware(fn, _opts) {
1066
- return fn;
818
+ all(route, handler, opts) {
819
+ return this.on("", route, handler, opts);
820
+ }
821
+ "~findRoute"(_event) {
822
+ return findRoute(this["~rou3"], _event.req.method, _event.url.pathname);
823
+ }
824
+ "~addRoute"(_route) {
825
+ addRoute(this["~rou3"], _route.method, _route.route, _route);
826
+ super["~addRoute"](_route);
1067
827
  }
1068
828
  use(arg1, arg2, arg3) {
1069
829
  let route;
@@ -1077,42 +837,29 @@ const H3Core = /* @__PURE__ */ (() => {
1077
837
  fn = arg1;
1078
838
  opts = arg2;
1079
839
  }
1080
- this._middleware.push(this._normalizeMiddleware(fn, {
840
+ if (typeof fn !== "function" && "handler" in fn) return this.mount(route || "", fn);
841
+ this["~middleware"].push(normalizeMiddleware(fn, {
1081
842
  ...opts,
1082
843
  route
1083
844
  }));
1084
845
  return this;
1085
846
  }
1086
847
  }
1087
- for (const method of HTTPMethods) H3Core$1.prototype[method.toLowerCase()] = function(route, handler, opts) {
848
+ for (const method of [
849
+ "GET",
850
+ "POST",
851
+ "PUT",
852
+ "DELETE",
853
+ "PATCH",
854
+ "HEAD",
855
+ "OPTIONS",
856
+ "CONNECT",
857
+ "TRACE"
858
+ ]) H3Core.prototype[method.toLowerCase()] = function(route, handler, opts) {
1088
859
  return this.on(method, route, handler, opts);
1089
860
  };
1090
- return H3Core$1;
861
+ return H3;
1091
862
  })();
1092
- var H3 = class extends H3Core {
1093
- /** @internal */
1094
- _rou3;
1095
- constructor(config = {}) {
1096
- super(config);
1097
- this._rou3 = createRouter();
1098
- }
1099
- _findRoute(_event) {
1100
- return findRoute(this._rou3, _event.req.method, _event.url.pathname);
1101
- }
1102
- _addRoute(_route) {
1103
- addRoute(this._rou3, _route.method, _route.route, _route);
1104
- super._addRoute(_route);
1105
- }
1106
- _normalizeMiddleware(fn, opts) {
1107
- return normalizeMiddleware(fn, opts);
1108
- }
1109
- };
1110
-
1111
- //#endregion
1112
- //#region src/adapters.ts
1113
- /**
1114
- * @deprecated Since h3 v2 you can directly use `app.fetch(request, init?, context?)`
1115
- */
1116
863
  function toWebHandler(app) {
1117
864
  return (request, context) => {
1118
865
  return Promise.resolve(app.request(request, void 0, context || request.context));
@@ -1141,8 +888,16 @@ function callNodeHandler(handler, req, res) {
1141
888
  return new Promise((resolve, reject) => {
1142
889
  res.once("close", () => resolve(kHandled));
1143
890
  res.once("finish", () => resolve(kHandled));
1144
- res.once("pipe", (stream) => resolve(stream));
1145
891
  res.once("error", (error) => reject(error));
892
+ res.once("pipe", (stream) => {
893
+ resolve(new Promise((resolve, reject) => {
894
+ stream.once("close", () => resolve(kHandled));
895
+ stream.once("error", (error) => {
896
+ console.error("[h3] Stream error in Node.js handler", { cause: error });
897
+ reject(kHandled);
898
+ });
899
+ }));
900
+ });
1146
901
  try {
1147
902
  if (isMiddleware) Promise.resolve(handler(req, res, (error) => error ? reject(new HTTPError({
1148
903
  cause: error,
@@ -1163,44 +918,19 @@ function callNodeHandler(handler, req, res) {
1163
918
  }
1164
919
  });
1165
920
  }
1166
-
1167
- //#endregion
1168
- //#region src/utils/route.ts
1169
- /**
1170
- * Define a route as a plugin that can be registered with app.register()
1171
- *
1172
- * @example
1173
- * ```js
1174
- * import { z } from "zod";
1175
- *
1176
- * const userRoute = defineRoute({
1177
- * method: 'POST',
1178
- * validate: {
1179
- * query: z.object({ id: z.string().uuid() }),
1180
- * body: z.object({ name: z.string() }),
1181
- * },
1182
- * handler: (event) => {
1183
- * return { success: true };
1184
- * }
1185
- * });
1186
- *
1187
- * app.register(userRoute);
1188
- * ```
1189
- */
1190
921
  function defineRoute(def) {
1191
922
  const handler = defineValidatedHandler(def);
1192
923
  return (h3) => {
1193
924
  h3.on(def.method, def.route, handler);
1194
925
  };
1195
926
  }
1196
-
1197
- //#endregion
1198
- //#region src/utils/internal/encoding.ts
1199
- /**
1200
- Base64 encoding based on https://github.com/denoland/std/tree/main/encoding (modified with url compatibility)
1201
- Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
1202
- https://github.com/denoland/std/blob/main/LICENSE
1203
- */
927
+ function removeRoute$1(app, method, route) {
928
+ const _method = method ? method.toUpperCase() : void 0;
929
+ route = new URL(route, "http://_").pathname;
930
+ removeRoute(app["~rou3"], _method || "", route);
931
+ const idx = app["~routes"].findIndex((r) => r.route === route && (_method == null || r.method === _method));
932
+ if (idx !== -1) app["~routes"].splice(idx, 1);
933
+ }
1204
934
  const textEncoder = /* @__PURE__ */ new TextEncoder();
1205
935
  const textDecoder = /* @__PURE__ */ new TextDecoder();
1206
936
  const base64Code = [
@@ -1295,19 +1025,6 @@ function validateBinaryLike(source) {
1295
1025
  else if (source instanceof ArrayBuffer) return new Uint8Array(source);
1296
1026
  throw new TypeError(`The input must be a Uint8Array, a string, or an ArrayBuffer.`);
1297
1027
  }
1298
-
1299
- //#endregion
1300
- //#region src/utils/internal/iterable.ts
1301
- /**
1302
- * The default implementation for {@link iterable}'s `serializer` argument.
1303
- * It serializes values as follows:
1304
- * - Instances of {@link String}, {@link Uint8Array} and `undefined` are returned as-is.
1305
- * - Objects are serialized through {@link JSON.stringify}.
1306
- * - Functions are serialized as `undefined`.
1307
- * - Values of type boolean, number, bigint or symbol are serialized using their `toString` function.
1308
- *
1309
- * @param value - The value to serialize to either a string or Uint8Array.
1310
- */
1311
1028
  function serializeIterableValue(value) {
1312
1029
  switch (typeof value) {
1313
1030
  case "string": return textEncoder.encode(value);
@@ -1315,57 +1032,31 @@ function serializeIterableValue(value) {
1315
1032
  case "number":
1316
1033
  case "bigint":
1317
1034
  case "symbol": return textEncoder.encode(value.toString());
1318
- case "object": {
1035
+ case "object":
1319
1036
  if (value instanceof Uint8Array) return value;
1320
1037
  return textEncoder.encode(JSON.stringify(value));
1321
- }
1322
1038
  }
1323
1039
  return new Uint8Array();
1324
1040
  }
1325
- function coerceIterable(iterable$1) {
1326
- if (typeof iterable$1 === "function") iterable$1 = iterable$1();
1327
- if (Symbol.iterator in iterable$1) return iterable$1[Symbol.iterator]();
1328
- if (Symbol.asyncIterator in iterable$1) return iterable$1[Symbol.asyncIterator]();
1329
- return iterable$1;
1330
- }
1331
-
1332
- //#endregion
1333
- //#region src/utils/response.ts
1334
- /**
1335
- * Respond with an empty payload.<br>
1336
- *
1337
- * @example
1338
- * app.get("/", () => noContent());
1339
- *
1340
- * @param status status code to be send. By default, it is `204 No Content`.
1341
- */
1041
+ function coerceIterable(iterable) {
1042
+ if (typeof iterable === "function") iterable = iterable();
1043
+ if (Symbol.iterator in iterable) return iterable[Symbol.iterator]();
1044
+ if (Symbol.asyncIterator in iterable) return iterable[Symbol.asyncIterator]();
1045
+ return iterable;
1046
+ }
1342
1047
  function noContent(status = 204) {
1343
1048
  return new HTTPResponse(null, {
1344
1049
  status,
1345
1050
  statusText: "No Content"
1346
1051
  });
1347
1052
  }
1348
- /**
1349
- * Send a redirect response to the client.
1350
- *
1351
- * It adds the `location` header to the response and sets the status code to 302 by default.
1352
- *
1353
- * In the body, it sends a simple HTML page with a meta refresh tag to redirect the client in case the headers are ignored.
1354
- *
1355
- * @example
1356
- * app.get("/", () => {
1357
- * return redirect("https://example.com");
1358
- * });
1359
- *
1360
- * @example
1361
- * app.get("/", () => {
1362
- * return redirect("https://example.com", 301); // Permanent redirect
1363
- * });
1364
- */
1365
1053
  function redirect(location, status = 302, statusText) {
1366
- const encodedLoc = location.replace(/"/g, "%22");
1367
- const body = `<html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}" /></head></html>`;
1368
- return new HTTPResponse(body, {
1054
+ return new HTTPResponse(`<html><head><meta http-equiv="refresh" content="0; url=${location.replace(/[&"<>]/g, (c) => ({
1055
+ "&": "&amp;",
1056
+ "\"": "&quot;",
1057
+ "<": "&lt;",
1058
+ ">": "&gt;"
1059
+ })[c])}" /></head></html>`, {
1369
1060
  status,
1370
1061
  statusText: statusText || (status === 301 ? "Moved Permanently" : "Found"),
1371
1062
  headers: {
@@ -1374,49 +1065,32 @@ function redirect(location, status = 302, statusText) {
1374
1065
  }
1375
1066
  });
1376
1067
  }
1377
- /**
1378
- * Write `HTTP/1.1 103 Early Hints` to the client.
1379
- */
1068
+ function redirectBack(event, opts = {}) {
1069
+ const referer = event.req.headers.get("referer");
1070
+ let location = opts.fallback ?? "/";
1071
+ if (referer && URL.canParse(referer)) {
1072
+ const refererURL = new URL(referer);
1073
+ if (refererURL.origin === event.url.origin) {
1074
+ let pathname = refererURL.pathname;
1075
+ if (pathname.startsWith("//")) pathname = "/" + pathname.replace(/^\/+/, "");
1076
+ location = pathname + (opts.allowQuery ? refererURL.search : "");
1077
+ }
1078
+ }
1079
+ return redirect(location, opts.status);
1080
+ }
1380
1081
  function writeEarlyHints(event, hints) {
1381
- if (!event.runtime?.node?.res?.writeEarlyHints) return;
1382
- return new Promise((resolve) => {
1082
+ if (event.runtime?.node?.res?.writeEarlyHints) return new Promise((resolve) => {
1383
1083
  event.runtime?.node?.res?.writeEarlyHints(hints, () => resolve());
1384
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);
1089
+ }
1385
1090
  }
1386
- /**
1387
- * Iterate a source of chunks and send back each chunk in order.
1388
- * Supports mixing async work together with emitting chunks.
1389
- *
1390
- * Each chunk must be a string or a buffer.
1391
- *
1392
- * For generator (yielding) functions, the returned value is treated the same as yielded values.
1393
- *
1394
- * @param iterable - Iterator that produces chunks of the response.
1395
- * @param serializer - Function that converts values from the iterable into stream-compatible values.
1396
- * @template Value - Test
1397
- *
1398
- * @example
1399
- * return iterable(async function* work() {
1400
- * // Open document body
1401
- * yield "<!DOCTYPE html>\n<html><body><h1>Executing...</h1><ol>\n";
1402
- * // Do work ...
1403
- * for (let i = 0; i < 1000) {
1404
- * await delay(1000);
1405
- * // Report progress
1406
- * yield `<li>Completed job #`;
1407
- * yield i;
1408
- * yield `</li>\n`;
1409
- * }
1410
- * // Close out the report
1411
- * return `</ol></body></html>`;
1412
- * })
1413
- * async function delay(ms) {
1414
- * return new Promise(resolve => setTimeout(resolve, ms));
1415
- * }
1416
- */
1417
- function iterable(iterable$1, options) {
1091
+ function iterable(iterable, options) {
1418
1092
  const serializer = options?.serializer ?? serializeIterableValue;
1419
- const iterator = coerceIterable(iterable$1);
1093
+ const iterator = coerceIterable(iterable);
1420
1094
  return new HTTPResponse(new ReadableStream({
1421
1095
  async pull(controller) {
1422
1096
  const { value, done } = await iterator.next();
@@ -1432,38 +1106,73 @@ function iterable(iterable$1, options) {
1432
1106
  }));
1433
1107
  }
1434
1108
  function html(first, ...values) {
1435
- const body = typeof first === "string" ? first : first.reduce((out, str, i) => out + str + (values[i] ?? ""), "");
1436
- return new HTTPResponse(body, { headers: { "content-type": "text/html; charset=utf-8" } });
1437
- }
1438
-
1439
- //#endregion
1440
- //#region src/utils/middleware.ts
1441
- /**
1442
- * Define a middleware that runs on each request.
1443
- */
1109
+ 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
+ }
1111
+ 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) {
1121
+ const text = await event.req.text();
1122
+ if (!text) return;
1123
+ if ((event.req.headers.get("content-type") || "").startsWith("application/x-www-form-urlencoded")) return parseURLEncodedBody(text);
1124
+ try {
1125
+ return JSON.parse(text);
1126
+ } catch {
1127
+ throw new HTTPError({
1128
+ status: 400,
1129
+ statusText: "Bad Request",
1130
+ message: "Invalid JSON body"
1131
+ });
1132
+ }
1133
+ }
1134
+ async function readValidatedBody(event, validate, options) {
1135
+ return validateData(await readBody(event), validate, options);
1136
+ }
1137
+ async function assertBodySize(event, limit) {
1138
+ if (!await isBodySizeWithin(event, limit)) throw new HTTPError({
1139
+ status: 413,
1140
+ statusText: "Request Entity Too Large",
1141
+ message: `Request body size exceeds the limit of ${limit} bytes`
1142
+ });
1143
+ }
1144
+ async function isBodySizeWithin(event, limit) {
1145
+ const req = event.req;
1146
+ if (req.body === null) return true;
1147
+ const contentLength = req.headers.get("content-length");
1148
+ if (contentLength) {
1149
+ if (req.headers.get("transfer-encoding")) throw new HTTPError({ status: 400 });
1150
+ if (+contentLength > limit) return false;
1151
+ }
1152
+ const reader = req.clone().body.getReader();
1153
+ let chunk = await reader.read();
1154
+ let size = 0;
1155
+ while (!chunk.done) {
1156
+ size += chunk.value.byteLength;
1157
+ if (size > limit) {
1158
+ reader.cancel();
1159
+ return false;
1160
+ }
1161
+ chunk = await reader.read();
1162
+ }
1163
+ return true;
1164
+ }
1444
1165
  function onRequest(hook) {
1445
1166
  return async function _onRequestMiddleware(event) {
1446
1167
  await hook(event);
1447
1168
  };
1448
1169
  }
1449
- /**
1450
- * Define a middleware that runs after Response is generated.
1451
- *
1452
- * You can return a new Response from the handler to replace the original response.
1453
- */
1454
1170
  function onResponse(hook) {
1455
1171
  return async function _onResponseMiddleware(event, next) {
1456
- const rawBody = await next();
1457
- const response = await toResponse(rawBody, event);
1458
- const hookResponse = await hook(response, event);
1459
- return hookResponse || response;
1172
+ const response = await toResponse(await next(), event);
1173
+ return await hook(response, event) || response;
1460
1174
  };
1461
1175
  }
1462
- /**
1463
- * Define a middleware that runs when an error occurs.
1464
- *
1465
- * You can return a new Response from the handler to gracefully handle the error.
1466
- */
1467
1176
  function onError(hook) {
1468
1177
  return async (event, next) => {
1469
1178
  try {
@@ -1481,9 +1190,12 @@ function onError(hook) {
1481
1190
  }
1482
1191
  };
1483
1192
  }
1484
-
1485
- //#endregion
1486
- //#region src/utils/internal/proxy.ts
1193
+ function bodyLimit(limit) {
1194
+ return async (event, next) => {
1195
+ await assertBodySize(event, limit);
1196
+ return next();
1197
+ };
1198
+ }
1487
1199
  const PayloadMethods = new Set([
1488
1200
  "PATCH",
1489
1201
  "POST",
@@ -1509,22 +1221,16 @@ function rewriteCookieProperty(header, map, property) {
1509
1221
  return newValue ? prefix + newValue : "";
1510
1222
  });
1511
1223
  }
1512
- function mergeHeaders(defaults$1, ...inputs) {
1224
+ function mergeHeaders(defaults, ...inputs) {
1513
1225
  const _inputs = inputs.filter(Boolean);
1514
- if (_inputs.length === 0) return defaults$1;
1515
- const merged = new Headers(defaults$1);
1226
+ if (_inputs.length === 0) return defaults;
1227
+ const merged = new Headers(defaults);
1516
1228
  for (const input of _inputs) {
1517
1229
  const entries = Array.isArray(input) ? input : typeof input.entries === "function" ? input.entries() : Object.entries(input);
1518
1230
  for (const [key, value] of entries) if (value !== void 0) merged.set(key, value);
1519
1231
  }
1520
1232
  return merged;
1521
1233
  }
1522
-
1523
- //#endregion
1524
- //#region src/utils/proxy.ts
1525
- /**
1526
- * Proxy the incoming request to a target URL.
1527
- */
1528
1234
  async function proxyRequest(event, target, opts = {}) {
1529
1235
  const requestBody = PayloadMethods.has(event.req.method) ? event.req.body : void 0;
1530
1236
  const method = opts.fetchOptions?.method || event.req.method;
@@ -1544,9 +1250,6 @@ async function proxyRequest(event, target, opts = {}) {
1544
1250
  }
1545
1251
  });
1546
1252
  }
1547
- /**
1548
- * Make a proxy request to a target URL and send the response back to the client.
1549
- */
1550
1253
  async function proxy(event, target, opts = {}) {
1551
1254
  const fetchOptions = {
1552
1255
  headers: opts.headers,
@@ -1564,13 +1267,12 @@ async function proxy(event, target, opts = {}) {
1564
1267
  const headers = new Headers();
1565
1268
  const cookies = [];
1566
1269
  for (const [key, value] of response.headers.entries()) {
1567
- if (key === "content-encoding") continue;
1568
- if (key === "content-length") continue;
1270
+ if (key === "content-encoding" || key === "content-length" || key === "transfer-encoding") continue;
1569
1271
  if (key === "set-cookie") {
1570
- cookies.push(...splitSetCookieString(value));
1272
+ cookies.push(value);
1571
1273
  continue;
1572
1274
  }
1573
- headers.set(key, value);
1275
+ headers.append(key, value);
1574
1276
  }
1575
1277
  if (cookies.length > 0) {
1576
1278
  const _cookies = cookies.map((cookie) => {
@@ -1587,9 +1289,6 @@ async function proxy(event, target, opts = {}) {
1587
1289
  headers
1588
1290
  });
1589
1291
  }
1590
- /**
1591
- * Get the request headers object without headers known to cause issues when proxying.
1592
- */
1593
1292
  function getProxyRequestHeaders(event, opts) {
1594
1293
  const headers = new EmptyObject();
1595
1294
  for (const [name, value] of event.req.headers.entries()) {
@@ -1605,9 +1304,6 @@ function getProxyRequestHeaders(event, opts) {
1605
1304
  }
1606
1305
  return headers;
1607
1306
  }
1608
- /**
1609
- * Make a fetch request with the event's context and headers.
1610
- */
1611
1307
  async function fetchWithEvent(event, url, init) {
1612
1308
  if (url[0] !== "/") return fetch(url, init);
1613
1309
  return event.app.fetch(createSubRequest(event, url, {
@@ -1623,123 +1319,281 @@ function createSubRequest(event, path, init) {
1623
1319
  req.ip = event.req.ip;
1624
1320
  return req;
1625
1321
  }
1626
-
1627
- //#endregion
1628
- //#region src/utils/internal/body.ts
1629
- function parseURLEncodedBody(body) {
1630
- const form = new URLSearchParams(body);
1631
- const parsedForm = new EmptyObject();
1632
- for (const [key, value] of form.entries()) if (hasProp(parsedForm, key)) {
1633
- if (!Array.isArray(parsedForm[key])) parsedForm[key] = [parsedForm[key]];
1634
- parsedForm[key].push(value);
1635
- } else parsedForm[key] = value;
1636
- return parsedForm;
1322
+ const COOKIE_MAX_AGE_LIMIT = 3456e4;
1323
+ function endIndex(str, min, len) {
1324
+ const index = str.indexOf(";", min);
1325
+ return index === -1 ? len : index;
1326
+ }
1327
+ function eqIndex(str, min, max) {
1328
+ const index = str.indexOf("=", min);
1329
+ return index < max ? index : -1;
1330
+ }
1331
+ function valueSlice(str, min, max) {
1332
+ if (min === max) return "";
1333
+ let start = min;
1334
+ let end = max;
1335
+ do {
1336
+ const code = str.charCodeAt(start);
1337
+ if (code !== 32 && code !== 9) break;
1338
+ } while (++start < end);
1339
+ while (end > start) {
1340
+ const code = str.charCodeAt(end - 1);
1341
+ if (code !== 32 && code !== 9) break;
1342
+ end--;
1343
+ }
1344
+ return str.slice(start, end);
1345
+ }
1346
+ const NullObject = /* @__PURE__ */ (() => {
1347
+ const C = function() {};
1348
+ C.prototype = Object.create(null);
1349
+ return C;
1350
+ })();
1351
+ function parse(str, options) {
1352
+ const obj = new NullObject();
1353
+ const len = str.length;
1354
+ if (len < 2) return obj;
1355
+ const dec = options?.decode || decode;
1356
+ const allowMultiple = options?.allowMultiple || false;
1357
+ let index = 0;
1358
+ do {
1359
+ const eqIdx = eqIndex(str, index, len);
1360
+ if (eqIdx === -1) break;
1361
+ const endIdx = endIndex(str, index, len);
1362
+ if (eqIdx > endIdx) {
1363
+ index = str.lastIndexOf(";", eqIdx - 1) + 1;
1364
+ continue;
1365
+ }
1366
+ const key = valueSlice(str, index, eqIdx);
1367
+ if (options?.filter && !options.filter(key)) {
1368
+ index = endIdx + 1;
1369
+ continue;
1370
+ }
1371
+ const val = dec(valueSlice(str, eqIdx + 1, endIdx));
1372
+ if (allowMultiple) {
1373
+ const existing = obj[key];
1374
+ if (existing === void 0) obj[key] = val;
1375
+ else if (Array.isArray(existing)) existing.push(val);
1376
+ else obj[key] = [existing, val];
1377
+ } else if (obj[key] === void 0) obj[key] = val;
1378
+ index = endIdx + 1;
1379
+ } while (index < len);
1380
+ return obj;
1381
+ }
1382
+ function decode(str) {
1383
+ if (!str.includes("%")) return str;
1384
+ try {
1385
+ return decodeURIComponent(str);
1386
+ } catch {
1387
+ return str;
1388
+ }
1389
+ }
1390
+ const cookieNameRegExp = /^[\u0021-\u003A\u003C\u003E-\u007E]+$/;
1391
+ const cookieValueRegExp = /^[\u0021-\u003A\u003C-\u007E]*$/;
1392
+ const domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
1393
+ const pathValueRegExp = /^[\u0020-\u003A\u003C-\u007E]*$/;
1394
+ const __toString = Object.prototype.toString;
1395
+ function serialize(_a0, _a1, _a2) {
1396
+ const isObj = typeof _a0 === "object" && _a0 !== null;
1397
+ const options = isObj ? _a1 : _a2;
1398
+ const stringify = options?.stringify || JSON.stringify;
1399
+ const cookie = isObj ? _a0 : {
1400
+ ..._a2,
1401
+ name: _a0,
1402
+ value: _a1 == void 0 ? "" : typeof _a1 === "string" ? _a1 : stringify(_a1)
1403
+ };
1404
+ const enc = options?.encode || encodeURIComponent;
1405
+ if (!cookieNameRegExp.test(cookie.name)) throw new TypeError(`argument name is invalid: ${cookie.name}`);
1406
+ const value = cookie.value ? enc(cookie.value) : "";
1407
+ if (!cookieValueRegExp.test(value)) throw new TypeError(`argument val is invalid: ${cookie.value}`);
1408
+ if (!cookie.secure) {
1409
+ if (cookie.partitioned) throw new TypeError(`Partitioned cookies must have the Secure attribute`);
1410
+ if (cookie.sameSite && String(cookie.sameSite).toLowerCase() === "none") throw new TypeError(`SameSite=None cookies must have the Secure attribute`);
1411
+ if (cookie.name.length > 9 && cookie.name.charCodeAt(0) === 95 && cookie.name.charCodeAt(1) === 95) {
1412
+ const nameLower = cookie.name.toLowerCase();
1413
+ if (nameLower.startsWith("__secure-") || nameLower.startsWith("__host-")) throw new TypeError(`${cookie.name} cookies must have the Secure attribute`);
1414
+ }
1415
+ }
1416
+ if (cookie.name.length > 7 && cookie.name.charCodeAt(0) === 95 && cookie.name.charCodeAt(1) === 95 && cookie.name.toLowerCase().startsWith("__host-")) {
1417
+ if (cookie.path !== "/") throw new TypeError(`__Host- cookies must have Path=/`);
1418
+ if (cookie.domain) throw new TypeError(`__Host- cookies must not have a Domain attribute`);
1419
+ }
1420
+ let str = cookie.name + "=" + value;
1421
+ if (cookie.maxAge !== void 0) {
1422
+ if (!Number.isInteger(cookie.maxAge)) throw new TypeError(`option maxAge is invalid: ${cookie.maxAge}`);
1423
+ str += "; Max-Age=" + Math.max(0, Math.min(cookie.maxAge, COOKIE_MAX_AGE_LIMIT));
1424
+ }
1425
+ if (cookie.domain) {
1426
+ if (!domainValueRegExp.test(cookie.domain)) throw new TypeError(`option domain is invalid: ${cookie.domain}`);
1427
+ str += "; Domain=" + cookie.domain;
1428
+ }
1429
+ if (cookie.path) {
1430
+ if (!pathValueRegExp.test(cookie.path)) throw new TypeError(`option path is invalid: ${cookie.path}`);
1431
+ str += "; Path=" + cookie.path;
1432
+ }
1433
+ if (cookie.expires) {
1434
+ if (!isDate(cookie.expires) || !Number.isFinite(cookie.expires.valueOf())) throw new TypeError(`option expires is invalid: ${cookie.expires}`);
1435
+ str += "; Expires=" + cookie.expires.toUTCString();
1436
+ }
1437
+ if (cookie.httpOnly) str += "; HttpOnly";
1438
+ if (cookie.secure) str += "; Secure";
1439
+ if (cookie.partitioned) str += "; Partitioned";
1440
+ if (cookie.priority) switch (typeof cookie.priority === "string" ? cookie.priority.toLowerCase() : void 0) {
1441
+ case "low":
1442
+ str += "; Priority=Low";
1443
+ break;
1444
+ case "medium":
1445
+ str += "; Priority=Medium";
1446
+ break;
1447
+ case "high":
1448
+ str += "; Priority=High";
1449
+ break;
1450
+ default: throw new TypeError(`option priority is invalid: ${cookie.priority}`);
1451
+ }
1452
+ if (cookie.sameSite) switch (typeof cookie.sameSite === "string" ? cookie.sameSite.toLowerCase() : cookie.sameSite) {
1453
+ case true:
1454
+ case "strict":
1455
+ str += "; SameSite=Strict";
1456
+ break;
1457
+ case "lax":
1458
+ str += "; SameSite=Lax";
1459
+ break;
1460
+ case "none":
1461
+ str += "; SameSite=None";
1462
+ break;
1463
+ default: throw new TypeError(`option sameSite is invalid: ${cookie.sameSite}`);
1464
+ }
1465
+ return str;
1466
+ }
1467
+ function isDate(val) {
1468
+ return __toString.call(val) === "[object Date]";
1469
+ }
1470
+ const maxAgeRegExp = /^-?\d+$/;
1471
+ const _nullProto = /* @__PURE__ */ Object.getPrototypeOf({});
1472
+ function parseSetCookie(str, options) {
1473
+ const len = str.length;
1474
+ let _endIdx = len;
1475
+ let eqIdx = -1;
1476
+ for (let i = 0; i < len; i++) {
1477
+ const c = str.charCodeAt(i);
1478
+ if (c === 59) {
1479
+ _endIdx = i;
1480
+ break;
1481
+ }
1482
+ if (c === 61 && eqIdx === -1) eqIdx = i;
1483
+ }
1484
+ if (eqIdx >= _endIdx) eqIdx = -1;
1485
+ const name = eqIdx === -1 ? "" : _trim(str, 0, eqIdx);
1486
+ if (name && name in _nullProto) return void 0;
1487
+ let value = eqIdx === -1 ? _trim(str, 0, _endIdx) : _trim(str, eqIdx + 1, _endIdx);
1488
+ if (!name && !value) return void 0;
1489
+ if (name.length + value.length > 4096) return void 0;
1490
+ if (options?.decode !== false) value = _decode(value, options?.decode);
1491
+ const setCookie = {
1492
+ name,
1493
+ value
1494
+ };
1495
+ let index = _endIdx + 1;
1496
+ while (index < len) {
1497
+ let endIdx = len;
1498
+ let attrEqIdx = -1;
1499
+ for (let i = index; i < len; i++) {
1500
+ const c = str.charCodeAt(i);
1501
+ if (c === 59) {
1502
+ endIdx = i;
1503
+ break;
1504
+ }
1505
+ if (c === 61 && attrEqIdx === -1) attrEqIdx = i;
1506
+ }
1507
+ if (attrEqIdx >= endIdx) attrEqIdx = -1;
1508
+ const attr = attrEqIdx === -1 ? _trim(str, index, endIdx) : _trim(str, index, attrEqIdx);
1509
+ const val = attrEqIdx === -1 ? void 0 : _trim(str, attrEqIdx + 1, endIdx);
1510
+ if (val === void 0 || val.length <= 1024) switch (attr.toLowerCase()) {
1511
+ case "httponly":
1512
+ setCookie.httpOnly = true;
1513
+ break;
1514
+ case "secure":
1515
+ setCookie.secure = true;
1516
+ break;
1517
+ case "partitioned":
1518
+ setCookie.partitioned = true;
1519
+ break;
1520
+ case "domain":
1521
+ if (val) setCookie.domain = (val.charCodeAt(0) === 46 ? val.slice(1) : val).toLowerCase();
1522
+ break;
1523
+ case "path":
1524
+ setCookie.path = val;
1525
+ break;
1526
+ case "max-age":
1527
+ if (val && maxAgeRegExp.test(val)) setCookie.maxAge = Math.min(Number(val), COOKIE_MAX_AGE_LIMIT);
1528
+ break;
1529
+ case "expires": {
1530
+ if (!val) break;
1531
+ const date = new Date(val);
1532
+ if (Number.isFinite(date.valueOf())) {
1533
+ const maxDate = new Date(Date.now() + COOKIE_MAX_AGE_LIMIT * 1e3);
1534
+ setCookie.expires = date > maxDate ? maxDate : date;
1535
+ }
1536
+ break;
1537
+ }
1538
+ case "priority": {
1539
+ if (!val) break;
1540
+ const priority = val.toLowerCase();
1541
+ if (priority === "low" || priority === "medium" || priority === "high") setCookie.priority = priority;
1542
+ break;
1543
+ }
1544
+ case "samesite": {
1545
+ if (!val) break;
1546
+ const sameSite = val.toLowerCase();
1547
+ if (sameSite === "lax" || sameSite === "strict" || sameSite === "none") setCookie.sameSite = sameSite;
1548
+ else setCookie.sameSite = "lax";
1549
+ break;
1550
+ }
1551
+ default: {
1552
+ const attrLower = attr.toLowerCase();
1553
+ if (attrLower && !(attrLower in _nullProto)) setCookie[attrLower] = val;
1554
+ }
1555
+ }
1556
+ index = endIdx + 1;
1557
+ }
1558
+ return setCookie;
1637
1559
  }
1638
-
1639
- //#endregion
1640
- //#region src/utils/body.ts
1641
- /**
1642
- * Reads request body and tries to parse using JSON.parse or URLSearchParams.
1643
- *
1644
- * @example
1645
- * app.get("/", async (event) => {
1646
- * const body = await readBody(event);
1647
- * });
1648
- *
1649
- * @param event H3 event passed by h3 handler
1650
- * @param encoding The character encoding to use, defaults to 'utf-8'.
1651
- *
1652
- * @return {*} The `Object`, `Array`, `String`, `Number`, `Boolean`, or `null` value corresponding to the request JSON body
1653
- */
1654
- async function readBody(event) {
1655
- const text = await event.req.text();
1656
- if (!text) return void 0;
1657
- const contentType = event.req.headers.get("content-type") || "";
1658
- if (contentType.startsWith("application/x-www-form-urlencoded")) return parseURLEncodedBody(text);
1560
+ function _trim(str, start, end) {
1561
+ if (start === end) return "";
1562
+ let s = start;
1563
+ let e = end;
1564
+ while (s < e && (str.charCodeAt(s) === 32 || str.charCodeAt(s) === 9)) s++;
1565
+ while (e > s && (str.charCodeAt(e - 1) === 32 || str.charCodeAt(e - 1) === 9)) e--;
1566
+ return str.slice(s, e);
1567
+ }
1568
+ function _decode(value, decode) {
1569
+ if (!decode && !value.includes("%")) return value;
1659
1570
  try {
1660
- return JSON.parse(text);
1571
+ return (decode || decodeURIComponent)(value);
1661
1572
  } catch {
1662
- throw new HTTPError({
1663
- status: 400,
1664
- statusText: "Bad Request",
1665
- message: "Invalid JSON body"
1666
- });
1573
+ return value;
1667
1574
  }
1668
1575
  }
1669
- /**
1670
- * Tries to read the request body via `readBody`, then uses the provided validation schema or function and either throws a validation error or returns the result.
1671
- *
1672
- * You can use a simple function to validate the body or use a Standard-Schema compatible library like `zod` to define a schema.
1673
- *
1674
- * @example
1675
- * app.get("/", async (event) => {
1676
- * const body = await readValidatedBody(event, (body) => {
1677
- * return typeof body === "object" && body !== null;
1678
- * });
1679
- * });
1680
- * @example
1681
- * import { z } from "zod";
1682
- *
1683
- * app.get("/", async (event) => {
1684
- * const objectSchema = z.object({
1685
- * name: z.string().min(3).max(20),
1686
- * age: z.number({ coerce: true }).positive().int(),
1687
- * });
1688
- * const body = await readValidatedBody(event, objectSchema);
1689
- * });
1690
- *
1691
- * @param event The HTTPEvent passed by the handler.
1692
- * @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.
1693
- * @throws If the validation function returns `false` or throws, a validation error will be thrown.
1694
- * @return {*} The `Object`, `Array`, `String`, `Number`, `Boolean`, or `null` value corresponding to the request JSON body.
1695
- * @see {readBody}
1696
- */
1697
- async function readValidatedBody(event, validate) {
1698
- const _body = await readBody(event);
1699
- return validateData(_body, validate);
1700
- }
1701
-
1702
- //#endregion
1703
- //#region src/utils/cookie.ts
1704
1576
  const CHUNKED_COOKIE = "__chunked__";
1705
1577
  const CHUNKS_MAX_LENGTH = 4e3;
1706
- /**
1707
- * Parse the request to get HTTP Cookie header string and returning an object of all cookie name-value pairs.
1708
- * @param event {HTTPEvent} H3 event or req passed by h3 handler
1709
- * @returns Object of cookie name-value pairs
1710
- * ```ts
1711
- * const cookies = parseCookies(event)
1712
- * ```
1713
- */
1714
1578
  function parseCookies(event) {
1715
1579
  return parse(event.req.headers.get("cookie") || "");
1716
1580
  }
1717
- /**
1718
- * Get a cookie value by name.
1719
- * @param event {HTTPEvent} H3 event or req passed by h3 handler
1720
- * @param name Name of the cookie to get
1721
- * @returns {*} Value of the cookie (String or undefined)
1722
- * ```ts
1723
- * const authorization = getCookie(request, 'Authorization')
1724
- * ```
1725
- */
1581
+ function getValidatedCookies(event, validate, options) {
1582
+ return validateData(parseCookies(event), validate, options);
1583
+ }
1726
1584
  function getCookie(event, name) {
1727
1585
  return parseCookies(event)[name];
1728
1586
  }
1729
- /**
1730
- * Set a cookie value by name.
1731
- * @param event {H3Event} H3 event or res passed by h3 handler
1732
- * @param name Name of the cookie to set
1733
- * @param value Value of the cookie to set
1734
- * @param options {CookieSerializeOptions} Options for serializing the cookie
1735
- * ```ts
1736
- * setCookie(res, 'Authorization', '1234567')
1737
- * ```
1738
- */
1739
1587
  function setCookie(event, name, value, options) {
1740
- const newCookie = serialize(name, value, {
1588
+ const { encode, stringify, ...attrs } = options ?? {};
1589
+ const newCookie = serialize({
1590
+ name,
1591
+ value,
1741
1592
  path: "/",
1742
- ...options
1593
+ ...attrs
1594
+ }, {
1595
+ encode,
1596
+ stringify
1743
1597
  });
1744
1598
  const currentCookies = event.res.headers.getSetCookie();
1745
1599
  if (currentCookies.length === 0) {
@@ -1749,59 +1603,32 @@ function setCookie(event, name, value, options) {
1749
1603
  const newCookieKey = _getDistinctCookieKey(name, options || {});
1750
1604
  event.res.headers.delete("set-cookie");
1751
1605
  for (const cookie of currentCookies) {
1752
- const _key = _getDistinctCookieKey(cookie.split("=")?.[0], parseSetCookie(cookie));
1753
- if (_key === newCookieKey) continue;
1606
+ const parsed = parseSetCookie(cookie);
1607
+ if (!parsed) continue;
1608
+ if (_getDistinctCookieKey(cookie.split("=")?.[0], parsed) === newCookieKey) continue;
1754
1609
  event.res.headers.append("set-cookie", cookie);
1755
1610
  }
1756
1611
  event.res.headers.append("set-cookie", newCookie);
1757
1612
  }
1758
- /**
1759
- * Remove a cookie by name.
1760
- * @param event {H3Event} H3 event or res passed by h3 handler
1761
- * @param name Name of the cookie to delete
1762
- * @param serializeOptions {CookieSerializeOptions} Cookie options
1763
- * ```ts
1764
- * deleteCookie(res, 'SessionId')
1765
- * ```
1766
- */
1767
1613
  function deleteCookie(event, name, serializeOptions) {
1768
1614
  setCookie(event, name, "", {
1769
1615
  ...serializeOptions,
1770
1616
  maxAge: 0
1771
1617
  });
1772
1618
  }
1773
- /**
1774
- * Get a chunked cookie value by name. Will join chunks together.
1775
- * @param event {HTTPEvent} { req: Request }
1776
- * @param name Name of the cookie to get
1777
- * @returns {*} Value of the cookie (String or undefined)
1778
- * ```ts
1779
- * const authorization = getCookie(request, 'Session')
1780
- * ```
1781
- */
1782
1619
  function getChunkedCookie(event, name) {
1783
1620
  const mainCookie = getCookie(event, name);
1784
1621
  if (!mainCookie || !mainCookie.startsWith(CHUNKED_COOKIE)) return mainCookie;
1785
1622
  const chunksCount = getChunkedCookieCount(mainCookie);
1786
- if (chunksCount === 0) return void 0;
1623
+ if (chunksCount === 0) return;
1787
1624
  const chunks = [];
1788
1625
  for (let i = 1; i <= chunksCount; i++) {
1789
1626
  const chunk = getCookie(event, chunkCookieName(name, i));
1790
- if (!chunk) return void 0;
1627
+ if (!chunk) return;
1791
1628
  chunks.push(chunk);
1792
1629
  }
1793
1630
  return chunks.join("");
1794
1631
  }
1795
- /**
1796
- * Set a cookie value by name. Chunked cookies will be created as needed.
1797
- * @param event {H3Event} H3 event or res passed by h3 handler
1798
- * @param name Name of the cookie to set
1799
- * @param value Value of the cookie to set
1800
- * @param options {CookieSerializeOptions} Options for serializing the cookie
1801
- * ```ts
1802
- * setCookie(res, 'Session', '<session data>')
1803
- * ```
1804
- */
1805
1632
  function setChunkedCookie(event, name, value, options) {
1806
1633
  const chunkMaxLength = options?.chunkMaxLength || CHUNKS_MAX_LENGTH;
1807
1634
  const chunkCount = Math.ceil(value.length / chunkMaxLength);
@@ -1814,8 +1641,7 @@ function setChunkedCookie(event, name, value, options) {
1814
1641
  setCookie(event, name, value, options);
1815
1642
  return;
1816
1643
  }
1817
- const mainCookieValue = `${CHUNKED_COOKIE}${chunkCount}`;
1818
- setCookie(event, name, mainCookieValue, options);
1644
+ setCookie(event, name, `${CHUNKED_COOKIE}${chunkCount}`, options);
1819
1645
  for (let i = 1; i <= chunkCount; i++) {
1820
1646
  const start = (i - 1) * chunkMaxLength;
1821
1647
  const end = start + chunkMaxLength;
@@ -1823,26 +1649,12 @@ function setChunkedCookie(event, name, value, options) {
1823
1649
  setCookie(event, chunkCookieName(name, i), chunkValue, options);
1824
1650
  }
1825
1651
  }
1826
- /**
1827
- * Remove a set of chunked cookies by name.
1828
- * @param event {H3Event} H3 event or res passed by h3 handler
1829
- * @param name Name of the cookie to delete
1830
- * @param serializeOptions {CookieSerializeOptions} Cookie options
1831
- * ```ts
1832
- * deleteCookie(res, 'Session')
1833
- * ```
1834
- */
1835
1652
  function deleteChunkedCookie(event, name, serializeOptions) {
1836
1653
  const mainCookie = getCookie(event, name);
1837
1654
  deleteCookie(event, name, serializeOptions);
1838
1655
  const chunksCount = getChunkedCookieCount(mainCookie);
1839
1656
  if (chunksCount >= 0) for (let i = 0; i < chunksCount; i++) deleteCookie(event, chunkCookieName(name, i + 1), serializeOptions);
1840
1657
  }
1841
- /**
1842
- * Cookies are unique by "cookie-name, domain-value, and path-value".
1843
- *
1844
- * @see https://httpwg.org/specs/rfc6265.html#rfc.section.4.1.2
1845
- */
1846
1658
  function _getDistinctCookieKey(name, options) {
1847
1659
  return [
1848
1660
  name,
@@ -1850,19 +1662,17 @@ function _getDistinctCookieKey(name, options) {
1850
1662
  options.path || "/"
1851
1663
  ].join(";");
1852
1664
  }
1665
+ const MAX_CHUNKED_COOKIE_COUNT = 100;
1853
1666
  function getChunkedCookieCount(cookie) {
1854
- if (!cookie?.startsWith(CHUNKED_COOKIE)) return Number.NaN;
1855
- return Number.parseInt(cookie.slice(CHUNKED_COOKIE.length));
1667
+ if (!cookie?.startsWith(CHUNKED_COOKIE)) return NaN;
1668
+ const count = Number.parseInt(cookie.slice(11));
1669
+ if (Number.isNaN(count) || count < 0 || count > MAX_CHUNKED_COOKIE_COUNT) return NaN;
1670
+ return count;
1856
1671
  }
1857
1672
  function chunkCookieName(name, chunkNumber) {
1858
1673
  return `${name}.${chunkNumber}`;
1859
1674
  }
1860
-
1861
- //#endregion
1862
- //#region src/utils/internal/event-stream.ts
1863
- /**
1864
- * A helper class for [server sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format)
1865
- */
1675
+ const _noop = () => {};
1866
1676
  var EventStream = class {
1867
1677
  _event;
1868
1678
  _transformStream = new TransformStream();
@@ -1876,7 +1686,7 @@ var EventStream = class {
1876
1686
  constructor(event, opts = {}) {
1877
1687
  this._event = event;
1878
1688
  this._writer = this._transformStream.writable.getWriter();
1879
- this._writer.closed.then(() => {
1689
+ this._writer.closed.catch(_noop).finally(() => {
1880
1690
  this._writerIsClosed = true;
1881
1691
  });
1882
1692
  if (opts.autoclose !== false) this._event.runtime?.node?.res?.once("close", () => this.close());
@@ -1909,7 +1719,9 @@ var EventStream = class {
1909
1719
  this._unsentData += formatEventStreamComment(comment);
1910
1720
  return;
1911
1721
  }
1912
- await this._writer.write(this._encoder.encode(formatEventStreamComment(comment))).catch();
1722
+ await this._writer.write(this._encoder.encode(formatEventStreamComment(comment))).catch(() => {
1723
+ this._writerIsClosed = true;
1724
+ });
1913
1725
  }
1914
1726
  async _sendEvent(message) {
1915
1727
  if (this._writerIsClosed) return;
@@ -1921,7 +1733,9 @@ var EventStream = class {
1921
1733
  this._unsentData += formatEventStreamMessage(message);
1922
1734
  return;
1923
1735
  }
1924
- await this._writer.write(this._encoder.encode(formatEventStreamMessage(message))).catch();
1736
+ await this._writer.write(this._encoder.encode(formatEventStreamMessage(message))).catch(() => {
1737
+ this._writerIsClosed = true;
1738
+ });
1925
1739
  }
1926
1740
  async _sendEvents(messages) {
1927
1741
  if (this._writerIsClosed) return;
@@ -1934,7 +1748,9 @@ var EventStream = class {
1934
1748
  this._unsentData += payload;
1935
1749
  return;
1936
1750
  }
1937
- await this._writer.write(this._encoder.encode(payload)).catch();
1751
+ await this._writer.write(this._encoder.encode(payload)).catch(() => {
1752
+ this._writerIsClosed = true;
1753
+ });
1938
1754
  }
1939
1755
  pause() {
1940
1756
  this._paused = true;
@@ -1949,13 +1765,12 @@ var EventStream = class {
1949
1765
  async flush() {
1950
1766
  if (this._writerIsClosed) return;
1951
1767
  if (this._unsentData?.length) {
1952
- await this._writer.write(this._encoder.encode(this._unsentData));
1768
+ await this._writer.write(this._encoder.encode(this._unsentData)).catch(() => {
1769
+ this._writerIsClosed = true;
1770
+ });
1953
1771
  this._unsentData = void 0;
1954
1772
  }
1955
1773
  }
1956
- /**
1957
- * Close the stream and the connection if the stream is being sent to the client
1958
- */
1959
1774
  async close() {
1960
1775
  if (this._disposed) return;
1961
1776
  if (!this._writerIsClosed) try {
@@ -1963,12 +1778,8 @@ var EventStream = class {
1963
1778
  } catch {}
1964
1779
  this._disposed = true;
1965
1780
  }
1966
- /**
1967
- * Triggers callback when the writable stream is closed.
1968
- * It is also triggered after calling the `close()` method.
1969
- */
1970
1781
  onClosed(cb) {
1971
- this._writer.closed.then(cb);
1782
+ this._writer.closed.then(cb).catch(_noop);
1972
1783
  }
1973
1784
  async send() {
1974
1785
  setEventStreamHeaders(this._event);
@@ -1978,16 +1789,21 @@ var EventStream = class {
1978
1789
  }
1979
1790
  };
1980
1791
  function formatEventStreamComment(comment) {
1981
- return `: ${comment}\n\n`;
1792
+ return comment.split(/\r\n|\r|\n/).map((l) => `: ${l}\n`).join("") + "\n";
1982
1793
  }
1983
1794
  function formatEventStreamMessage(message) {
1984
1795
  let result = "";
1985
- if (message.id) result += `id: ${message.id}\n`;
1986
- if (message.event) result += `event: ${message.event}\n`;
1796
+ if (message.id) result += `id: ${_sanitizeSingleLine(message.id)}\n`;
1797
+ if (message.event) result += `event: ${_sanitizeSingleLine(message.event)}\n`;
1987
1798
  if (typeof message.retry === "number" && Number.isInteger(message.retry)) result += `retry: ${message.retry}\n`;
1988
- result += `data: ${message.data}\n\n`;
1799
+ const data = typeof message.data === "string" ? message.data : "";
1800
+ for (const line of data.split(/\r\n|\r|\n/)) result += `data: ${line}\n`;
1801
+ result += "\n";
1989
1802
  return result;
1990
1803
  }
1804
+ function _sanitizeSingleLine(value) {
1805
+ return value.replace(/[\n\r]/g, "");
1806
+ }
1991
1807
  function formatEventStreamMessages(messages) {
1992
1808
  let result = "";
1993
1809
  for (const msg of messages) result += formatEventStreamMessage(msg);
@@ -1999,63 +1815,50 @@ function setEventStreamHeaders(event) {
1999
1815
  event.res.headers.set("x-accel-buffering", "no");
2000
1816
  if (event.req.headers.get("connection") === "keep-alive") event.res.headers.set("connection", "keep-alive");
2001
1817
  }
2002
-
2003
- //#endregion
2004
- //#region src/utils/event-stream.ts
2005
- /**
2006
- * Initialize an EventStream instance for creating [server sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
2007
- *
2008
- * @experimental This function is experimental and might be unstable in some environments.
2009
- *
2010
- * @example
2011
- *
2012
- * ```ts
2013
- * import { createEventStream, sendEventStream } from "h3";
2014
- *
2015
- * app.get("/sse", (event) => {
2016
- * const eventStream = createEventStream(event);
2017
- *
2018
- * // Send a message every second
2019
- * const interval = setInterval(async () => {
2020
- * await eventStream.push("Hello world");
2021
- * }, 1000);
2022
- *
2023
- * // cleanup the interval and close the stream when the connection is terminated
2024
- * eventStream.onClosed(async () => {
2025
- * console.log("closing SSE...");
2026
- * clearInterval(interval);
2027
- * await eventStream.close();
2028
- * });
2029
- *
2030
- * return eventStream.send();
2031
- * });
2032
- * ```
2033
- */
2034
1818
  function createEventStream(event, opts) {
2035
1819
  return new EventStream(event, opts);
2036
1820
  }
2037
-
2038
- //#endregion
2039
- //#region src/utils/cache.ts
2040
- /**
2041
- * Check request caching headers (`If-Modified-Since`) and add caching headers (Last-Modified, Cache-Control)
2042
- * Note: `public` cache control will be added by default
2043
- * @returns `true` when cache headers are matching. When `true` is returned, no response should be sent anymore
2044
- */
1821
+ function setServerTiming(event, name, opts) {
1822
+ if (!_isValidToken(name)) throw new TypeError(`Invalid Server-Timing metric name: ${name}`);
1823
+ if (opts?.dur !== void 0 && (!Number.isFinite(opts.dur) || opts.dur < 0)) throw new TypeError(`Invalid Server-Timing duration: ${opts.dur}`);
1824
+ const value = name + (opts?.desc ? `;desc="${_escapeDesc(opts.desc)}"` : "") + (opts?.dur !== void 0 ? `;dur=${opts.dur}` : "");
1825
+ event.res.headers.append("server-timing", value);
1826
+ const ctx = event.context;
1827
+ if (!Array.isArray(ctx.timing)) ctx.timing = [];
1828
+ ctx.timing.push({
1829
+ name,
1830
+ ...opts
1831
+ });
1832
+ }
1833
+ async function withServerTiming(event, name, fn) {
1834
+ const start = performance.now();
1835
+ try {
1836
+ return await fn();
1837
+ } finally {
1838
+ setServerTiming(event, name, { dur: performance.now() - start });
1839
+ }
1840
+ }
1841
+ const _tokenRE = /^[\w!#$%&'*+.^`|~-]+$/;
1842
+ function _isValidToken(value) {
1843
+ return _tokenRE.test(value);
1844
+ }
1845
+ function _escapeDesc(value) {
1846
+ return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
1847
+ }
2045
1848
  function handleCacheHeaders(event, opts) {
2046
1849
  const cacheControls = ["public", ...opts.cacheControls || []];
2047
1850
  let cacheMatched = false;
2048
1851
  if (opts.maxAge !== void 0) cacheControls.push(`max-age=${+opts.maxAge}`, `s-maxage=${+opts.maxAge}`);
2049
1852
  if (opts.modifiedTime) {
2050
1853
  const modifiedTime = new Date(opts.modifiedTime);
1854
+ modifiedTime.setMilliseconds(0);
2051
1855
  const ifModifiedSince = event.req.headers.get("if-modified-since");
2052
1856
  event.res.headers.set("last-modified", modifiedTime.toUTCString());
2053
1857
  if (ifModifiedSince && new Date(ifModifiedSince) >= modifiedTime) cacheMatched = true;
2054
1858
  }
2055
1859
  if (opts.etag) {
2056
1860
  event.res.headers.set("etag", opts.etag);
2057
- const ifNonMatch = event.req.headers.get("if-none-match");
2058
- if (ifNonMatch === opts.etag) cacheMatched = true;
1861
+ if (event.req.headers.get("if-none-match") === opts.etag) cacheMatched = true;
2059
1862
  }
2060
1863
  event.res.headers.set("cache-control", cacheControls.join(", "));
2061
1864
  if (cacheMatched) {
@@ -2064,27 +1867,6 @@ function handleCacheHeaders(event, opts) {
2064
1867
  }
2065
1868
  return false;
2066
1869
  }
2067
-
2068
- //#endregion
2069
- //#region src/utils/internal/path.ts
2070
- function withLeadingSlash(path) {
2071
- if (!path || path === "/") return "/";
2072
- return path[0] === "/" ? path : `/${path}`;
2073
- }
2074
- function withoutTrailingSlash(path) {
2075
- if (!path || path === "/") return "/";
2076
- return path[path.length - 1] === "/" ? path.slice(0, -1) : path;
2077
- }
2078
- function withoutBase(input = "", base = "") {
2079
- if (!base || base === "/") return input;
2080
- const _base = withoutTrailingSlash(base);
2081
- if (!input.startsWith(_base)) return input;
2082
- const trimmed = input.slice(_base.length);
2083
- return trimmed[0] === "/" ? trimmed : "/" + trimmed;
2084
- }
2085
-
2086
- //#endregion
2087
- //#region src/utils/internal/mime.ts
2088
1870
  const COMMON_MIME_TYPES = {
2089
1871
  ".html": "text/html",
2090
1872
  ".htm": "text/html",
@@ -2116,12 +1898,6 @@ function getExtension(path) {
2116
1898
  function getType(ext) {
2117
1899
  return ext ? COMMON_MIME_TYPES[ext] : void 0;
2118
1900
  }
2119
-
2120
- //#endregion
2121
- //#region src/utils/static.ts
2122
- /**
2123
- * Dynamically serve static assets based on the request path.
2124
- */
2125
1901
  async function serveStatic(event, options) {
2126
1902
  if (options.headers) {
2127
1903
  const entries = Array.isArray(options.headers) ? options.headers : typeof options.headers.entries === "function" ? options.headers.entries() : Object.entries(options.headers);
@@ -2132,7 +1908,7 @@ async function serveStatic(event, options) {
2132
1908
  event.res.headers.set("allow", "GET, HEAD");
2133
1909
  throw new HTTPError({ status: 405 });
2134
1910
  }
2135
- const originalId = decodeURI(withLeadingSlash(withoutTrailingSlash(event.url.pathname)));
1911
+ const originalId = resolveDotSegments(decodeURI(withLeadingSlash(withoutTrailingSlash(event.url.pathname))));
2136
1912
  const acceptEncodings = parseAcceptEncoding(event.req.headers.get("accept-encoding") || "", options.encodings);
2137
1913
  if (acceptEncodings.length > 1) event.res.headers.set("vary", "accept-encoding");
2138
1914
  let id = originalId;
@@ -2160,8 +1936,7 @@ async function serveStatic(event, options) {
2160
1936
  if (!event.res.headers.get("last-modified")) event.res.headers.set("last-modified", mtimeDate.toUTCString());
2161
1937
  }
2162
1938
  if (meta.etag && !event.res.headers.has("etag")) event.res.headers.set("etag", meta.etag);
2163
- const ifNotMatch = meta.etag && event.req.headers.get("if-none-match") === meta.etag;
2164
- if (ifNotMatch) return new HTTPResponse(null, {
1939
+ if (meta.etag && event.req.headers.get("if-none-match") === meta.etag) return new HTTPResponse(null, {
2165
1940
  status: 304,
2166
1941
  statusText: "Not Modified"
2167
1942
  });
@@ -2174,8 +1949,7 @@ async function serveStatic(event, options) {
2174
1949
  if (meta.encoding && !event.res.headers.get("content-encoding")) event.res.headers.set("content-encoding", meta.encoding);
2175
1950
  if (meta.size !== void 0 && meta.size > 0 && !event.req.headers.get("content-length")) event.res.headers.set("content-length", meta.size + "");
2176
1951
  if (event.req.method === "HEAD") return new HTTPResponse(null, { status: 200 });
2177
- const contents = await options.getContents(id);
2178
- return new HTTPResponse(contents || null, { status: 200 });
1952
+ return new HTTPResponse(await options.getContents(id) || null, { status: 200 });
2179
1953
  }
2180
1954
  function parseAcceptEncoding(header, encodingMap) {
2181
1955
  if (!encodingMap || !header) return [];
@@ -2186,21 +1960,6 @@ function idSearchPaths(id, encodings, indexNames) {
2186
1960
  for (const suffix of ["", ...indexNames]) for (const encoding of [...encodings, ""]) ids.push(`${id}${suffix}${encoding}`);
2187
1961
  return ids;
2188
1962
  }
2189
-
2190
- //#endregion
2191
- //#region src/utils/base.ts
2192
- /**
2193
- * Returns a new event handler that removes the base url of the event before calling the original handler.
2194
- *
2195
- * @example
2196
- * const api = new H3()
2197
- * .get("/", () => "Hello API!");
2198
- * const app = new H3();
2199
- * .use("/api/**", withBase("/api", api.handler));
2200
- *
2201
- * @param base The base path to prefix.
2202
- * @param handler The event handler to use with the adapted path.
2203
- */
2204
1963
  function withBase(base, input) {
2205
1964
  base = withoutTrailingSlash(base);
2206
1965
  const handler = toEventHandler(input);
@@ -2215,10 +1974,6 @@ function withBase(base, input) {
2215
1974
  }
2216
1975
  };
2217
1976
  }
2218
-
2219
- //#endregion
2220
- //#region src/utils/internal/iron-crypto.ts
2221
- /** The default encryption and integrity settings. */
2222
1977
  const defaults = /* @__PURE__ */ Object.freeze({
2223
1978
  ttl: 0,
2224
1979
  timestampSkewSec: 60,
@@ -2236,7 +1991,6 @@ const defaults = /* @__PURE__ */ Object.freeze({
2236
1991
  minPasswordlength: 32
2237
1992
  })
2238
1993
  });
2239
- /** Configuration of each supported algorithm. */
2240
1994
  const algorithms = /* @__PURE__ */ Object.freeze({
2241
1995
  "aes-128-ctr": /* @__PURE__ */ Object.freeze({
2242
1996
  keyBits: 128,
@@ -2254,9 +2008,7 @@ const algorithms = /* @__PURE__ */ Object.freeze({
2254
2008
  name: "SHA-256"
2255
2009
  })
2256
2010
  });
2257
- /** MAC normalization prefix. */
2258
2011
  const macPrefix = "Fe26.2";
2259
- /** Serializes, encrypts, and signs objects into an iron protocol string. */
2260
2012
  async function seal(object, password, opts) {
2261
2013
  const now = Date.now() + (opts.localtimeOffsetMsec || 0);
2262
2014
  if (!password) throw new Error("Empty password");
@@ -2268,10 +2020,8 @@ async function seal(object, password, opts) {
2268
2020
  const expiration = opts.ttl ? now + opts.ttl : "";
2269
2021
  const macBaseString = `${macPrefix}*${id}*${key.salt}*${iv}*${encryptedB64}*${expiration}`;
2270
2022
  const mac = await hmacWithPassword(integrity, opts.integrity, macBaseString);
2271
- const sealed = `${macBaseString}*${mac.salt}*${mac.digest}`;
2272
- return sealed;
2023
+ return `${macBaseString}*${mac.salt}*${mac.digest}`;
2273
2024
  }
2274
- /** Verifies, decrypts, and reconstruct an iron protocol string into an object. */
2275
2025
  async function unseal(sealed, password, opts) {
2276
2026
  const now = Date.now() + (opts.localtimeOffsetMsec || 0);
2277
2027
  if (!password) throw new Error("Empty password");
@@ -2279,11 +2029,10 @@ async function unseal(sealed, password, opts) {
2279
2029
  if (parts.length !== 8) throw new Error("Incorrect number of sealed components");
2280
2030
  const [prefix, passwordId, encryptionSalt, encryptionIv, encryptedB64, expiration, hmacSalt, hmac] = parts;
2281
2031
  const macBaseString = `${prefix}*${passwordId}*${encryptionSalt}*${encryptionIv}*${encryptedB64}*${expiration}`;
2282
- if (macPrefix !== prefix) throw new Error("Wrong mac prefix");
2032
+ if ("Fe26.2" !== prefix) throw new Error("Wrong mac prefix");
2283
2033
  if (expiration) {
2284
2034
  if (!/^\d+$/.test(expiration)) throw new Error("Invalid expiration");
2285
- const exp = Number.parseInt(expiration, 10);
2286
- if (exp <= now - opts.timestampSkewSec * 1e3) throw new Error("Expired seal");
2035
+ if (Number.parseInt(expiration, 10) <= now - opts.timestampSkewSec * 1e3) throw new Error("Expired seal");
2287
2036
  }
2288
2037
  let pass = "";
2289
2038
  const _passwordId = passwordId || "default";
@@ -2291,11 +2040,10 @@ async function unseal(sealed, password, opts) {
2291
2040
  else if (_passwordId in password) pass = password[_passwordId];
2292
2041
  else throw new Error(`Cannot find password: ${_passwordId}`);
2293
2042
  pass = normalizePassword(pass);
2294
- const mac = await hmacWithPassword(pass.integrity, {
2043
+ if (!fixedTimeComparison((await hmacWithPassword(pass.integrity, {
2295
2044
  ...opts.integrity,
2296
2045
  salt: hmacSalt
2297
- }, macBaseString);
2298
- if (!fixedTimeComparison(mac.digest, hmac)) throw new Error("Bad hmac value");
2046
+ }, macBaseString)).digest, hmac)) throw new Error("Bad hmac value");
2299
2047
  const encrypted = base64Decode(encryptedB64);
2300
2048
  const decryptOptions = {
2301
2049
  ...opts.encryption,
@@ -2305,7 +2053,6 @@ async function unseal(sealed, password, opts) {
2305
2053
  const decrypted = await decrypt(pass.encryption, decryptOptions, encrypted);
2306
2054
  return decrypted ? JSON.parse(decrypted) : null;
2307
2055
  }
2308
- /** Calculates a HMAC digest. */
2309
2056
  async function hmacWithPassword(password, options, data) {
2310
2057
  const key = await generateKey(password, {
2311
2058
  ...options,
@@ -2313,13 +2060,11 @@ async function hmacWithPassword(password, options, data) {
2313
2060
  });
2314
2061
  const textBuffer = textEncoder.encode(data);
2315
2062
  const signed = await crypto.subtle.sign({ name: "HMAC" }, key.key, textBuffer);
2316
- const digest = base64Encode(new Uint8Array(signed));
2317
2063
  return {
2318
- digest,
2064
+ digest: base64Encode(new Uint8Array(signed)),
2319
2065
  salt: key.salt
2320
2066
  };
2321
2067
  }
2322
- /** Generates a key from the password. */
2323
2068
  async function generateKey(password, options) {
2324
2069
  if (!password?.length) throw new Error("Empty password");
2325
2070
  if (options == null || typeof options !== "object") throw new Error("Bad options");
@@ -2344,8 +2089,7 @@ async function generateKey(password, options) {
2344
2089
  salt = [...new Uint8Array(randomSalt)].map((x) => x.toString(16).padStart(2, "0")).join("");
2345
2090
  }
2346
2091
  const derivedKey = await pbkdf2(password, salt, options.iterations, algorithm.keyBits / 8, "SHA-1");
2347
- const importedEncryptionKey = await crypto.subtle.importKey("raw", derivedKey, id, false, usage);
2348
- resultKey = importedEncryptionKey;
2092
+ resultKey = await crypto.subtle.importKey("raw", derivedKey, id, false, usage);
2349
2093
  resultSalt = salt;
2350
2094
  } else {
2351
2095
  if (password.length < algorithm.keyBits / 8) throw new Error("Key buffer (password) too small");
@@ -2361,19 +2105,16 @@ async function generateKey(password, options) {
2361
2105
  iv: resultIV
2362
2106
  };
2363
2107
  }
2364
- /** Provides an asynchronous Password-Based Key Derivation Function 2 (PBKDF2) implementation. */
2365
2108
  async function pbkdf2(password, salt, iterations, keyLength, hash) {
2366
2109
  const passwordBuffer = textEncoder.encode(password);
2367
2110
  const importedKey = await crypto.subtle.importKey("raw", passwordBuffer, { name: "PBKDF2" }, false, ["deriveBits"]);
2368
- const saltBuffer = textEncoder.encode(salt);
2369
2111
  const params = {
2370
2112
  name: "PBKDF2",
2371
2113
  hash,
2372
- salt: saltBuffer,
2114
+ salt: textEncoder.encode(salt),
2373
2115
  iterations
2374
2116
  };
2375
- const derivation = await crypto.subtle.deriveBits(params, importedKey, keyLength * 8);
2376
- return derivation;
2117
+ return await crypto.subtle.deriveBits(params, importedKey, keyLength * 8);
2377
2118
  }
2378
2119
  async function encrypt(password, options, data) {
2379
2120
  const key = await generateKey(password, options);
@@ -2402,14 +2143,12 @@ function getEncryptParams(algorithm, key, data) {
2402
2143
  typeof data === "string" ? textEncoder.encode(data) : data
2403
2144
  ];
2404
2145
  }
2405
- /** Returns true if `a` is equal to `b`, without leaking timing information that would allow an attacker to guess one of the values. */
2406
2146
  function fixedTimeComparison(a, b) {
2407
2147
  let mismatch = a.length === b.length ? 0 : 1;
2408
2148
  if (mismatch) b = a;
2409
2149
  for (let i = 0; i < a.length; i += 1) mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
2410
2150
  return mismatch === 0;
2411
2151
  }
2412
- /** Normalizes a password parameter. */
2413
2152
  function normalizePassword(password) {
2414
2153
  if (typeof password === "string" || password instanceof Uint8Array) return {
2415
2154
  encryption: password,
@@ -2426,46 +2165,30 @@ function normalizePassword(password) {
2426
2165
  integrity: password.integrity
2427
2166
  };
2428
2167
  }
2429
- /** Generate cryptographically strong pseudorandom bits. */
2430
2168
  function randomBits(bits) {
2431
2169
  if (bits < 1) throw new Error("Invalid random bits count");
2432
- const bytes = Math.ceil(bits / 8);
2433
- return randomBytes(bytes);
2170
+ return randomBytes(Math.ceil(bits / 8));
2434
2171
  }
2435
- /** Generates cryptographically strong pseudorandom bytes. */
2436
2172
  function randomBytes(size) {
2437
2173
  const bytes = new Uint8Array(size);
2438
2174
  crypto.getRandomValues(bytes);
2439
2175
  return bytes;
2440
2176
  }
2441
-
2442
- //#endregion
2443
- //#region src/utils/internal/session.ts
2444
2177
  const kGetSession = /* @__PURE__ */ Symbol.for("h3.internal.session.promise");
2445
- const DEFAULT_SESSION_NAME = "h3";
2446
2178
  const DEFAULT_SESSION_COOKIE = {
2447
2179
  path: "/",
2448
2180
  secure: true,
2449
2181
  httpOnly: true
2450
2182
  };
2451
-
2452
- //#endregion
2453
- //#region src/utils/session.ts
2454
- /**
2455
- * Create a session manager for the current request.
2456
- *
2457
- */
2458
2183
  async function useSession(event, config) {
2459
- const sessionName = config.name || DEFAULT_SESSION_NAME;
2184
+ const sessionName = config.name || "h3";
2460
2185
  await getSession(event, config);
2461
2186
  const sessionManager = {
2462
2187
  get id() {
2463
- const context = getEventContext(event);
2464
- return context?.sessions?.[sessionName]?.id;
2188
+ return getEventContext(event)?.sessions?.[sessionName]?.id;
2465
2189
  },
2466
2190
  get data() {
2467
- const context = getEventContext(event);
2468
- return context.sessions?.[sessionName]?.data || {};
2191
+ return getEventContext(event).sessions?.[sessionName]?.data || {};
2469
2192
  },
2470
2193
  update: async (update) => {
2471
2194
  await updateSession(event, config, update);
@@ -2478,11 +2201,8 @@ async function useSession(event, config) {
2478
2201
  };
2479
2202
  return sessionManager;
2480
2203
  }
2481
- /**
2482
- * Get the session for the current request.
2483
- */
2484
2204
  async function getSession(event, config) {
2485
- const sessionName = config.name || DEFAULT_SESSION_NAME;
2205
+ const sessionName = config.name || "h3";
2486
2206
  const context = getEventContext(event);
2487
2207
  if (!context.sessions) context.sessions = new EmptyObject();
2488
2208
  const existingSession = context.sessions[sessionName];
@@ -2516,42 +2236,26 @@ async function getSession(event, config) {
2516
2236
  }
2517
2237
  return session;
2518
2238
  }
2519
- /**
2520
- * Update the session data for the current request.
2521
- */
2522
2239
  async function updateSession(event, config, update) {
2523
- const sessionName = config.name || DEFAULT_SESSION_NAME;
2524
- const context = getEventContext(event);
2525
- const session = context.sessions?.[sessionName] || await getSession(event, config);
2240
+ const sessionName = config.name || "h3";
2241
+ const session = getEventContext(event).sessions?.[sessionName] || await getSession(event, config);
2526
2242
  if (typeof update === "function") update = update(session.data);
2527
2243
  if (update) Object.assign(session.data, update);
2528
- if (config.cookie !== false && event.res) {
2529
- const sealed = await sealSession(event, config);
2530
- setChunkedCookie(event, sessionName, sealed, {
2531
- ...DEFAULT_SESSION_COOKIE,
2532
- expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
2533
- ...config.cookie
2534
- });
2535
- }
2244
+ if (config.cookie !== false && event.res) setChunkedCookie(event, sessionName, await sealSession(event, config), {
2245
+ ...DEFAULT_SESSION_COOKIE,
2246
+ expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
2247
+ ...config.cookie
2248
+ });
2536
2249
  return session;
2537
2250
  }
2538
- /**
2539
- * Encrypt and sign the session data for the current request.
2540
- */
2541
2251
  async function sealSession(event, config) {
2542
- const sessionName = config.name || DEFAULT_SESSION_NAME;
2543
- const context = getEventContext(event);
2544
- const session = context.sessions?.[sessionName] || await getSession(event, config);
2545
- const sealed = await seal(session, config.password, {
2252
+ const sessionName = config.name || "h3";
2253
+ return await seal(getEventContext(event).sessions?.[sessionName] || await getSession(event, config), config.password, {
2546
2254
  ...defaults,
2547
2255
  ttl: config.maxAge ? config.maxAge * 1e3 : 0,
2548
2256
  ...config.seal
2549
2257
  });
2550
- return sealed;
2551
2258
  }
2552
- /**
2553
- * Decrypt and verify the session data for the current request.
2554
- */
2555
2259
  async function unsealSession(_event, config, sealed) {
2556
2260
  const unsealed = await unseal(sealed, config.password, {
2557
2261
  ...defaults,
@@ -2559,30 +2263,20 @@ async function unsealSession(_event, config, sealed) {
2559
2263
  ...config.seal
2560
2264
  });
2561
2265
  if (config.maxAge) {
2562
- const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
2563
- if (age > config.maxAge * 1e3) throw new Error("Session expired!");
2266
+ if (Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY) > config.maxAge * 1e3) throw new Error("Session expired!");
2564
2267
  }
2565
2268
  return unsealed;
2566
2269
  }
2567
- /**
2568
- * Clear the session data for the current request.
2569
- */
2570
2270
  function clearSession(event, config) {
2571
2271
  const context = getEventContext(event);
2572
- const sessionName = config.name || DEFAULT_SESSION_NAME;
2272
+ const sessionName = config.name || "h3";
2573
2273
  if (context.sessions?.[sessionName]) delete context.sessions[sessionName];
2574
- if (event.res && config.cookie !== false) setChunkedCookie(event, sessionName, "", {
2274
+ if (event.res && config.cookie !== false) deleteChunkedCookie(event, sessionName, {
2575
2275
  ...DEFAULT_SESSION_COOKIE,
2576
2276
  ...config.cookie
2577
2277
  });
2578
2278
  return Promise.resolve();
2579
2279
  }
2580
-
2581
- //#endregion
2582
- //#region src/utils/internal/cors.ts
2583
- /**
2584
- * Resolve CORS options.
2585
- */
2586
2280
  function resolveCorsOptions(options = {}) {
2587
2281
  const defaultOptions = {
2588
2282
  origin: "*",
@@ -2593,7 +2287,7 @@ function resolveCorsOptions(options = {}) {
2593
2287
  maxAge: false,
2594
2288
  preflight: { statusCode: 204 }
2595
2289
  };
2596
- return {
2290
+ const resolved = {
2597
2291
  ...defaultOptions,
2598
2292
  ...options,
2599
2293
  preflight: {
@@ -2601,10 +2295,9 @@ function resolveCorsOptions(options = {}) {
2601
2295
  ...options.preflight
2602
2296
  }
2603
2297
  };
2298
+ if (resolved.credentials && (!options.origin || options.origin === "*")) console.warn("[h3] CORS: `credentials: true` with wildcard origin is not allowed. Browsers will reject the response.");
2299
+ return resolved;
2604
2300
  }
2605
- /**
2606
- * Check if the origin is allowed.
2607
- */
2608
2301
  function isCorsOriginAllowed(origin, options) {
2609
2302
  const { origin: originOption } = options;
2610
2303
  if (!origin) return false;
@@ -2616,9 +2309,6 @@ function isCorsOriginAllowed(origin, options) {
2616
2309
  });
2617
2310
  return originOption === origin;
2618
2311
  }
2619
- /**
2620
- * Create the `access-control-allow-origin` header.
2621
- */
2622
2312
  function createOriginHeaders(event, options) {
2623
2313
  const { origin: originOption } = options;
2624
2314
  const origin = event.req.headers.get("origin");
@@ -2633,26 +2323,17 @@ function createOriginHeaders(event, options) {
2633
2323
  };
2634
2324
  return {};
2635
2325
  }
2636
- /**
2637
- * Create the `access-control-allow-methods` header.
2638
- */
2639
2326
  function createMethodsHeaders(options) {
2640
2327
  const { methods } = options;
2641
2328
  if (!methods) return {};
2642
2329
  if (methods === "*") return { "access-control-allow-methods": "*" };
2643
2330
  return methods.length > 0 ? { "access-control-allow-methods": methods.join(",") } : {};
2644
2331
  }
2645
- /**
2646
- * Create the `access-control-allow-credentials` header.
2647
- */
2648
2332
  function createCredentialsHeaders(options) {
2649
2333
  const { credentials } = options;
2650
2334
  if (credentials) return { "access-control-allow-credentials": "true" };
2651
2335
  return {};
2652
2336
  }
2653
- /**
2654
- * Create the `access-control-allow-headers` and `vary` headers.
2655
- */
2656
2337
  function createAllowHeaderHeaders(event, options) {
2657
2338
  const { allowHeaders } = options;
2658
2339
  if (!allowHeaders || allowHeaders === "*" || allowHeaders.length === 0) {
@@ -2667,37 +2348,22 @@ function createAllowHeaderHeaders(event, options) {
2667
2348
  vary: "access-control-request-headers"
2668
2349
  };
2669
2350
  }
2670
- /**
2671
- * Create the `access-control-expose-headers` header.
2672
- */
2673
2351
  function createExposeHeaders(options) {
2674
2352
  const { exposeHeaders } = options;
2675
2353
  if (!exposeHeaders) return {};
2676
2354
  if (exposeHeaders === "*") return { "access-control-expose-headers": exposeHeaders };
2677
2355
  return { "access-control-expose-headers": exposeHeaders.join(",") };
2678
2356
  }
2679
- /**
2680
- * Create the `access-control-max-age` header.
2681
- */
2682
2357
  function createMaxAgeHeader(options) {
2683
2358
  const { maxAge } = options;
2684
2359
  if (maxAge) return { "access-control-max-age": maxAge };
2685
2360
  return {};
2686
2361
  }
2687
-
2688
- //#endregion
2689
- //#region src/utils/cors.ts
2690
- /**
2691
- * Check if the incoming request is a CORS preflight request.
2692
- */
2693
2362
  function isPreflightRequest(event) {
2694
2363
  const origin = event.req.headers.get("origin");
2695
2364
  const accessControlRequestMethod = event.req.headers.get("access-control-request-method");
2696
2365
  return event.req.method === "OPTIONS" && !!origin && !!accessControlRequestMethod;
2697
2366
  }
2698
- /**
2699
- * Append CORS preflight headers to the response.
2700
- */
2701
2367
  function appendCorsPreflightHeaders(event, options) {
2702
2368
  const headers = {
2703
2369
  ...createOriginHeaders(event, options),
@@ -2706,43 +2372,22 @@ function appendCorsPreflightHeaders(event, options) {
2706
2372
  ...createAllowHeaderHeaders(event, options),
2707
2373
  ...createMaxAgeHeader(options)
2708
2374
  };
2709
- for (const [key, value] of Object.entries(headers)) event.res.headers.append(key, value);
2375
+ for (const [key, value] of Object.entries(headers)) {
2376
+ event.res.headers.append(key, value);
2377
+ event.res.errHeaders.append(key, value);
2378
+ }
2710
2379
  }
2711
- /**
2712
- * Append CORS headers to the response.
2713
- */
2714
2380
  function appendCorsHeaders(event, options) {
2715
2381
  const headers = {
2716
2382
  ...createOriginHeaders(event, options),
2717
2383
  ...createCredentialsHeaders(options),
2718
2384
  ...createExposeHeaders(options)
2719
2385
  };
2720
- for (const [key, value] of Object.entries(headers)) event.res.headers.append(key, value);
2721
- }
2722
- /**
2723
- * Handle CORS for the incoming request.
2724
- *
2725
- * If the incoming request is a CORS preflight request, it will append the CORS preflight headers and send a 204 response.
2726
- *
2727
- * If return value is not `false`, the request is handled and no further action is needed.
2728
- *
2729
- * @example
2730
- * const app = new H3();
2731
- * const router = createRouter();
2732
- * router.use("/", async (event) => {
2733
- * const corsRes = handleCors(event, {
2734
- * origin: "*",
2735
- * preflight: {
2736
- * statusCode: 204,
2737
- * },
2738
- * methods: "*",
2739
- * });
2740
- * if (corsRes !== false) {
2741
- * return corsRes;
2742
- * }
2743
- * // Your code here
2744
- * });
2745
- */
2386
+ for (const [key, value] of Object.entries(headers)) {
2387
+ event.res.headers.append(key, value);
2388
+ event.res.errHeaders.append(key, value);
2389
+ }
2390
+ }
2746
2391
  function handleCors(event, options) {
2747
2392
  const _options = resolveCorsOptions(options);
2748
2393
  if (isPreflightRequest(event)) {
@@ -2752,30 +2397,46 @@ function handleCors(event, options) {
2752
2397
  appendCorsHeaders(event, _options);
2753
2398
  return false;
2754
2399
  }
2755
-
2756
- //#endregion
2757
- //#region src/utils/auth.ts
2758
- /**
2759
- * Apply basic authentication for current request.
2760
- *
2761
- * @example
2762
- * import { defineHandler, requireBasicAuth } from "h3";
2763
- * export default defineHandler(async (event) => {
2764
- * await requireBasicAuth(event, { password: "test" });
2765
- * return `Hello, ${event.context.basicAuth.username}!`;
2766
- * });
2767
- */
2400
+ const _textEncoder = /* @__PURE__ */ new TextEncoder();
2401
+ function timingSafeEqual(a, b) {
2402
+ const aBuf = _textEncoder.encode(a);
2403
+ const bBuf = _textEncoder.encode(b);
2404
+ const aLen = aBuf.length;
2405
+ const bLen = bBuf.length;
2406
+ const len = Math.max(aLen, bLen);
2407
+ let result = aLen === bLen ? 0 : 1;
2408
+ for (let i = 0; i < len; i++) result |= (aBuf[i % aLen] ?? 0) ^ (bBuf[i % bLen] ?? 0);
2409
+ return result === 0;
2410
+ }
2411
+ function randomJitter() {
2412
+ const randomBuffer = new Uint32Array(1);
2413
+ crypto.getRandomValues(randomBuffer);
2414
+ const jitter = randomBuffer[0] % 100;
2415
+ return new Promise((resolve) => setTimeout(resolve, jitter));
2416
+ }
2768
2417
  async function requireBasicAuth(event, opts) {
2769
- if (!opts.validate && !opts.password) throw new Error("You must provide either a validate function or a password for basic auth.");
2418
+ if (!opts.validate && !opts.password) throw new HTTPError({
2419
+ message: "Either 'password' or 'validate' option must be provided",
2420
+ status: 500
2421
+ });
2770
2422
  const authHeader = event.req.headers.get("authorization");
2771
- if (!authHeader) throw autheFailed(event);
2423
+ if (!authHeader) throw authFailed(event);
2772
2424
  const [authType, b64auth] = authHeader.split(" ");
2773
- if (authType !== "Basic" || !b64auth) throw autheFailed(event, opts?.realm);
2774
- const [username, password] = atob(b64auth).split(":");
2775
- if (!username || !password) throw autheFailed(event, opts?.realm);
2776
- if (opts.username && username !== opts.username) throw autheFailed(event, opts?.realm);
2777
- if (opts.password && password !== opts.password) throw autheFailed(event, opts?.realm);
2778
- if (opts.validate && !await opts.validate(username, password)) throw autheFailed(event, opts?.realm);
2425
+ if (!b64auth || authType.toLowerCase() !== "basic") throw authFailed(event, opts?.realm);
2426
+ let authDecoded;
2427
+ try {
2428
+ authDecoded = atob(b64auth);
2429
+ } catch {
2430
+ throw authFailed(event, opts?.realm);
2431
+ }
2432
+ const colonIndex = authDecoded.indexOf(":");
2433
+ const username = authDecoded.slice(0, colonIndex);
2434
+ const password = authDecoded.slice(colonIndex + 1);
2435
+ if (!username || !password) throw authFailed(event, opts?.realm);
2436
+ if (opts.username && !timingSafeEqual(username, opts.username) || opts.password && !timingSafeEqual(password, opts.password) || opts.validate && !await opts.validate(username, password)) {
2437
+ await randomJitter();
2438
+ throw authFailed(event, opts?.realm);
2439
+ }
2779
2440
  const context = getEventContext(event);
2780
2441
  context.basicAuth = {
2781
2442
  username,
@@ -2784,37 +2445,19 @@ async function requireBasicAuth(event, opts) {
2784
2445
  };
2785
2446
  return true;
2786
2447
  }
2787
- /**
2788
- * Create a basic authentication middleware.
2789
- *
2790
- * @example
2791
- * import { H3, serve, basicAuth } from "h3";
2792
- * const auth = basicAuth({ password: "test" });
2793
- * app.get("/", (event) => `Hello ${event.context.basicAuth?.username}!`, [auth]);
2794
- * serve(app, { port: 3000 });
2795
- */
2796
2448
  function basicAuth(opts) {
2797
2449
  return async (event, next) => {
2798
2450
  await requireBasicAuth(event, opts);
2799
2451
  return next();
2800
2452
  };
2801
2453
  }
2802
- function autheFailed(event, realm = "") {
2454
+ function authFailed(event, realm = "") {
2803
2455
  return new HTTPError({
2804
2456
  status: 401,
2805
2457
  statusText: "Authentication required",
2806
2458
  headers: { "www-authenticate": `Basic realm=${JSON.stringify(realm)}` }
2807
2459
  });
2808
2460
  }
2809
-
2810
- //#endregion
2811
- //#region src/utils/fingerprint.ts
2812
- /**
2813
- *
2814
- * Get a unique fingerprint for the incoming request.
2815
- *
2816
- * @experimental Behavior of this utility might change in the future versions
2817
- */
2818
2461
  async function getRequestFingerprint(event, opts = {}) {
2819
2462
  const fingerprint = [];
2820
2463
  if (opts.ip !== false) fingerprint.push(getRequestIP(event, { xForwardedFor: opts.xForwardedFor }));
@@ -2825,77 +2468,171 @@ async function getRequestFingerprint(event, opts = {}) {
2825
2468
  if (!fingerprintString) return null;
2826
2469
  if (opts.hash === false) return fingerprintString;
2827
2470
  const buffer = await crypto.subtle.digest(opts.hash || "SHA-1", new TextEncoder().encode(fingerprintString));
2828
- const hash = [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
2829
- return hash;
2830
- }
2831
-
2832
- //#endregion
2833
- //#region src/utils/ws.ts
2834
- /**
2835
- * Define WebSocket hooks.
2836
- *
2837
- * @see https://h3.dev/guide/websocket
2838
- */
2471
+ return [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
2472
+ }
2839
2473
  function defineWebSocket(hooks) {
2840
2474
  return hooks;
2841
2475
  }
2842
- /**
2843
- * Define WebSocket event handler.
2844
- *
2845
- * @see https://h3.dev/guide/websocket
2846
- */
2847
2476
  function defineWebSocketHandler(hooks) {
2848
- return defineHandler(function _webSocketHandler() {
2849
- return Object.assign(new Response("WebSocket upgrade is required.", { status: 426 }), { crossws: hooks });
2477
+ return defineHandler(function _webSocketHandler(event) {
2478
+ const crossws = typeof hooks === "function" ? hooks(event) : hooks;
2479
+ return Object.assign(new Response("WebSocket upgrade is required.", { status: 426 }), { crossws });
2850
2480
  });
2851
2481
  }
2852
-
2853
- //#endregion
2854
- //#region src/_deprecated.ts
2855
- /** @deprecated Use `HTTPError` */
2482
+ const PARSE_ERROR = -32700;
2483
+ const INVALID_REQUEST = -32600;
2484
+ const METHOD_NOT_FOUND = -32601;
2485
+ const INVALID_PARAMS = -32602;
2486
+ function defineJsonRpcHandler(opts = {}) {
2487
+ const methodMap = createMethodMap(opts.methods);
2488
+ const handler = async (event) => {
2489
+ if (event.req.method !== "POST") throw new HTTPError({ status: 405 });
2490
+ let body;
2491
+ try {
2492
+ body = await event.req.json();
2493
+ } catch {
2494
+ return createJsonRpcError(null, PARSE_ERROR, "Parse error");
2495
+ }
2496
+ const result = await processJsonRpcBody(body, methodMap, event);
2497
+ return result === void 0 ? new HTTPResponse("", { status: 202 }) : result;
2498
+ };
2499
+ return defineHandler({
2500
+ ...opts,
2501
+ handler
2502
+ });
2503
+ }
2504
+ function defineJsonRpcWebSocketHandler(opts) {
2505
+ const methodMap = createMethodMap(opts.methods);
2506
+ return defineWebSocketHandler({
2507
+ ...opts.hooks,
2508
+ async message(peer, message) {
2509
+ let body;
2510
+ try {
2511
+ body = message.json();
2512
+ } catch {
2513
+ peer.send(JSON.stringify(createJsonRpcError(null, PARSE_ERROR, "Parse error")));
2514
+ return;
2515
+ }
2516
+ const result = await processJsonRpcBody(body, methodMap, peer);
2517
+ if (result !== void 0) peer.send(JSON.stringify(result));
2518
+ }
2519
+ });
2520
+ }
2521
+ function createMethodMap(methods) {
2522
+ const methodMap = Object.create(null);
2523
+ for (const key of Object.keys(methods)) methodMap[key] = methods[key];
2524
+ return methodMap;
2525
+ }
2526
+ async function processJsonRpcBody(body, methodMap, context) {
2527
+ if (!body || typeof body !== "object") return createJsonRpcError(null, PARSE_ERROR, "Parse error");
2528
+ const requests = Array.isArray(body) ? body : [body];
2529
+ if (requests.length === 0) return createJsonRpcError(null, INVALID_REQUEST, "Invalid Request");
2530
+ const finalResponses = (await Promise.all(requests.map((raw) => processJsonRpcMethod(raw, methodMap, context)))).filter((r) => r !== void 0);
2531
+ if (finalResponses.length === 0) return;
2532
+ return Array.isArray(body) ? finalResponses : finalResponses[0];
2533
+ }
2534
+ async function processJsonRpcMethod(raw, methodMap, context) {
2535
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return createJsonRpcError(null, INVALID_REQUEST, "Invalid Request");
2536
+ 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");
2538
+ 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
+ if (req.method.startsWith("rpc.")) return isNotification(req) ? void 0 : createJsonRpcError(req.id, METHOD_NOT_FOUND, "Method not found");
2540
+ const method = req.method;
2541
+ const params = req.params;
2542
+ const notification = isNotification(req);
2543
+ const id = notification ? void 0 : req.id;
2544
+ const methodHandler = methodMap[method];
2545
+ if (!methodHandler) return notification ? void 0 : createJsonRpcError(id, METHOD_NOT_FOUND, "Method not found");
2546
+ try {
2547
+ const rpcReq = {
2548
+ jsonrpc: "2.0",
2549
+ method,
2550
+ params
2551
+ };
2552
+ if (!notification) rpcReq.id = id;
2553
+ const result = await methodHandler(rpcReq, context);
2554
+ return notification ? void 0 : {
2555
+ jsonrpc: "2.0",
2556
+ id,
2557
+ result: result ?? null
2558
+ };
2559
+ } catch (error_) {
2560
+ if (notification) return;
2561
+ const h3Error = HTTPError.isError(error_) ? error_ : {
2562
+ status: 500,
2563
+ message: "Internal error",
2564
+ data: error_ != null && typeof error_ === "object" && "message" in error_ ? error_.message : void 0
2565
+ };
2566
+ const statusCode = h3Error.status;
2567
+ const statusMessage = h3Error.message;
2568
+ return createJsonRpcError(id, mapHttpStatusToJsonRpcError(statusCode), statusMessage, h3Error.data);
2569
+ }
2570
+ }
2571
+ function mapHttpStatusToJsonRpcError(status) {
2572
+ switch (status) {
2573
+ case 400:
2574
+ case 422: return INVALID_PARAMS;
2575
+ case 401: return -32001;
2576
+ case 403: return -32003;
2577
+ case 404: return -32004;
2578
+ case 408: return -32008;
2579
+ case 409: return -32009;
2580
+ case 429: return -32029;
2581
+ default:
2582
+ if (status >= 300 && status < 500) return -32e3;
2583
+ return -32603;
2584
+ }
2585
+ }
2586
+ function isNotification(req) {
2587
+ return !("id" in req);
2588
+ }
2589
+ function isValidId(id) {
2590
+ if (id === null) return true;
2591
+ if (typeof id === "string") return true;
2592
+ return typeof id === "number" && Number.isInteger(id);
2593
+ }
2594
+ const createJsonRpcError = (id, code, message, data) => {
2595
+ const error = {
2596
+ code,
2597
+ message
2598
+ };
2599
+ if (data !== void 0) error.data = data;
2600
+ return {
2601
+ jsonrpc: "2.0",
2602
+ id,
2603
+ error
2604
+ };
2605
+ };
2856
2606
  const H3Error = HTTPError;
2857
2607
  function createError(arg1, arg2) {
2858
2608
  return new HTTPError(arg1, arg2);
2859
2609
  }
2860
- /**
2861
- * @deprecated Use `HTTPError.isError`
2862
- */
2863
2610
  function isError(input) {
2864
2611
  return HTTPError.isError(input);
2865
2612
  }
2866
- /** @deprecated Please use `event.url` */
2867
2613
  const getRequestPath = (event) => event.path;
2868
- /** @deprecated Please use `event.req.headers.get(name)` */
2869
2614
  function getRequestHeader(event, name) {
2870
2615
  return event.req.headers.get(name) || void 0;
2871
2616
  }
2872
- /** @deprecated Please use `event.req.headers.get(name)` */
2873
2617
  const getHeader = getRequestHeader;
2874
- /** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
2875
2618
  function getRequestHeaders(event) {
2876
2619
  return Object.fromEntries(event.req.headers.entries());
2877
2620
  }
2878
- /** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
2879
2621
  const getHeaders = getRequestHeaders;
2880
- /** @deprecated Please use `event.req.method` */
2881
2622
  function getMethod(event, defaultMethod = "GET") {
2882
2623
  return (event.req.method || defaultMethod).toUpperCase();
2883
2624
  }
2884
- /** @deprecated Please use `event.req.text()` or `event.req.arrayBuffer()` */
2885
2625
  function readRawBody(event, encoding = "utf8") {
2886
2626
  return encoding ? event.req.text() : event.req.arrayBuffer().then((r) => new Uint8Array(r));
2887
2627
  }
2888
- /** @deprecated Please use `event.req.formData()` */
2889
2628
  async function readFormDataBody(event) {
2890
2629
  return event.req.formData();
2891
2630
  }
2892
- /** @deprecated Please use `event.req.formData()` */
2893
2631
  const readFormData = readFormDataBody;
2894
- /** @deprecated Please use `event.req.formData()` */
2895
2632
  async function readMultipartFormData(event) {
2896
2633
  const formData = await event.req.formData();
2897
2634
  return Promise.all([...formData.entries()].map(async ([key, value]) => {
2898
- return value instanceof Blob ? {
2635
+ return typeof value === "object" ? {
2899
2636
  name: key,
2900
2637
  type: value.type,
2901
2638
  filename: value.name,
@@ -2906,122 +2643,82 @@ async function readMultipartFormData(event) {
2906
2643
  };
2907
2644
  }));
2908
2645
  }
2909
- /** @deprecated Please use `event.req.body` */
2910
2646
  function getBodyStream(event) {
2911
2647
  return event.req.body || void 0;
2912
2648
  }
2913
- /** @deprecated Please use `event.req.body` */
2914
2649
  const getRequestWebStream = getBodyStream;
2915
- /** @deprecated Please directly return stream */
2916
2650
  function sendStream(_event, value) {
2917
2651
  return value;
2918
2652
  }
2919
- /** @deprecated Please use `return noContent(event)` */
2920
2653
  const sendNoContent = (_, code) => noContent(code);
2921
- /** @deprecated Please use `return redirect(event, code)` */
2922
2654
  const sendRedirect = (_, loc, code) => redirect(loc, code);
2923
- /** @deprecated Please directly return response */
2924
2655
  const sendWebResponse = (response) => response;
2925
- /** @deprecated Please use `return proxy(event)` */
2926
2656
  const sendProxy = proxy;
2927
- /** @deprecated Please use `return iterable(event, value)` */
2928
2657
  const sendIterable = (_event, val, options) => {
2929
2658
  return iterable(val, options);
2930
2659
  };
2931
- /** @deprecated Please use `event.res.statusText` */
2932
2660
  function getResponseStatusText(event) {
2933
2661
  return event.res.statusText || "";
2934
2662
  }
2935
- /** @deprecated Please use `event.res.headers.append(name, value)` */
2936
2663
  function appendResponseHeader(event, name, value) {
2937
2664
  if (Array.isArray(value)) for (const valueItem of value) event.res.headers.append(name, valueItem);
2938
2665
  else event.res.headers.append(name, value);
2939
2666
  }
2940
- /** @deprecated Please use `event.res.headers.append(name, value)` */
2941
2667
  const appendHeader = appendResponseHeader;
2942
- /** @deprecated Please use `event.res.headers.set(name, value)` */
2943
2668
  function setResponseHeader(event, name, value) {
2944
2669
  if (Array.isArray(value)) {
2945
2670
  event.res.headers.delete(name);
2946
2671
  for (const valueItem of value) event.res.headers.append(name, valueItem);
2947
2672
  } else event.res.headers.set(name, value);
2948
2673
  }
2949
- /** @deprecated Please use `event.res.headers.set(name, value)` */
2950
2674
  const setHeader = setResponseHeader;
2951
- /** @deprecated Please use `event.res.headers.set(name, value)` */
2952
2675
  function setResponseHeaders(event, headers) {
2953
2676
  for (const [name, value] of Object.entries(headers)) event.res.headers.set(name, value);
2954
2677
  }
2955
- /** @deprecated Please use `event.res.headers.set(name, value)` */
2956
2678
  const setHeaders = setResponseHeaders;
2957
- /** @deprecated Please use `event.res.status` */
2958
2679
  function getResponseStatus(event) {
2959
2680
  return event.res.status || 200;
2960
2681
  }
2961
- /** @deprecated Please directly set `event.res.status` and `event.res.statusText` */
2962
2682
  function setResponseStatus(event, code, text) {
2963
2683
  if (code) event.res.status = sanitizeStatusCode(code, event.res.status);
2964
2684
  if (text) event.res.statusText = sanitizeStatusMessage(text);
2965
2685
  }
2966
- /** @deprecated Please use `event.res.headers.set("content-type", type)` */
2967
2686
  function defaultContentType(event, type) {
2968
2687
  if (type && event.res.status !== 304 && !event.res.headers.has("content-type")) event.res.headers.set("content-type", type);
2969
2688
  }
2970
- /** @deprecated Please use `Object.fromEntries(event.res.headers.entries())` */
2971
2689
  function getResponseHeaders(event) {
2972
2690
  return Object.fromEntries(event.res.headers.entries());
2973
2691
  }
2974
- /** @deprecated Please use `event.res.headers.get(name)` */
2975
2692
  function getResponseHeader(event, name) {
2976
2693
  return event.res.headers.get(name) || void 0;
2977
2694
  }
2978
- /** @deprecated Please use `event.res.headers.delete(name)` instead. */
2979
2695
  function removeResponseHeader(event, name) {
2980
2696
  return event.res.headers.delete(name);
2981
2697
  }
2982
- /** @deprecated Please use `event.res.headers.append(name, value)` */
2983
2698
  function appendResponseHeaders(event, headers) {
2984
2699
  for (const [name, value] of Object.entries(headers)) appendResponseHeader(event, name, value);
2985
2700
  }
2986
- /** @deprecated Please use `event.res.headers.append(name, value)` */
2987
2701
  const appendHeaders = appendResponseHeaders;
2988
- /** @deprecated Please use `event.res.headers.delete` */
2989
2702
  function clearResponseHeaders(event, headerNames) {
2990
2703
  if (headerNames && headerNames.length > 0) for (const name of headerNames) event.res.headers.delete(name);
2991
2704
  else for (const name of event.res.headers.keys()) event.res.headers.delete(name);
2992
2705
  }
2993
- /** Please use `defineHandler` */
2994
2706
  const defineEventHandler = defineHandler;
2995
- /** Please use `defineHandler` */
2996
2707
  const eventHandler = defineHandler;
2997
- /** Please use `defineLazyEventHandler` */
2998
2708
  const lazyEventHandler = defineLazyEventHandler;
2999
- /** @deprecated Please use `defineNodeHandler` */
3000
2709
  const defineNodeListener = defineNodeHandler;
3001
- /** @deprecated Please use `defineNodeHandler` */
3002
2710
  const fromNodeMiddleware = fromNodeHandler;
3003
- /**
3004
- * @deprecated please use `toNodeHandler` from `h3/node`.
3005
- */
3006
2711
  function toNodeHandler(app) {
3007
2712
  if (toNodeHandler._isWarned !== true) {
3008
2713
  console.warn(`[h3] "toNodeHandler" export from h3 is deprecated. Please import "toNodeHandler" from "h3/node".`);
3009
2714
  toNodeHandler._isWarned = true;
3010
2715
  }
3011
- const _toNodeHandler = (toNodeHandler._toNodeHandler ??= () => {
3012
- const _require = globalThis.process.getBuiltinModule("node:module").createRequire(import.meta.url);
3013
- return _require("srvx/node").toNodeHandler;
3014
- })();
3015
- return _toNodeHandler(app.fetch);
2716
+ return (toNodeHandler._toNodeHandler ??= () => {
2717
+ return globalThis.process.getBuiltinModule("node:module").createRequire(import.meta.url)("srvx/node").toNodeHandler;
2718
+ })()(app.fetch);
3016
2719
  }
3017
- /** @deprecated Please use `toNodeHandler` */
3018
2720
  const toNodeListener = toNodeHandler;
3019
- /** @deprecated Please use `new H3()` */
3020
2721
  const createApp = (config) => new H3(config);
3021
- /** @deprecated Please use `new H3()` */
3022
2722
  const createRouter$1 = (config) => new H3(config);
3023
- /** @deprecated Please use `withBase()` */
3024
2723
  const useBase = withBase;
3025
-
3026
- //#endregion
3027
- export { H3, H3Core, H3Error, H3Event, HTTPError, HTTPResponse, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, basicAuth, callMiddleware, clearResponseHeaders, clearSession, createApp, createError, createEventStream, createRouter$1 as createRouter, defaultContentType, defineEventHandler, defineHandler, 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, 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, setChunkedCookie, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeHandler, toNodeListener, toRequest, toResponse, toWebHandler, unsealSession, updateSession, useBase, useSession, withBase, writeEarlyHints };
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 };