acrylblends 0.2.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 +22 -16
- package/dist/index.js +65 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugins/acryl-inspect/LICENSE +21 -0
- package/plugins/acryl-inspect/plugin.json +12 -0
- package/plugins/acryl-inspect/src/index.ts +3 -0
- package/plugins/acryl-inspect/src/report.ts +126 -0
- package/plugins/acryl-inspect/src/service.ts +68 -0
- package/plugins/acryl-inspect/tests/report.spec.ts +63 -0
- package/plugins/acryl-inspect/tests/service.spec.ts +121 -0
- package/plugins/acryl-inspect/upstream.json +18 -0
- package/plugins/acryl-node-server/LICENSE +47 -0
- package/plugins/acryl-node-server/plugin.json +13 -0
- 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 +107 -0
- 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 +141 -0
- package/plugins/acryl-node-server/upstream.json +18 -0
- package/plugins/acryl-router/LICENSE +47 -0
- package/plugins/acryl-router/plugin.json +13 -0
- 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 -0
- package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
- package/plugins/acryl-router/src/index.ts +6 -0
- package/plugins/acryl-router/src/middleware/index.ts +25 -0
- package/plugins/acryl-router/src/middleware/loopback-only.ts +88 -0
- package/plugins/acryl-router/src/service.ts +356 -0
- package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
- package/plugins/acryl-router/tests/live-install.spec.ts +64 -0
- package/plugins/acryl-router/tests/loader.spec.ts +72 -0
- package/plugins/acryl-router/tests/service.spec.ts +409 -0
- package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
- package/plugins/acryl-router/upstream.json +20 -0
- package/plugins/acryl-router-http/LICENSE +21 -0
- package/plugins/acryl-router-http/plugin.json +12 -0
- package/plugins/acryl-router-http/src/bridge.ts +54 -0
- package/plugins/acryl-router-http/src/index.ts +63 -0
- package/plugins/acryl-router-http/src/serve.ts +24 -0
- package/plugins/acryl-router-http/tests/adapter.spec.ts +174 -0
- package/plugins/acryl-router-http/tests/stream.spec.ts +133 -0
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +101 -0
- package/plugins/acryl-router-http/upstream.json +22 -0
- package/plugins/acryl-state/LICENSE +21 -0
- package/plugins/acryl-state/NOTICE +30 -0
- package/plugins/acryl-state/plugin.json +12 -0
- package/plugins/acryl-state/src/cell.ts +67 -0
- package/plugins/acryl-state/src/index.ts +3 -0
- package/plugins/acryl-state/src/react.ts +11 -0
- package/plugins/acryl-state/src/service.ts +119 -0
- package/plugins/acryl-state/tests/cell.spec.ts +71 -0
- package/plugins/acryl-state/tests/react.spec.ts +39 -0
- package/plugins/acryl-state/tests/service.spec.ts +132 -0
- package/plugins/acryl-state/upstream.json +22 -0
- package/plugins/builder/script.ts +6 -10
- package/plugins/extensions/index.ts +20 -3
- package/plugins/extensions/resolve.ts +28 -3
- package/plugins/openai/prompt.ts +12 -6
- package/plugins/tasks/index.ts +14 -0
- package/plugins/tsconfig.json +14 -5
- package/templates/agent/blend.yaml +5 -7
- package/templates/agent/src/app.ts +8 -8
- package/templates/agent/src/routes.ts +39 -38
- package/templates/agent/template.json +3 -3
- package/templates/agent/test/builder.test.ts +22 -14
- package/templates/agent/test/model.test.ts +3 -3
- package/templates/agent/tsconfig.json +8 -5
- package/templates/{app → blank}/blend.yaml +3 -3
- package/templates/{app → blank}/src/app.ts +5 -5
- package/templates/blank/src/routes.ts +16 -0
- package/templates/blank/template.json +1 -0
- package/templates/{app → blank}/test/app.test.ts +1 -1
- package/templates/{app → blank}/tsconfig.json +2 -4
- package/templates/todo/README.md +27 -0
- package/templates/todo/blend.yaml +25 -0
- package/templates/todo/gitignore +2 -0
- package/templates/todo/package.json +23 -0
- package/templates/todo/src/app.ts +71 -0
- package/templates/todo/src/main.ts +19 -0
- package/templates/todo/src/page.ts +28 -0
- package/templates/todo/src/todo.ts +123 -0
- package/templates/todo/template.json +1 -0
- package/templates/todo/test/todo.test.ts +145 -0
- package/templates/todo/tsconfig.json +17 -0
- package/templates/todo/upstream.json +12 -0
- package/plugins/events/index.ts +0 -42
- package/plugins/events/plugin.json +0 -9
- package/plugins/http/index.ts +0 -64
- package/plugins/http/plugin.json +0 -10
- package/plugins/server/index.ts +0 -138
- package/plugins/server/plugin.json +0 -10
- package/templates/app/src/routes.ts +0 -14
- package/templates/app/template.json +0 -1
- /package/templates/{app → blank}/README.md +0 -0
- /package/templates/{app → blank}/gitignore +0 -0
- /package/templates/{app → blank}/package.json +0 -0
- /package/templates/{app → blank}/src/main.ts +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { methodNotAllowed } from './index.ts'
|
|
2
|
+
import { App } from '../../app.ts'
|
|
3
|
+
|
|
4
|
+
describe('Method Not Allowed Middleware', () => {
|
|
5
|
+
it('infers the environment from the app', () => {
|
|
6
|
+
const app = new App<{ Variables: { requestId: string } }>()
|
|
7
|
+
const middleware = methodNotAllowed({
|
|
8
|
+
app,
|
|
9
|
+
onMethodNotAllowed: (c) => c.text(c.var.requestId, 405),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
expect(middleware).toBeTypeOf('function')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns 405 with every allowed method for an existing path', async () => {
|
|
16
|
+
const app = new App()
|
|
17
|
+
app.use(methodNotAllowed({ app }))
|
|
18
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
19
|
+
app.post('/resource', (c) => c.text('POST'))
|
|
20
|
+
app.delete('/resource', (c) => c.text('DELETE'))
|
|
21
|
+
|
|
22
|
+
const res = await app.request('/resource', { method: 'PUT' })
|
|
23
|
+
|
|
24
|
+
expect(res.status).toBe(405)
|
|
25
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, POST, DELETE')
|
|
26
|
+
expect(await res.text()).toBe('Method Not Allowed')
|
|
27
|
+
|
|
28
|
+
const resUsingCachedRouter = await app.request('/resource', { method: 'OPTIONS' })
|
|
29
|
+
expect(resUsingCachedRouter.status).toBe(405)
|
|
30
|
+
expect(resUsingCachedRouter.headers.get('Allow')).toBe('GET, HEAD, POST, DELETE')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('does not affect an allowed method', async () => {
|
|
34
|
+
const app = new App()
|
|
35
|
+
app.use(methodNotAllowed({ app }))
|
|
36
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
37
|
+
|
|
38
|
+
const res = await app.request('/resource')
|
|
39
|
+
|
|
40
|
+
expect(res.status).toBe(200)
|
|
41
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
42
|
+
expect(await res.text()).toBe('GET')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('returns 404 when the path does not exist', async () => {
|
|
46
|
+
const app = new App()
|
|
47
|
+
app.use(methodNotAllowed({ app }))
|
|
48
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
49
|
+
|
|
50
|
+
const res = await app.request('/missing', { method: 'POST' })
|
|
51
|
+
|
|
52
|
+
expect(res.status).toBe(404)
|
|
53
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('matches parameterized and overlapping routes without duplicate methods', async () => {
|
|
57
|
+
const app = new App()
|
|
58
|
+
app.use(methodNotAllowed({ app }))
|
|
59
|
+
app.get('/users/:id', (_c, next) => next())
|
|
60
|
+
app.get('/users/me', (c) => c.text('me'))
|
|
61
|
+
app.patch('/users/:id', (c) => c.text(c.req.param('id')))
|
|
62
|
+
|
|
63
|
+
const res = await app.request('/users/me', { method: 'POST' })
|
|
64
|
+
|
|
65
|
+
expect(res.status).toBe(405)
|
|
66
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, PATCH')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('preserves an intentional 404 from a handler registered for the request method', async () => {
|
|
70
|
+
const app = new App()
|
|
71
|
+
app.use(methodNotAllowed({ app }))
|
|
72
|
+
app.get('/users/:id', (c) => c.notFound())
|
|
73
|
+
app.post('/users/:id', (c) => c.text('Created'))
|
|
74
|
+
|
|
75
|
+
const res = await app.request('/users/123')
|
|
76
|
+
|
|
77
|
+
expect(res.status).toBe(404)
|
|
78
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('preserves an intentional 404 for HEAD when a GET route is registered', async () => {
|
|
82
|
+
const app = new App()
|
|
83
|
+
app.use(methodNotAllowed({ app }))
|
|
84
|
+
app.get('/users/:id', (c) => c.notFound())
|
|
85
|
+
app.post('/users/:id', (c) => c.text('Created'))
|
|
86
|
+
|
|
87
|
+
const res = await app.request('/users/123', { method: 'HEAD' })
|
|
88
|
+
|
|
89
|
+
expect(res.status).toBe(404)
|
|
90
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('preserves a 404 returned by the error handler', async () => {
|
|
94
|
+
const app = new App()
|
|
95
|
+
app.onError((_error, c) => c.text('Handled error', 404))
|
|
96
|
+
app.use(methodNotAllowed({ app }))
|
|
97
|
+
app.use('/resource', () => {
|
|
98
|
+
throw new Error('boom')
|
|
99
|
+
})
|
|
100
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
101
|
+
|
|
102
|
+
const res = await app.request('/resource', { method: 'POST' })
|
|
103
|
+
|
|
104
|
+
expect(res.status).toBe(404)
|
|
105
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
106
|
+
expect(await res.text()).toBe('Handled error')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('returns 405 for HEAD when the path does not support GET', async () => {
|
|
110
|
+
const app = new App()
|
|
111
|
+
app.use(methodNotAllowed({ app }))
|
|
112
|
+
app.post('/resource', (c) => c.text('POST'))
|
|
113
|
+
|
|
114
|
+
const res = await app.request('/resource', { method: 'HEAD' })
|
|
115
|
+
|
|
116
|
+
expect(res.status).toBe(405)
|
|
117
|
+
expect(res.headers.get('Allow')).toBe('POST')
|
|
118
|
+
expect(await res.text()).toBe('')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('does not advertise explicit HEAD routes that App cannot dispatch', async () => {
|
|
122
|
+
const app = new App()
|
|
123
|
+
app.use(methodNotAllowed({ app }))
|
|
124
|
+
app.on('HEAD', '/resource', (c) => c.text('HEAD'))
|
|
125
|
+
|
|
126
|
+
const headRes = await app.request('/resource', { method: 'HEAD' })
|
|
127
|
+
expect(headRes.status).toBe(404)
|
|
128
|
+
expect(headRes.headers.has('Allow')).toBe(false)
|
|
129
|
+
|
|
130
|
+
const putRes = await app.request('/resource', { method: 'PUT' })
|
|
131
|
+
expect(putRes.status).toBe(404)
|
|
132
|
+
expect(putRes.headers.has('Allow')).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('supports custom methods', async () => {
|
|
136
|
+
const app = new App()
|
|
137
|
+
app.use(methodNotAllowed({ app }))
|
|
138
|
+
app.on('PURGE', '/cache', (c) => c.text('Purged'))
|
|
139
|
+
|
|
140
|
+
const res = await app.request('/cache', { method: 'POST' })
|
|
141
|
+
|
|
142
|
+
expect(res.status).toBe(405)
|
|
143
|
+
expect(res.headers.get('Allow')).toBe('PURGE')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('compares request methods case-sensitively', async () => {
|
|
147
|
+
const app = new App()
|
|
148
|
+
app.use(methodNotAllowed({ app }))
|
|
149
|
+
app.patch('/item', (c) => c.text('PATCH'))
|
|
150
|
+
|
|
151
|
+
const request = new Request('http://localhost/item', { method: 'PATCH' })
|
|
152
|
+
Object.defineProperty(request, 'method', { value: 'patch' })
|
|
153
|
+
const res = await app.request(request)
|
|
154
|
+
|
|
155
|
+
expect(res.status).toBe(405)
|
|
156
|
+
expect(res.headers.get('Allow')).toBe('PATCH')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('works with routes mounted using app.route()', async () => {
|
|
160
|
+
const api = new App()
|
|
161
|
+
api.get('/resource', (c) => c.text('GET'))
|
|
162
|
+
api.post('/resource', (c) => c.text('POST'))
|
|
163
|
+
|
|
164
|
+
const app = new App()
|
|
165
|
+
app.use(methodNotAllowed({ app }))
|
|
166
|
+
app.route('/api', api)
|
|
167
|
+
|
|
168
|
+
const res = await app.request('/api/resource', { method: 'DELETE' })
|
|
169
|
+
|
|
170
|
+
expect(res.status).toBe(405)
|
|
171
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('works when installed in an app mounted using app.route()', async () => {
|
|
175
|
+
const api = new App().basePath('/v1')
|
|
176
|
+
api.use(methodNotAllowed({ app: api }))
|
|
177
|
+
api.get('/resource', (c) => c.text('GET'))
|
|
178
|
+
api.post('/resource', (c) => c.text('POST'))
|
|
179
|
+
|
|
180
|
+
const app = new App()
|
|
181
|
+
app.route('/api', api)
|
|
182
|
+
|
|
183
|
+
const res = await app.request('/api/v1/resource', { method: 'DELETE' })
|
|
184
|
+
|
|
185
|
+
expect(res.status).toBe(405)
|
|
186
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('ignores ALL routes when collecting allowed methods', async () => {
|
|
190
|
+
const app = new App()
|
|
191
|
+
app.use(methodNotAllowed({ app }))
|
|
192
|
+
app.all('/resource', (_c, next) => next())
|
|
193
|
+
|
|
194
|
+
const res = await app.request('/resource', { method: 'POST' })
|
|
195
|
+
|
|
196
|
+
expect(res.status).toBe(404)
|
|
197
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('does not invoke the error handler for a 405 response', async () => {
|
|
201
|
+
const app = new App()
|
|
202
|
+
const onError = vi.fn(() => new Response('error', { status: 500 }))
|
|
203
|
+
app.onError(onError)
|
|
204
|
+
app.use(methodNotAllowed({ app }))
|
|
205
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
206
|
+
|
|
207
|
+
const res = await app.request('/resource', { method: 'POST' })
|
|
208
|
+
|
|
209
|
+
expect(res.status).toBe(405)
|
|
210
|
+
expect(onError).not.toHaveBeenCalled()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('preserves downstream headers and lets outer middleware observe the final status', async () => {
|
|
214
|
+
const app = new App()
|
|
215
|
+
let observedStatus: number | undefined
|
|
216
|
+
|
|
217
|
+
app.use(async (c, next) => {
|
|
218
|
+
await next()
|
|
219
|
+
observedStatus = c.res.status
|
|
220
|
+
})
|
|
221
|
+
app.use(methodNotAllowed({ app }))
|
|
222
|
+
app.use(async (c, next) => {
|
|
223
|
+
await next()
|
|
224
|
+
c.header('X-Downstream', 'true')
|
|
225
|
+
})
|
|
226
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
227
|
+
|
|
228
|
+
const res = await app.request('/resource', { method: 'POST' })
|
|
229
|
+
|
|
230
|
+
expect(res.status).toBe(405)
|
|
231
|
+
expect(res.headers.get('X-Downstream')).toBe('true')
|
|
232
|
+
expect(observedStatus).toBe(405)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('replaces stale representation headers from the not-found response', async () => {
|
|
236
|
+
const app = new App()
|
|
237
|
+
app.use(methodNotAllowed({ app }))
|
|
238
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
239
|
+
app.post('/resource', (c) => c.text('POST'))
|
|
240
|
+
app.notFound((c) =>
|
|
241
|
+
c.text('Missing', 404, {
|
|
242
|
+
Allow: 'BOGUS',
|
|
243
|
+
'Content-Length': '7',
|
|
244
|
+
})
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
const res = await app.request('/resource', { method: 'PUT' })
|
|
248
|
+
|
|
249
|
+
expect(res.status).toBe(405)
|
|
250
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
|
|
251
|
+
expect(res.headers.has('Content-Length')).toBe(false)
|
|
252
|
+
expect(await res.text()).toBe('Method Not Allowed')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('supports a custom method-not-allowed response', async () => {
|
|
256
|
+
const app = new App()
|
|
257
|
+
app.use(
|
|
258
|
+
methodNotAllowed({
|
|
259
|
+
app,
|
|
260
|
+
onMethodNotAllowed: (c, allowedMethods) =>
|
|
261
|
+
c.json({ error: 'Method Not Allowed', allowedMethods }, 405, {
|
|
262
|
+
Allow: allowedMethods.join(', '),
|
|
263
|
+
}),
|
|
264
|
+
})
|
|
265
|
+
)
|
|
266
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
267
|
+
app.post('/resource', (c) => c.text('POST'))
|
|
268
|
+
|
|
269
|
+
const res = await app.request('/resource', { method: 'PUT' })
|
|
270
|
+
|
|
271
|
+
expect(res.status).toBe(405)
|
|
272
|
+
expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
|
|
273
|
+
expect(res.headers.get('Content-Type')).toMatch(/^application\/json/)
|
|
274
|
+
expect(await res.json()).toEqual({
|
|
275
|
+
error: 'Method Not Allowed',
|
|
276
|
+
allowedMethods: ['GET', 'HEAD', 'POST'],
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('leaves a non-404 custom not-found response unchanged', async () => {
|
|
281
|
+
const app = new App()
|
|
282
|
+
app.use(methodNotAllowed({ app }))
|
|
283
|
+
app.get('/resource', (c) => c.text('GET'))
|
|
284
|
+
app.notFound((c) => c.text('Missing', 410))
|
|
285
|
+
|
|
286
|
+
const res = await app.request('/resource', { method: 'POST' })
|
|
287
|
+
|
|
288
|
+
expect(res.status).toBe(410)
|
|
289
|
+
expect(res.headers.has('Allow')).toBe(false)
|
|
290
|
+
expect(await res.text()).toBe('Missing')
|
|
291
|
+
})
|
|
292
|
+
})
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Method Not Allowed Middleware for App.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Context } from '../../context.ts'
|
|
7
|
+
import { basePath, matchedRoutes } from '../../helper/route/index.ts'
|
|
8
|
+
import type { App } from '../../app.ts'
|
|
9
|
+
import { METHOD_NAME_ALL } from '../../router.ts'
|
|
10
|
+
import { TrieRouter } from '../../router/trie-router/index.ts'
|
|
11
|
+
import type { Env, MiddlewareHandler } from '../../types.ts'
|
|
12
|
+
|
|
13
|
+
type MethodNotAllowedHandler<E extends Env> = (
|
|
14
|
+
c: Context<E>,
|
|
15
|
+
allowedMethods: string[]
|
|
16
|
+
) => Response | Promise<Response>
|
|
17
|
+
|
|
18
|
+
type MethodNotAllowedOptions<E extends Env> = {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
app: App<E, any, any>
|
|
21
|
+
onMethodNotAllowed?: MethodNotAllowedHandler<E>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Method Not Allowed Middleware for App.
|
|
26
|
+
*
|
|
27
|
+
* Returns a `405 Method Not Allowed` response with an `Allow` header when the request path
|
|
28
|
+
* matches a registered route but the request method is not supported.
|
|
29
|
+
*
|
|
30
|
+
* @param {MethodNotAllowedOptions} options - The options for the middleware.
|
|
31
|
+
* @param {App} options.app - The App instance used by the application.
|
|
32
|
+
* @param {MethodNotAllowedHandler} [options.onMethodNotAllowed] - Generates the response, including its `Allow` header.
|
|
33
|
+
* @returns {MiddlewareHandler} The middleware handler function.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const app = new App()
|
|
38
|
+
*
|
|
39
|
+
* app.use(methodNotAllowed({ app }))
|
|
40
|
+
*
|
|
41
|
+
* app.get('/hello', (c) => c.text('Hello!'))
|
|
42
|
+
* app.post('/hello', (c) => c.text('Posted!'))
|
|
43
|
+
*
|
|
44
|
+
* // PUT /hello -> 405 Method Not Allowed
|
|
45
|
+
* // Allow: GET, HEAD, POST
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* app.use(
|
|
51
|
+
* methodNotAllowed({
|
|
52
|
+
* app,
|
|
53
|
+
* onMethodNotAllowed: (c, methods) =>
|
|
54
|
+
* c.json({ error: 'Method Not Allowed' }, 405, { Allow: methods.join(', ') }),
|
|
55
|
+
* })
|
|
56
|
+
* )
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export const methodNotAllowed = <E extends Env = Env>(
|
|
60
|
+
options: MethodNotAllowedOptions<E>
|
|
61
|
+
): MiddlewareHandler<E> => {
|
|
62
|
+
let methodRouter: TrieRouter<string[]> | undefined
|
|
63
|
+
|
|
64
|
+
return async function methodNotAllowed(c, next) {
|
|
65
|
+
const routeIndex = c.req.routeIndex
|
|
66
|
+
await next()
|
|
67
|
+
|
|
68
|
+
if (c.res.status !== 404) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (c.error) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!methodRouter) {
|
|
77
|
+
const methodsByPath = new Map<string, Set<string>>()
|
|
78
|
+
|
|
79
|
+
for (const route of options.app.routes) {
|
|
80
|
+
// App does not distinguish middleware registered with `app.use()` from handlers
|
|
81
|
+
// registered with `app.all()`, so `ALL` routes cannot contribute to the Allow header.
|
|
82
|
+
if (route.method === METHOD_NAME_ALL || route.method === 'HEAD') {
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const methods = methodsByPath.get(route.path) ?? new Set<string>()
|
|
87
|
+
methods.add(route.method)
|
|
88
|
+
|
|
89
|
+
// App dispatches HEAD requests to GET routes.
|
|
90
|
+
if (route.method === 'GET') {
|
|
91
|
+
methods.add('HEAD')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
methodsByPath.set(route.path, methods)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
methodRouter = new TrieRouter()
|
|
98
|
+
for (const [path, methods] of methodsByPath) {
|
|
99
|
+
methodRouter.add(METHOD_NAME_ALL, path, [...methods])
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let requestPath = c.req.path
|
|
104
|
+
const routes = matchedRoutes(c)
|
|
105
|
+
const currentRoute = routes[routeIndex]
|
|
106
|
+
const sourceRoute = options.app.routes.find((route) => route.handler === methodNotAllowed)
|
|
107
|
+
if (currentRoute && sourceRoute) {
|
|
108
|
+
const currentBasePathParts = basePath(c, routeIndex).split('/').filter(Boolean)
|
|
109
|
+
const sourceBasePathLength = sourceRoute.basePath.split('/').filter(Boolean).length
|
|
110
|
+
const mountBasePathParts = currentBasePathParts.slice(
|
|
111
|
+
0,
|
|
112
|
+
currentBasePathParts.length - sourceBasePathLength
|
|
113
|
+
)
|
|
114
|
+
if (mountBasePathParts.length > 0) {
|
|
115
|
+
const mountBasePath = `/${mountBasePathParts.join('/')}`
|
|
116
|
+
requestPath = c.req.path.slice(mountBasePath.length) || '/'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const allowedMethods = new Set<string>()
|
|
121
|
+
for (const [methods] of methodRouter.match(METHOD_NAME_ALL, requestPath)[0]) {
|
|
122
|
+
for (const method of methods) {
|
|
123
|
+
allowedMethods.add(method)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (allowedMethods.size === 0 || allowedMethods.has(c.req.method)) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Prevent stale headers from being carried over from the 404 response.
|
|
132
|
+
c.res.headers.delete('Allow')
|
|
133
|
+
c.res.headers.delete('Content-Length')
|
|
134
|
+
|
|
135
|
+
const methods = [...allowedMethods]
|
|
136
|
+
const allow = methods.join(', ')
|
|
137
|
+
c.res = options.onMethodNotAllowed
|
|
138
|
+
? await options.onMethodNotAllowed(c, methods)
|
|
139
|
+
: c.text('Method Not Allowed', 405, { Allow: allow })
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { App } from '../../app.ts'
|
|
2
|
+
import { methodOverride } from './index.ts'
|
|
3
|
+
|
|
4
|
+
describe('Method Override Middleware', () => {
|
|
5
|
+
describe('Form', () => {
|
|
6
|
+
const app = new App()
|
|
7
|
+
app.use('/posts/*', methodOverride({ app }))
|
|
8
|
+
app.use('/posts-custom/*', methodOverride({ app, form: 'custom-input-name' }))
|
|
9
|
+
app.on(['post', 'delete'], ['/posts', '/posts-custom'], async (c) => {
|
|
10
|
+
const form = await c.req.formData()
|
|
11
|
+
return c.json({
|
|
12
|
+
method: c.req.method,
|
|
13
|
+
message: form.get('message'),
|
|
14
|
+
contentType: c.req.header('content-type') ?? '',
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('multipart/form-data', () => {
|
|
19
|
+
it('Should override POST to DELETE', async () => {
|
|
20
|
+
const form = new FormData()
|
|
21
|
+
form.append('message', 'Hello')
|
|
22
|
+
form.append('_method', 'DELETE')
|
|
23
|
+
const res = await app.request('/posts', {
|
|
24
|
+
body: form,
|
|
25
|
+
method: 'POST',
|
|
26
|
+
})
|
|
27
|
+
expect(res.status).toBe(200)
|
|
28
|
+
const data = await res.json()
|
|
29
|
+
expect(data.method).toBe('DELETE')
|
|
30
|
+
expect(data.message).toBe('Hello')
|
|
31
|
+
expect(data.contentType).toMatch(/^multipart\/form-data;/)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('Should override POST to DELETE - with a custom form input name', async () => {
|
|
35
|
+
const form = new FormData()
|
|
36
|
+
form.append('message', 'Hello')
|
|
37
|
+
form.append('custom-input-name', 'DELETE')
|
|
38
|
+
const res = await app.request('/posts-custom', {
|
|
39
|
+
body: form,
|
|
40
|
+
method: 'POST',
|
|
41
|
+
})
|
|
42
|
+
expect(res.status).toBe(200)
|
|
43
|
+
const data = await res.json()
|
|
44
|
+
expect(data.method).toBe('DELETE')
|
|
45
|
+
expect(data.message).toBe('Hello')
|
|
46
|
+
expect(data.contentType).toMatch(/^multipart\/form-data;/)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('Should override POST to PATCH - not found', async () => {
|
|
50
|
+
const form = new FormData()
|
|
51
|
+
form.append('message', 'Hello')
|
|
52
|
+
form.append('_method', 'PATCH')
|
|
53
|
+
const res = await app.request('/posts', {
|
|
54
|
+
body: form,
|
|
55
|
+
method: 'POST',
|
|
56
|
+
})
|
|
57
|
+
expect(res.status).toBe(404)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe('application/x-www-form-urlencoded', () => {
|
|
62
|
+
it('Should override POST to DELETE', async () => {
|
|
63
|
+
const params = new URLSearchParams()
|
|
64
|
+
params.append('message', 'Hello')
|
|
65
|
+
params.append('_method', 'DELETE')
|
|
66
|
+
const res = await app.request('/posts', {
|
|
67
|
+
body: params,
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
70
|
+
},
|
|
71
|
+
method: 'POST',
|
|
72
|
+
})
|
|
73
|
+
expect(res.status).toBe(200)
|
|
74
|
+
const data = await res.json()
|
|
75
|
+
expect(data.method).toBe('DELETE')
|
|
76
|
+
expect(data.message).toBe('Hello')
|
|
77
|
+
expect(data.contentType).toBe('application/x-www-form-urlencoded')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('Should override POST to DELETE - with a custom form input name', async () => {
|
|
81
|
+
const params = new URLSearchParams()
|
|
82
|
+
params.append('message', 'Hello')
|
|
83
|
+
params.append('custom-input-name', 'DELETE')
|
|
84
|
+
const res = await app.request('/posts-custom', {
|
|
85
|
+
body: params,
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
88
|
+
},
|
|
89
|
+
method: 'POST',
|
|
90
|
+
})
|
|
91
|
+
expect(res.status).toBe(200)
|
|
92
|
+
const data = await res.json()
|
|
93
|
+
expect(data.method).toBe('DELETE')
|
|
94
|
+
expect(data.message).toBe('Hello')
|
|
95
|
+
expect(data.contentType).toBe('application/x-www-form-urlencoded')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('Should override POST to DELETE - with charset in Content-Type', async () => {
|
|
99
|
+
const params = new URLSearchParams()
|
|
100
|
+
params.append('message', 'Hello')
|
|
101
|
+
params.append('_method', 'DELETE')
|
|
102
|
+
const res = await app.request('/posts', {
|
|
103
|
+
body: params,
|
|
104
|
+
headers: {
|
|
105
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
106
|
+
},
|
|
107
|
+
method: 'POST',
|
|
108
|
+
})
|
|
109
|
+
expect(res.status).toBe(200)
|
|
110
|
+
const data = await res.json()
|
|
111
|
+
expect(data.method).toBe('DELETE')
|
|
112
|
+
expect(data.message).toBe('Hello')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('Should override POST to PATCH - not found', async () => {
|
|
116
|
+
const form = new FormData()
|
|
117
|
+
form.append('message', 'Hello')
|
|
118
|
+
form.append('_method', 'PATCH')
|
|
119
|
+
const res = await app.request('/posts', {
|
|
120
|
+
body: form,
|
|
121
|
+
method: 'POST',
|
|
122
|
+
})
|
|
123
|
+
expect(res.status).toBe(404)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('Header', () => {
|
|
129
|
+
const app = new App()
|
|
130
|
+
app.use('/posts/*', methodOverride({ app, header: 'X-METHOD-OVERRIDE' }))
|
|
131
|
+
app.on(['get', 'post', 'delete'], '/posts', async (c) => {
|
|
132
|
+
return c.json({
|
|
133
|
+
method: c.req.method,
|
|
134
|
+
headerValue: c.req.header('X-METHOD-OVERRIDE') ?? null,
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('Should override POST to DELETE', async () => {
|
|
139
|
+
const res = await app.request('/posts', {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: {
|
|
142
|
+
'X-METHOD-OVERRIDE': 'DELETE',
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
expect(res.status).toBe(200)
|
|
146
|
+
expect(await res.json()).toEqual({
|
|
147
|
+
method: 'DELETE',
|
|
148
|
+
headerValue: null,
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('Should not override GET request', async () => {
|
|
153
|
+
const res = await app.request('/posts', {
|
|
154
|
+
method: 'GET',
|
|
155
|
+
headers: {
|
|
156
|
+
'X-METHOD-OVERRIDE': 'DELETE',
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
expect(res.status).toBe(200)
|
|
160
|
+
expect(await res.json()).toEqual({
|
|
161
|
+
method: 'GET',
|
|
162
|
+
headerValue: 'DELETE', // It does not modify the headers.
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('Query', () => {
|
|
168
|
+
const app = new App()
|
|
169
|
+
app.use('/posts/*', methodOverride({ app, query: '_method' }))
|
|
170
|
+
app.on(['get', 'post', 'delete'], '/posts', async (c) => {
|
|
171
|
+
return c.json({
|
|
172
|
+
method: c.req.method,
|
|
173
|
+
queryValue: c.req.query('_method') ?? null,
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('Should override POST to DELETE', async () => {
|
|
178
|
+
const res = await app.request('/posts?_method=delete', {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
})
|
|
181
|
+
expect(res.status).toBe(200)
|
|
182
|
+
expect(await res.json()).toEqual({
|
|
183
|
+
method: 'DELETE',
|
|
184
|
+
queryValue: null,
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('Should override POST to DELETE when the request has a body', async () => {
|
|
189
|
+
const app = new App()
|
|
190
|
+
app.use('/posts/*', methodOverride({ app, query: '_method' }))
|
|
191
|
+
app.on(['post', 'delete'], '/posts', async (c) => {
|
|
192
|
+
return c.json({
|
|
193
|
+
method: c.req.method,
|
|
194
|
+
body: await c.req.text(),
|
|
195
|
+
queryValue: c.req.query('_method') ?? null,
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
const res = await app.request('/posts?_method=delete', {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
body: new URLSearchParams({ message: 'Hello' }),
|
|
201
|
+
})
|
|
202
|
+
expect(res.status).toBe(200)
|
|
203
|
+
expect(await res.json()).toEqual({
|
|
204
|
+
method: 'DELETE',
|
|
205
|
+
body: 'message=Hello',
|
|
206
|
+
queryValue: null,
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('Should not override GET request', async () => {
|
|
211
|
+
const res = await app.request('/posts?_method=delete', {
|
|
212
|
+
method: 'GET',
|
|
213
|
+
})
|
|
214
|
+
expect(res.status).toBe(200)
|
|
215
|
+
expect(await res.json()).toEqual({
|
|
216
|
+
method: 'GET',
|
|
217
|
+
queryValue: 'delete', // It does not modify the queries.
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
})
|