acrylblends 0.3.0 → 0.4.1
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/index.d.ts +6 -1
- package/dist/index.js +84 -5
- package/dist/index.js.map +1 -1
- package/docs/framework/LICENSE +23 -0
- package/docs/framework/README.md +63 -0
- package/docs/framework/UPSTREAM.md +3 -0
- package/docs/framework/api-summary.md +138 -0
- package/docs/framework/concepts.md +105 -0
- package/docs/framework/context.md +129 -0
- package/docs/framework/getting-started.md +146 -0
- package/docs/framework/helpers.md +118 -0
- package/docs/framework/middleware.md +154 -0
- package/docs/framework/migration-from-0-3.md +108 -0
- package/docs/framework/migration-from-dsh-webserver.md +103 -0
- package/docs/framework/node-server.md +75 -0
- package/docs/framework/plugin-lifecycle.md +111 -0
- package/docs/framework/request.md +106 -0
- package/docs/framework/routing.md +188 -0
- package/docs/framework/surfaces.md +71 -0
- package/docs/framework/upstream.json +9 -0
- package/docs/framework/validation.md +135 -0
- package/package.json +4 -2
- package/plugins/acryl-inspect/plugin.json +2 -1
- package/plugins/acryl-inspect/src/report.ts +21 -13
- package/plugins/acryl-inspect/src/service.ts +9 -7
- package/plugins/acryl-inspect/tests/report.spec.ts +13 -8
- package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
- package/plugins/acryl-inspect/upstream.json +2 -2
- package/plugins/acryl-node-server/LICENSE +26 -0
- package/plugins/acryl-node-server/plugin.json +2 -1
- package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
- package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
- package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
- package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
- package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
- package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
- package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
- package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
- package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
- package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
- package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
- package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
- package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
- package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
- package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
- package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
- package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
- package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
- package/plugins/acryl-node-server/src/index.ts +19 -6
- package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
- package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
- package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
- package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
- package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
- package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
- package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
- package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
- package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
- package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
- package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
- package/plugins/acryl-node-server/upstream.json +3 -3
- package/plugins/acryl-router/LICENSE +26 -0
- package/plugins/acryl-router/plugin.json +2 -1
- package/plugins/acryl-router/src/client.ts +2 -0
- package/plugins/acryl-router/src/connection.ts +42 -0
- package/plugins/acryl-router/src/core/app-base.ts +544 -0
- package/plugins/acryl-router/src/core/app.test.ts +3833 -0
- package/plugins/acryl-router/src/core/app.ts +34 -0
- package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
- package/plugins/acryl-router/src/core/client/client.ts +249 -0
- package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
- package/plugins/acryl-router/src/core/client/index.ts +17 -0
- package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
- package/plugins/acryl-router/src/core/client/types.ts +393 -0
- package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
- package/plugins/acryl-router/src/core/client/utils.ts +117 -0
- package/plugins/acryl-router/src/core/compose.test.ts +751 -0
- package/plugins/acryl-router/src/core/compose.ts +73 -0
- package/plugins/acryl-router/src/core/context.test.ts +629 -0
- package/plugins/acryl-router/src/core/context.ts +781 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
- package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
- package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
- package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
- package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
- package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
- package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
- package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
- package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
- package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
- package/plugins/acryl-router/src/core/http-exception.ts +77 -0
- package/plugins/acryl-router/src/core/index.ts +53 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
- package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
- package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
- package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
- package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
- package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
- package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
- package/plugins/acryl-router/src/core/request/constants.ts +1 -0
- package/plugins/acryl-router/src/core/request.test.ts +751 -0
- package/plugins/acryl-router/src/core/request.ts +502 -0
- package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
- package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
- package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
- package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
- package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
- package/plugins/acryl-router/src/core/router/utils.ts +1 -0
- package/plugins/acryl-router/src/core/router.ts +103 -0
- package/plugins/acryl-router/src/core/types.test.ts +4134 -0
- package/plugins/acryl-router/src/core/types.ts +2782 -0
- package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
- package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
- package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
- package/plugins/acryl-router/src/core/utils/body.ts +263 -0
- package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
- package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
- package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
- package/plugins/acryl-router/src/core/utils/color.ts +56 -0
- package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
- package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
- package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
- package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
- package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
- package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
- package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
- package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
- package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
- package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
- package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
- package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
- package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
- package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
- package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
- package/plugins/acryl-router/src/core/utils/html.ts +182 -0
- package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
- package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
- package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
- package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
- package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
- package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
- package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
- package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
- package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
- package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
- package/plugins/acryl-router/src/core/utils/types.ts +123 -0
- package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
- package/plugins/acryl-router/src/core/utils/url.ts +337 -0
- package/plugins/acryl-router/src/core/validator/index.ts +8 -0
- package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
- package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
- package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
- package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
- package/plugins/acryl-router/src/helpers/index.ts +13 -3
- package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
- package/plugins/acryl-router/src/index.ts +6 -6
- package/plugins/acryl-router/src/middleware/index.ts +25 -6
- package/plugins/acryl-router/src/middleware/loopback-only.ts +53 -14
- package/plugins/acryl-router/src/service.ts +262 -64
- package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
- package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
- package/plugins/acryl-router/tests/loader.spec.ts +3 -2
- package/plugins/acryl-router/tests/loopback-only.spec.ts +80 -0
- package/plugins/acryl-router/tests/service.spec.ts +281 -66
- package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/plugin.json +2 -1
- package/plugins/acryl-router-http/src/bridge.ts +3 -1
- package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
- package/plugins/acryl-router-http/upstream.json +3 -3
- package/plugins/acryl-state/NOTICE +30 -0
- package/plugins/acryl-state/plugin.json +1 -1
- package/plugins/acryl-state/src/cell.ts +1 -1
- package/plugins/acryl-state/src/service.ts +33 -15
- package/plugins/acryl-state/upstream.json +6 -4
- package/plugins/builder/script.ts +4 -7
- package/plugins/extensions/index.ts +6 -5
- package/plugins/extensions/resolve.ts +28 -3
- package/plugins/openai/prompt.ts +9 -7
- package/plugins/tsconfig.json +5 -4
- package/shared/AGENTS.core.md +54 -0
- package/templates/agent/AGENTS.md +6 -0
- package/templates/agent/README.md +2 -2
- package/templates/agent/blend.yaml +1 -1
- package/templates/agent/src/routes.ts +37 -27
- package/templates/agent/test/model.test.ts +2 -1
- package/templates/agent/tsconfig.json +8 -5
- package/templates/blank/AGENTS.md +5 -0
- package/templates/blank/README.md +1 -1
- package/templates/blank/src/routes.ts +6 -4
- package/templates/blank/tsconfig.json +2 -4
- package/templates/todo/AGENTS.md +5 -0
- package/templates/todo/src/page.ts +1 -1
- package/templates/todo/src/todo.ts +58 -29
- package/templates/todo/tsconfig.json +2 -4
- package/templates/todo/upstream.json +2 -2
- package/plugins/acryl-router/src/compose.ts +0 -21
- package/plugins/acryl-router/src/context.ts +0 -59
- package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
- package/plugins/acryl-router/src/helpers/sse.ts +0 -83
- package/plugins/acryl-router/src/helpers/validate.ts +0 -44
- package/plugins/acryl-router/src/http-error.ts +0 -21
- package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
- package/plugins/acryl-router/src/middleware/cors.ts +0 -46
- package/plugins/acryl-router/src/middleware/logger.ts +0 -21
- package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
- package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
- package/plugins/acryl-router/src/pattern.ts +0 -89
- package/plugins/acryl-router/src/registrar.ts +0 -90
- package/plugins/acryl-router/src/table.ts +0 -156
- package/plugins/acryl-router/src/types.ts +0 -73
- package/plugins/acryl-router/src/util.ts +0 -23
- package/plugins/acryl-router/tests/api.spec.ts +0 -223
- package/plugins/acryl-router/tests/conformance.ts +0 -170
- package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
- package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
- package/plugins/acryl-router/tests/table.spec.ts +0 -47
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { Context } from '../context.ts'
|
|
2
|
+
import { getCookie } from '../helper/cookie/index.ts'
|
|
3
|
+
import { HTTPException } from '../http-exception.ts'
|
|
4
|
+
import type { Env, MiddlewareHandler, TypedResponse, ValidationTargets, FormValue } from '../types.ts'
|
|
5
|
+
import type { BodyData } from '../utils/body.ts'
|
|
6
|
+
import { bufferToFormData } from '../utils/buffer.ts'
|
|
7
|
+
import type { InferInput } from './utils.ts'
|
|
8
|
+
|
|
9
|
+
type ValidationTargetKeysWithBody = 'form' | 'json'
|
|
10
|
+
type ValidationTargetByMethod<M> = M extends 'get' | 'head' // GET and HEAD request must not have a body content.
|
|
11
|
+
? Exclude<keyof ValidationTargets, ValidationTargetKeysWithBody>
|
|
12
|
+
: keyof ValidationTargets
|
|
13
|
+
|
|
14
|
+
export type ValidationFunction<
|
|
15
|
+
InputType,
|
|
16
|
+
OutputType,
|
|
17
|
+
E extends Env = {},
|
|
18
|
+
P extends string = string,
|
|
19
|
+
> = (
|
|
20
|
+
value: InputType,
|
|
21
|
+
c: Context<E, P>
|
|
22
|
+
) => OutputType | TypedResponse | Promise<OutputType> | Promise<TypedResponse>
|
|
23
|
+
|
|
24
|
+
const jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
|
|
25
|
+
const multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i
|
|
26
|
+
const urlencodedRegex = /^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i
|
|
27
|
+
|
|
28
|
+
export type ExtractValidationResponse<VF> = VF extends (value: any, c: any) => infer R
|
|
29
|
+
? R extends Promise<infer PR>
|
|
30
|
+
? PR extends TypedResponse<infer T, infer S, infer F>
|
|
31
|
+
? TypedResponse<T, S, F>
|
|
32
|
+
: PR extends Response
|
|
33
|
+
? PR
|
|
34
|
+
: PR extends undefined
|
|
35
|
+
? never // undefined → never
|
|
36
|
+
: never // anything else → never
|
|
37
|
+
: R extends TypedResponse<infer T, infer S, infer F>
|
|
38
|
+
? TypedResponse<T, S, F>
|
|
39
|
+
: R extends Response
|
|
40
|
+
? R
|
|
41
|
+
: R extends undefined
|
|
42
|
+
? never // undefined → never
|
|
43
|
+
: never // anything else → never
|
|
44
|
+
: never // Can't extract → never
|
|
45
|
+
|
|
46
|
+
export const validator = <
|
|
47
|
+
InputType,
|
|
48
|
+
P extends string,
|
|
49
|
+
M extends string,
|
|
50
|
+
U extends ValidationTargetByMethod<M>,
|
|
51
|
+
P2 extends string = P,
|
|
52
|
+
// Capture the actual validation function as a type
|
|
53
|
+
VF extends (
|
|
54
|
+
value: unknown extends InputType ? ValidationTargets[U] : InputType,
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
c: Context<any, P2>
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
) => any = (
|
|
59
|
+
value: unknown extends InputType ? ValidationTargets[U] : InputType,
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
c: Context<any, P2>
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
) => any,
|
|
64
|
+
V extends {
|
|
65
|
+
in: {
|
|
66
|
+
[K in U]: K extends 'json'
|
|
67
|
+
? unknown extends InputType
|
|
68
|
+
? ExtractValidatorOutput<VF>
|
|
69
|
+
: InputType
|
|
70
|
+
: InferInput<ExtractValidatorOutput<VF>, K, FormValue>
|
|
71
|
+
}
|
|
72
|
+
out: { [K in U]: ExtractValidatorOutput<VF> }
|
|
73
|
+
} = {
|
|
74
|
+
in: {
|
|
75
|
+
[K in U]: K extends 'json'
|
|
76
|
+
? unknown extends InputType
|
|
77
|
+
? ExtractValidatorOutput<VF>
|
|
78
|
+
: InputType
|
|
79
|
+
: InferInput<ExtractValidatorOutput<VF>, K, FormValue>
|
|
80
|
+
}
|
|
81
|
+
out: { [K in U]: ExtractValidatorOutput<VF> }
|
|
82
|
+
},
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
E extends Env = any,
|
|
85
|
+
>(
|
|
86
|
+
target: U,
|
|
87
|
+
validationFunc: VF
|
|
88
|
+
): MiddlewareHandler<E, P, V, ExtractValidationResponse<VF>> => {
|
|
89
|
+
return async (c, next) => {
|
|
90
|
+
let value = {}
|
|
91
|
+
const contentType = c.req.header('Content-Type')
|
|
92
|
+
|
|
93
|
+
switch (target) {
|
|
94
|
+
case 'json':
|
|
95
|
+
if (!contentType || !jsonRegex.test(contentType)) {
|
|
96
|
+
break
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
value = await c.req.json()
|
|
100
|
+
} catch {
|
|
101
|
+
const message = 'Malformed JSON in request body'
|
|
102
|
+
throw new HTTPException(400, { message })
|
|
103
|
+
}
|
|
104
|
+
break
|
|
105
|
+
case 'form': {
|
|
106
|
+
if (
|
|
107
|
+
!contentType ||
|
|
108
|
+
!(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))
|
|
109
|
+
) {
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let formData: FormData
|
|
114
|
+
|
|
115
|
+
if (c.req.bodyCache.formData) {
|
|
116
|
+
formData = await c.req.bodyCache.formData
|
|
117
|
+
} else {
|
|
118
|
+
try {
|
|
119
|
+
const arrayBuffer = await c.req.arrayBuffer()
|
|
120
|
+
formData = await bufferToFormData(arrayBuffer, contentType)
|
|
121
|
+
c.req.bodyCache.formData = formData
|
|
122
|
+
} catch (e) {
|
|
123
|
+
let message = 'Malformed FormData request.'
|
|
124
|
+
message += e instanceof Error ? ` ${e.message}` : ` ${String(e)}`
|
|
125
|
+
throw new HTTPException(400, { message })
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const form: BodyData<{ all: true }> = Object.create(null)
|
|
130
|
+
formData.forEach((value, key) => {
|
|
131
|
+
if (key.endsWith('[]')) {
|
|
132
|
+
;((form[key] ??= []) as unknown[]).push(value)
|
|
133
|
+
} else if (Array.isArray(form[key])) {
|
|
134
|
+
;(form[key] as unknown[]).push(value)
|
|
135
|
+
} else if (Object.hasOwn(form, key)) {
|
|
136
|
+
form[key] = [form[key] as string | File, value]
|
|
137
|
+
} else {
|
|
138
|
+
form[key] = value
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
value = form
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
case 'query':
|
|
145
|
+
value = Object.fromEntries(
|
|
146
|
+
Object.entries(c.req.queries()).map(([k, v]) => {
|
|
147
|
+
return v.length === 1 ? [k, v[0]] : [k, v]
|
|
148
|
+
})
|
|
149
|
+
)
|
|
150
|
+
break
|
|
151
|
+
case 'param':
|
|
152
|
+
value = c.req.param() as Record<string, string>
|
|
153
|
+
break
|
|
154
|
+
case 'header':
|
|
155
|
+
value = c.req.header()
|
|
156
|
+
break
|
|
157
|
+
case 'cookie':
|
|
158
|
+
value = getCookie(c)
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const res = await validationFunc(value as never, c as never)
|
|
163
|
+
|
|
164
|
+
if (res instanceof Response) {
|
|
165
|
+
return res as ExtractValidationResponse<VF>
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
c.req.addValidatedData(target, res as never)
|
|
169
|
+
|
|
170
|
+
return (await next()) as ExtractValidationResponse<VF>
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
|
+
export type ExtractValidatorOutput<VF> = VF extends (value: any, c: any) => infer R
|
|
176
|
+
? R extends Promise<infer PR>
|
|
177
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
178
|
+
PR extends Response | TypedResponse<any, any, any>
|
|
179
|
+
? never
|
|
180
|
+
: PR
|
|
181
|
+
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
+
R extends Response | TypedResponse<any, any, any>
|
|
183
|
+
? never
|
|
184
|
+
: R
|
|
185
|
+
: never
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { getConnInfo, connectionOf } from '../connection.ts'
|
|
2
|
+
export type { AddressType, ConnInfo, GetConnInfo, NetAddrInfo } from '../core/helper/conninfo/index.ts'
|
|
3
|
+
export { accepts } from '../core/helper/accepts/index.ts'
|
|
4
|
+
export { createFactory, createMiddleware, Factory } from '../core/helper/factory/index.ts'
|
|
5
|
+
export { deleteCookie, generateCookie, generateSignedCookie, getCookie, getSignedCookie, setCookie, setSignedCookie } from '../core/helper/cookie/index.ts'
|
|
6
|
+
export { getExtension, getMimeType, mimes, type BaseMime } from '../core/utils/mime.ts'
|
|
7
|
+
export { html, raw } from '../core/helper/html/index.ts'
|
|
8
|
+
export { matchedRoutes, routePath, baseRoutePath, basePath } from '../core/helper/route/index.ts'
|
|
9
|
+
export { testClient } from '../core/helper/testing/index.ts'
|
|
10
|
+
export { stream, streamSSE, streamText, SSEStreamingApi, type SSEMessage } from '../core/helper/streaming/index.ts'
|
|
11
|
+
export { validator, type ValidationFunction } from '../core/validator/index.ts'
|
|
12
|
+
export { schemaValidator, type StandardSchema } from './schema-validator.ts'
|
|
13
|
+
export { createWSMessageEvent, defineWebSocketHelper, WSContext, type SendOptions, type UpgradeWebSocket, type WSContextInit, type WSEvents, type WSMessageReceive, type WSReadyState } from '../core/helper/websocket/index.ts'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Env, MiddlewareHandler, ValidationTargets } from '../core/types.ts'
|
|
2
|
+
import { validator } from '../core/validator/index.ts'
|
|
3
|
+
|
|
4
|
+
/** The slice of the Standard Schema interface this needs; Schemastery (what Cordis uses), Zod and Valibot all implement it. */
|
|
5
|
+
export interface StandardSchema<T = unknown> {
|
|
6
|
+
readonly '~standard': {
|
|
7
|
+
validate(value: unknown): StandardResult<T> | Promise<StandardResult<T>>
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type StandardResult<T> =
|
|
12
|
+
| { readonly value: T, readonly issues?: undefined }
|
|
13
|
+
| { readonly issues: readonly { readonly message: string, readonly path?: readonly unknown[] | undefined }[] }
|
|
14
|
+
|
|
15
|
+
const pathOf = (path: readonly unknown[] | undefined): string =>
|
|
16
|
+
(path ?? []).map(part => String(typeof part === 'object' && part !== null && 'key' in part ? (part as { key: unknown }).key : part)).join('.')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The core `validator` driven by a Standard Schema. A failure answers 400 with `{ error, issues }`; success makes the parsed value the
|
|
20
|
+
* handler's `c.req.valid(target)`, typed by the schema.
|
|
21
|
+
*/
|
|
22
|
+
export function schemaValidator<Target extends keyof ValidationTargets, Out, E extends Env = Env, P extends string = string>(
|
|
23
|
+
target: Target,
|
|
24
|
+
schema: StandardSchema<Out>,
|
|
25
|
+
): MiddlewareHandler<E, P, { in: { [K in Target]: Out }, out: { [K in Target]: Out } }> {
|
|
26
|
+
const middleware = validator(target, async (value, c) => {
|
|
27
|
+
const result = await schema['~standard'].validate(value)
|
|
28
|
+
if (result.issues !== undefined) {
|
|
29
|
+
return c.json({ error: `invalid ${target}`, issues: result.issues.map(issue => ({ message: issue.message, path: pathOf(issue.path) })) }, 400)
|
|
30
|
+
}
|
|
31
|
+
return result.value
|
|
32
|
+
})
|
|
33
|
+
// The core infers its own input and output types from the function; the schema's output type is the one a handler should see.
|
|
34
|
+
return middleware as unknown as MiddlewareHandler<E, P, { in: { [K in Target]: Out }, out: { [K in Target]: Out } }>
|
|
35
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { Router, Config, apply, name, type RouterHttpService } from './service.ts'
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
6
|
-
export type {
|
|
1
|
+
export { Router, Config, apply, name, type MountableApp, type RouterHttpService, type TrailingSlash } from './service.ts'
|
|
2
|
+
export { connectionOf, getConnInfo, type ConnectionInfo, type RouterBindings, type RouterEnv } from './connection.ts'
|
|
3
|
+
export { App, Context } from './core/index.ts'
|
|
4
|
+
export type { AppRequest, ContextRenderer, ContextVariableMap, Env, ErrorHandler, ExecutionContext, Handler, Input, MiddlewareHandler, Next, NotFoundHandler, NotFoundResponse, Schema, ToSchema, TypedResponse, ValidationTargets } from './core/index.ts'
|
|
5
|
+
export { HTTPException } from './core/http-exception.ts'
|
|
6
|
+
export type { ContentfulStatusCode, StatusCode } from './core/utils/http-status.ts'
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
1
|
+
export { basicAuth } from '../core/middleware/basic-auth/index.ts'
|
|
2
|
+
export { bearerAuth } from '../core/middleware/bearer-auth/index.ts'
|
|
3
|
+
export { bodyLimit } from '../core/middleware/body-limit/index.ts'
|
|
4
|
+
export { every, except, some } from '../core/middleware/combine/index.ts'
|
|
5
|
+
export { compress } from '../core/middleware/compress/index.ts'
|
|
6
|
+
export { contextStorage, getContext, tryGetContext } from '../core/middleware/context-storage/index.ts'
|
|
7
|
+
export { cors } from '../core/middleware/cors/index.ts'
|
|
8
|
+
export { csrf } from '../core/middleware/csrf/index.ts'
|
|
9
|
+
export { etag } from '../core/middleware/etag/index.ts'
|
|
10
|
+
export { jwk } from '../core/middleware/jwk/index.ts'
|
|
11
|
+
export { AlgorithmTypes, decode, jwt, sign, verify, verifyWithJwks, type JwtVariables } from '../core/middleware/jwt/index.ts'
|
|
12
|
+
export { ipRestriction, type IPRestrictionRule, type IPRestrictionRules } from '../core/middleware/ip-restriction/index.ts'
|
|
13
|
+
export { detectFromCookie, detectFromHeader, detectFromPath, detectFromQuery, languageDetector } from '../core/middleware/language/index.ts'
|
|
14
|
+
export { logger } from '../core/middleware/logger/index.ts'
|
|
15
|
+
export { methodNotAllowed } from '../core/middleware/method-not-allowed/index.ts'
|
|
16
|
+
export { methodOverride } from '../core/middleware/method-override/index.ts'
|
|
17
|
+
export { poweredBy } from '../core/middleware/powered-by/index.ts'
|
|
18
|
+
export { prettyJSON } from '../core/middleware/pretty-json/index.ts'
|
|
19
|
+
export { requestId, type RequestIdVariables } from '../core/middleware/request-id/index.ts'
|
|
20
|
+
export { serveStatic, type ServeStaticOptions } from '../core/middleware/serve-static/index.ts'
|
|
21
|
+
export { NONCE, secureHeaders, type SecureHeadersVariables } from '../core/middleware/secure-headers/index.ts'
|
|
22
|
+
export { timeout } from '../core/middleware/timeout/index.ts'
|
|
23
|
+
export { endTime, setMetric, startTime, timing, wrapTime, type TimingVariables } from '../core/middleware/timing/index.ts'
|
|
24
|
+
export { appendTrailingSlash, trimTrailingSlash } from '../core/middleware/trailing-slash/index.ts'
|
|
25
|
+
export { LOOPBACK_HOSTNAMES, fromOwnPage, loopbackAuthority, loopbackOnly, sameOrigin, type LoopbackOptions } from './loopback-only.ts'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BlockList, isIP } from 'node:net'
|
|
2
|
-
import
|
|
2
|
+
import { connectionOf } from '../connection.ts'
|
|
3
|
+
import type { Context } from '../core/context.ts'
|
|
4
|
+
import type { MiddlewareHandler } from '../core/types.ts'
|
|
3
5
|
|
|
4
6
|
export interface LoopbackOptions {
|
|
5
7
|
/** Hostnames a `Host` header may name. Default: the loopback names `127.0.0.1`, `localhost` and `[::1]`. */
|
|
@@ -17,6 +19,13 @@ export interface LoopbackOptions {
|
|
|
17
19
|
* checked in full: only a call with no connection facts at all counts as in process.
|
|
18
20
|
*/
|
|
19
21
|
readonly inProcess?: 'deny' | 'allow'
|
|
22
|
+
/**
|
|
23
|
+
* Who may call. `local` (default): any program on this machine, a browser page only as `mutations` allows. `own-page`: only this app's own page,
|
|
24
|
+
* for private routes a script on the machine must not drive or read either. A request then shows an `Origin` that is exactly this authority (with
|
|
25
|
+
* `Sec-Fetch-Site` absent or `same-origin`), or, for a read (GET, HEAD, OPTIONS), which browsers often send without `Origin`, `Sec-Fetch-Site:
|
|
26
|
+
* same-origin` and a `Referer` from this authority. `mutations` is not consulted: every request is held to that rule.
|
|
27
|
+
*/
|
|
28
|
+
readonly callers?: 'local' | 'own-page'
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
const loopback = new BlockList()
|
|
@@ -27,15 +36,15 @@ const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])
|
|
|
27
36
|
|
|
28
37
|
/**
|
|
29
38
|
* The authority a loopback request addressed: the `Host` header as a URL, or `undefined` when the request does not come from a loopback peer to the
|
|
30
|
-
* real local port, names another host, or is marked cross-site by the browser. The peer and the port come from the transport (`c
|
|
39
|
+
* real local port, names another host, or is marked cross-site by the browser. The peer and the port come from the transport (`connectionOf(c)`), never
|
|
31
40
|
* from a header; an in-process call has neither and is refused.
|
|
32
41
|
*/
|
|
33
42
|
export const LOOPBACK_HOSTNAMES: readonly string[] = ['127.0.0.1', 'localhost', '[::1]']
|
|
34
43
|
|
|
35
|
-
export function loopbackAuthority(c:
|
|
36
|
-
const { remoteAddress, localPort } = c
|
|
37
|
-
const host = c.req.
|
|
38
|
-
if (remoteAddress === undefined || localPort === undefined || host ===
|
|
44
|
+
export function loopbackAuthority(c: Context, hostnames: readonly string[] = LOOPBACK_HOSTNAMES): URL | undefined {
|
|
45
|
+
const { remoteAddress, localPort } = connectionOf(c)
|
|
46
|
+
const host = c.req.header('host')
|
|
47
|
+
if (remoteAddress === undefined || localPort === undefined || host === undefined) return undefined
|
|
39
48
|
const address = (remoteAddress.replace(/^\[|\]$/gu, '').split('%', 1)[0] ?? '').replace(/^::ffff:/iu, '')
|
|
40
49
|
const family = isIP(address)
|
|
41
50
|
if (family === 0 || !loopback.check(address, family === 4 ? 'ipv4' : 'ipv6')) return undefined
|
|
@@ -49,15 +58,15 @@ export function loopbackAuthority(c: RouteContext, hostnames: readonly string[]
|
|
|
49
58
|
authority.protocol !== 'http:'
|
|
50
59
|
|| Number(authority.port || '80') !== localPort
|
|
51
60
|
|| !hostnames.includes(authority.hostname)
|
|
52
|
-
|| c.req.
|
|
61
|
+
|| c.req.header('sec-fetch-site') === 'cross-site'
|
|
53
62
|
) return undefined
|
|
54
63
|
return authority
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
/** Whether a changing request is acceptable by its `Origin`: absent (not a browser cross-origin change) or naming this same authority. `null` and any other origin are not. */
|
|
58
|
-
export function sameOrigin(c:
|
|
59
|
-
const origin = c.req.
|
|
60
|
-
if (origin ===
|
|
67
|
+
export function sameOrigin(c: Context, authority: URL): boolean {
|
|
68
|
+
const origin = c.req.header('origin')
|
|
69
|
+
if (origin === undefined) return true
|
|
61
70
|
try {
|
|
62
71
|
const parsed = new URL(origin)
|
|
63
72
|
return parsed.protocol === 'http:' && parsed.host === authority.host && parsed.pathname === '/'
|
|
@@ -66,17 +75,47 @@ export function sameOrigin(c: RouteContext, authority: URL): boolean {
|
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
function originOf(value: string | undefined): string | undefined {
|
|
79
|
+
if (value === undefined) return undefined
|
|
80
|
+
try {
|
|
81
|
+
return new URL(value).origin
|
|
82
|
+
} catch {
|
|
83
|
+
return undefined
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Whether the request comes from a page served by this authority itself: the `own-page` rule of {@link LoopbackOptions.callers}. */
|
|
88
|
+
export function fromOwnPage(c: Context, authority: URL): boolean {
|
|
89
|
+
const origin = c.req.header('origin')
|
|
90
|
+
const fetchSite = c.req.header('sec-fetch-site')
|
|
91
|
+
if (origin !== undefined && originOf(origin) === origin && origin === authority.origin) return fetchSite === undefined || fetchSite === 'same-origin'
|
|
92
|
+
return SAFE_METHODS.has(c.req.method) && fetchSite === 'same-origin' && originOf(c.req.header('referer')) === authority.origin
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Only requests from this machine to this app: loopback peer, the real port, an allowed `Host`, and for mutations a matching `Origin` (with
|
|
97
|
+
* `callers: 'own-page'`, every request from this app's own page). Everything else gets a 403.
|
|
98
|
+
*/
|
|
99
|
+
export function loopbackOnly(options: LoopbackOptions = {}): MiddlewareHandler {
|
|
71
100
|
const mutations = options.mutations ?? 'same-origin'
|
|
72
101
|
const inProcess = options.inProcess ?? 'deny'
|
|
102
|
+
const ownPage = options.callers === 'own-page'
|
|
73
103
|
return async (c, next) => {
|
|
74
|
-
|
|
104
|
+
const connection = connectionOf(c)
|
|
105
|
+
if (inProcess === 'allow' && connection.remoteAddress === undefined && connection.localPort === undefined) {
|
|
106
|
+
await next()
|
|
107
|
+
return
|
|
108
|
+
}
|
|
75
109
|
const authority = loopbackAuthority(c, options.hostnames)
|
|
76
110
|
if (authority === undefined) return c.json({ error: 'request authority rejected' }, 403)
|
|
111
|
+
if (ownPage) {
|
|
112
|
+
if (!fromOwnPage(c, authority)) return c.json({ error: 'only this app\'s own page may call this' }, 403)
|
|
113
|
+
await next()
|
|
114
|
+
return
|
|
115
|
+
}
|
|
77
116
|
if (mutations === 'same-origin' && !SAFE_METHODS.has(c.req.method) && !sameOrigin(c, authority)) {
|
|
78
117
|
return c.json({ error: 'a change must come from this app\'s own origin' }, 403)
|
|
79
118
|
}
|
|
80
|
-
|
|
119
|
+
await next()
|
|
81
120
|
}
|
|
82
121
|
}
|