acrylblends 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -1
- package/dist/index.js +84 -5
- package/dist/index.js.map +1 -1
- package/docs/framework/LICENSE +23 -0
- package/docs/framework/README.md +63 -0
- package/docs/framework/UPSTREAM.md +3 -0
- package/docs/framework/api-summary.md +138 -0
- package/docs/framework/concepts.md +105 -0
- package/docs/framework/context.md +129 -0
- package/docs/framework/getting-started.md +146 -0
- package/docs/framework/helpers.md +118 -0
- package/docs/framework/middleware.md +154 -0
- package/docs/framework/migration-from-0-3.md +108 -0
- package/docs/framework/migration-from-dsh-webserver.md +103 -0
- package/docs/framework/node-server.md +75 -0
- package/docs/framework/plugin-lifecycle.md +111 -0
- package/docs/framework/request.md +106 -0
- package/docs/framework/routing.md +188 -0
- package/docs/framework/surfaces.md +71 -0
- package/docs/framework/upstream.json +9 -0
- package/docs/framework/validation.md +135 -0
- package/package.json +4 -2
- package/plugins/acryl-inspect/plugin.json +2 -1
- package/plugins/acryl-inspect/src/report.ts +21 -13
- package/plugins/acryl-inspect/src/service.ts +9 -7
- package/plugins/acryl-inspect/tests/report.spec.ts +13 -8
- package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
- package/plugins/acryl-inspect/upstream.json +2 -2
- package/plugins/acryl-node-server/LICENSE +26 -0
- package/plugins/acryl-node-server/plugin.json +2 -1
- package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
- package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
- package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
- package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
- package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
- package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
- package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
- package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
- package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
- package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
- package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
- package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
- package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
- package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
- package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
- package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
- package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
- package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
- package/plugins/acryl-node-server/src/index.ts +19 -6
- package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
- package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
- package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
- package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
- package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
- package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
- package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
- package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
- package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
- package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
- package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
- package/plugins/acryl-node-server/upstream.json +3 -3
- package/plugins/acryl-router/LICENSE +26 -0
- package/plugins/acryl-router/plugin.json +2 -1
- package/plugins/acryl-router/src/client.ts +2 -0
- package/plugins/acryl-router/src/connection.ts +42 -0
- package/plugins/acryl-router/src/core/app-base.ts +544 -0
- package/plugins/acryl-router/src/core/app.test.ts +3833 -0
- package/plugins/acryl-router/src/core/app.ts +34 -0
- package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
- package/plugins/acryl-router/src/core/client/client.ts +249 -0
- package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
- package/plugins/acryl-router/src/core/client/index.ts +17 -0
- package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
- package/plugins/acryl-router/src/core/client/types.ts +393 -0
- package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
- package/plugins/acryl-router/src/core/client/utils.ts +117 -0
- package/plugins/acryl-router/src/core/compose.test.ts +751 -0
- package/plugins/acryl-router/src/core/compose.ts +73 -0
- package/plugins/acryl-router/src/core/context.test.ts +629 -0
- package/plugins/acryl-router/src/core/context.ts +781 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
- package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
- package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
- package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
- package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
- package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
- package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
- package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
- package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
- package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
- package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
- package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
- package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
- package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
- package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
- package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
- package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
- package/plugins/acryl-router/src/core/http-exception.ts +77 -0
- package/plugins/acryl-router/src/core/index.ts +53 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
- package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
- package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
- package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
- package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
- package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
- package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
- package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
- package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
- package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
- package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
- package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
- package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
- package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
- package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
- package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
- package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
- package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
- package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
- package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
- package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
- package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
- package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
- package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
- package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
- package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
- package/plugins/acryl-router/src/core/request/constants.ts +1 -0
- package/plugins/acryl-router/src/core/request.test.ts +751 -0
- package/plugins/acryl-router/src/core/request.ts +502 -0
- package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
- package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
- package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
- package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
- package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
- package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
- package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
- package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
- package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
- package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
- package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
- package/plugins/acryl-router/src/core/router/utils.ts +1 -0
- package/plugins/acryl-router/src/core/router.ts +103 -0
- package/plugins/acryl-router/src/core/types.test.ts +4134 -0
- package/plugins/acryl-router/src/core/types.ts +2782 -0
- package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
- package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
- package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
- package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
- package/plugins/acryl-router/src/core/utils/body.ts +263 -0
- package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
- package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
- package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
- package/plugins/acryl-router/src/core/utils/color.ts +56 -0
- package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
- package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
- package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
- package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
- package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
- package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
- package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
- package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
- package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
- package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
- package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
- package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
- package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
- package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
- package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
- package/plugins/acryl-router/src/core/utils/html.ts +182 -0
- package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
- package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
- package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
- package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
- package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
- package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
- package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
- package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
- package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
- package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
- package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
- package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
- package/plugins/acryl-router/src/core/utils/types.ts +123 -0
- package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
- package/plugins/acryl-router/src/core/utils/url.ts +337 -0
- package/plugins/acryl-router/src/core/validator/index.ts +8 -0
- package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
- package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
- package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
- package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
- package/plugins/acryl-router/src/helpers/index.ts +13 -3
- package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
- package/plugins/acryl-router/src/index.ts +6 -6
- package/plugins/acryl-router/src/middleware/index.ts +25 -6
- package/plugins/acryl-router/src/middleware/loopback-only.ts +53 -14
- package/plugins/acryl-router/src/service.ts +262 -64
- package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
- package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
- package/plugins/acryl-router/tests/loader.spec.ts +3 -2
- package/plugins/acryl-router/tests/loopback-only.spec.ts +80 -0
- package/plugins/acryl-router/tests/service.spec.ts +281 -66
- package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/plugin.json +2 -1
- package/plugins/acryl-router-http/src/bridge.ts +3 -1
- package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
- package/plugins/acryl-router-http/upstream.json +3 -3
- package/plugins/acryl-state/NOTICE +30 -0
- package/plugins/acryl-state/plugin.json +1 -1
- package/plugins/acryl-state/src/cell.ts +1 -1
- package/plugins/acryl-state/src/service.ts +33 -15
- package/plugins/acryl-state/upstream.json +6 -4
- package/plugins/builder/script.ts +4 -7
- package/plugins/extensions/index.ts +6 -5
- package/plugins/extensions/resolve.ts +28 -3
- package/plugins/openai/prompt.ts +9 -7
- package/plugins/tsconfig.json +5 -4
- package/shared/AGENTS.core.md +54 -0
- package/templates/agent/AGENTS.md +6 -0
- package/templates/agent/README.md +2 -2
- package/templates/agent/blend.yaml +1 -1
- package/templates/agent/src/routes.ts +37 -27
- package/templates/agent/test/model.test.ts +2 -1
- package/templates/agent/tsconfig.json +8 -5
- package/templates/blank/AGENTS.md +5 -0
- package/templates/blank/README.md +1 -1
- package/templates/blank/src/routes.ts +6 -4
- package/templates/blank/tsconfig.json +2 -4
- package/templates/todo/AGENTS.md +5 -0
- package/templates/todo/src/page.ts +1 -1
- package/templates/todo/src/todo.ts +58 -29
- package/templates/todo/tsconfig.json +2 -4
- package/templates/todo/upstream.json +2 -2
- package/plugins/acryl-router/src/compose.ts +0 -21
- package/plugins/acryl-router/src/context.ts +0 -59
- package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
- package/plugins/acryl-router/src/helpers/sse.ts +0 -83
- package/plugins/acryl-router/src/helpers/validate.ts +0 -44
- package/plugins/acryl-router/src/http-error.ts +0 -21
- package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
- package/plugins/acryl-router/src/middleware/cors.ts +0 -46
- package/plugins/acryl-router/src/middleware/logger.ts +0 -21
- package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
- package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
- package/plugins/acryl-router/src/pattern.ts +0 -89
- package/plugins/acryl-router/src/registrar.ts +0 -90
- package/plugins/acryl-router/src/table.ts +0 -156
- package/plugins/acryl-router/src/types.ts +0 -73
- package/plugins/acryl-router/src/util.ts +0 -23
- package/plugins/acryl-router/tests/api.spec.ts +0 -223
- package/plugins/acryl-router/tests/conformance.ts +0 -170
- package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
- package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
- package/plugins/acryl-router/tests/table.spec.ts +0 -47
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { HttpResponse, http } from 'msw'
|
|
2
|
+
import { setupServer } from 'msw/node'
|
|
3
|
+
import { App } from '../app.ts'
|
|
4
|
+
import type { Expect, Equal } from '../utils/types.ts'
|
|
5
|
+
import { hc } from './client.ts'
|
|
6
|
+
import {
|
|
7
|
+
buildSearchParams,
|
|
8
|
+
deepMerge,
|
|
9
|
+
parseResponse,
|
|
10
|
+
mergePath,
|
|
11
|
+
removeIndexString,
|
|
12
|
+
replaceUrlParam,
|
|
13
|
+
replaceUrlProtocol,
|
|
14
|
+
} from './utils.ts'
|
|
15
|
+
|
|
16
|
+
describe('mergePath', () => {
|
|
17
|
+
it('Should merge paths correctly', () => {
|
|
18
|
+
expect(mergePath('http://localhost', '/api')).toBe('http://localhost/api')
|
|
19
|
+
expect(mergePath('http://localhost/', '/api')).toBe('http://localhost/api')
|
|
20
|
+
expect(mergePath('http://localhost', 'api')).toBe('http://localhost/api')
|
|
21
|
+
expect(mergePath('http://localhost/', 'api')).toBe('http://localhost/api')
|
|
22
|
+
expect(mergePath('http://localhost/', '/')).toBe('http://localhost/')
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('replaceUrlParams', () => {
|
|
27
|
+
it('Should replace correctly', () => {
|
|
28
|
+
const url = 'http://localhost/posts/:postId/comments/:commentId'
|
|
29
|
+
const params = {
|
|
30
|
+
postId: '123',
|
|
31
|
+
commentId: '456',
|
|
32
|
+
}
|
|
33
|
+
const replacedUrl = replaceUrlParam(url, params)
|
|
34
|
+
expect(replacedUrl).toBe('http://localhost/posts/123/comments/456')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('Should replace correctly when there is regex pattern', () => {
|
|
38
|
+
const url = 'http://localhost/posts/:postId{[abc]+}/comments/:commentId{[0-9]+}'
|
|
39
|
+
const params = {
|
|
40
|
+
postId: 'abc',
|
|
41
|
+
commentId: '456',
|
|
42
|
+
}
|
|
43
|
+
const replacedUrl = replaceUrlParam(url, params)
|
|
44
|
+
expect(replacedUrl).toBe('http://localhost/posts/abc/comments/456')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('Should replace correctly when there is regex pattern with length limit', () => {
|
|
48
|
+
const url = 'http://localhost/year/:year{[1-9]{1}[0-9]{3}}/month/:month{[0-9]{2}}'
|
|
49
|
+
const params = {
|
|
50
|
+
year: '2024',
|
|
51
|
+
month: '2',
|
|
52
|
+
}
|
|
53
|
+
const replacedUrl = replaceUrlParam(url, params)
|
|
54
|
+
expect(replacedUrl).toBe('http://localhost/year/2024/month/2')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('Should replace correctly when it has optional parameters', () => {
|
|
58
|
+
const url = 'http://localhost/something/:firstId/:secondId/:version?'
|
|
59
|
+
const params = {
|
|
60
|
+
firstId: '123',
|
|
61
|
+
secondId: '456',
|
|
62
|
+
version: undefined,
|
|
63
|
+
}
|
|
64
|
+
const replacedUrl = replaceUrlParam(url, params)
|
|
65
|
+
expect(replacedUrl).toBe('http://localhost/something/123/456')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('Should not replace a param whose name is a prefix of another param', () => {
|
|
69
|
+
const url = 'http://localhost/:idType/:id'
|
|
70
|
+
const params = {
|
|
71
|
+
id: '1',
|
|
72
|
+
idType: '2',
|
|
73
|
+
}
|
|
74
|
+
const replacedUrl = replaceUrlParam(url, params)
|
|
75
|
+
expect(replacedUrl).toBe('http://localhost/2/1')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('Should replace correctly when parameter value contains $ characters', () => {
|
|
79
|
+
expect(replaceUrlParam('http://localhost/items/:id', { id: 'item$&' })).toBe(
|
|
80
|
+
'http://localhost/items/item$&'
|
|
81
|
+
)
|
|
82
|
+
expect(replaceUrlParam('http://localhost/items/:id', { id: '$100' })).toBe(
|
|
83
|
+
'http://localhost/items/$100'
|
|
84
|
+
)
|
|
85
|
+
expect(replaceUrlParam('http://localhost/users/:id', { id: 'test$`' })).toBe(
|
|
86
|
+
'http://localhost/users/test$`'
|
|
87
|
+
)
|
|
88
|
+
expect(replaceUrlParam('http://localhost/users/:id/posts', { id: "test$'" })).toBe(
|
|
89
|
+
"http://localhost/users/test$'/posts"
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('buildSearchParams', () => {
|
|
95
|
+
it('Should build URLSearchParams correctly', () => {
|
|
96
|
+
const query = {
|
|
97
|
+
id: '123',
|
|
98
|
+
type: 'test',
|
|
99
|
+
tag: ['a', 'b'],
|
|
100
|
+
}
|
|
101
|
+
const searchParams = buildSearchParams(query)
|
|
102
|
+
expect(searchParams.toString()).toBe('id=123&type=test&tag=a&tag=b')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('Should skip an undefined entry inside an array, as it does for a top-level value', () => {
|
|
106
|
+
// `['a', undefined, 'b']` used to serialise as `tag=a&tag=undefined&tag=b`,
|
|
107
|
+
// sending the literal string "undefined" to the server.
|
|
108
|
+
const searchParams = buildSearchParams({
|
|
109
|
+
tag: ['a', undefined as unknown as string, 'b'],
|
|
110
|
+
})
|
|
111
|
+
expect(searchParams.toString()).toBe('tag=a&tag=b')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('Should keep an empty string in an array', () => {
|
|
115
|
+
const searchParams = buildSearchParams({ tag: ['a', '', 'b'] })
|
|
116
|
+
expect(searchParams.toString()).toBe('tag=a&tag=&tag=b')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('Should drop an array whose entries are all undefined', () => {
|
|
120
|
+
const searchParams = buildSearchParams({
|
|
121
|
+
tag: [undefined, undefined] as unknown as string[],
|
|
122
|
+
})
|
|
123
|
+
expect(searchParams.toString()).toBe('')
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
describe('replaceUrlProtocol', () => {
|
|
128
|
+
it('Should replace http to ws', () => {
|
|
129
|
+
const url = 'http://localhost'
|
|
130
|
+
const newUrl = replaceUrlProtocol(url, 'ws')
|
|
131
|
+
expect(newUrl).toBe('ws://localhost')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('Should replace https to wss', () => {
|
|
135
|
+
const url = 'https://localhost'
|
|
136
|
+
const newUrl = replaceUrlProtocol(url, 'ws')
|
|
137
|
+
expect(newUrl).toBe('wss://localhost')
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('Should replace ws to http', () => {
|
|
141
|
+
const url = 'ws://localhost'
|
|
142
|
+
const newUrl = replaceUrlProtocol(url, 'http')
|
|
143
|
+
expect(newUrl).toBe('http://localhost')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('Should replace wss to https', () => {
|
|
147
|
+
const url = 'wss://localhost'
|
|
148
|
+
const newUrl = replaceUrlProtocol(url, 'http')
|
|
149
|
+
expect(newUrl).toBe('https://localhost')
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('removeIndexString', () => {
|
|
154
|
+
it('Should remove last `/index` string', () => {
|
|
155
|
+
let url = 'http://localhost/index'
|
|
156
|
+
let newUrl = removeIndexString(url)
|
|
157
|
+
expect(newUrl).toBe('http://localhost/')
|
|
158
|
+
|
|
159
|
+
url = '/index'
|
|
160
|
+
newUrl = removeIndexString(url)
|
|
161
|
+
expect(newUrl).toBe('')
|
|
162
|
+
|
|
163
|
+
url = '/sub/index'
|
|
164
|
+
newUrl = removeIndexString(url)
|
|
165
|
+
expect(newUrl).toBe('/sub')
|
|
166
|
+
|
|
167
|
+
url = '/subindex'
|
|
168
|
+
newUrl = removeIndexString(url)
|
|
169
|
+
expect(newUrl).toBe('/subindex')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('Should remove `/index` with query parameters', () => {
|
|
173
|
+
let url = 'http://localhost/index?page=123&limit=20'
|
|
174
|
+
let newUrl = removeIndexString(url)
|
|
175
|
+
expect(newUrl).toBe('http://localhost/?page=123&limit=20')
|
|
176
|
+
|
|
177
|
+
url = 'https://example.com/index?q=search'
|
|
178
|
+
newUrl = removeIndexString(url)
|
|
179
|
+
expect(newUrl).toBe('https://example.com/?q=search')
|
|
180
|
+
|
|
181
|
+
url = '/api/index?filter=test'
|
|
182
|
+
newUrl = removeIndexString(url)
|
|
183
|
+
expect(newUrl).toBe('/api?filter=test')
|
|
184
|
+
|
|
185
|
+
url = '/index?a=1&b=2&c=3'
|
|
186
|
+
newUrl = removeIndexString(url)
|
|
187
|
+
expect(newUrl).toBe('?a=1&b=2&c=3')
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('deepMerge', () => {
|
|
192
|
+
it('should return the source object if the target object is not an object', () => {
|
|
193
|
+
const target = null
|
|
194
|
+
const source = 'not an object' as unknown as Record<string, unknown>
|
|
195
|
+
const result = deepMerge(target, source)
|
|
196
|
+
expect(result).toEqual(source)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('should merge two objects with object properties', () => {
|
|
200
|
+
expect(
|
|
201
|
+
deepMerge(
|
|
202
|
+
{ headers: { acryl: '1' }, timeout: 2, params: {} },
|
|
203
|
+
{ headers: { acryl: '2', demo: 2 }, params: undefined }
|
|
204
|
+
)
|
|
205
|
+
).toStrictEqual({
|
|
206
|
+
params: undefined,
|
|
207
|
+
headers: { acryl: '2', demo: 2 },
|
|
208
|
+
timeout: 2,
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe('parseResponse', async () => {
|
|
214
|
+
const _app = new App()
|
|
215
|
+
.get('/text', (c) => c.text('hi'))
|
|
216
|
+
.get('/json', (c) => c.json({ message: 'hi' }))
|
|
217
|
+
.get('/might-error-json', (c) => {
|
|
218
|
+
if (Math.random() > 0.5) {
|
|
219
|
+
return c.json({ error: 'error' }, 500)
|
|
220
|
+
}
|
|
221
|
+
return c.json({ data: [{ id: 1 }, { id: 2 }] })
|
|
222
|
+
})
|
|
223
|
+
.get('/might-error-mixed-json-text', (c) => {
|
|
224
|
+
if (Math.random() > 0.5) {
|
|
225
|
+
return c.text('500 Internal Server Error', 500)
|
|
226
|
+
}
|
|
227
|
+
return c.json({ message: 'Success' })
|
|
228
|
+
})
|
|
229
|
+
.get('/200-explicit', (c) => c.text('OK', 200))
|
|
230
|
+
.get('/404', (c) => c.text('404 Not Found', 404))
|
|
231
|
+
.get('/500', (c) => c.text('500 Internal Server Error', 500))
|
|
232
|
+
.get('/raw', (c) => {
|
|
233
|
+
c.header('content-type', '')
|
|
234
|
+
return c.body('hello')
|
|
235
|
+
})
|
|
236
|
+
.get('/rawUnknown', (c) => {
|
|
237
|
+
c.header('content-type', 'x/custom-type')
|
|
238
|
+
return c.body('hello')
|
|
239
|
+
})
|
|
240
|
+
.get('/rawBuffer', (c) => {
|
|
241
|
+
c.header('content-type', 'x/custom-type')
|
|
242
|
+
return c.body(new TextEncoder().encode('acryl'))
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const client = hc<typeof _app>('http://localhost')
|
|
246
|
+
|
|
247
|
+
const server = setupServer(
|
|
248
|
+
http.get('http://localhost/text', () => {
|
|
249
|
+
return HttpResponse.text('hi')
|
|
250
|
+
}),
|
|
251
|
+
http.get('http://localhost/json', () => {
|
|
252
|
+
return HttpResponse.json({ message: 'hi' })
|
|
253
|
+
}),
|
|
254
|
+
http.get('http://localhost/might-error-json', () => {
|
|
255
|
+
if (Math.random() > 0.5) {
|
|
256
|
+
return HttpResponse.json({ error: 'error' }, { status: 500 })
|
|
257
|
+
}
|
|
258
|
+
return HttpResponse.json({ data: [{ id: 1 }, { id: 2 }] })
|
|
259
|
+
}),
|
|
260
|
+
http.get('http://localhost/might-error-mixed-json-text', () => {
|
|
261
|
+
if (Math.random() > 0.5) {
|
|
262
|
+
return HttpResponse.text('500 Internal Server Error', { status: 500 })
|
|
263
|
+
}
|
|
264
|
+
return HttpResponse.json({ message: 'Success' })
|
|
265
|
+
}),
|
|
266
|
+
http.get('http://localhost/200-explicit', () => {
|
|
267
|
+
return HttpResponse.text('OK', { status: 200 })
|
|
268
|
+
}),
|
|
269
|
+
http.get('http://localhost/404', () => {
|
|
270
|
+
return HttpResponse.text('404 Not Found', { status: 404 })
|
|
271
|
+
}),
|
|
272
|
+
http.get('http://localhost/500', () => {
|
|
273
|
+
return HttpResponse.text('500 Internal Server Error', { status: 500 })
|
|
274
|
+
}),
|
|
275
|
+
http.get('http://localhost/raw', () => {
|
|
276
|
+
return HttpResponse.text('hello', {
|
|
277
|
+
headers: {
|
|
278
|
+
'content-type': '',
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
}),
|
|
282
|
+
http.get('http://localhost/rawUnknown', () => {
|
|
283
|
+
return HttpResponse.text('hello', {
|
|
284
|
+
headers: {
|
|
285
|
+
'content-type': 'x/custom-type',
|
|
286
|
+
},
|
|
287
|
+
})
|
|
288
|
+
}),
|
|
289
|
+
http.get('http://localhost/rawBuffer', () => {
|
|
290
|
+
return HttpResponse.arrayBuffer(new TextEncoder().encode('acryl').buffer, {
|
|
291
|
+
headers: {
|
|
292
|
+
'content-type': 'x/custom-type',
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
}),
|
|
296
|
+
http.get('http://localhost/noRoute', () => HttpResponse.error())
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
beforeAll(() => server.listen())
|
|
300
|
+
afterEach(() => server.resetHandlers())
|
|
301
|
+
afterAll(() => server.close())
|
|
302
|
+
|
|
303
|
+
await Promise.all([
|
|
304
|
+
it('should auto parse the text response - async fetch', async () => {
|
|
305
|
+
const result = await parseResponse(client.text.$get())
|
|
306
|
+
expect(result).toBe('hi')
|
|
307
|
+
type _verify = Expect<Equal<typeof result, 'hi'>>
|
|
308
|
+
}),
|
|
309
|
+
it('should auto parse the text response - sync fetch', async () => {
|
|
310
|
+
const result = await parseResponse(await client.text.$get())
|
|
311
|
+
expect(result).toBe('hi')
|
|
312
|
+
type _verify = Expect<Equal<typeof result, 'hi'>>
|
|
313
|
+
}),
|
|
314
|
+
it('should auto parse text response - explicit 200', async () => {
|
|
315
|
+
const result = await parseResponse(client['200-explicit'].$get())
|
|
316
|
+
expect(result).toBe('OK')
|
|
317
|
+
type _verify = Expect<Equal<typeof result, 'OK'>>
|
|
318
|
+
}),
|
|
319
|
+
it('should auto parse the json response - async fetch', async () => {
|
|
320
|
+
const result = await parseResponse(client.json.$get())
|
|
321
|
+
expect(result).toEqual({ message: 'hi' })
|
|
322
|
+
type _verify = Expect<Equal<typeof result, { message: string }>>
|
|
323
|
+
}),
|
|
324
|
+
it('should auto parse the json response - sync fetch', async () => {
|
|
325
|
+
const result = await parseResponse(await client.json.$get())
|
|
326
|
+
expect(result).toEqual({ message: 'hi' })
|
|
327
|
+
type _verify = Expect<Equal<typeof result, { message: string }>>
|
|
328
|
+
}),
|
|
329
|
+
it('should throw error when the response is not ok', async () => {
|
|
330
|
+
await expect(parseResponse(client['404'].$get())).rejects.toThrowError('404 Not Found')
|
|
331
|
+
}),
|
|
332
|
+
it('should parse as text for raw responses without content-type header', async () => {
|
|
333
|
+
const result = await parseResponse(client.raw.$get())
|
|
334
|
+
expect(result).toBe('hello')
|
|
335
|
+
type _verify = Expect<Equal<typeof result, 'hello'>>
|
|
336
|
+
}),
|
|
337
|
+
it('should parse as unknown string for raw buffer responses with unknown content-type header', async () => {
|
|
338
|
+
const result = await parseResponse(client.rawBuffer.$get())
|
|
339
|
+
expect(result).toMatchInlineSnapshot('"acryl"')
|
|
340
|
+
type _verify = Expect<Equal<typeof result, string>>
|
|
341
|
+
}),
|
|
342
|
+
it('should throw error matching snapshots', async () => {
|
|
343
|
+
// Defined 404 route
|
|
344
|
+
await expect(parseResponse(client['404'].$get())).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
345
|
+
'[DetailedError: 404 Not Found]'
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
// Defined 500 route
|
|
349
|
+
await expect(parseResponse(client['500'].$get())).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
350
|
+
'[DetailedError: 500 Internal Server Error]'
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
// Not defined route: MSW simulates a network error, so fetch itself rejects
|
|
354
|
+
// Note: the error in this test case is thrown at the `fetch` call (.$get()), not during the `parseResponse` call, so I think `parseResponse` should not try to catch and wrap it into a structured error, which could be inconsistent, if the user awaited the fetch.
|
|
355
|
+
await expect(
|
|
356
|
+
// @ts-expect-error noRoute is not defined
|
|
357
|
+
parseResponse(client['noRoute'].$get())
|
|
358
|
+
).rejects.toThrowErrorMatchingInlineSnapshot('[TypeError: Failed to fetch]')
|
|
359
|
+
}),
|
|
360
|
+
it('(type-only) should bypass error responses in the result type inference - simple 404', async () => {
|
|
361
|
+
type ResultType = Awaited<
|
|
362
|
+
ReturnType<typeof parseResponse<Awaited<ReturnType<(typeof client)['404']['$get']>>>>
|
|
363
|
+
>
|
|
364
|
+
type _verify = Expect<Equal<Awaited<ResultType>, undefined>>
|
|
365
|
+
}),
|
|
366
|
+
it('(type-only) should bypass error responses in the result type inference - conditional - json', async () => {
|
|
367
|
+
type ResultType = Awaited<
|
|
368
|
+
ReturnType<
|
|
369
|
+
typeof parseResponse<Awaited<ReturnType<(typeof client)['might-error-json']['$get']>>>
|
|
370
|
+
>
|
|
371
|
+
>
|
|
372
|
+
type _verify = Expect<Equal<ResultType, { data: { id: number }[] }>>
|
|
373
|
+
}),
|
|
374
|
+
it('(type-only) should bypass error responses in the result type inference - conditional - mixed json/text', async () => {
|
|
375
|
+
type ResultType = Awaited<
|
|
376
|
+
ReturnType<
|
|
377
|
+
typeof parseResponse<
|
|
378
|
+
Awaited<ReturnType<(typeof client)['might-error-mixed-json-text']['$get']>>
|
|
379
|
+
>
|
|
380
|
+
>
|
|
381
|
+
>
|
|
382
|
+
type _verify = Expect<Equal<ResultType, { message: string }>>
|
|
383
|
+
}),
|
|
384
|
+
])
|
|
385
|
+
|
|
386
|
+
it('should parse json response with _bodyInit when body is undefined', async () => {
|
|
387
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
388
|
+
ok: true,
|
|
389
|
+
status: 200,
|
|
390
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
391
|
+
body: undefined,
|
|
392
|
+
_bodyInit: '{"message":"test"}',
|
|
393
|
+
json: async () => ({ message: 'test' }),
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
global.fetch = mockFetch
|
|
397
|
+
|
|
398
|
+
const mockClientResponse = {
|
|
399
|
+
$get: mockFetch,
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const result = await parseResponse(mockClientResponse.$get())
|
|
403
|
+
expect(result).toEqual({ message: 'test' })
|
|
404
|
+
})
|
|
405
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientErrorStatusCode,
|
|
3
|
+
ContentfulStatusCode,
|
|
4
|
+
ServerErrorStatusCode,
|
|
5
|
+
} from '../utils/http-status.ts'
|
|
6
|
+
import { fetchRP, DetailedError } from './fetch-result-please.ts'
|
|
7
|
+
import type { ClientResponse, FilterClientResponseByStatusCode, ObjectType } from './types.ts'
|
|
8
|
+
|
|
9
|
+
export { DetailedError }
|
|
10
|
+
|
|
11
|
+
export const mergePath = (base: string, path: string) => {
|
|
12
|
+
base = base.replace(/\/+$/, '')
|
|
13
|
+
base = base + '/'
|
|
14
|
+
path = path.replace(/^\/+/, '')
|
|
15
|
+
return base + path
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const replaceUrlParam = (urlString: string, params: Record<string, string | undefined>) => {
|
|
19
|
+
for (const [k, v] of Object.entries(params)) {
|
|
20
|
+
const reg = new RegExp('/:' + k + '(?:{[^/]+})?\\??(?=/|$)')
|
|
21
|
+
urlString = urlString.replace(reg, () => (v ? `/${v}` : ''))
|
|
22
|
+
}
|
|
23
|
+
return urlString
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const buildSearchParams = (query: Record<string, string | string[]>) => {
|
|
27
|
+
const searchParams = new URLSearchParams()
|
|
28
|
+
|
|
29
|
+
for (const [k, v] of Object.entries(query)) {
|
|
30
|
+
if (v === undefined) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(v)) {
|
|
35
|
+
for (const v2 of v) {
|
|
36
|
+
if (v2 === undefined) {
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
searchParams.append(k, v2)
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
searchParams.set(k, v)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return searchParams
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const replaceUrlProtocol = (urlString: string, protocol: 'ws' | 'http') => {
|
|
50
|
+
switch (protocol) {
|
|
51
|
+
case 'ws':
|
|
52
|
+
return urlString.replace(/^http/, 'ws')
|
|
53
|
+
case 'http':
|
|
54
|
+
return urlString.replace(/^ws/, 'http')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const removeIndexString = (urlString: string) => {
|
|
59
|
+
if (/^https?:\/\/[^\/]+?\/index(?=\?|$)/.test(urlString)) {
|
|
60
|
+
return urlString.replace(/\/index(?=\?|$)/, '/')
|
|
61
|
+
}
|
|
62
|
+
return urlString.replace(/\/index(?=\?|$)/, '')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isObject(item: unknown): item is ObjectType {
|
|
66
|
+
return typeof item === 'object' && item !== null && !Array.isArray(item)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function deepMerge<T>(target: T, source: Record<string, unknown>): T {
|
|
70
|
+
if (!isObject(target) && !isObject(source)) {
|
|
71
|
+
return source as T
|
|
72
|
+
}
|
|
73
|
+
const merged = { ...target } as ObjectType<T>
|
|
74
|
+
|
|
75
|
+
for (const key in source) {
|
|
76
|
+
const value = source[key]
|
|
77
|
+
if (isObject(merged[key]) && isObject(value)) {
|
|
78
|
+
merged[key] = deepMerge(merged[key], value)
|
|
79
|
+
} else {
|
|
80
|
+
merged[key] = value as T[keyof T] & T
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return merged as T
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Shortcut to get a consumable response from `hc`'s fetch calls (Response), with types inference.
|
|
89
|
+
*
|
|
90
|
+
* Smartly parse the response data, throwing a structured error if the response is not `ok`. ({@link DetailedError})
|
|
91
|
+
*
|
|
92
|
+
* @example const result = await parseResponse(client.posts.$get())
|
|
93
|
+
*/
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
export async function parseResponse<T extends ClientResponse<any>>(
|
|
96
|
+
fetchRes: T | Promise<T>
|
|
97
|
+
): Promise<
|
|
98
|
+
FilterClientResponseByStatusCode<
|
|
99
|
+
T,
|
|
100
|
+
Exclude<ContentfulStatusCode, ClientErrorStatusCode | ServerErrorStatusCode> // Filter out the error responses
|
|
101
|
+
> extends never
|
|
102
|
+
? // Filtered responses does not include any contentful responses, exit with undefined
|
|
103
|
+
undefined
|
|
104
|
+
: // Filtered responses includes contentful responses, proceed to infer the type
|
|
105
|
+
FilterClientResponseByStatusCode<
|
|
106
|
+
T,
|
|
107
|
+
Exclude<ContentfulStatusCode, ClientErrorStatusCode | ServerErrorStatusCode>
|
|
108
|
+
> extends ClientResponse<infer RT, infer _, infer RF>
|
|
109
|
+
? RF extends 'json'
|
|
110
|
+
? RT
|
|
111
|
+
: RT extends string
|
|
112
|
+
? RT
|
|
113
|
+
: string
|
|
114
|
+
: undefined
|
|
115
|
+
> {
|
|
116
|
+
return fetchRP(fetchRes)
|
|
117
|
+
}
|