hono 4.12.34 → 4.13.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/dist/cjs/context.js +28 -13
- package/dist/cjs/hono-base.js +3 -2
- package/dist/cjs/jsx/base.js +26 -14
- package/dist/cjs/jsx/hooks/index.js +2 -2
- package/dist/cjs/middleware/cache/index.js +103 -8
- package/dist/cjs/middleware/compress/index.js +5 -0
- package/dist/cjs/middleware/cors/index.js +1 -1
- package/dist/cjs/middleware/etag/index.js +1 -1
- package/dist/cjs/middleware/jwk/jwk.js +9 -4
- package/dist/cjs/middleware/jwt/jwt.js +9 -4
- package/dist/cjs/middleware/method-not-allowed/index.js +90 -0
- package/dist/cjs/request.js +6 -8
- package/dist/cjs/router/reg-exp-router/node.js +55 -56
- package/dist/cjs/router/reg-exp-router/router.js +64 -85
- package/dist/cjs/router/reg-exp-router/trie.js +13 -5
- package/dist/cjs/router.js +1 -1
- package/dist/cjs/utils/cookie.js +1 -1
- package/dist/cjs/utils/url.js +7 -7
- package/dist/context.js +28 -13
- package/dist/hono-base.js +3 -2
- package/dist/jsx/base.js +26 -14
- package/dist/jsx/hooks/index.js +2 -2
- package/dist/middleware/cache/index.js +103 -8
- package/dist/middleware/compress/index.js +5 -0
- package/dist/middleware/cors/index.js +1 -1
- package/dist/middleware/etag/index.js +1 -1
- package/dist/middleware/jwk/jwk.js +9 -4
- package/dist/middleware/jwt/jwt.js +9 -4
- package/dist/middleware/method-not-allowed/index.js +68 -0
- package/dist/request.js +7 -9
- package/dist/router/reg-exp-router/node.js +55 -56
- package/dist/router/reg-exp-router/router.js +64 -85
- package/dist/router/reg-exp-router/trie.js +13 -5
- package/dist/router.js +1 -1
- package/dist/types/client/types.d.ts +1 -1
- package/dist/types/hono-base.d.ts +4 -3
- package/dist/types/jsx/base.d.ts +4 -2
- package/dist/types/jsx/dom/index.d.ts +5 -5
- package/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
- package/dist/types/jsx/dom/server.d.ts +5 -5
- package/dist/types/jsx/hooks/index.d.ts +8 -6
- package/dist/types/jsx/index.d.ts +5 -5
- package/dist/types/middleware/cache/index.d.ts +6 -4
- package/dist/types/middleware/cors/index.d.ts +1 -1
- package/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
- package/dist/types/middleware/jwk/jwk.d.ts +2 -0
- package/dist/types/middleware/jwt/jwt.d.ts +2 -0
- package/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
- package/dist/types/router/reg-exp-router/node.d.ts +1 -1
- package/dist/types/router/reg-exp-router/trie.d.ts +2 -1
- package/dist/types/router.d.ts +1 -1
- package/dist/types/utils/headers.d.ts +2 -2
- package/dist/types/utils/url.d.ts +1 -0
- package/dist/utils/cookie.js +2 -2
- package/dist/utils/url.js +5 -6
- package/package.json +9 -1
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
// src/middleware/cache/index.ts
|
|
2
|
+
import { cloneRawRequest } from "../../request.js";
|
|
3
|
+
import { sha256 } from "../../utils/crypto.js";
|
|
2
4
|
var defaultCacheableStatusCodes = [200];
|
|
5
|
+
var defaultMaxQueryBodySize = 64 * 1024;
|
|
6
|
+
var cacheKeyPath = "/.hono/cache";
|
|
7
|
+
var cacheKeyParameter = "__hono_cache_key";
|
|
8
|
+
var cacheMethodKeyParameter = "__hono_cache_method";
|
|
9
|
+
var queryDigestKeyParameter = "__hono_query_digest";
|
|
10
|
+
var cacheVaryKeyParameter = "__hono_cache_vary";
|
|
11
|
+
var queryRepresentationMetadataHeaders = [
|
|
12
|
+
"content-type",
|
|
13
|
+
"content-encoding",
|
|
14
|
+
"content-language",
|
|
15
|
+
"content-location"
|
|
16
|
+
];
|
|
3
17
|
var shouldSkipCacheControl = (cacheControl) => !!cacheControl && /(?:^|,\s*)(?:private|no-(?:store|cache))(?:\s*(?:=|,|$))/i.test(cacheControl);
|
|
4
18
|
var parseVaryDirectives = (vary) => {
|
|
5
19
|
if (vary == null) {
|
|
@@ -7,17 +21,86 @@ var parseVaryDirectives = (vary) => {
|
|
|
7
21
|
}
|
|
8
22
|
return (Array.isArray(vary) ? vary : vary.split(",")).map((directive) => directive.trim().toLowerCase()).filter(Boolean);
|
|
9
23
|
};
|
|
24
|
+
var createCacheKey = (key, requestUrl, request, varyHeaders) => {
|
|
25
|
+
const url = new URL(cacheKeyPath, requestUrl);
|
|
26
|
+
url.searchParams.append(cacheKeyParameter, key.split("#", 1)[0]);
|
|
27
|
+
url.searchParams.append(cacheMethodKeyParameter, request.method);
|
|
28
|
+
if (request.method === "QUERY") {
|
|
29
|
+
url.searchParams.append(queryDigestKeyParameter, request.digest);
|
|
30
|
+
}
|
|
31
|
+
for (const header of varyHeaders) {
|
|
32
|
+
url.searchParams.append(cacheVaryKeyParameter, JSON.stringify(header));
|
|
33
|
+
}
|
|
34
|
+
return url.href;
|
|
35
|
+
};
|
|
10
36
|
var shouldSkipCache = (res, optionsVaryDirectives, responseVary) => responseVary.length && (!optionsVaryDirectives || responseVary.some((name) => !optionsVaryDirectives.has(name))) || shouldSkipCacheControl(res.headers.get("Cache-Control")) || res.headers.has("Set-Cookie");
|
|
37
|
+
var reportCacheNotAvailable = (onCacheNotAvailable, reason) => {
|
|
38
|
+
if (onCacheNotAvailable === false) {
|
|
39
|
+
} else if (onCacheNotAvailable) {
|
|
40
|
+
onCacheNotAvailable(reason);
|
|
41
|
+
} else {
|
|
42
|
+
console.log(reason);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var createQueryDigest = async (c, maxQueryBodySize) => {
|
|
46
|
+
if (!globalThis.crypto?.subtle) {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
if (c.req.raw.bodyUsed && Object.keys(c.req.bodyCache)[0] === "formData") {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const requestHeaders = c.req.raw.headers;
|
|
54
|
+
const metadata = new TextEncoder().encode(
|
|
55
|
+
JSON.stringify(
|
|
56
|
+
queryRepresentationMetadataHeaders.map((header) => [header, requestHeaders.get(header)])
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
const body = (await cloneRawRequest(c.req)).body;
|
|
60
|
+
const chunks = [];
|
|
61
|
+
let bodySize = 0;
|
|
62
|
+
if (body) {
|
|
63
|
+
const reader = body.getReader();
|
|
64
|
+
for (; ; ) {
|
|
65
|
+
const { done, value } = await reader.read();
|
|
66
|
+
if (done) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
bodySize += value.byteLength;
|
|
70
|
+
if (bodySize > maxQueryBodySize) {
|
|
71
|
+
void reader.cancel().catch(() => {
|
|
72
|
+
});
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
chunks.push(value);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const data = new Uint8Array(metadata.byteLength + bodySize);
|
|
79
|
+
data.set(metadata);
|
|
80
|
+
let offset = metadata.byteLength;
|
|
81
|
+
for (const chunk of chunks) {
|
|
82
|
+
data.set(chunk, offset);
|
|
83
|
+
offset += chunk.byteLength;
|
|
84
|
+
}
|
|
85
|
+
return await sha256(data) ?? void 0;
|
|
86
|
+
} catch {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
11
90
|
var cache = (options) => {
|
|
12
91
|
if (!globalThis.caches) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
console.log("Cache Middleware is not enabled because caches is not defined.");
|
|
18
|
-
}
|
|
92
|
+
reportCacheNotAvailable(
|
|
93
|
+
options.onCacheNotAvailable,
|
|
94
|
+
"Cache Middleware is not enabled because caches is not defined."
|
|
95
|
+
);
|
|
19
96
|
return async (_c, next) => await next();
|
|
20
97
|
}
|
|
98
|
+
if (!globalThis.crypto?.subtle) {
|
|
99
|
+
reportCacheNotAvailable(
|
|
100
|
+
options.onCacheNotAvailable,
|
|
101
|
+
"Cache Middleware cannot cache QUERY requests because Web Crypto is not available."
|
|
102
|
+
);
|
|
103
|
+
}
|
|
21
104
|
if (options.wait === void 0) {
|
|
22
105
|
options.wait = false;
|
|
23
106
|
}
|
|
@@ -32,6 +115,7 @@ var cache = (options) => {
|
|
|
32
115
|
const cacheableStatusCodes = new Set(
|
|
33
116
|
options.cacheableStatusCodes ?? defaultCacheableStatusCodes
|
|
34
117
|
);
|
|
118
|
+
const maxQueryBodySize = options.maxQueryBodySize ?? defaultMaxQueryBodySize;
|
|
35
119
|
const addHeader = (c, responseVary) => {
|
|
36
120
|
if (cacheControlDirectives) {
|
|
37
121
|
const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0].toLowerCase()) ?? [];
|
|
@@ -60,20 +144,31 @@ var cache = (options) => {
|
|
|
60
144
|
}
|
|
61
145
|
};
|
|
62
146
|
return async function cache2(c, next) {
|
|
63
|
-
if (c.req.method !== "GET" || c.req.raw.headers.has("Authorization")) {
|
|
147
|
+
if (c.req.method !== "GET" && c.req.method !== "QUERY" || c.req.raw.headers.has("Authorization")) {
|
|
64
148
|
await next();
|
|
65
149
|
return;
|
|
66
150
|
}
|
|
151
|
+
let cacheKeyRequest = { method: "GET" };
|
|
152
|
+
if (c.req.method === "QUERY") {
|
|
153
|
+
const digest = await createQueryDigest(c, maxQueryBodySize);
|
|
154
|
+
if (digest === void 0) {
|
|
155
|
+
await next();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
cacheKeyRequest = { method: "QUERY", digest };
|
|
159
|
+
}
|
|
67
160
|
let key = c.req.url;
|
|
68
161
|
if (options.keyGenerator) {
|
|
69
162
|
key = await options.keyGenerator(c);
|
|
70
163
|
}
|
|
164
|
+
const varyHeaders = [];
|
|
71
165
|
if (varyDirectives) {
|
|
72
166
|
for (const directive of varyDirectives) {
|
|
73
167
|
const value = c.req.raw.headers.get(directive) ?? "";
|
|
74
|
-
|
|
168
|
+
varyHeaders.push([directive, value]);
|
|
75
169
|
}
|
|
76
170
|
}
|
|
171
|
+
key = createCacheKey(key, c.req.url, cacheKeyRequest, varyHeaders);
|
|
77
172
|
const cacheName = typeof options.cacheName === "function" ? await options.cacheName(c) : options.cacheName;
|
|
78
173
|
const cache3 = await caches.open(cacheName);
|
|
79
174
|
const response = await cache3.match(key);
|
|
@@ -21,6 +21,7 @@ var selectEncoding = (header, candidates) => {
|
|
|
21
21
|
}
|
|
22
22
|
return best?.encoding;
|
|
23
23
|
};
|
|
24
|
+
var varyAcceptEncodingRegExp = /(?:^|,)\s*accept-encoding\s*(?:,|$)/i;
|
|
24
25
|
var compress = (options) => {
|
|
25
26
|
const threshold = options?.threshold ?? 1024;
|
|
26
27
|
const candidates = options?.encoding ? [options.encoding] : ENCODING_TYPES;
|
|
@@ -44,6 +45,10 @@ var compress = (options) => {
|
|
|
44
45
|
!shouldTransform(ctx.res)) {
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
48
|
+
const current = ctx.res.headers.get("Vary");
|
|
49
|
+
if (current !== "*" && !(current && varyAcceptEncodingRegExp.test(current))) {
|
|
50
|
+
ctx.header("Vary", current ? `${current}, Accept-Encoding` : "Accept-Encoding");
|
|
51
|
+
}
|
|
47
52
|
const accepted = ctx.req.header("Accept-Encoding");
|
|
48
53
|
const encoding = selectEncoding(accepted, candidates);
|
|
49
54
|
if (!encoding || !ctx.res.body) {
|
|
@@ -48,7 +48,7 @@ var etag = (options) => {
|
|
|
48
48
|
}
|
|
49
49
|
etag3 = weak ? `W/"${hash}"` : `"${hash}"`;
|
|
50
50
|
}
|
|
51
|
-
const matched = ifNoneMatch === "*" ? (c.req.method === "GET" || c.req.method === "HEAD") && res.ok : etagMatches(etag3, ifNoneMatch);
|
|
51
|
+
const matched = ifNoneMatch === "*" ? (c.req.method === "GET" || c.req.method === "HEAD" || c.req.method === "QUERY") && res.ok : etagMatches(etag3, ifNoneMatch);
|
|
52
52
|
if (matched) {
|
|
53
53
|
c.res = new Response(null, {
|
|
54
54
|
status: 304,
|
|
@@ -24,7 +24,8 @@ var jwk = (options, init) => {
|
|
|
24
24
|
res: unauthorizedResponse({
|
|
25
25
|
ctx,
|
|
26
26
|
error: "invalid_request",
|
|
27
|
-
errDescription
|
|
27
|
+
errDescription,
|
|
28
|
+
realm: options.realm
|
|
28
29
|
})
|
|
29
30
|
});
|
|
30
31
|
} else {
|
|
@@ -62,7 +63,8 @@ var jwk = (options, init) => {
|
|
|
62
63
|
res: unauthorizedResponse({
|
|
63
64
|
ctx,
|
|
64
65
|
error: "invalid_request",
|
|
65
|
-
errDescription
|
|
66
|
+
errDescription,
|
|
67
|
+
realm: options.realm
|
|
66
68
|
})
|
|
67
69
|
});
|
|
68
70
|
}
|
|
@@ -89,7 +91,8 @@ var jwk = (options, init) => {
|
|
|
89
91
|
ctx,
|
|
90
92
|
error: "invalid_token",
|
|
91
93
|
statusText: "Unauthorized",
|
|
92
|
-
errDescription: "token verification failure"
|
|
94
|
+
errDescription: "token verification failure",
|
|
95
|
+
realm: options.realm
|
|
93
96
|
}),
|
|
94
97
|
cause
|
|
95
98
|
});
|
|
@@ -99,11 +102,13 @@ var jwk = (options, init) => {
|
|
|
99
102
|
};
|
|
100
103
|
};
|
|
101
104
|
function unauthorizedResponse(opts) {
|
|
105
|
+
const realm = (opts.realm ?? opts.ctx.req.url).replace(/"/g, '\\"');
|
|
106
|
+
const errDescription = opts.errDescription.replace(/"/g, '\\"');
|
|
102
107
|
return new Response("Unauthorized", {
|
|
103
108
|
status: 401,
|
|
104
109
|
statusText: opts.statusText,
|
|
105
110
|
headers: {
|
|
106
|
-
"WWW-Authenticate": `Bearer realm="${
|
|
111
|
+
"WWW-Authenticate": `Bearer realm="${realm}",error="${opts.error}",error_description="${errDescription}"`
|
|
107
112
|
}
|
|
108
113
|
});
|
|
109
114
|
}
|
|
@@ -27,7 +27,8 @@ var jwt = (options) => {
|
|
|
27
27
|
res: unauthorizedResponse({
|
|
28
28
|
ctx,
|
|
29
29
|
error: "invalid_request",
|
|
30
|
-
errDescription
|
|
30
|
+
errDescription,
|
|
31
|
+
realm: options.realm
|
|
31
32
|
})
|
|
32
33
|
});
|
|
33
34
|
} else {
|
|
@@ -62,7 +63,8 @@ var jwt = (options) => {
|
|
|
62
63
|
res: unauthorizedResponse({
|
|
63
64
|
ctx,
|
|
64
65
|
error: "invalid_request",
|
|
65
|
-
errDescription
|
|
66
|
+
errDescription,
|
|
67
|
+
realm: options.realm
|
|
66
68
|
})
|
|
67
69
|
});
|
|
68
70
|
}
|
|
@@ -83,7 +85,8 @@ var jwt = (options) => {
|
|
|
83
85
|
ctx,
|
|
84
86
|
error: "invalid_token",
|
|
85
87
|
statusText: "Unauthorized",
|
|
86
|
-
errDescription: "token verification failure"
|
|
88
|
+
errDescription: "token verification failure",
|
|
89
|
+
realm: options.realm
|
|
87
90
|
}),
|
|
88
91
|
cause
|
|
89
92
|
});
|
|
@@ -93,11 +96,13 @@ var jwt = (options) => {
|
|
|
93
96
|
};
|
|
94
97
|
};
|
|
95
98
|
function unauthorizedResponse(opts) {
|
|
99
|
+
const realm = (opts.realm ?? opts.ctx.req.url).replace(/"/g, '\\"');
|
|
100
|
+
const errDescription = opts.errDescription.replace(/"/g, '\\"');
|
|
96
101
|
return new Response("Unauthorized", {
|
|
97
102
|
status: 401,
|
|
98
103
|
statusText: opts.statusText,
|
|
99
104
|
headers: {
|
|
100
|
-
"WWW-Authenticate": `Bearer realm="${
|
|
105
|
+
"WWW-Authenticate": `Bearer realm="${realm}",error="${opts.error}",error_description="${errDescription}"`
|
|
101
106
|
}
|
|
102
107
|
});
|
|
103
108
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/middleware/method-not-allowed/index.ts
|
|
2
|
+
import { basePath, matchedRoutes } from "../../helper/route/index.js";
|
|
3
|
+
import { METHOD_NAME_ALL } from "../../router.js";
|
|
4
|
+
import { TrieRouter } from "../../router/trie-router/index.js";
|
|
5
|
+
var methodNotAllowed = (options) => {
|
|
6
|
+
let methodRouter;
|
|
7
|
+
return async function methodNotAllowed2(c, next) {
|
|
8
|
+
const routeIndex = c.req.routeIndex;
|
|
9
|
+
await next();
|
|
10
|
+
if (c.res.status !== 404) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (c.error) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!methodRouter) {
|
|
17
|
+
const methodsByPath = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const route of options.app.routes) {
|
|
19
|
+
if (route.method === METHOD_NAME_ALL || route.method === "HEAD") {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const methods2 = methodsByPath.get(route.path) ?? /* @__PURE__ */ new Set();
|
|
23
|
+
methods2.add(route.method);
|
|
24
|
+
if (route.method === "GET") {
|
|
25
|
+
methods2.add("HEAD");
|
|
26
|
+
}
|
|
27
|
+
methodsByPath.set(route.path, methods2);
|
|
28
|
+
}
|
|
29
|
+
methodRouter = new TrieRouter();
|
|
30
|
+
for (const [path, methods2] of methodsByPath) {
|
|
31
|
+
methodRouter.add(METHOD_NAME_ALL, path, [...methods2]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let requestPath = c.req.path;
|
|
35
|
+
const routes = matchedRoutes(c);
|
|
36
|
+
const currentRoute = routes[routeIndex];
|
|
37
|
+
const sourceRoute = options.app.routes.find((route) => route.handler === methodNotAllowed2);
|
|
38
|
+
if (currentRoute && sourceRoute) {
|
|
39
|
+
const currentBasePathParts = basePath(c, routeIndex).split("/").filter(Boolean);
|
|
40
|
+
const sourceBasePathLength = sourceRoute.basePath.split("/").filter(Boolean).length;
|
|
41
|
+
const mountBasePathParts = currentBasePathParts.slice(
|
|
42
|
+
0,
|
|
43
|
+
currentBasePathParts.length - sourceBasePathLength
|
|
44
|
+
);
|
|
45
|
+
if (mountBasePathParts.length > 0) {
|
|
46
|
+
const mountBasePath = `/${mountBasePathParts.join("/")}`;
|
|
47
|
+
requestPath = c.req.path.slice(mountBasePath.length) || "/";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const allowedMethods = /* @__PURE__ */ new Set();
|
|
51
|
+
for (const [methods2] of methodRouter.match(METHOD_NAME_ALL, requestPath)[0]) {
|
|
52
|
+
for (const method of methods2) {
|
|
53
|
+
allowedMethods.add(method);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (allowedMethods.size === 0 || allowedMethods.has(c.req.method)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
c.res.headers.delete("Allow");
|
|
60
|
+
c.res.headers.delete("Content-Length");
|
|
61
|
+
const methods = [...allowedMethods];
|
|
62
|
+
const allow = methods.join(", ");
|
|
63
|
+
c.res = options.onMethodNotAllowed ? await options.onMethodNotAllowed(c, methods) : c.text("Method Not Allowed", 405, { Allow: allow });
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export {
|
|
67
|
+
methodNotAllowed
|
|
68
|
+
};
|
package/dist/request.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import { HTTPException } from "./http-exception.js";
|
|
3
3
|
import { GET_MATCH_RESULT } from "./request/constants.js";
|
|
4
4
|
import { parseBody } from "./utils/body.js";
|
|
5
|
-
import {
|
|
6
|
-
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
5
|
+
import { getQueryParam, getQueryParams, tryDecodeURIComponent } from "./utils/url.js";
|
|
7
6
|
var HonoRequest = class {
|
|
8
7
|
/**
|
|
9
8
|
* `.raw` can get the raw Request object.
|
|
@@ -42,7 +41,6 @@ var HonoRequest = class {
|
|
|
42
41
|
this.raw = request;
|
|
43
42
|
this.path = path;
|
|
44
43
|
this.#matchResult = matchResult;
|
|
45
|
-
this.#validatedData = {};
|
|
46
44
|
}
|
|
47
45
|
param(key) {
|
|
48
46
|
return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
|
|
@@ -50,7 +48,7 @@ var HonoRequest = class {
|
|
|
50
48
|
#getDecodedParam(key) {
|
|
51
49
|
const paramKey = this.#matchResult[0][this.routeIndex][1][key];
|
|
52
50
|
const param = this.#getParamValue(paramKey);
|
|
53
|
-
return param &&
|
|
51
|
+
return param && tryDecodeURIComponent(param);
|
|
54
52
|
}
|
|
55
53
|
#getAllDecodedParams() {
|
|
56
54
|
const decoded = {};
|
|
@@ -58,7 +56,7 @@ var HonoRequest = class {
|
|
|
58
56
|
for (const key of keys) {
|
|
59
57
|
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
|
|
60
58
|
if (value !== void 0) {
|
|
61
|
-
decoded[key] =
|
|
59
|
+
decoded[key] = tryDecodeURIComponent(value);
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
return decoded;
|
|
@@ -91,8 +89,7 @@ var HonoRequest = class {
|
|
|
91
89
|
if (cachedBody) {
|
|
92
90
|
return cachedBody;
|
|
93
91
|
}
|
|
94
|
-
const anyCachedKey
|
|
95
|
-
if (anyCachedKey) {
|
|
92
|
+
for (const anyCachedKey in bodyCache) {
|
|
96
93
|
return bodyCache[anyCachedKey].then((body) => {
|
|
97
94
|
if (anyCachedKey === "json") {
|
|
98
95
|
body = JSON.stringify(body);
|
|
@@ -195,10 +192,11 @@ var HonoRequest = class {
|
|
|
195
192
|
* @param data - The validated data to add.
|
|
196
193
|
*/
|
|
197
194
|
addValidatedData(target, data) {
|
|
198
|
-
|
|
195
|
+
;
|
|
196
|
+
(this.#validatedData ??= {})[target] = data;
|
|
199
197
|
}
|
|
200
198
|
valid(target) {
|
|
201
|
-
return this.#validatedData[target];
|
|
199
|
+
return this.#validatedData?.[target];
|
|
202
200
|
}
|
|
203
201
|
/**
|
|
204
202
|
* `.url()` can get the request url strings.
|
|
@@ -12,7 +12,7 @@ function compareKey(a, b) {
|
|
|
12
12
|
return 1;
|
|
13
13
|
}
|
|
14
14
|
if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
|
|
15
|
-
return 1;
|
|
15
|
+
return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
|
|
16
16
|
} else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
|
|
17
17
|
return -1;
|
|
18
18
|
}
|
|
@@ -24,76 +24,75 @@ function compareKey(a, b) {
|
|
|
24
24
|
return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
|
|
25
25
|
}
|
|
26
26
|
var Node = class _Node {
|
|
27
|
+
// handler index of a dynamic path, or -1 for a static path terminal
|
|
27
28
|
#index;
|
|
28
29
|
#varIndex;
|
|
29
30
|
#children = /* @__PURE__ */ Object.create(null);
|
|
30
|
-
insert(tokens, index, paramMap, context,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
|
|
52
|
-
if (/\((?!\?:)/.test(regexpStr)) {
|
|
53
|
-
throw PATH_ERROR;
|
|
31
|
+
insert(tokens, index, paramMap, context, isStatic) {
|
|
32
|
+
let node = this;
|
|
33
|
+
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
34
|
+
const token = tokens[i];
|
|
35
|
+
const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
36
|
+
let nextNode;
|
|
37
|
+
if (pattern) {
|
|
38
|
+
const name = pattern[1];
|
|
39
|
+
let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
|
|
40
|
+
if (name && pattern[2]) {
|
|
41
|
+
if (regexpStr === ".*") {
|
|
42
|
+
throw PATH_ERROR;
|
|
43
|
+
}
|
|
44
|
+
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
|
|
45
|
+
if (/\((?!\?:)/.test(regexpStr)) {
|
|
46
|
+
throw PATH_ERROR;
|
|
47
|
+
}
|
|
48
|
+
if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
|
|
49
|
+
throw PATH_ERROR;
|
|
50
|
+
}
|
|
54
51
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
nextNode = node.#children[regexpStr];
|
|
53
|
+
if (!nextNode) {
|
|
54
|
+
if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
|
|
55
|
+
for (const k in node.#children) {
|
|
56
|
+
if (
|
|
57
|
+
// a single-char pattern coexists with single-char literals as a literal does
|
|
58
|
+
(regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
|
|
59
|
+
) {
|
|
60
|
+
throw PATH_ERROR;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
nextNode = node.#children[regexpStr] = new _Node();
|
|
62
65
|
}
|
|
63
|
-
if (pathErrorCheckOnly) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
node = this.#children[regexpStr] = new _Node();
|
|
67
66
|
if (name !== "") {
|
|
68
|
-
|
|
67
|
+
nextNode.#varIndex ??= context.varIndex++;
|
|
68
|
+
paramMap.push([name, nextNode.#varIndex]);
|
|
69
69
|
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)) {
|
|
80
|
-
throw PATH_ERROR;
|
|
70
|
+
} else {
|
|
71
|
+
nextNode = node.#children[token];
|
|
72
|
+
if (!nextNode) {
|
|
73
|
+
for (const k in node.#children) {
|
|
74
|
+
if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
|
|
75
|
+
throw PATH_ERROR;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
nextNode = node.#children[token] = new _Node();
|
|
81
79
|
}
|
|
82
|
-
if (pathErrorCheckOnly) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
node = this.#children[token] = new _Node();
|
|
86
80
|
}
|
|
81
|
+
node = nextNode;
|
|
82
|
+
}
|
|
83
|
+
if (node.#index !== void 0) {
|
|
84
|
+
throw PATH_ERROR;
|
|
87
85
|
}
|
|
88
|
-
node
|
|
86
|
+
node.#index = isStatic ? -1 : index;
|
|
89
87
|
}
|
|
90
88
|
buildRegExpStr() {
|
|
91
89
|
const childKeys = Object.keys(this.#children).sort(compareKey);
|
|
92
90
|
const strList = childKeys.map((k) => {
|
|
93
91
|
const c = this.#children[k];
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
const childStr = c.buildRegExpStr();
|
|
93
|
+
return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
|
|
94
|
+
}).filter(Boolean);
|
|
95
|
+
if (typeof this.#index === "number" && this.#index !== -1) {
|
|
97
96
|
strList.unshift(`#${this.#index}`);
|
|
98
97
|
}
|
|
99
98
|
if (strList.length === 0) {
|