acrylblends 0.3.0 → 0.4.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/index.d.ts +6 -1
- package/dist/index.js +28 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugins/acryl-inspect/plugin.json +2 -1
- package/plugins/acryl-inspect/src/report.ts +16 -13
- package/plugins/acryl-inspect/src/service.ts +9 -7
- package/plugins/acryl-inspect/tests/report.spec.ts +7 -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 +19 -13
- package/plugins/acryl-router/src/service.ts +256 -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/service.spec.ts +264 -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 +2 -2
- package/plugins/extensions/resolve.ts +28 -3
- package/plugins/openai/prompt.ts +9 -7
- package/plugins/tsconfig.json +5 -4
- 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/src/routes.ts +6 -4
- package/templates/blank/tsconfig.json +2 -4
- package/templates/todo/src/page.ts +1 -1
- package/templates/todo/src/todo.ts +56 -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
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/** The route helpers (spec 046): bounded body reading, server-sent events, and validation. */
|
|
2
|
-
|
|
3
|
-
import { Context } from '@deepseek-ai/cordis'
|
|
4
|
-
import z from '@deepseek-ai/schemastery'
|
|
5
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
6
|
-
import { apply, name, type Router } from '../src/index.ts'
|
|
7
|
-
import { readBytes, readJson, readText, streamSSE, valid, validate } from '../src/helpers/index.ts'
|
|
8
|
-
import { bodyLimit } from '../src/middleware/index.ts'
|
|
9
|
-
|
|
10
|
-
const contexts: Context[] = []
|
|
11
|
-
const settle = (ms = 0): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
|
|
12
|
-
afterEach(async () => {
|
|
13
|
-
while (contexts.length > 0) await contexts.pop()?.fiber.dispose()
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
async function mount(): Promise<{ ctx: Context, router: Router }> {
|
|
17
|
-
const ctx = new Context()
|
|
18
|
-
contexts.push(ctx)
|
|
19
|
-
ctx.plugin({ name, apply })
|
|
20
|
-
await settle()
|
|
21
|
-
return { ctx, router: ctx.router }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('readJson / readText / readBytes', () => {
|
|
25
|
-
it('reads a JSON body, and an empty body as no bytes', async () => {
|
|
26
|
-
const { router } = await mount()
|
|
27
|
-
router.post('/j', async c => c.json({ got: await readJson(c) }))
|
|
28
|
-
router.post('/t', async c => c.text(String((await readBytes(c)).byteLength)))
|
|
29
|
-
expect(await (await router.request('/j', { method: 'POST', body: '{"a":1}' })).json()).toEqual({ got: { a: 1 } })
|
|
30
|
-
expect(await (await router.request('/t', { method: 'POST' })).text()).toBe('0')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('answers 400 for bad JSON and 413 over the limit, counting the bytes it reads', async () => {
|
|
34
|
-
const { router } = await mount()
|
|
35
|
-
router.post('/j', async c => c.json(await readJson(c, { maxBytes: 16 })))
|
|
36
|
-
expect((await router.request('/j', { method: 'POST', body: '{nope' })).status).toBe(400)
|
|
37
|
-
const big = await router.request('/j', { method: 'POST', body: JSON.stringify({ pad: 'x'.repeat(100) }) })
|
|
38
|
-
expect(big.status).toBe(413)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('stops a chunked body that has no declared length at the limit', async () => {
|
|
42
|
-
const { router } = await mount()
|
|
43
|
-
router.post('/t', async c => c.text(await readText(c, { maxBytes: 8 })))
|
|
44
|
-
const stream = new ReadableStream<Uint8Array>({ start(controller) { controller.enqueue(new TextEncoder().encode('0123456789')); controller.close() } })
|
|
45
|
-
const response = await router.request(new Request('http://localhost/t', { method: 'POST', body: stream, duplex: 'half' } as RequestInit))
|
|
46
|
-
expect(response.status).toBe(413)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('uses the limit the bodyLimit middleware set', async () => {
|
|
50
|
-
const { router } = await mount()
|
|
51
|
-
router.use(bodyLimit({ maxBytes: 4 }))
|
|
52
|
-
router.post('/t', async c => c.text(await readText(c)))
|
|
53
|
-
const stream = new ReadableStream<Uint8Array>({ start(controller) { controller.enqueue(new TextEncoder().encode('abcdefgh')); controller.close() } })
|
|
54
|
-
expect((await router.request(new Request('http://localhost/t', { method: 'POST', body: stream, duplex: 'half' } as RequestInit))).status).toBe(413)
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
describe('streamSSE', () => {
|
|
59
|
-
async function read(response: Response, count: number): Promise<string[]> {
|
|
60
|
-
const reader = response.body!.getReader()
|
|
61
|
-
const decoder = new TextDecoder()
|
|
62
|
-
let buffer = ''
|
|
63
|
-
const out: string[] = []
|
|
64
|
-
while (out.length < count) {
|
|
65
|
-
const { done, value } = await reader.read()
|
|
66
|
-
if (done) break
|
|
67
|
-
buffer += decoder.decode(value, { stream: true })
|
|
68
|
-
for (let at = buffer.indexOf('\n\n'); at !== -1; at = buffer.indexOf('\n\n')) { out.push(buffer.slice(0, at)); buffer = buffer.slice(at + 2) }
|
|
69
|
-
}
|
|
70
|
-
reader.releaseLock()
|
|
71
|
-
return out
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
it('sends events with ids, names, JSON and multi-line text, and a comment', async () => {
|
|
75
|
-
const { router } = await mount()
|
|
76
|
-
router.get('/s', c => streamSSE(c, stream => {
|
|
77
|
-
stream.send({ n: 1 }, { event: 'tick', id: '7' })
|
|
78
|
-
stream.send('line one\nline two')
|
|
79
|
-
stream.comment('keep-alive')
|
|
80
|
-
}))
|
|
81
|
-
const events = await read(await router.request('/s'), 3)
|
|
82
|
-
expect(events).toEqual(['id: 7\nevent: tick\ndata: {"n":1}', 'data: line one\ndata: line two', ': keep-alive'])
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('runs its cleanup once when the client aborts, and ends the stream', async () => {
|
|
86
|
-
const { router } = await mount()
|
|
87
|
-
let cleaned = 0
|
|
88
|
-
router.get('/s', c => streamSSE(c, () => () => { cleaned += 1 }))
|
|
89
|
-
const controller = new AbortController()
|
|
90
|
-
const response = await router.request('/s', { signal: controller.signal })
|
|
91
|
-
const reader = response.body!.getReader()
|
|
92
|
-
controller.abort()
|
|
93
|
-
await expect(reader.read()).resolves.toMatchObject({ done: true })
|
|
94
|
-
await settle(5)
|
|
95
|
-
expect(cleaned).toBe(1)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('lets the server close the stream, and runs the cleanup once even if cancelled afterwards', async () => {
|
|
99
|
-
const { router } = await mount()
|
|
100
|
-
let cleaned = 0
|
|
101
|
-
router.get('/s', c => streamSSE(c, stream => { stream.send('only'); stream.close(); return () => { cleaned += 1 } }))
|
|
102
|
-
const response = await router.request('/s')
|
|
103
|
-
const reader = response.body!.getReader()
|
|
104
|
-
expect((await reader.read()).done).toBe(false)
|
|
105
|
-
await expect(reader.read()).resolves.toMatchObject({ done: true })
|
|
106
|
-
await reader.cancel()
|
|
107
|
-
expect(cleaned).toBe(1)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('is ended and awaited when its plugin unloads the router', async () => {
|
|
111
|
-
const { ctx, router } = await mount()
|
|
112
|
-
let cleaned = 0
|
|
113
|
-
router.get('/s', c => streamSSE(c, () => () => { cleaned += 1 }))
|
|
114
|
-
const response = await router.request('/s')
|
|
115
|
-
const reader = response.body!.getReader()
|
|
116
|
-
const ended = reader.read()
|
|
117
|
-
await ctx.fiber.dispose()
|
|
118
|
-
contexts.length = 0
|
|
119
|
-
await expect(ended).resolves.toMatchObject({ done: true })
|
|
120
|
-
expect(cleaned).toBe(1)
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('validate', () => {
|
|
125
|
-
const Body = z.object({ name: z.string().required(), age: z.number().default(0) })
|
|
126
|
-
|
|
127
|
-
it('hands the handler the parsed value, with defaults applied', async () => {
|
|
128
|
-
const { router } = await mount()
|
|
129
|
-
router.post('/u', validate('json', Body), c => c.json(valid<{ name: string, age: number }>(c, 'json')))
|
|
130
|
-
const response = await router.request('/u', { method: 'POST', body: '{"name":"ada"}' })
|
|
131
|
-
expect(response.status).toBe(200)
|
|
132
|
-
expect(await response.json()).toEqual({ name: 'ada', age: 0 })
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('answers 400 with the issues for an invalid body, query or parameter', async () => {
|
|
136
|
-
const { router } = await mount()
|
|
137
|
-
router.post('/u', validate('json', Body), c => c.text('ok'))
|
|
138
|
-
router.get('/q', validate('query', z.object({ page: z.string().required() })), c => c.text('ok'))
|
|
139
|
-
router.get('/p/:id', validate('param', z.object({ id: z.string().pattern(/^\d+$/u).required() })), c => c.text('ok'))
|
|
140
|
-
const bad = await router.request('/u', { method: 'POST', body: '{}' })
|
|
141
|
-
expect(bad.status).toBe(400)
|
|
142
|
-
expect(((await bad.json()) as { error: string }).error).toBe('invalid json')
|
|
143
|
-
expect((await router.request('/q')).status).toBe(400)
|
|
144
|
-
expect((await router.request('/q?page=2')).status).toBe(200)
|
|
145
|
-
expect((await router.request('/p/abc')).status).toBe(400)
|
|
146
|
-
expect((await router.request('/p/42')).status).toBe(200)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('tells the author when valid() is used without validate()', async () => {
|
|
150
|
-
const { router } = await mount()
|
|
151
|
-
router.get('/x', c => c.text(String(valid(c, 'json'))))
|
|
152
|
-
expect((await router.request('/x')).status).toBe(500)
|
|
153
|
-
})
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
describe('streamSSE keepalive and validate on bad JSON', () => {
|
|
157
|
-
it('sends a comment line on an idle stream and stops when it ends', async () => {
|
|
158
|
-
const { router } = await mount()
|
|
159
|
-
router.get('/s', c => streamSSE(c, () => undefined, { keepAliveMs: 10 }))
|
|
160
|
-
const response = await router.request('/s')
|
|
161
|
-
const reader = response.body!.getReader()
|
|
162
|
-
const chunk = new TextDecoder().decode((await reader.read()).value)
|
|
163
|
-
expect(chunk).toBe(': keepalive\n\n')
|
|
164
|
-
await reader.cancel()
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('answers 400, not 500, when the body is not JSON', async () => {
|
|
168
|
-
const { router } = await mount()
|
|
169
|
-
router.post('/v', validate('json', z.object({ a: z.number() }) as never), c => c.json(valid(c, 'json')))
|
|
170
|
-
expect((await router.request('/v', { method: 'POST', body: '{nope' })).status).toBe(400)
|
|
171
|
-
})
|
|
172
|
-
})
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/** The built-in middleware (spec 046), each through the real Cordis service and `router.request`. */
|
|
2
|
-
|
|
3
|
-
import { Context } from '@deepseek-ai/cordis'
|
|
4
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
-
import { apply, name, type Router } from '../src/index.ts'
|
|
6
|
-
import { bodyLimit, cors, logger, loopbackOnly, requestId, secureHeaders } from '../src/middleware/index.ts'
|
|
7
|
-
|
|
8
|
-
const contexts: Context[] = []
|
|
9
|
-
const settle = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0))
|
|
10
|
-
afterEach(async () => {
|
|
11
|
-
while (contexts.length > 0) await contexts.pop()?.fiber.dispose()
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
async function mount(): Promise<Router> {
|
|
15
|
-
const ctx = new Context()
|
|
16
|
-
contexts.push(ctx)
|
|
17
|
-
ctx.plugin({ name, apply })
|
|
18
|
-
await settle()
|
|
19
|
-
return ctx.router
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('secureHeaders', () => {
|
|
23
|
-
it('adds the hardening headers without overriding what the handler set, and lets a default be removed', async () => {
|
|
24
|
-
const router = await mount()
|
|
25
|
-
router.use(secureHeaders({ headers: { 'x-frame-options': '' } }))
|
|
26
|
-
router.get('/a', c => c.text('a'))
|
|
27
|
-
router.get('/b', () => new Response('b', { headers: { 'referrer-policy': 'origin' } }))
|
|
28
|
-
const a = await router.request('/a')
|
|
29
|
-
expect(a.headers.get('x-content-type-options')).toBe('nosniff')
|
|
30
|
-
expect(a.headers.get('x-frame-options')).toBeNull()
|
|
31
|
-
expect((await router.request('/b')).headers.get('referrer-policy')).toBe('origin')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('also works on an immutable response', async () => {
|
|
35
|
-
const router = await mount()
|
|
36
|
-
router.use(secureHeaders())
|
|
37
|
-
router.get('/go', () => Response.redirect('http://example.test/x', 302))
|
|
38
|
-
const response = await router.request('/go')
|
|
39
|
-
expect(response.status).toBe(302)
|
|
40
|
-
expect(response.headers.get('x-content-type-options')).toBe('nosniff')
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
describe('requestId', () => {
|
|
45
|
-
it('keeps a well-formed incoming id, replaces a bad one, and exposes it to handlers', async () => {
|
|
46
|
-
const router = await mount()
|
|
47
|
-
router.use(requestId({ generate: () => 'generated-1' }))
|
|
48
|
-
router.get('/id', c => c.text(c.get<string>('requestId') ?? ''))
|
|
49
|
-
const kept = await router.request('/id', { headers: { 'x-request-id': 'abc-123' } })
|
|
50
|
-
expect(await kept.text()).toBe('abc-123')
|
|
51
|
-
expect(kept.headers.get('x-request-id')).toBe('abc-123')
|
|
52
|
-
const replaced = await router.request('/id', { headers: { 'x-request-id': 'bad id with spaces' } })
|
|
53
|
-
expect(await replaced.text()).toBe('generated-1')
|
|
54
|
-
expect((await router.request('/id')).headers.get('x-request-id')).toBe('generated-1')
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
describe('logger', () => {
|
|
59
|
-
it('writes an in line and an out line with status and time to the sink, and reports a throw', async () => {
|
|
60
|
-
const router = await mount()
|
|
61
|
-
const lines: string[] = []
|
|
62
|
-
router.use(logger(line => lines.push(line)))
|
|
63
|
-
router.get('/ok', c => c.text('ok'))
|
|
64
|
-
router.get('/boom', () => { throw new Error('boom') })
|
|
65
|
-
await router.request('/ok')
|
|
66
|
-
await router.request('/boom')
|
|
67
|
-
expect(lines[0]).toBe('<-- GET /ok')
|
|
68
|
-
expect(lines[1]).toMatch(/^--> GET \/ok 200 \d+ms$/)
|
|
69
|
-
expect(lines[2]).toBe('<-- GET /boom')
|
|
70
|
-
expect(lines[3]).toMatch(/^--> GET \/boom threw boom \d+ms$/)
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
describe('bodyLimit', () => {
|
|
75
|
-
it('refuses a declared length over the limit and lets a smaller one through', async () => {
|
|
76
|
-
const router = await mount()
|
|
77
|
-
router.use(bodyLimit({ maxBytes: 10 }))
|
|
78
|
-
router.post('/in', async c => c.text(await c.req.text()))
|
|
79
|
-
const big = await router.request('/in', { method: 'POST', body: 'x'.repeat(50), headers: { 'content-length': '50' } })
|
|
80
|
-
expect(big.status).toBe(413)
|
|
81
|
-
expect(await big.json()).toEqual({ error: 'payload too large', maxBytes: 10 })
|
|
82
|
-
expect(await (await router.request('/in', { method: 'POST', body: 'small', headers: { 'content-length': '5' } })).text()).toBe('small')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('uses a custom error response', async () => {
|
|
86
|
-
const router = await mount()
|
|
87
|
-
router.use(bodyLimit({ maxBytes: 1, onError: c => c.text('too big', 400) }))
|
|
88
|
-
router.post('/in', c => c.text('ok'))
|
|
89
|
-
const response = await router.request('/in', { method: 'POST', body: 'xx', headers: { 'content-length': '2' } })
|
|
90
|
-
expect(response.status).toBe(400)
|
|
91
|
-
expect(await response.text()).toBe('too big')
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
describe('cors', () => {
|
|
96
|
-
it('answers a preflight itself and adds the headers to normal responses', async () => {
|
|
97
|
-
const router = await mount()
|
|
98
|
-
router.use(cors({ origin: ['https://app.example'], allowMethods: ['GET', 'POST'], maxAge: 600, credentials: true }))
|
|
99
|
-
router.get('/x', c => c.text('x'))
|
|
100
|
-
const preflight = await router.request('/x', { method: 'OPTIONS', headers: { origin: 'https://app.example', 'access-control-request-method': 'POST', 'access-control-request-headers': 'x-a, x-b' } })
|
|
101
|
-
expect(preflight.status).toBe(204)
|
|
102
|
-
expect(preflight.headers.get('access-control-allow-origin')).toBe('https://app.example')
|
|
103
|
-
expect(preflight.headers.get('access-control-allow-methods')).toBe('GET,POST')
|
|
104
|
-
expect(preflight.headers.get('access-control-allow-headers')).toBe('x-a,x-b')
|
|
105
|
-
expect(preflight.headers.get('access-control-max-age')).toBe('600')
|
|
106
|
-
expect(preflight.headers.get('access-control-allow-credentials')).toBe('true')
|
|
107
|
-
const normal = await router.request('/x', { headers: { origin: 'https://app.example' } })
|
|
108
|
-
expect(normal.headers.get('access-control-allow-origin')).toBe('https://app.example')
|
|
109
|
-
expect(normal.headers.get('vary')).toContain('Origin')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('does not allow an origin outside the list', async () => {
|
|
113
|
-
const router = await mount()
|
|
114
|
-
router.use(cors({ origin: 'https://app.example' }))
|
|
115
|
-
router.get('/x', c => c.text('x'))
|
|
116
|
-
const response = await router.request('/x', { headers: { origin: 'https://evil.example' } })
|
|
117
|
-
expect(response.headers.get('access-control-allow-origin')).toBeNull()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('loopbackOnly', () => {
|
|
122
|
-
const peer = { remoteAddress: '127.0.0.1', localPort: 3105 }
|
|
123
|
-
const headers = (extra: Record<string, string> = {}): Record<string, string> => ({ host: '127.0.0.1:3105', ...extra })
|
|
124
|
-
|
|
125
|
-
it('lets a loopback peer on the real port through and refuses everything else with a 403', async () => {
|
|
126
|
-
const router = await mount()
|
|
127
|
-
router.use(loopbackOnly())
|
|
128
|
-
router.get('/x', c => c.text('x'))
|
|
129
|
-
expect((await router.request('/x', { headers: headers() }, peer)).status).toBe(200)
|
|
130
|
-
expect((await router.request('/x', { headers: headers() }, { remoteAddress: '203.0.113.9', localPort: 3105 })).status).toBe(403)
|
|
131
|
-
expect((await router.request('/x', { headers: headers({ host: '127.0.0.1:9999' }) }, peer)).status).toBe(403)
|
|
132
|
-
expect((await router.request('/x', { headers: headers({ host: 'evil.example:3105' }) }, peer)).status).toBe(403)
|
|
133
|
-
expect((await router.request('/x', { headers: headers({ 'sec-fetch-site': 'cross-site' }) }, peer)).status).toBe(403)
|
|
134
|
-
expect((await router.request('/x', { headers: headers() })).status).toBe(403)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
it('accepts an IPv4-mapped loopback address and ::1', async () => {
|
|
138
|
-
const router = await mount()
|
|
139
|
-
router.use(loopbackOnly())
|
|
140
|
-
router.get('/x', c => c.text('x'))
|
|
141
|
-
expect((await router.request('/x', { headers: headers() }, { remoteAddress: '::ffff:127.0.0.1', localPort: 3105 })).status).toBe(200)
|
|
142
|
-
expect((await router.request('/x', { headers: headers() }, { remoteAddress: '::1', localPort: 3105 })).status).toBe(200)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('refuses a change from a foreign or null Origin, and accepts this app\'s own', async () => {
|
|
146
|
-
const router = await mount()
|
|
147
|
-
router.use(loopbackOnly())
|
|
148
|
-
router.post('/do', c => c.text('done'))
|
|
149
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ origin: 'http://evil.example' }) }, peer)).status).toBe(403)
|
|
150
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ origin: 'null' }) }, peer)).status).toBe(403)
|
|
151
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ origin: 'http://127.0.0.1:3105' }) }, peer)).status).toBe(200)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('accepts a change with no Origin (curl) only when the Host is a loopback address with this port', async () => {
|
|
155
|
-
const router = await mount()
|
|
156
|
-
router.use(loopbackOnly())
|
|
157
|
-
router.post('/do', c => c.text('done'))
|
|
158
|
-
expect((await router.request('/do', { method: 'POST', headers: headers() }, peer)).status).toBe(200)
|
|
159
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ host: 'localhost:3105' }) }, peer)).status).toBe(200)
|
|
160
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ host: '[::1]:3105' }) }, { remoteAddress: '::1', localPort: 3105 })).status).toBe(200)
|
|
161
|
-
// DNS rebinding: the page's own domain arrives as the Host, so a foreign Host is refused with or without an Origin.
|
|
162
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ host: 'rebind.example:3105' }) }, peer)).status).toBe(403)
|
|
163
|
-
expect((await router.request('/do', { method: 'POST', headers: headers({ host: '127.0.0.1:9999' }) }, peer)).status).toBe(403)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('refuses an in-process call by default and lets it through when told the caller is the host', async () => {
|
|
167
|
-
const router = await mount()
|
|
168
|
-
router.get('/guarded', loopbackOnly(), c => c.text('x'))
|
|
169
|
-
router.get('/host', loopbackOnly({ inProcess: 'allow' }), c => c.text('x'))
|
|
170
|
-
expect((await router.request('/guarded')).status).toBe(403)
|
|
171
|
-
expect((await router.request('/host')).status).toBe(200)
|
|
172
|
-
// A request with any connection facts is checked in full, even when inProcess is allowed.
|
|
173
|
-
expect((await router.request('/host', { headers: { host: '127.0.0.1:3105' } }, { remoteAddress: '203.0.113.9', localPort: 3105 })).status).toBe(403)
|
|
174
|
-
expect((await router.request('/host', { headers: { host: '127.0.0.1:3105' } }, { remoteAddress: '127.0.0.1' })).status).toBe(403)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('can check only the peer when mutations are none', async () => {
|
|
178
|
-
const router = await mount()
|
|
179
|
-
router.use(loopbackOnly({ mutations: 'none' }))
|
|
180
|
-
router.post('/do', c => c.text('done'))
|
|
181
|
-
expect((await router.request('/do', { method: 'POST', headers: headers() }, peer)).status).toBe(200)
|
|
182
|
-
})
|
|
183
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { RouteTable } from '../src/table.ts'
|
|
3
|
-
import { conformance, type Candidate } from './conformance.ts'
|
|
4
|
-
|
|
5
|
-
function tableCandidate(trailingSlash: 'strict' | 'ignore' = 'strict', onError?: (e: unknown) => void): Candidate & { table: RouteTable } {
|
|
6
|
-
const table = new RouteTable({ trailingSlash, ...(onError === undefined ? {} : { onError }) })
|
|
7
|
-
return {
|
|
8
|
-
table,
|
|
9
|
-
route: (method, path, handler) => table.add(method, path, handler),
|
|
10
|
-
use: (path, middleware) => table.use(path, middleware),
|
|
11
|
-
fetch: request => table.dispatch(request, new AbortController().signal),
|
|
12
|
-
size: () => table.size,
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
conformance('RouteTable (the pure core)', () => tableCandidate())
|
|
17
|
-
|
|
18
|
-
describe('RouteTable specifics', () => {
|
|
19
|
-
it('keeps /a and /a/ distinct under strict and joins them under ignore', async () => {
|
|
20
|
-
const strict = tableCandidate('strict')
|
|
21
|
-
strict.route('GET', '/a', c => c.text('a'))
|
|
22
|
-
expect((await strict.fetch(new Request('http://t/a/'))).status).toBe(404)
|
|
23
|
-
const loose = tableCandidate('ignore')
|
|
24
|
-
loose.route('GET', '/a', c => c.text('a'))
|
|
25
|
-
expect((await loose.fetch(new Request('http://t/a/'))).status).toBe(200)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('reports handler errors to onError and still answers 500', async () => {
|
|
29
|
-
const seen: unknown[] = []
|
|
30
|
-
const c = tableCandidate('strict', e => seen.push(e))
|
|
31
|
-
c.route('GET', '/x', () => { throw new Error('nope') })
|
|
32
|
-
expect((await c.fetch(new Request('http://t/x'))).status).toBe(500)
|
|
33
|
-
expect(seen).toHaveLength(1)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('answers 499 instead of 500 when the request was aborted', async () => {
|
|
37
|
-
const c = tableCandidate()
|
|
38
|
-
const controller = new AbortController()
|
|
39
|
-
c.route('GET', '/slow', async ctx => {
|
|
40
|
-
await new Promise((_resolve, reject) => { ctx.signal.addEventListener('abort', () => reject(new Error('aborted')), { once: true }) })
|
|
41
|
-
return ctx.text('never')
|
|
42
|
-
})
|
|
43
|
-
const pending = c.table.dispatch(new Request('http://t/slow'), controller.signal)
|
|
44
|
-
controller.abort()
|
|
45
|
-
expect((await pending).status).toBe(499)
|
|
46
|
-
})
|
|
47
|
-
})
|