@depup/h3 2.0.1-depup.0
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/LICENSE +21 -0
- package/README.md +25 -0
- package/bin/h3.mjs +36 -0
- package/changes.json +5 -0
- package/dist/THIRD-PARTY-LICENSES.md +70 -0
- package/dist/_entries/bun.d.mts +6 -0
- package/dist/_entries/bun.mjs +16 -0
- package/dist/_entries/cloudflare.d.mts +6 -0
- package/dist/_entries/cloudflare.mjs +16 -0
- package/dist/_entries/deno.d.mts +6 -0
- package/dist/_entries/deno.mjs +16 -0
- package/dist/_entries/generic.d.mts +6 -0
- package/dist/_entries/generic.mjs +16 -0
- package/dist/_entries/node.d.mts +10 -0
- package/dist/_entries/node.mjs +19 -0
- package/dist/_entries/service-worker.d.mts +6 -0
- package/dist/_entries/service-worker.mjs +16 -0
- package/dist/_utils.mjs +240 -0
- package/dist/cache.mjs +599 -0
- package/dist/cache2.mjs +50 -0
- package/dist/cors.mjs +292 -0
- package/dist/docs/0.guide/0.index/index.md +117 -0
- package/dist/docs/0.guide/1.basics/0.lifecycle.md +68 -0
- package/dist/docs/0.guide/1.basics/1.routing.md +167 -0
- package/dist/docs/0.guide/1.basics/2.middleware.md +97 -0
- package/dist/docs/0.guide/1.basics/3.handler.md +165 -0
- package/dist/docs/0.guide/1.basics/4.response.md +171 -0
- package/dist/docs/0.guide/1.basics/5.error.md +117 -0
- package/dist/docs/0.guide/1.basics/6.nested-apps.md +57 -0
- package/dist/docs/0.guide/2.rules.md +698 -0
- package/dist/docs/0.guide/3.api/0.h3.md +144 -0
- package/dist/docs/0.guide/3.api/1.h3event.md +160 -0
- package/dist/docs/0.guide/4.advanced/0.plugins.md +50 -0
- package/dist/docs/0.guide/4.advanced/1.websocket.md +176 -0
- package/dist/docs/0.guide/4.advanced/2.nightly.md +13 -0
- package/dist/docs/1.utils/0.index/index.md +46 -0
- package/dist/docs/1.utils/1.request.md +447 -0
- package/dist/docs/1.utils/2.response.md +172 -0
- package/dist/docs/1.utils/3.cookie.md +33 -0
- package/dist/docs/1.utils/4.security.md +175 -0
- package/dist/docs/1.utils/5.proxy.md +57 -0
- package/dist/docs/1.utils/6.mcp.md +75 -0
- package/dist/docs/1.utils/7.more.md +117 -0
- package/dist/docs/1.utils/8.community.md +48 -0
- package/dist/docs/2.examples/0.index/index.md +17 -0
- package/dist/docs/2.examples/1.handle-cookie.md +67 -0
- package/dist/docs/2.examples/2.handle-query.md +76 -0
- package/dist/docs/2.examples/3.handle-session.md +210 -0
- package/dist/docs/2.examples/4.serve-static-assets.md +66 -0
- package/dist/docs/2.examples/5.stream-response.md +76 -0
- package/dist/docs/2.examples/6.validate-data.md +193 -0
- package/dist/docs/3.migration/0.index/index.md +204 -0
- package/dist/docs/README.md +37 -0
- package/dist/h3.d.mts +1669 -0
- package/dist/h3.mjs +1809 -0
- package/dist/index.d.mts +1634 -0
- package/dist/match.d.mts +123 -0
- package/dist/middleware.mjs +123 -0
- package/dist/normalize.mjs +645 -0
- package/dist/path.mjs +42 -0
- package/dist/proxy.mjs +254 -0
- package/dist/response.mjs +465 -0
- package/dist/rules/cache.d.mts +29 -0
- package/dist/rules/cache.mjs +163 -0
- package/dist/rules/compiler.d.mts +94 -0
- package/dist/rules/compiler.mjs +173 -0
- package/dist/rules/index.d.mts +77 -0
- package/dist/rules/index.mjs +34 -0
- package/dist/rules/proxy.d.mts +3 -0
- package/dist/rules/proxy.mjs +14 -0
- package/dist/tracing.d.mts +33 -0
- package/dist/tracing.mjs +89 -0
- package/package.json +148 -0
package/dist/_utils.mjs
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { HTTPError, decodePreservingSeparators, getURLPathname, joinURL, withoutBase } from "./response.mjs";
|
|
2
|
+
import { isCanonicalPath, resolveDotSegments } from "./path.mjs";
|
|
3
|
+
const CANONICAL_OPTS = { decodeSlashes: true };
|
|
4
|
+
const MERGED_OPTS = {
|
|
5
|
+
decodeSlashes: true,
|
|
6
|
+
mergeSlashes: true
|
|
7
|
+
};
|
|
8
|
+
function canonicalPath(pathname) {
|
|
9
|
+
return resolveDotSegments(pathname, CANONICAL_OPTS);
|
|
10
|
+
}
|
|
11
|
+
function decodedPath(pathname) {
|
|
12
|
+
let decoded = pathname;
|
|
13
|
+
for (let pass = 0; hasDecodableEscape(decoded); pass++) {
|
|
14
|
+
if (pass >= MAX_PASSES) return decoded;
|
|
15
|
+
const input = pass < EXACT_PASSES ? decoded : flattenNesting(decoded);
|
|
16
|
+
let next;
|
|
17
|
+
try {
|
|
18
|
+
next = decodePreservingSeparators(input);
|
|
19
|
+
} catch {
|
|
20
|
+
return input;
|
|
21
|
+
}
|
|
22
|
+
if (next === input) return input;
|
|
23
|
+
decoded = next;
|
|
24
|
+
}
|
|
25
|
+
return decoded;
|
|
26
|
+
}
|
|
27
|
+
const PATH_ENCODE_RE = /(?:[\u0000-\u0020"#<>?^`{}]|[^\u0000-\u007E])+/gu;
|
|
28
|
+
function encodedReading(pathname) {
|
|
29
|
+
return pathname.replace(PATH_ENCODE_RE, (run) => encodeURIComponent(run.replace(/[\uD800-\uDFFF]/gu, "�")));
|
|
30
|
+
}
|
|
31
|
+
const EXACT_PASSES = 8;
|
|
32
|
+
const MAX_PASSES = 24;
|
|
33
|
+
const CHAR_2 = 50;
|
|
34
|
+
const CHAR_5 = 53;
|
|
35
|
+
function needsCanonicalPasses(pathname) {
|
|
36
|
+
return !isCanonicalPath(pathname, MERGED_OPTS);
|
|
37
|
+
}
|
|
38
|
+
function mergedCanonicalPath(pathname, canonical) {
|
|
39
|
+
const merged = resolveDotSegments(pathname, MERGED_OPTS);
|
|
40
|
+
return merged === canonical ? void 0 : merged;
|
|
41
|
+
}
|
|
42
|
+
function isPathInScope(pathname, base) {
|
|
43
|
+
if (!base) return true;
|
|
44
|
+
if (!isEveryCanonicalReadingInScope(pathname, base)) return false;
|
|
45
|
+
const decoded = decodedPath(pathname);
|
|
46
|
+
return decoded === pathname || isEveryCanonicalReadingInScope(decoded, base);
|
|
47
|
+
}
|
|
48
|
+
function isPathInServedScope(pathname, base) {
|
|
49
|
+
const decodedBase = decodedPath(base);
|
|
50
|
+
if (decodedBase === base) return isPathInScope(pathname, base);
|
|
51
|
+
return isEveryCanonicalReadingInScope(pathname, base) && isEveryCanonicalReadingInScope(decodedPath(pathname), decodedBase);
|
|
52
|
+
}
|
|
53
|
+
function isEveryCanonicalReadingInScope(pathname, base) {
|
|
54
|
+
if (!needsCanonicalPasses(pathname)) return isCanonicalInScope(pathname, base);
|
|
55
|
+
return isCanonicalInScope(canonicalPath(pathname), base) && isCanonicalInScope(resolveDotSegments(pathname, MERGED_OPTS), base);
|
|
56
|
+
}
|
|
57
|
+
function isCanonicalInScope(canonical, base) {
|
|
58
|
+
return canonical === base || canonical.startsWith(base + "/");
|
|
59
|
+
}
|
|
60
|
+
function hasDecodableEscape(value) {
|
|
61
|
+
for (let i = value.indexOf("%"); i !== -1; i = value.indexOf("%", i + 1)) {
|
|
62
|
+
const byte = escapeByte(value, nestingEnd(value, i));
|
|
63
|
+
if (byte !== 47 && byte !== 92) return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function flattenNesting(path) {
|
|
68
|
+
let flat = "";
|
|
69
|
+
let last = 0;
|
|
70
|
+
for (let i = path.indexOf("%"); i !== -1; i = path.indexOf("%", i + 1)) {
|
|
71
|
+
const end = nestingEnd(path, i);
|
|
72
|
+
if (end === i + 1) continue;
|
|
73
|
+
const byte = escapeByte(path, end);
|
|
74
|
+
if (byte === 47 || byte === 92) continue;
|
|
75
|
+
flat += path.slice(last, i) + (byte === -1 ? "%25" : "%");
|
|
76
|
+
last = end;
|
|
77
|
+
}
|
|
78
|
+
return last === 0 ? path : flat + path.slice(last);
|
|
79
|
+
}
|
|
80
|
+
function nestingEnd(value, index) {
|
|
81
|
+
let end = index + 1;
|
|
82
|
+
while (value.charCodeAt(end) === CHAR_2 && value.charCodeAt(end + 1) === CHAR_5) end += 2;
|
|
83
|
+
return end;
|
|
84
|
+
}
|
|
85
|
+
function escapeByte(value, index) {
|
|
86
|
+
const high = hexDigit(value.charCodeAt(index));
|
|
87
|
+
const low = hexDigit(value.charCodeAt(index + 1));
|
|
88
|
+
return high === -1 || low === -1 ? -1 : high * 16 + low;
|
|
89
|
+
}
|
|
90
|
+
function hexDigit(code) {
|
|
91
|
+
if (code >= 48 && code <= 57) return code - 48;
|
|
92
|
+
if (code >= 97 && code <= 102) return code - 87;
|
|
93
|
+
if (code >= 65 && code <= 70) return code - 55;
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
const QUERY_UNSAFE_RE = /[#&+;=?]/g;
|
|
97
|
+
const asQueryValue = (tail) => tail.replace(QUERY_UNSAFE_RE, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());
|
|
98
|
+
const asPath = (tail) => tail;
|
|
99
|
+
const AUTHORITY_RE = /^(?:[a-z][a-z\d+.-]*:[/\\]*|[/\\]{2,})[^/\\?#]*/i;
|
|
100
|
+
function authorityEnd(target) {
|
|
101
|
+
return AUTHORITY_RE.exec(target)?.[0].length ?? 0;
|
|
102
|
+
}
|
|
103
|
+
function isHostPositionSplat(target) {
|
|
104
|
+
const index = target.indexOf("**");
|
|
105
|
+
return index !== -1 && index <= authorityEnd(target);
|
|
106
|
+
}
|
|
107
|
+
function parseSplatTemplate(target) {
|
|
108
|
+
let index = target.indexOf("**");
|
|
109
|
+
if (index === -1 || index <= authorityEnd(target)) return;
|
|
110
|
+
const marker = target.search(/[?#]/);
|
|
111
|
+
const valueStart = marker === -1 ? Number.POSITIVE_INFINITY : marker;
|
|
112
|
+
const literals = [];
|
|
113
|
+
const encoders = [];
|
|
114
|
+
let last = 0;
|
|
115
|
+
for (; index !== -1; index = target.indexOf("**", last)) {
|
|
116
|
+
literals.push(target.slice(last, index));
|
|
117
|
+
encoders.push(index > valueStart ? asQueryValue : asPath);
|
|
118
|
+
last = index + 2;
|
|
119
|
+
}
|
|
120
|
+
literals.push(target.slice(last));
|
|
121
|
+
return {
|
|
122
|
+
literals,
|
|
123
|
+
encoders
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function interpolateSplat(template, tail) {
|
|
127
|
+
const { literals, encoders } = template;
|
|
128
|
+
let resolved = literals[0];
|
|
129
|
+
for (let i = 0; i < encoders.length; i++) resolved += encoders[i](tail) + literals[i + 1];
|
|
130
|
+
return resolved;
|
|
131
|
+
}
|
|
132
|
+
const LEADING_SEPARATOR_RUN_RE = /^(?:[/\\]|%(?:25)*(?:2f|5c))+/i;
|
|
133
|
+
const DYNAMIC_PATTERN_RE = /[:*()\\]/;
|
|
134
|
+
const VARIABLE_WIDTH_SEGMENT_RE = /^\*\*|^:.*[?*+]$/;
|
|
135
|
+
function prepareRuleTarget(options) {
|
|
136
|
+
const target = options?.to;
|
|
137
|
+
if (!target) return;
|
|
138
|
+
const base = options?.base;
|
|
139
|
+
if (target.endsWith("/**")) {
|
|
140
|
+
const baseTarget = target.slice(0, -3);
|
|
141
|
+
const baseTargetPath = targetBasePath(baseTarget);
|
|
142
|
+
const resolveTail = prepareTailResolver(base);
|
|
143
|
+
return (event) => {
|
|
144
|
+
const resolved = joinURL(baseTarget, resolveTail(event) + event.url.search);
|
|
145
|
+
assertTargetInScope(resolved, baseTargetPath);
|
|
146
|
+
return resolved;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const template = base === void 0 ? void 0 : parseSplatTemplate(target);
|
|
150
|
+
if (template) {
|
|
151
|
+
const prefix = template.literals[0];
|
|
152
|
+
const baseTargetPath = targetBasePath(prefix);
|
|
153
|
+
const resolveTail = prepareTailResolver(base);
|
|
154
|
+
return (event) => {
|
|
155
|
+
const resolved = interpolateSplat(template, resolveTail(event).slice(1));
|
|
156
|
+
assertTargetInScope(resolved, baseTargetPath);
|
|
157
|
+
assertTargetOrigin(resolved, prefix, event.url);
|
|
158
|
+
return appendSearch(resolved, event.url.search);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return (event) => appendSearch(target, event.url.search);
|
|
162
|
+
}
|
|
163
|
+
function prepareTailResolver(base) {
|
|
164
|
+
const baseSegments = base && DYNAMIC_PATTERN_RE.test(base) ? patternSegmentCount(base) : 0;
|
|
165
|
+
return (event) => {
|
|
166
|
+
const rawPath = event.url.pathname;
|
|
167
|
+
let scopeBase = base;
|
|
168
|
+
if (baseSegments) {
|
|
169
|
+
scopeBase = leadingSegments(rawPath, baseSegments);
|
|
170
|
+
if (scopeBase === void 0) throw new HTTPError({ status: 400 });
|
|
171
|
+
}
|
|
172
|
+
if (!scopeBase) return rawPath.replace(LEADING_SEPARATOR_RUN_RE, "/");
|
|
173
|
+
if (!isLiterallyInScope(rawPath, scopeBase)) {
|
|
174
|
+
const decoded = decodedPath(rawPath);
|
|
175
|
+
const derived = decoded === rawPath || !isLiterallyInScope(decoded, decodedPath(scopeBase)) ? void 0 : leadingSegments(rawPath, countSegments(scopeBase));
|
|
176
|
+
if (derived === void 0) throw new HTTPError({ status: 400 });
|
|
177
|
+
scopeBase = derived;
|
|
178
|
+
}
|
|
179
|
+
return withoutBase(rawPath, scopeBase);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function targetBasePath(prefix) {
|
|
183
|
+
const path = getURLPathname(prefix);
|
|
184
|
+
return path.endsWith("/") ? path.slice(0, -1) : path;
|
|
185
|
+
}
|
|
186
|
+
function appendSearch(target, search) {
|
|
187
|
+
if (!search) return target;
|
|
188
|
+
const hashIndex = target.indexOf("#");
|
|
189
|
+
const targetBase = hashIndex === -1 ? target : target.slice(0, hashIndex);
|
|
190
|
+
const targetHash = hashIndex === -1 ? "" : target.slice(hashIndex);
|
|
191
|
+
return targetBase + (targetBase.includes("?") ? targetBase.endsWith("?") || targetBase.endsWith("&") ? "" : "&" : "?") + search.slice(1) + targetHash;
|
|
192
|
+
}
|
|
193
|
+
function assertTargetInScope(resolved, baseTargetPath) {
|
|
194
|
+
if (!isFinalTargetInScope(getURLPathname(resolved), baseTargetPath)) throw new HTTPError({ status: 400 });
|
|
195
|
+
}
|
|
196
|
+
function assertTargetOrigin(resolved, prefix, url) {
|
|
197
|
+
let expected;
|
|
198
|
+
let actual;
|
|
199
|
+
try {
|
|
200
|
+
expected = new URL(prefix, url);
|
|
201
|
+
actual = new URL(resolved, url);
|
|
202
|
+
} catch {
|
|
203
|
+
throw new HTTPError({ status: 400 });
|
|
204
|
+
}
|
|
205
|
+
if (expected.origin !== actual.origin || expected.protocol !== actual.protocol) throw new HTTPError({ status: 400 });
|
|
206
|
+
}
|
|
207
|
+
function isFinalTargetInScope(pathname, baseTargetPath) {
|
|
208
|
+
if (baseTargetPath) return isPathInScope(pathname, baseTargetPath);
|
|
209
|
+
const run = LEADING_SEPARATOR_RUN_RE.exec(pathname);
|
|
210
|
+
return run === null || run[0] === "/";
|
|
211
|
+
}
|
|
212
|
+
function isLiterallyInScope(pathname, base) {
|
|
213
|
+
return isPathInServedScope(pathname, base) && (pathname === base || pathname.startsWith(base + "/"));
|
|
214
|
+
}
|
|
215
|
+
function patternSegmentCount(base) {
|
|
216
|
+
const segments = splitSegments(base);
|
|
217
|
+
let depth = 0;
|
|
218
|
+
for (const segment of segments) {
|
|
219
|
+
if (VARIABLE_WIDTH_SEGMENT_RE.test(segment)) return 0;
|
|
220
|
+
for (let i = 0; i < segment.length; i++) if (segment[i] === "{") depth++;
|
|
221
|
+
else if (segment[i] === "}") depth--;
|
|
222
|
+
if (depth !== 0) return 0;
|
|
223
|
+
}
|
|
224
|
+
return segments.length;
|
|
225
|
+
}
|
|
226
|
+
function countSegments(base) {
|
|
227
|
+
return splitSegments(base).length;
|
|
228
|
+
}
|
|
229
|
+
function splitSegments(base) {
|
|
230
|
+
return (base.startsWith("/") ? base.slice(1) : base).split("/");
|
|
231
|
+
}
|
|
232
|
+
function leadingSegments(pathname, count) {
|
|
233
|
+
let index = 0;
|
|
234
|
+
for (let i = 0; i < count; i++) {
|
|
235
|
+
index = pathname.indexOf("/", index + 1);
|
|
236
|
+
if (index === -1) return i === count - 1 ? pathname : void 0;
|
|
237
|
+
}
|
|
238
|
+
return pathname.slice(0, index);
|
|
239
|
+
}
|
|
240
|
+
export { canonicalPath, decodedPath, encodedReading, isHostPositionSplat, isPathInServedScope, mergedCanonicalPath, needsCanonicalPasses, prepareRuleTarget };
|