acrylblends 1.0.3 → 1.0.5
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/docs/framework/agents/index.md +6 -4
- package/docs/framework/workshop/README.md +3 -3
- package/docs/framework/workshop/facilitator.md +1 -0
- package/lib/accept-CNNHu4Cf.js +193 -0
- package/lib/{accepts-XBuZPVRI.js → accepts-Btq83xPd.js} +1 -1
- package/lib/accepts.js +1 -1
- package/lib/acryl-base.js +1 -331
- package/lib/adapter-CaZ6FZUU.js +37 -0
- package/lib/adapter.js +1 -36
- package/lib/{app-BM0rTCp7.js → app-CQcGWjrk.js} +4 -4
- package/lib/app-base-CpRMZL4v.js +332 -0
- package/lib/{audit-BXnYsLge.d.ts → audit-D5z9uwOP.d.ts} +14 -1
- package/lib/{base-NSN_SNv-.js → base-D_6kfOr-.js} +1 -1
- package/lib/basic-auth-C0HTRpXh.js +20 -0
- package/lib/basic-auth-DFLflXNe.js +95 -0
- package/lib/basic-auth.js +1 -94
- package/lib/bearer-auth-Cwxt-vgS.js +94 -0
- package/lib/bearer-auth.js +1 -93
- package/lib/body-D7f4e7w_.js +94 -0
- package/lib/body-limit-WmIjWRLt.js +66 -0
- package/lib/body-limit.js +1 -65
- package/lib/buffer-CkEOJCcY.js +51 -0
- package/lib/bun.js +3 -3
- package/lib/cache-B0bHH7s5.js +169 -0
- package/lib/cache.js +1 -168
- package/lib/{client-DDLXwhBo.js → client-BrMFnMEd.js} +1 -1
- package/lib/{client-modules-CLvWOjbd.js → client-modules-drj4mjhj.js} +4 -4
- package/lib/client.js +1 -1
- package/lib/color-COa3DplO.js +37 -0
- package/lib/combine-TWuGdcbC.js +137 -0
- package/lib/combine.js +1 -136
- package/lib/{components-cEW3ovK0.js → components-B2ka9Ax1.js} +4 -4
- package/lib/{components-JXVm45kK.js → components-PVXwaOaC.js} +2 -2
- package/lib/compress-BzWqsHF1.js +79 -0
- package/lib/compress-C-9vK4B2.d.ts +11 -0
- package/lib/compress-DCyrrQ5D.js +11 -0
- package/lib/compress.d.ts +1 -1
- package/lib/compress.js +2 -78
- package/lib/concurrent-DrF3d2c-.js +30 -0
- package/lib/conninfo-BCROmdnf.d.ts +11 -0
- package/lib/conninfo-CWJwieh4.js +19 -0
- package/lib/constants-CBSEudrb.js +7 -0
- package/lib/{context-DDO0cfuP.js → context-CkOh7Ygv.js} +2 -2
- package/lib/context-storage-iuPoI_CR.js +52 -0
- package/lib/context-storage.js +1 -51
- package/lib/cookie-B1LlkzOe.js +125 -0
- package/lib/cookie-BHIduy2T.js +82 -0
- package/lib/cookie.js +1 -81
- package/lib/cordis/db-sqlite.d.ts +1 -1
- package/lib/cordis/door.d.ts +17 -3
- package/lib/cordis/door.js +123 -16
- package/lib/cordis/inspect.js +4 -4
- package/lib/cordis/kv-db.d.ts +1 -1
- package/lib/cordis/kv-db.js +1 -1
- package/lib/cordis/kv-json.d.ts +1 -1
- package/lib/cordis/kv-json.js +1 -1
- package/lib/cordis/node-server.d.ts +1 -1
- package/lib/cordis/node-server.js +3 -116
- package/lib/cordis/router-http.d.ts +1 -1
- package/lib/cordis/router-http.js +2 -2
- package/lib/cordis/router.d.ts +1 -1
- package/lib/cordis/router.js +4 -4
- package/lib/cordis/state.js +7 -7
- package/lib/{core-CaY8XDZ6.js → core-BOu1jYua.js} +1 -1
- package/lib/cors-CbAQ-8qU.js +106 -0
- package/lib/cors.js +1 -105
- package/lib/crypto-iVP-N-tS.js +34 -0
- package/lib/csrf-DqqOZtgy.js +101 -0
- package/lib/csrf.js +1 -100
- package/lib/css-C5CPVMvM.js +134 -0
- package/lib/css.js +2 -133
- package/lib/db/sqlite.d.ts +1 -25
- package/lib/dev-CLgu-JRe.js +50 -0
- package/lib/dev.js +1 -49
- package/lib/dom-YjND9Jt3.js +74 -0
- package/lib/door/host.d.ts +2 -2
- package/lib/door/host.js +1 -1
- package/lib/door/page.d.ts +12 -1
- package/lib/door/page.js +22 -1
- package/lib/door/protocol.d.ts +2 -2
- package/lib/door/protocol.js +2 -197
- package/lib/encode-BhbNLrjp.js +33 -0
- package/lib/factory-CLdSf-xS.js +30 -0
- package/lib/factory.js +1 -29
- package/lib/handler-C0wZ0KwO.js +12 -0
- package/lib/helpers-BEjcdbgw.js +5 -0
- package/lib/{hooks-w72iLeFR.js → hooks-CfC07ri0.js} +1 -1
- package/lib/html-CgNa89xw.js +31 -0
- package/lib/html-DGExBFNg.js +103 -0
- package/lib/html.d.ts +2 -5
- package/lib/html.js +2 -30
- package/lib/http-exception-MjWkT89V.js +54 -0
- package/lib/http-exception.js +1 -53
- package/lib/{index-D4nVR3wu.d.ts → index-DMOtA-NJ.d.ts} +7 -1
- package/lib/index-dAzQsJG9.d.ts +6 -0
- package/lib/index.js +3 -3
- package/lib/ip-restriction-jKtKCRM_.js +166 -0
- package/lib/ip-restriction.js +1 -165
- package/lib/ipaddr-DCoIo528.js +238 -0
- package/lib/jsx/dom/client.js +1 -1
- package/lib/jsx/dom/jsx-dev-runtime.js +1 -1
- package/lib/jsx/dom/jsx-runtime.js +2 -1
- package/lib/jsx/dom/server.js +3 -3
- package/lib/jsx/dom.js +5 -73
- package/lib/jsx/jsx-dev-runtime.d.ts +2 -5
- package/lib/jsx/jsx-dev-runtime.js +2 -13
- package/lib/jsx/jsx-runtime.d.ts +2 -2
- package/lib/jsx/jsx-runtime.js +4 -4
- package/lib/jsx/streaming.js +1 -1
- package/lib/jsx-CUQgttxU.js +47 -0
- package/lib/jsx-dev-runtime-B4WTyq8r.js +14 -0
- package/lib/{jsx-dev-runtime-CWLRA2F3.js → jsx-dev-runtime-BLfA-EhI.js} +2 -2
- package/lib/jsx-dev-runtime-CGt4kNYr.d.ts +6 -0
- package/lib/jsx-renderer-CfifM4hT.js +99 -0
- package/lib/jsx-renderer.js +1 -98
- package/lib/jsx-runtime-COCRWBJ9.js +2 -0
- package/lib/jsx.js +5 -46
- package/lib/{jwk-LkzeoaU8.js → jwk-CDPpYQpD.js} +3 -3
- package/lib/jwk.js +1 -1
- package/lib/{jwt-BGJBw5AF.js → jwt-BXoIA2fQ.js} +2 -2
- package/lib/{jwt-CskdhEW9.js → jwt-lyS4Q3mT.js} +3 -3
- package/lib/jwt.js +2 -2
- package/lib/kv/db.d.ts +1 -10
- package/lib/kv/db.js +1 -56
- package/lib/kv/json.d.ts +1 -9
- package/lib/kv/json.js +1 -223
- package/lib/{language-D9a4zqvn.js → language-DeGRtLJt.js} +2 -2
- package/lib/language.js +1 -1
- package/lib/{linear-router-C3FPib6h.js → linear-router-CmZn_JLV.js} +2 -2
- package/lib/logger-Ckxu8diz.js +54 -0
- package/lib/logger.js +1 -53
- package/lib/loopback-only-BbasoF7R.js +90 -0
- package/lib/loopback-only.js +1 -89
- package/lib/method-not-allowed-TO2_hnfl.js +82 -0
- package/lib/method-not-allowed.js +1 -81
- package/lib/method-override-DnZKfJbV.js +103 -0
- package/lib/method-override.js +1 -102
- package/lib/middleware-BfG_fp8P.js +12 -0
- package/lib/mime-BnScXkQn.js +77 -0
- package/lib/mount-QkK_3NtJ.js +67 -0
- package/lib/mount.js +1 -66
- package/lib/node-server/conninfo.d.ts +1 -10
- package/lib/node-server/conninfo.js +1 -18
- package/lib/node-server/register.js +25 -2
- package/lib/node-server/serve-static.d.ts +1 -17
- package/lib/node-server/serve-static.js +1 -1
- package/lib/node-server.d.ts +2 -2
- package/lib/node-server.js +4 -4
- package/lib/open-BqK4D2Wn.d.ts +11 -0
- package/lib/open-But9E2rL.js +57 -0
- package/lib/open-CULVJgOc.d.ts +12 -0
- package/lib/open-Dp4sxspc.js +224 -0
- package/lib/open-T7kgbO2O.d.ts +27 -0
- package/lib/{pattern-router-HZxjDGEQ.js → pattern-router-CJBecJBd.js} +1 -1
- package/lib/powered-by-CXQNjNIX.js +24 -0
- package/lib/powered-by.js +1 -23
- package/lib/pretty-json-CeC1yvYt.js +39 -0
- package/lib/pretty-json.js +1 -38
- package/lib/{protocol-BizVt4J6.d.ts → protocol-C37gPePD.d.ts} +9 -2
- package/lib/protocol-Dt0AZBOz.js +213 -0
- package/lib/proxy-C2w_mH7k.js +121 -0
- package/lib/proxy.js +1 -120
- package/lib/quick.js +4 -4
- package/lib/{reg-exp-router-DC1R3qoY.js → reg-exp-router-C_WV84eV.js} +2 -2
- package/lib/request-DvMgGtlW.js +323 -0
- package/lib/request.js +1 -322
- package/lib/route-D8m-a2I1.js +126 -0
- package/lib/route.js +1 -125
- package/lib/router/linear-router.js +1 -1
- package/lib/router/pattern-router.js +1 -1
- package/lib/router/reg-exp-router.js +1 -1
- package/lib/router/smart-router.js +1 -1
- package/lib/router/trie-router.js +1 -1
- package/lib/router-Be_o6nFu.js +35 -0
- package/lib/router.js +1 -34
- package/lib/schema-validator-EK3cUUCt.js +22 -0
- package/lib/schema-validator.js +1 -21
- package/lib/{secure-headers-mP3j6Hm6.js → secure-headers-C5ZOcXqk.js} +1 -1
- package/lib/secure-headers.js +1 -1
- package/lib/serve-static-B7sqnftW.d.ts +18 -0
- package/lib/{serve-static-3uo_9rjF.js → serve-static-C1Nkj_xB.js} +2 -2
- package/lib/{serve-static-Cp5adzGo.js → serve-static-EiJf1zkO.js} +4 -4
- package/lib/serve-static.js +1 -1
- package/lib/{smart-router-C5CZ5XSM.js → smart-router-BjxkDQCE.js} +1 -1
- package/lib/{snapshot-text-DmE0YfeZ.js → snapshot-text-Bbx43mN0.js} +57 -9
- package/lib/{src-2Z4mJg0x.js → src-BTBm4OPD.js} +22 -6
- package/lib/src-LX0E4ec4.js +117 -0
- package/lib/{ssg-BwjcA-Ge.js → ssg-BEl7MHqg.js} +5 -5
- package/lib/ssg.js +1 -1
- package/lib/stream-CzM_SHkq.js +88 -0
- package/lib/{streaming-DHST3Scg.js → streaming-BlDaph8U.js} +3 -3
- package/lib/streaming.js +1 -1
- package/lib/{test-websocket-D8WWqE5G.js → test-websocket-BLeEvybj.js} +2 -2
- package/lib/testing-BQx5T_UX.js +17 -0
- package/lib/testing.js +1 -16
- package/lib/timeout-FeqZ9A-0.js +42 -0
- package/lib/timeout.js +1 -41
- package/lib/tiny.js +2 -2
- package/lib/trailing-slash-DwTx-1sk.js +85 -0
- package/lib/trailing-slash.js +1 -84
- package/lib/{trie-router-CfFbapzL.js → trie-router-JydLKUa2.js} +2 -2
- package/lib/url-BPh4d6j-.js +197 -0
- package/lib/utils/accept.js +1 -192
- package/lib/utils/basic-auth.js +1 -19
- package/lib/utils/body.js +1 -93
- package/lib/utils/buffer.js +1 -50
- package/lib/utils/color.js +1 -36
- package/lib/utils/compress.d.ts +1 -10
- package/lib/utils/compress.js +1 -10
- package/lib/utils/concurrent.js +1 -29
- package/lib/utils/constants.js +1 -6
- package/lib/utils/cookie.js +1 -124
- package/lib/utils/crypto.js +1 -33
- package/lib/utils/encode.js +1 -32
- package/lib/utils/handler.js +1 -11
- package/lib/utils/html.js +1 -102
- package/lib/utils/ipaddr.js +1 -237
- package/lib/utils/jwt.js +1 -1
- package/lib/utils/mime.js +1 -76
- package/lib/utils/stream.js +1 -87
- package/lib/utils/url.js +1 -196
- package/lib/{validator-D84mq-dO.js → validator-Btiyt5VJ.js} +3 -3
- package/lib/validator.js +1 -1
- package/lib/websocket-DsyBIKVA.js +56 -0
- package/lib/ws.js +1 -55
- package/package.json +1 -1
- package/plugins/acryl-door/src/cli/acryl.ts +5 -3
- package/plugins/acryl-door/src/host/channel.ts +36 -0
- package/plugins/acryl-door/src/host/chat-tools.ts +2 -0
- package/plugins/acryl-door/src/host/door.ts +44 -5
- package/plugins/acryl-door/src/host/shim.ts +23 -0
- package/plugins/acryl-door/src/host/verbs.ts +4 -3
- package/plugins/acryl-door/src/index.ts +39 -3
- package/plugins/acryl-door/src/page/driver/driver.ts +22 -1
- package/plugins/acryl-door/src/page/protocol.ts +13 -2
- package/plugins/acryl-node-server/src/register.ts +29 -2
- package/plugins/acryl-router/src/service.ts +19 -1
- package/plugins/agent/model.ts +6 -1
- package/plugins/agent/tools.ts +12 -2
- package/plugins/agent-panel/client/describe.ts +44 -0
- package/plugins/agent-panel/client/main.ts +67 -35
- package/plugins/agent-panel/index.ts +137 -35
- package/plugins/desktop/attach.ts +72 -0
- package/plugins/desktop/main.ts +17 -4
- package/plugins/extensions/index.ts +3 -4
- package/plugins/extensions/stage.ts +33 -5
- package/plugins/openai/index.ts +1 -1
- package/shared/DEFAULTS.md +4 -0
- package/templates/agent/AGENTS.md +2 -0
- package/templates/agent/blend.yaml +4 -1
- package/templates/agent/src/app.ts +2 -0
- package/templates/agent/src/routes.ts +1 -1
- package/templates/agent/template.json +1 -0
- package/templates/agent/test/builder.test.ts +26 -21
- package/lib/helpers-CvTauQT5.js +0 -5
- package/lib/middleware-CPQ8himN.js +0 -12
package/lib/bearer-auth.js
CHANGED
|
@@ -1,94 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { timingSafeEqual } from "./utils/buffer.js";
|
|
3
|
-
//#region ../../plugins/acryl-router/src/core/middleware/bearer-auth/index.ts
|
|
4
|
-
const TOKEN_STRINGS = "[A-Za-z0-9._~+/-]+=*";
|
|
5
|
-
const PREFIX = "Bearer";
|
|
6
|
-
const HEADER = "Authorization";
|
|
7
|
-
/**
|
|
8
|
-
* Bearer Auth Middleware for Acryl.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @template E - The environment type.
|
|
12
|
-
* @param {BearerAuthOptions} options - The options for the bearer authentication middleware.
|
|
13
|
-
* @param {string | string[]} [options.token] - The string or array of strings to validate the incoming bearer token against.
|
|
14
|
-
* @param {Function} [options.verifyToken] - The function to verify the token.
|
|
15
|
-
* @param {string} [options.realm=""] - The domain name of the realm, as part of the returned WWW-Authenticate challenge header.
|
|
16
|
-
* @param {string} [options.prefix="Bearer"] - The prefix (or known as `schema`) for the Authorization header value. If set to the empty string, no prefix is expected.
|
|
17
|
-
* @param {string} [options.headerName=Authorization] - The header name.
|
|
18
|
-
* @param {Function} [options.hashFunction] - A function to handle hashing for safe comparison of authentication tokens.
|
|
19
|
-
* @param {string | object | MessageFunction} [options.noAuthenticationHeader.message="Unauthorized"] - The no authentication header message.
|
|
20
|
-
* @param {string | object | MessageFunction} [options.noAuthenticationHeader.wwwAuthenticateHeader="Bearer realm=\"\""] - The response header value for the WWW-Authenticate header when no authentication header is provided.
|
|
21
|
-
* @param {string | object | MessageFunction} [options.invalidAuthenticationHeader.message="Bad Request"] - The invalid authentication header message.
|
|
22
|
-
* @param {string | object | MessageFunction} [options.invalidAuthenticationHeader.wwwAuthenticateHeader="Bearer error=\"invalid_request\""] - The response header value for the WWW-Authenticate header when authentication header is invalid.
|
|
23
|
-
* @param {string | object | MessageFunction} [options.invalidToken.message="Unauthorized"] - The invalid token message.
|
|
24
|
-
* @param {string | object | MessageFunction} [options.invalidToken.wwwAuthenticateHeader="Bearer error=\"invalid_token\""] - The response header value for the WWW-Authenticate header when token is invalid.
|
|
25
|
-
* @returns {MiddlewareHandler<E>} The middleware handler function.
|
|
26
|
-
* @throws {Error} If neither "token" nor "verifyToken" options are provided.
|
|
27
|
-
* @throws {HTTPException} If authentication fails, with 401 status code for missing or invalid token, or 400 status code for invalid request.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* const app = new Acryl()
|
|
32
|
-
*
|
|
33
|
-
* const token = process.env.API_TOKEN // never a literal in source
|
|
34
|
-
*
|
|
35
|
-
* app.use('/api/*', bearerAuth({ token }))
|
|
36
|
-
*
|
|
37
|
-
* app.get('/api/page', (c) => {
|
|
38
|
-
* return c.json({ message: 'You are authorized' })
|
|
39
|
-
* })
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
const bearerAuth = (options) => {
|
|
43
|
-
if (!("token" in options || "verifyToken" in options)) throw new Error("bearer auth middleware requires options for \"token\" or \"verifyToken\"");
|
|
44
|
-
if (!options.realm) options.realm = "";
|
|
45
|
-
if (options.prefix === void 0) options.prefix = PREFIX;
|
|
46
|
-
const realm = options.realm?.replace(/"/g, "\\\"");
|
|
47
|
-
const prefix = options.prefix;
|
|
48
|
-
const tokenRegexp = new RegExp(`^${TOKEN_STRINGS}$`);
|
|
49
|
-
const wwwAuthenticatePrefix = prefix === "" ? "" : `${prefix} `;
|
|
50
|
-
const throwHTTPException = async (c, status, wwwAuthenticateHeader, messageOption) => {
|
|
51
|
-
const wwwAuthenticateHeaderValue = typeof wwwAuthenticateHeader === "function" ? await wwwAuthenticateHeader(c) : wwwAuthenticateHeader;
|
|
52
|
-
const headers = { "WWW-Authenticate": typeof wwwAuthenticateHeaderValue === "string" ? wwwAuthenticateHeaderValue : `${wwwAuthenticatePrefix}${Object.entries(wwwAuthenticateHeaderValue).map(([key, value]) => `${key}="${value}"`).join(",")}` };
|
|
53
|
-
const responseMessage = typeof messageOption === "function" ? await messageOption(c) : messageOption;
|
|
54
|
-
throw new HTTPException(status, { res: typeof responseMessage === "string" ? new Response(responseMessage, {
|
|
55
|
-
status,
|
|
56
|
-
headers
|
|
57
|
-
}) : new Response(JSON.stringify(responseMessage), {
|
|
58
|
-
status,
|
|
59
|
-
headers: {
|
|
60
|
-
...headers,
|
|
61
|
-
"content-type": "application/json"
|
|
62
|
-
}
|
|
63
|
-
}) });
|
|
64
|
-
};
|
|
65
|
-
return async function bearerAuth(c, next) {
|
|
66
|
-
const headerToken = c.req.header(options.headerName || HEADER);
|
|
67
|
-
if (!headerToken) await throwHTTPException(c, 401, options.noAuthenticationHeader?.wwwAuthenticateHeader || `${wwwAuthenticatePrefix}realm="${realm}"`, options.noAuthenticationHeader?.message || options.noAuthenticationHeaderMessage || "Unauthorized");
|
|
68
|
-
else {
|
|
69
|
-
let tokenValue;
|
|
70
|
-
if (prefix === "") tokenValue = headerToken;
|
|
71
|
-
else {
|
|
72
|
-
const headerLower = headerToken.toLowerCase();
|
|
73
|
-
const prefixLower = prefix.toLowerCase();
|
|
74
|
-
if (headerLower.startsWith(prefixLower) && headerToken[prefix.length] === " ") tokenValue = headerToken.slice(prefix.length).trimStart();
|
|
75
|
-
}
|
|
76
|
-
if (!tokenValue || !tokenRegexp.test(tokenValue)) await throwHTTPException(c, 400, options.invalidAuthenticationHeader?.wwwAuthenticateHeader || `${wwwAuthenticatePrefix}error="invalid_request"`, options.invalidAuthenticationHeader?.message || options.invalidAuthenticationHeaderMessage || "Bad Request");
|
|
77
|
-
else {
|
|
78
|
-
let equal = false;
|
|
79
|
-
if ("verifyToken" in options) equal = await options.verifyToken(tokenValue, c);
|
|
80
|
-
else if (typeof options.token === "string") equal = await timingSafeEqual(options.token, tokenValue, options.hashFunction);
|
|
81
|
-
else if (Array.isArray(options.token) && options.token.length > 0) {
|
|
82
|
-
for (const token of options.token) if (await timingSafeEqual(token, tokenValue, options.hashFunction)) {
|
|
83
|
-
equal = true;
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!equal) await throwHTTPException(c, 401, options.invalidToken?.wwwAuthenticateHeader || `${wwwAuthenticatePrefix}error="invalid_token"`, options.invalidToken?.message || options.invalidTokenMessage || "Unauthorized");
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
await next();
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
//#endregion
|
|
1
|
+
import { t as bearerAuth } from "./bearer-auth-Cwxt-vgS.js";
|
|
94
2
|
export { bearerAuth };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { t as bufferToFormData } from "./buffer-CkEOJCcY.js";
|
|
2
|
+
//#region ../../plugins/acryl-router/src/core/utils/body.ts
|
|
3
|
+
const MAX_NESTED_OBJECTS = 1e4;
|
|
4
|
+
const isRawRequest = (request) => "headers" in request;
|
|
5
|
+
const parseBody = async (request, options = Object.create(null)) => {
|
|
6
|
+
const { all = false, dot = false } = options;
|
|
7
|
+
const mediaType = (isRawRequest(request) ? request.headers : request.raw.headers).get("Content-Type")?.split(";")[0].trim().toLowerCase();
|
|
8
|
+
if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") return parseFormData(request, {
|
|
9
|
+
all,
|
|
10
|
+
dot
|
|
11
|
+
});
|
|
12
|
+
return {};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Parses form data from a request.
|
|
16
|
+
*
|
|
17
|
+
* @template T - The type of the parsed body data.
|
|
18
|
+
* @param {AcrylRequest | Request} request - The request object containing form data.
|
|
19
|
+
* @param {ParseBodyOptions} options - Options for parsing the form data.
|
|
20
|
+
* @returns {Promise<T>} The parsed body data.
|
|
21
|
+
*/
|
|
22
|
+
async function parseFormData(request, options) {
|
|
23
|
+
if (!isRawRequest(request) && request.bodyCache.formData) return convertFormDataToBodyData(await request.bodyCache.formData, options);
|
|
24
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
25
|
+
const formDataPromise = bufferToFormData(await request.arrayBuffer(), headers.get("Content-Type") || "");
|
|
26
|
+
if (!isRawRequest(request)) request.bodyCache.formData = formDataPromise;
|
|
27
|
+
const formData = await formDataPromise;
|
|
28
|
+
if (formData) return convertFormDataToBodyData(formData, options);
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Converts form data to body data based on the provided options.
|
|
33
|
+
*
|
|
34
|
+
* @template T - The type of the parsed body data.
|
|
35
|
+
* @param {FormData} formData - The form data to convert.
|
|
36
|
+
* @param {ParseBodyOptions} options - Options for parsing the form data.
|
|
37
|
+
* @returns {T} The converted body data.
|
|
38
|
+
*/
|
|
39
|
+
function convertFormDataToBodyData(formData, options) {
|
|
40
|
+
const form = Object.create(null);
|
|
41
|
+
const nestingState = { count: 0 };
|
|
42
|
+
formData.forEach((value, key) => {
|
|
43
|
+
if (!(options.all || key.endsWith("[]"))) form[key] = value;
|
|
44
|
+
else handleParsingAllValues(form, key, value);
|
|
45
|
+
});
|
|
46
|
+
if (options.dot) Object.entries(form).forEach(([key, value]) => {
|
|
47
|
+
if (key.includes(".")) {
|
|
48
|
+
handleParsingNestedValues(form, key, value, nestingState);
|
|
49
|
+
delete form[key];
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return form;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Handles parsing all values for a given key, supporting multiple values as arrays.
|
|
56
|
+
*
|
|
57
|
+
* @param {BodyData} form - The form data object.
|
|
58
|
+
* @param {string} key - The key to parse.
|
|
59
|
+
* @param {FormDataEntryValue} value - The value to assign.
|
|
60
|
+
*/
|
|
61
|
+
const handleParsingAllValues = (form, key, value) => {
|
|
62
|
+
if (form[key] !== void 0) if (Array.isArray(form[key])) form[key].push(value);
|
|
63
|
+
else form[key] = [form[key], value];
|
|
64
|
+
else if (!key.endsWith("[]")) form[key] = value;
|
|
65
|
+
else form[key] = [value];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Handles parsing nested values using dot notation keys.
|
|
69
|
+
*
|
|
70
|
+
* @param {BodyData} form - The form data object.
|
|
71
|
+
* @param {string} key - The dot notation key.
|
|
72
|
+
* @param {BodyDataValue} value - The value to assign.
|
|
73
|
+
*/
|
|
74
|
+
const handleParsingNestedValues = (form, key, value, state) => {
|
|
75
|
+
if (/(?:^|\.)__proto__\./.test(key)) return;
|
|
76
|
+
let nestedForm = form;
|
|
77
|
+
const keys = key.split(".", 34);
|
|
78
|
+
if (keys.length > 33) throwNestingLimitExceeded();
|
|
79
|
+
keys.forEach((key, index) => {
|
|
80
|
+
if (index === keys.length - 1) nestedForm[key] = value;
|
|
81
|
+
else {
|
|
82
|
+
if (!nestedForm[key] || typeof nestedForm[key] !== "object" || Array.isArray(nestedForm[key]) || nestedForm[key] instanceof File) {
|
|
83
|
+
if (state.count++ >= MAX_NESTED_OBJECTS) throwNestingLimitExceeded();
|
|
84
|
+
nestedForm[key] = Object.create(null);
|
|
85
|
+
}
|
|
86
|
+
nestedForm = nestedForm[key];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const throwNestingLimitExceeded = () => {
|
|
91
|
+
throw new Error("Nesting limit exceeded");
|
|
92
|
+
};
|
|
93
|
+
//#endregion
|
|
94
|
+
export { parseBody as t };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { t as HTTPException } from "./http-exception-MjWkT89V.js";
|
|
2
|
+
//#region ../../plugins/acryl-router/src/core/middleware/body-limit/index.ts
|
|
3
|
+
const ERROR_MESSAGE = "Payload Too Large";
|
|
4
|
+
/**
|
|
5
|
+
* Body Limit Middleware for Acryl.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* @param {BodyLimitOptions} options - The options for the body limit middleware.
|
|
9
|
+
* @param {number} options.maxSize - The maximum body size allowed.
|
|
10
|
+
* @param {OnError} [options.onError] - The error handler to be invoked if the specified body size is exceeded.
|
|
11
|
+
* @returns {MiddlewareHandler} The middleware handler function.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const app = new Acryl()
|
|
16
|
+
*
|
|
17
|
+
* app.post(
|
|
18
|
+
* '/upload',
|
|
19
|
+
* bodyLimit({
|
|
20
|
+
* maxSize: 50 * 1024, // 50kb
|
|
21
|
+
* onError: (c) => {
|
|
22
|
+
* return c.text('overflow :(', 413)
|
|
23
|
+
* },
|
|
24
|
+
* }),
|
|
25
|
+
* async (c) => {
|
|
26
|
+
* const body = await c.req.parseBody()
|
|
27
|
+
* if (body['file'] instanceof File) {
|
|
28
|
+
* console.log(`Got file sized: ${body['file'].size}`)
|
|
29
|
+
* }
|
|
30
|
+
* return c.text('pass :)')
|
|
31
|
+
* }
|
|
32
|
+
* )
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const bodyLimit = (options) => {
|
|
36
|
+
const onError = options.onError || (() => {
|
|
37
|
+
throw new HTTPException(413, { res: new Response(ERROR_MESSAGE, { status: 413 }) });
|
|
38
|
+
});
|
|
39
|
+
const maxSize = options.maxSize;
|
|
40
|
+
return async function bodyLimit(c, next) {
|
|
41
|
+
if (!c.req.raw.body) return next();
|
|
42
|
+
const hasTransferEncoding = c.req.raw.headers.has("transfer-encoding");
|
|
43
|
+
if (c.req.raw.headers.has("content-length") && !hasTransferEncoding) return parseInt(c.req.raw.headers.get("content-length") || "0", 10) > maxSize ? onError(c) : next();
|
|
44
|
+
let size = 0;
|
|
45
|
+
const chunks = [];
|
|
46
|
+
const rawReader = c.req.raw.body.getReader();
|
|
47
|
+
for (;;) {
|
|
48
|
+
const { done, value } = await rawReader.read();
|
|
49
|
+
if (done) break;
|
|
50
|
+
size += value.length;
|
|
51
|
+
if (size > maxSize) return onError(c);
|
|
52
|
+
chunks.push(value);
|
|
53
|
+
}
|
|
54
|
+
const requestInit = {
|
|
55
|
+
body: new ReadableStream({ start(controller) {
|
|
56
|
+
for (const chunk of chunks) controller.enqueue(chunk);
|
|
57
|
+
controller.close();
|
|
58
|
+
} }),
|
|
59
|
+
duplex: "half"
|
|
60
|
+
};
|
|
61
|
+
c.req.raw = new Request(c.req.raw, requestInit);
|
|
62
|
+
return next();
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { bodyLimit as t };
|
package/lib/body-limit.js
CHANGED
|
@@ -1,66 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region ../../plugins/acryl-router/src/core/middleware/body-limit/index.ts
|
|
3
|
-
const ERROR_MESSAGE = "Payload Too Large";
|
|
4
|
-
/**
|
|
5
|
-
* Body Limit Middleware for Acryl.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @param {BodyLimitOptions} options - The options for the body limit middleware.
|
|
9
|
-
* @param {number} options.maxSize - The maximum body size allowed.
|
|
10
|
-
* @param {OnError} [options.onError] - The error handler to be invoked if the specified body size is exceeded.
|
|
11
|
-
* @returns {MiddlewareHandler} The middleware handler function.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* const app = new Acryl()
|
|
16
|
-
*
|
|
17
|
-
* app.post(
|
|
18
|
-
* '/upload',
|
|
19
|
-
* bodyLimit({
|
|
20
|
-
* maxSize: 50 * 1024, // 50kb
|
|
21
|
-
* onError: (c) => {
|
|
22
|
-
* return c.text('overflow :(', 413)
|
|
23
|
-
* },
|
|
24
|
-
* }),
|
|
25
|
-
* async (c) => {
|
|
26
|
-
* const body = await c.req.parseBody()
|
|
27
|
-
* if (body['file'] instanceof File) {
|
|
28
|
-
* console.log(`Got file sized: ${body['file'].size}`)
|
|
29
|
-
* }
|
|
30
|
-
* return c.text('pass :)')
|
|
31
|
-
* }
|
|
32
|
-
* )
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
const bodyLimit = (options) => {
|
|
36
|
-
const onError = options.onError || (() => {
|
|
37
|
-
throw new HTTPException(413, { res: new Response(ERROR_MESSAGE, { status: 413 }) });
|
|
38
|
-
});
|
|
39
|
-
const maxSize = options.maxSize;
|
|
40
|
-
return async function bodyLimit(c, next) {
|
|
41
|
-
if (!c.req.raw.body) return next();
|
|
42
|
-
const hasTransferEncoding = c.req.raw.headers.has("transfer-encoding");
|
|
43
|
-
if (c.req.raw.headers.has("content-length") && !hasTransferEncoding) return parseInt(c.req.raw.headers.get("content-length") || "0", 10) > maxSize ? onError(c) : next();
|
|
44
|
-
let size = 0;
|
|
45
|
-
const chunks = [];
|
|
46
|
-
const rawReader = c.req.raw.body.getReader();
|
|
47
|
-
for (;;) {
|
|
48
|
-
const { done, value } = await rawReader.read();
|
|
49
|
-
if (done) break;
|
|
50
|
-
size += value.length;
|
|
51
|
-
if (size > maxSize) return onError(c);
|
|
52
|
-
chunks.push(value);
|
|
53
|
-
}
|
|
54
|
-
const requestInit = {
|
|
55
|
-
body: new ReadableStream({ start(controller) {
|
|
56
|
-
for (const chunk of chunks) controller.enqueue(chunk);
|
|
57
|
-
controller.close();
|
|
58
|
-
} }),
|
|
59
|
-
duplex: "half"
|
|
60
|
-
};
|
|
61
|
-
c.req.raw = new Request(c.req.raw, requestInit);
|
|
62
|
-
return next();
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
//#endregion
|
|
1
|
+
import { t as bodyLimit } from "./body-limit-WmIjWRLt.js";
|
|
66
2
|
export { bodyLimit };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { i as sha256 } from "./crypto-iVP-N-tS.js";
|
|
2
|
+
//#region ../../plugins/acryl-router/src/core/utils/buffer.ts
|
|
3
|
+
/**
|
|
4
|
+
* @module
|
|
5
|
+
* Buffer utility.
|
|
6
|
+
*/
|
|
7
|
+
const equal = (a, b) => {
|
|
8
|
+
if (a === b) return true;
|
|
9
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
10
|
+
const va = new DataView(a);
|
|
11
|
+
const vb = new DataView(b);
|
|
12
|
+
let i = va.byteLength;
|
|
13
|
+
while (i--) if (va.getUint8(i) !== vb.getUint8(i)) return false;
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
const constantTimeEqualString = (a, b) => {
|
|
17
|
+
const aLen = a.length;
|
|
18
|
+
const bLen = b.length;
|
|
19
|
+
const maxLen = Math.max(aLen, bLen);
|
|
20
|
+
let out = aLen ^ bLen;
|
|
21
|
+
for (let i = 0; i < maxLen; i++) {
|
|
22
|
+
const aChar = i < aLen ? a.charCodeAt(i) : 0;
|
|
23
|
+
const bChar = i < bLen ? b.charCodeAt(i) : 0;
|
|
24
|
+
out |= aChar ^ bChar;
|
|
25
|
+
}
|
|
26
|
+
return out === 0;
|
|
27
|
+
};
|
|
28
|
+
const timingSafeEqualString = async (a, b, hashFunction) => {
|
|
29
|
+
if (!hashFunction) hashFunction = sha256;
|
|
30
|
+
const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]);
|
|
31
|
+
if (sa == null || sb == null || typeof sa !== "string" || typeof sb !== "string") return false;
|
|
32
|
+
const hashEqual = constantTimeEqualString(sa, sb);
|
|
33
|
+
const originalEqual = constantTimeEqualString(a, b);
|
|
34
|
+
return hashEqual && originalEqual;
|
|
35
|
+
};
|
|
36
|
+
const timingSafeEqual = async (a, b, hashFunction) => {
|
|
37
|
+
if (typeof a === "string" && typeof b === "string") return timingSafeEqualString(a, b, hashFunction);
|
|
38
|
+
if (!hashFunction) hashFunction = sha256;
|
|
39
|
+
const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]);
|
|
40
|
+
if (!sa || !sb || typeof sa !== "string" || typeof sb !== "string") return false;
|
|
41
|
+
return timingSafeEqualString(sa, sb);
|
|
42
|
+
};
|
|
43
|
+
const bufferToString = (buffer) => {
|
|
44
|
+
if (buffer instanceof ArrayBuffer) return new TextDecoder("utf-8").decode(buffer);
|
|
45
|
+
return buffer;
|
|
46
|
+
};
|
|
47
|
+
const bufferToFormData = (arrayBuffer, contentType) => {
|
|
48
|
+
return new Response(arrayBuffer, { headers: { "Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase()) } }).formData();
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { timingSafeEqual as i, bufferToString as n, equal as r, bufferToFormData as t };
|
package/lib/bun.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as serveStatic$1 } from "./serve-static-
|
|
2
|
-
import { c as toSSG$1 } from "./ssg-
|
|
3
|
-
import {
|
|
1
|
+
import { t as serveStatic$1 } from "./serve-static-EiJf1zkO.js";
|
|
2
|
+
import { c as toSSG$1 } from "./ssg-BEl7MHqg.js";
|
|
3
|
+
import { n as createWSMessageEvent, r as defineWebSocketHelper, t as WSContext } from "./websocket-DsyBIKVA.js";
|
|
4
4
|
import { stat } from "node:fs/promises";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
//#region ../../plugins/acryl-router/src/core/adapter/bun/serve-static.ts
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { i as sha256 } from "./crypto-iVP-N-tS.js";
|
|
2
|
+
import { n as cloneRawRequest } from "./request-DvMgGtlW.js";
|
|
3
|
+
//#region ../../plugins/acryl-router/src/core/middleware/cache/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* status codes that can be cached by default.
|
|
6
|
+
*/
|
|
7
|
+
const defaultCacheableStatusCodes = [200];
|
|
8
|
+
const defaultMaxQueryBodySize = 64 * 1024;
|
|
9
|
+
const cacheKeyPath = "/.acryl-router/cache";
|
|
10
|
+
const cacheKeyParameter = "__acryl_cache_key";
|
|
11
|
+
const cacheMethodKeyParameter = "__acryl_cache_method";
|
|
12
|
+
const queryDigestKeyParameter = "__acryl_query_digest";
|
|
13
|
+
const cacheVaryKeyParameter = "__acryl_cache_vary";
|
|
14
|
+
const queryRepresentationMetadataHeaders = [
|
|
15
|
+
"content-type",
|
|
16
|
+
"content-encoding",
|
|
17
|
+
"content-language",
|
|
18
|
+
"content-location"
|
|
19
|
+
];
|
|
20
|
+
const shouldSkipCacheControl = (cacheControl) => !!cacheControl && /(?:^|,\s*)(?:private|no-(?:store|cache))(?:\s*(?:=|,|$))/i.test(cacheControl);
|
|
21
|
+
const parseVaryDirectives = (vary) => {
|
|
22
|
+
if (vary == null) return [];
|
|
23
|
+
return (Array.isArray(vary) ? vary : vary.split(",")).map((directive) => directive.trim().toLowerCase()).filter(Boolean);
|
|
24
|
+
};
|
|
25
|
+
const createCacheKey = (key, requestUrl, request, varyHeaders) => {
|
|
26
|
+
const url = new URL(cacheKeyPath, requestUrl);
|
|
27
|
+
url.searchParams.append(cacheKeyParameter, key);
|
|
28
|
+
url.searchParams.append(cacheMethodKeyParameter, request.method);
|
|
29
|
+
if (request.method === "QUERY") url.searchParams.append(queryDigestKeyParameter, request.digest);
|
|
30
|
+
for (const header of varyHeaders) url.searchParams.append(cacheVaryKeyParameter, JSON.stringify(header));
|
|
31
|
+
return url.href;
|
|
32
|
+
};
|
|
33
|
+
const shouldSkipCache = (res, optionsVaryDirectives, responseVary) => responseVary.length && (!optionsVaryDirectives || responseVary.some((name) => !optionsVaryDirectives.has(name))) || shouldSkipCacheControl(res.headers.get("Cache-Control")) || res.headers.has("Set-Cookie");
|
|
34
|
+
const reportCacheNotAvailable = (onCacheNotAvailable, reason) => {
|
|
35
|
+
if (onCacheNotAvailable === false) {} else if (onCacheNotAvailable) onCacheNotAvailable(reason);
|
|
36
|
+
else console.log(reason);
|
|
37
|
+
};
|
|
38
|
+
const createQueryDigest = async (c, maxQueryBodySize) => {
|
|
39
|
+
if (!globalThis.crypto?.subtle) return;
|
|
40
|
+
if (c.req.raw.bodyUsed && Object.keys(c.req.bodyCache)[0] === "formData") return;
|
|
41
|
+
try {
|
|
42
|
+
const requestHeaders = c.req.raw.headers;
|
|
43
|
+
const metadata = new TextEncoder().encode(JSON.stringify(queryRepresentationMetadataHeaders.map((header) => [header, requestHeaders.get(header)])));
|
|
44
|
+
const body = (await cloneRawRequest(c.req)).body;
|
|
45
|
+
const chunks = [];
|
|
46
|
+
let bodySize = 0;
|
|
47
|
+
if (body) {
|
|
48
|
+
const reader = body.getReader();
|
|
49
|
+
for (;;) {
|
|
50
|
+
const { done, value } = await reader.read();
|
|
51
|
+
if (done) break;
|
|
52
|
+
bodySize += value.byteLength;
|
|
53
|
+
if (bodySize > maxQueryBodySize) {
|
|
54
|
+
reader.cancel().catch(() => {});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
chunks.push(value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const data = new Uint8Array(metadata.byteLength + bodySize);
|
|
61
|
+
data.set(metadata);
|
|
62
|
+
let offset = metadata.byteLength;
|
|
63
|
+
for (const chunk of chunks) {
|
|
64
|
+
data.set(chunk, offset);
|
|
65
|
+
offset += chunk.byteLength;
|
|
66
|
+
}
|
|
67
|
+
return await sha256(data) ?? void 0;
|
|
68
|
+
} catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Cache Middleware for Acryl.
|
|
74
|
+
*
|
|
75
|
+
*
|
|
76
|
+
* @param {Object} options - The options for the cache middleware.
|
|
77
|
+
* @param {string | Function} options.cacheName - The name of the cache. Can be used to store multiple caches with different identifiers.
|
|
78
|
+
* @param {boolean} [options.wait=false] - A boolean indicating if Acryl should wait for the Promise of the `cache.put` function to resolve before continuing with the request. Required to be true for the Deno environment.
|
|
79
|
+
* @param {string} [options.cacheControl] - A string of directives for the `Cache-Control` header.
|
|
80
|
+
* @param {string | string[]} [options.vary] - Adds the configured request headers to the cache key variants and sets the `Vary` header in the response. If the original response header already contains a `Vary` header, the values are merged, removing any duplicates.
|
|
81
|
+
* @param {Function} [options.keyGenerator] - Generates keys for every request in the `cacheName` store. This can be used to cache data based on request parameters or context parameters. QUERY keys additionally include a digest of the request content and its representation metadata.
|
|
82
|
+
* @param {number} [options.maxQueryBodySize=65536] - The maximum QUERY request body size in bytes that can be cached. Larger QUERY requests bypass the cache.
|
|
83
|
+
* @param {number[]} [options.cacheableStatusCodes=[200]] - An array of status codes that can be cached.
|
|
84
|
+
* @param {Function | false} [options.onCacheNotAvailable] - A callback invoked with the reason when `globalThis.caches` is not available or QUERY caching cannot use Web Crypto. By default, the reason is logged to the console. Set to `false` to suppress the log, or provide a custom function.
|
|
85
|
+
* @returns {MiddlewareHandler} The middleware handler function.
|
|
86
|
+
* @throws {Error} If the `vary` option includes "*".
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* app.use(
|
|
91
|
+
* '*',
|
|
92
|
+
* cache({
|
|
93
|
+
* cacheName: 'my-app',
|
|
94
|
+
* cacheControl: 'max-age=3600',
|
|
95
|
+
* })
|
|
96
|
+
* )
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
const cache = (options) => {
|
|
100
|
+
if (!globalThis.caches) {
|
|
101
|
+
reportCacheNotAvailable(options.onCacheNotAvailable, "Cache Middleware is not enabled because caches is not defined.");
|
|
102
|
+
return async (_c, next) => await next();
|
|
103
|
+
}
|
|
104
|
+
if (!globalThis.crypto?.subtle) reportCacheNotAvailable(options.onCacheNotAvailable, "Cache Middleware cannot cache QUERY requests because Web Crypto is not available.");
|
|
105
|
+
if (options.wait === void 0) options.wait = false;
|
|
106
|
+
const cacheControlDirectives = options.cacheControl?.split(",").map((directive) => directive.toLowerCase());
|
|
107
|
+
const optionsVaryList = parseVaryDirectives(options.vary);
|
|
108
|
+
const varyDirectives = optionsVaryList.length ? new Set(optionsVaryList) : void 0;
|
|
109
|
+
if (varyDirectives?.has("*")) throw new Error("Middleware vary configuration cannot include \"*\", as it disallows effective caching.");
|
|
110
|
+
const cacheableStatusCodes = new Set(options.cacheableStatusCodes ?? defaultCacheableStatusCodes);
|
|
111
|
+
const maxQueryBodySize = options.maxQueryBodySize ?? defaultMaxQueryBodySize;
|
|
112
|
+
const addHeader = (c, responseVary) => {
|
|
113
|
+
if (cacheControlDirectives) {
|
|
114
|
+
const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0].toLowerCase()) ?? [];
|
|
115
|
+
for (const directive of cacheControlDirectives) {
|
|
116
|
+
let [name, value] = directive.trim().split("=", 2);
|
|
117
|
+
name = name.toLowerCase();
|
|
118
|
+
if (!existingDirectives.includes(name)) c.header("Cache-Control", `${name}${value ? `=${value}` : ""}`, { append: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (varyDirectives) if (responseVary.length === 0) c.header("Vary", Array.from(varyDirectives).join(", "));
|
|
122
|
+
else {
|
|
123
|
+
const merged = new Set(varyDirectives);
|
|
124
|
+
for (const directive of responseVary) merged.add(directive);
|
|
125
|
+
if (merged.has("*")) c.header("Vary", "*");
|
|
126
|
+
else c.header("Vary", Array.from(merged).join(", "));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
return async function cache(c, next) {
|
|
130
|
+
if (c.req.method !== "GET" && c.req.method !== "QUERY" || c.req.raw.headers.has("Authorization")) {
|
|
131
|
+
await next();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let cacheKeyRequest = { method: "GET" };
|
|
135
|
+
if (c.req.method === "QUERY") {
|
|
136
|
+
const digest = await createQueryDigest(c, maxQueryBodySize);
|
|
137
|
+
if (digest === void 0) {
|
|
138
|
+
await next();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
cacheKeyRequest = {
|
|
142
|
+
method: "QUERY",
|
|
143
|
+
digest
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
let key = c.req.url;
|
|
147
|
+
if (options.keyGenerator) key = await options.keyGenerator(c);
|
|
148
|
+
const varyHeaders = [];
|
|
149
|
+
if (varyDirectives) for (const directive of varyDirectives) {
|
|
150
|
+
const value = c.req.raw.headers.get(directive) ?? "";
|
|
151
|
+
varyHeaders.push([directive, value]);
|
|
152
|
+
}
|
|
153
|
+
key = createCacheKey(key, c.req.url, cacheKeyRequest, varyHeaders);
|
|
154
|
+
const cacheName = typeof options.cacheName === "function" ? await options.cacheName(c) : options.cacheName;
|
|
155
|
+
const cache = await caches.open(cacheName);
|
|
156
|
+
const response = await cache.match(key);
|
|
157
|
+
if (response) return new Response(response.body, response);
|
|
158
|
+
await next();
|
|
159
|
+
if (!cacheableStatusCodes.has(c.res.status)) return;
|
|
160
|
+
const responseVary = parseVaryDirectives(c.res.headers.get("Vary"));
|
|
161
|
+
addHeader(c, responseVary);
|
|
162
|
+
if (shouldSkipCache(c.res, varyDirectives, responseVary)) return;
|
|
163
|
+
const res = c.res.clone();
|
|
164
|
+
if (options.wait) await cache.put(key, res);
|
|
165
|
+
else c.executionCtx.waitUntil(cache.put(key, res));
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
//#endregion
|
|
169
|
+
export { cache as t };
|