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