acrylblends 0.4.1 → 0.5.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/LICENSE +73 -0
- package/docs/framework/README.md +51 -51
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/agents/index.md +24 -0
- package/docs/framework/api/acryl.md +259 -0
- package/docs/framework/api/context.md +527 -0
- package/docs/framework/api/exception.md +80 -0
- package/docs/framework/api/index.md +12 -0
- package/docs/framework/api/presets.md +65 -0
- package/docs/framework/api/request.md +396 -0
- package/docs/framework/api/routing.md +303 -0
- package/docs/framework/{api-summary.md → api/summary.md} +19 -17
- package/docs/framework/concepts/benchmarks.md +26 -0
- package/docs/framework/concepts/developer-experience.md +5 -0
- package/docs/framework/concepts/middleware.md +21 -0
- package/docs/framework/concepts/motivation.md +14 -0
- package/docs/framework/concepts/routers.md +63 -0
- package/docs/framework/concepts/stacks.md +235 -0
- package/docs/framework/concepts/web-standard.md +37 -0
- package/docs/framework/{getting-started.md → cordis/acryl-in-a-cordis-app.md} +16 -16
- package/docs/framework/{concepts.md → cordis/concepts.md} +2 -2
- package/docs/framework/cordis/middleware.md +8 -0
- package/docs/framework/{migration-from-0-3.md → cordis/migration-from-0-3.md} +16 -13
- package/docs/framework/cordis/migration-from-0-4.md +14 -0
- package/docs/framework/{migration-from-dsh-webserver.md → cordis/migration-from-dsh-webserver.md} +7 -7
- package/docs/framework/{routing.md → cordis/mounting.md} +5 -108
- package/docs/framework/{node-server.md → cordis/node-server.md} +4 -4
- package/docs/framework/{surfaces.md → cordis/surfaces.md} +2 -2
- package/docs/framework/cordis/transport-and-cancellation.md +57 -0
- package/docs/framework/{validation.md → cordis/validation.md} +9 -9
- package/docs/framework/getting-started/basic.md +196 -0
- package/docs/framework/getting-started/bun.md +221 -0
- package/docs/framework/getting-started/nodejs.md +344 -0
- package/docs/framework/getting-started/other-runtimes.md +23 -0
- package/docs/framework/guides/best-practices.md +231 -0
- package/docs/framework/guides/create-acrylblends.md +79 -0
- package/docs/framework/guides/examples.md +3 -0
- package/docs/framework/guides/faq.md +19 -0
- package/docs/framework/guides/helpers.md +33 -0
- package/docs/framework/guides/jsx-dom.md +299 -0
- package/docs/framework/guides/jsx.md +440 -0
- package/docs/framework/guides/middleware.md +253 -0
- package/docs/framework/guides/rpc.md +886 -0
- package/docs/framework/guides/testing.md +133 -0
- package/docs/framework/guides/validation.md +383 -0
- package/docs/framework/helpers/accepts.md +60 -0
- package/docs/framework/helpers/adapter.md +87 -0
- package/docs/framework/helpers/conninfo.md +112 -0
- package/docs/framework/helpers/cookie.md +232 -0
- package/docs/framework/helpers/css.md +285 -0
- package/docs/framework/helpers/dev.md +67 -0
- package/docs/framework/helpers/factory.md +180 -0
- package/docs/framework/helpers/html.md +123 -0
- package/docs/framework/helpers/jwt.md +184 -0
- package/docs/framework/helpers/proxy.md +118 -0
- package/docs/framework/helpers/route.md +143 -0
- package/docs/framework/helpers/ssg.md +393 -0
- package/docs/framework/helpers/streaming.md +119 -0
- package/docs/framework/helpers/testing.md +101 -0
- package/docs/framework/helpers/websocket.md +215 -0
- package/docs/framework/middleware/builtin/basic-auth.md +165 -0
- package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
- package/docs/framework/middleware/builtin/body-limit.md +71 -0
- package/docs/framework/middleware/builtin/cache.md +122 -0
- package/docs/framework/middleware/builtin/combine.md +94 -0
- package/docs/framework/middleware/builtin/compress.md +59 -0
- package/docs/framework/middleware/builtin/context-storage.md +78 -0
- package/docs/framework/middleware/builtin/cors.md +135 -0
- package/docs/framework/middleware/builtin/csrf.md +105 -0
- package/docs/framework/middleware/builtin/etag.md +54 -0
- package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
- package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
- package/docs/framework/middleware/builtin/jwk.md +192 -0
- package/docs/framework/middleware/builtin/jwt.md +145 -0
- package/docs/framework/middleware/builtin/language.md +232 -0
- package/docs/framework/middleware/builtin/logger.md +74 -0
- package/docs/framework/middleware/builtin/loopback-only.md +31 -0
- package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
- package/docs/framework/middleware/builtin/method-override.md +80 -0
- package/docs/framework/middleware/builtin/mount.md +106 -0
- package/docs/framework/middleware/builtin/pretty-json.md +53 -0
- package/docs/framework/middleware/builtin/request-id.md +94 -0
- package/docs/framework/middleware/builtin/secure-headers.md +247 -0
- package/docs/framework/middleware/builtin/timeout.md +98 -0
- package/docs/framework/middleware/builtin/timing.md +101 -0
- package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
- package/docs/framework/middleware/third-party.md +4 -0
- package/docs/framework/upstream.json +3 -3
- package/lib/accepts-XBuZPVRI.js +52 -0
- package/lib/accepts.d.ts +33 -0
- package/lib/accepts.js +2 -0
- package/lib/acryl-base.d.ts +2 -0
- package/lib/acryl-base.js +332 -0
- package/lib/adapter.d.ts +12 -0
- package/lib/adapter.js +37 -0
- package/lib/app-BM0rTCp7.js +26 -0
- package/lib/app-CmAYW1Al.d.ts +21 -0
- package/lib/basic-auth.d.ts +72 -0
- package/lib/basic-auth.js +95 -0
- package/lib/bearer-auth.d.ts +89 -0
- package/lib/bearer-auth.js +94 -0
- package/lib/body-limit.d.ts +42 -0
- package/lib/body-limit.js +66 -0
- package/lib/client-BHTr318g.js +266 -0
- package/lib/client.d.ts +3 -0
- package/lib/client.js +2 -0
- package/lib/combine.d.ts +85 -0
- package/lib/combine.js +137 -0
- package/lib/compose-BuW2B6PZ.js +50 -0
- package/lib/compress.d.ts +38 -0
- package/lib/compress.js +79 -0
- package/lib/connection-C7xgP1hP.js +24 -0
- package/lib/conninfo.d.ts +25 -0
- package/lib/conninfo.js +2 -0
- package/lib/constants-Dy7ZvN1H.js +4 -0
- package/lib/context-Bm7rI-oK.d.ts +1234 -0
- package/lib/context-DDO0cfuP.js +365 -0
- package/lib/context-storage.d.ts +38 -0
- package/lib/context-storage.js +52 -0
- package/lib/cookie-ClMaKDQf.d.ts +40 -0
- package/lib/cookie.d.ts +24 -0
- package/lib/cookie.js +82 -0
- package/lib/cors.d.ts +52 -0
- package/lib/cors.js +106 -0
- package/lib/csrf.d.ts +75 -0
- package/lib/csrf.js +101 -0
- package/lib/etag.d.ts +40 -0
- package/lib/etag.js +135 -0
- package/lib/factory.d.ts +35 -0
- package/lib/factory.js +30 -0
- package/lib/headers-tu2YYN8T.d.ts +11 -0
- package/lib/html.d.ts +6 -0
- package/lib/html.js +31 -0
- package/lib/http-exception.d.ts +57 -0
- package/lib/http-exception.js +54 -0
- package/lib/http-status-DaAwGAFL.d.ts +35 -0
- package/lib/index-B-3F4wt2.d.ts +12 -0
- package/lib/index-UTBmM3GX.d.ts +68 -0
- package/lib/index-oOClm_Xk.d.ts +45 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +28 -0
- package/lib/ip-restriction.d.ts +68 -0
- package/lib/ip-restriction.js +166 -0
- package/lib/jwk.d.ts +52 -0
- package/lib/jwk.js +117 -0
- package/lib/jwt-BGJBw5AF.js +403 -0
- package/lib/jwt-Dg493Tai.d.ts +51 -0
- package/lib/jwt.d.ts +72 -0
- package/lib/jwt.js +115 -0
- package/lib/language.d.ts +88 -0
- package/lib/language.js +198 -0
- package/lib/logger.d.ts +22 -0
- package/lib/logger.js +54 -0
- package/lib/loopback-only.d.ts +45 -0
- package/lib/loopback-only.js +90 -0
- package/lib/method-not-allowed.d.ts +47 -0
- package/lib/method-not-allowed.js +82 -0
- package/lib/method-override.d.ts +46 -0
- package/lib/method-override.js +103 -0
- package/lib/mime-BQOYHl-l.d.ts +72 -0
- package/lib/node-server/conninfo.d.ts +11 -0
- package/lib/node-server/conninfo.js +19 -0
- package/lib/node-server/serve-static.d.ts +19 -0
- package/lib/node-server/serve-static.js +2 -0
- package/lib/node-server.d.ts +103 -0
- package/lib/node-server.js +1325 -0
- package/lib/powered-by.d.ts +28 -0
- package/lib/powered-by.js +24 -0
- package/lib/pretty-json.d.ts +40 -0
- package/lib/pretty-json.js +39 -0
- package/lib/reg-exp-router-DC1R3qoY.js +363 -0
- package/lib/request-id.d.ts +45 -0
- package/lib/request-id.js +34 -0
- package/lib/request.d.ts +2 -0
- package/lib/request.js +323 -0
- package/lib/route.d.ts +78 -0
- package/lib/route.js +126 -0
- package/lib/router/linear-router.d.ts +11 -0
- package/lib/router/linear-router.js +96 -0
- package/lib/router/pattern-router.d.ts +11 -0
- package/lib/router/pattern-router.js +41 -0
- package/lib/router/reg-exp-router.d.ts +35 -0
- package/lib/router/reg-exp-router.js +2 -0
- package/lib/router/smart-router.d.ts +15 -0
- package/lib/router/smart-router.js +2 -0
- package/lib/router/trie-router.d.ts +11 -0
- package/lib/router/trie-router.js +2 -0
- package/lib/router-CvUxUTOq.d.ts +99 -0
- package/lib/router.d.ts +2 -0
- package/lib/router.js +35 -0
- package/lib/schema-validator.d.ts +28 -0
- package/lib/schema-validator.js +22 -0
- package/lib/secure-headers.d.ts +125 -0
- package/lib/secure-headers.js +163 -0
- package/lib/serve-static-Dw_m6KxR.js +213 -0
- package/lib/serve-static.d.ts +31 -0
- package/lib/serve-static.js +77 -0
- package/lib/smart-router-C5CZ5XSM.js +49 -0
- package/lib/stream-CPdqZ0Fp.d.ts +34 -0
- package/lib/streaming-DHST3Scg.js +96 -0
- package/lib/streaming.d.ts +23 -0
- package/lib/streaming.js +2 -0
- package/lib/testing.d.ts +10 -0
- package/lib/testing.js +17 -0
- package/lib/timeout.d.ts +30 -0
- package/lib/timeout.js +42 -0
- package/lib/timing.d.ts +130 -0
- package/lib/timing.js +183 -0
- package/lib/trailing-slash.d.ts +91 -0
- package/lib/trailing-slash.js +85 -0
- package/lib/trie-router-CfFbapzL.js +146 -0
- package/lib/types-CDO6I3B0.d.ts +40 -0
- package/lib/types-CHesD1KG.d.ts +195 -0
- package/lib/types-CwrQjlaM.d.ts +105 -0
- package/lib/types-DR95J00T.d.ts +29 -0
- package/lib/types.d.ts +2 -0
- package/lib/types.js +4 -0
- package/lib/utils/accept.d.ts +9 -0
- package/lib/utils/accept.js +193 -0
- package/lib/utils/basic-auth.d.ts +8 -0
- package/lib/utils/basic-auth.js +20 -0
- package/lib/utils/body.d.ts +2 -0
- package/lib/utils/body.js +94 -0
- package/lib/utils/buffer.d.ts +19 -0
- package/lib/utils/buffer.js +51 -0
- package/lib/utils/color.d.ts +24 -0
- package/lib/utils/color.js +37 -0
- package/lib/utils/compress.d.ts +11 -0
- package/lib/utils/compress.js +11 -0
- package/lib/utils/concurrent.d.ts +17 -0
- package/lib/utils/concurrent.js +30 -0
- package/lib/utils/constants.d.ts +7 -0
- package/lib/utils/constants.js +7 -0
- package/lib/utils/cookie.d.ts +2 -0
- package/lib/utils/cookie.js +125 -0
- package/lib/utils/crypto.d.ts +14 -0
- package/lib/utils/crypto.js +34 -0
- package/lib/utils/encode.d.ts +11 -0
- package/lib/utils/encode.js +33 -0
- package/lib/utils/filepath.d.ts +14 -0
- package/lib/utils/filepath.js +25 -0
- package/lib/utils/handler.d.ts +9 -0
- package/lib/utils/handler.js +12 -0
- package/lib/utils/headers.d.ts +2 -0
- package/lib/utils/headers.js +1 -0
- package/lib/utils/html.d.ts +46 -0
- package/lib/utils/html.js +103 -0
- package/lib/utils/http-status.d.ts +2 -0
- package/lib/utils/http-status.js +1 -0
- package/lib/utils/ipaddr.d.ts +56 -0
- package/lib/utils/ipaddr.js +238 -0
- package/lib/utils/jwt.d.ts +24 -0
- package/lib/utils/jwt.js +2 -0
- package/lib/utils/mime.d.ts +2 -0
- package/lib/utils/mime.js +77 -0
- package/lib/utils/stream.d.ts +2 -0
- package/lib/utils/stream.js +88 -0
- package/lib/utils/types.d.ts +2 -0
- package/lib/utils/types.js +1 -0
- package/lib/utils/url.d.ts +46 -0
- package/lib/utils/url.js +197 -0
- package/lib/utils-dw8lxqjL.js +4 -0
- package/lib/validator-D84mq-dO.js +65 -0
- package/lib/validator.d.ts +42 -0
- package/lib/validator.js +2 -0
- package/lib/ws.d.ts +2 -0
- package/lib/ws.js +53 -0
- package/package.json +312 -14
- package/plugins/acryl-inspect/src/service.ts +2 -2
- package/plugins/acryl-inspect/tests/service.spec.ts +3 -3
- package/plugins/acryl-inspect/upstream.json +2 -2
- package/plugins/acryl-node-server/tests/adapter/app.ts +2 -2
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -1
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +2 -2
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +4 -4
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +5 -5
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +2 -2
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +20 -20
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +2 -2
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +50 -50
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +4 -4
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +9 -9
- package/plugins/acryl-node-server/tests/server.spec.ts +6 -6
- package/plugins/acryl-node-server/upstream.json +2 -2
- package/plugins/acryl-router/src/client.ts +1 -1
- package/plugins/acryl-router/src/connection.ts +2 -2
- package/plugins/acryl-router/src/core/app-base.ts +29 -29
- package/plugins/acryl-router/src/core/app.test.ts +175 -175
- package/plugins/acryl-router/src/core/app.ts +8 -8
- package/plugins/acryl-router/src/core/client/client.test.ts +76 -76
- package/plugins/acryl-router/src/core/client/client.ts +2 -2
- package/plugins/acryl-router/src/core/client/fetch-result-please.ts +1 -1
- package/plugins/acryl-router/src/core/client/index.ts +1 -1
- package/plugins/acryl-router/src/core/client/types.test.ts +6 -6
- package/plugins/acryl-router/src/core/client/types.ts +16 -16
- package/plugins/acryl-router/src/core/client/utils.test.ts +2 -2
- package/plugins/acryl-router/src/core/context.test.ts +4 -4
- package/plugins/acryl-router/src/core/context.ts +6 -6
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +3 -3
- package/plugins/acryl-router/src/core/helper/accepts/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +6 -6
- package/plugins/acryl-router/src/core/helper/adapter/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/conninfo/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +8 -8
- package/plugins/acryl-router/src/core/helper/cookie/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +9 -9
- package/plugins/acryl-router/src/core/helper/factory/index.ts +10 -10
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +4 -4
- package/plugins/acryl-router/src/core/helper/html/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +7 -7
- package/plugins/acryl-router/src/core/helper/route/index.ts +8 -8
- package/plugins/acryl-router/src/core/helper/streaming/index.ts +1 -1
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +6 -6
- package/plugins/acryl-router/src/core/helper/testing/index.ts +4 -4
- package/plugins/acryl-router/src/core/helper/websocket/index.ts +2 -1
- package/plugins/acryl-router/src/core/http-exception.ts +1 -1
- package/plugins/acryl-router/src/core/index.ts +9 -9
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +5 -5
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +9 -9
- package/plugins/acryl-router/src/core/middleware/combine/index.ts +7 -7
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +9 -9
- package/plugins/acryl-router/src/core/middleware/compress/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +5 -5
- package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +7 -7
- package/plugins/acryl-router/src/core/middleware/cors/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +10 -10
- package/plugins/acryl-router/src/core/middleware/csrf/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +42 -42
- package/plugins/acryl-router/src/core/middleware/etag/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +16 -16
- package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +7 -6
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +16 -16
- package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +4 -3
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/language/language.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +5 -5
- package/plugins/acryl-router/src/core/middleware/logger/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +24 -24
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +8 -8
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +5 -5
- package/plugins/acryl-router/src/core/middleware/method-override/index.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +30 -30
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +15 -15
- package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +35 -35
- package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +23 -23
- package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +1 -1
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +2 -2
- package/plugins/acryl-router/src/core/middleware/timeout/index.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +14 -14
- package/plugins/acryl-router/src/core/middleware/timing/timing.ts +3 -3
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +7 -7
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +7 -7
- package/plugins/acryl-router/src/core/request.test.ts +42 -42
- package/plugins/acryl-router/src/core/request.ts +6 -6
- package/plugins/acryl-router/src/core/router/linear-router/index.ts +1 -1
- package/plugins/acryl-router/src/core/router/pattern-router/index.ts +1 -1
- package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +1 -1
- package/plugins/acryl-router/src/core/router/smart-router/index.ts +1 -1
- package/plugins/acryl-router/src/core/router/trie-router/index.ts +1 -1
- package/plugins/acryl-router/src/core/types.test.ts +115 -115
- package/plugins/acryl-router/src/core/types.ts +72 -72
- package/plugins/acryl-router/src/core/utils/body.ts +8 -8
- package/plugins/acryl-router/src/core/utils/html.ts +1 -1
- package/plugins/acryl-router/src/core/utils/jwt/jws.ts +3 -3
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +2 -2
- package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +3 -3
- package/plugins/acryl-router/src/core/utils/jwt/types.ts +1 -1
- package/plugins/acryl-router/src/core/utils/types.ts +42 -0
- package/plugins/acryl-router/src/core/utils/url.test.ts +2 -2
- package/plugins/acryl-router/src/core/validator/index.ts +1 -1
- package/plugins/acryl-router/src/core/validator/validator.test.ts +28 -28
- package/plugins/acryl-router/src/deprecated.ts +15 -0
- package/plugins/acryl-router/src/index.ts +3 -2
- package/plugins/acryl-router/src/service.ts +10 -10
- package/plugins/acryl-router/tests/docs-examples.spec.ts +11 -11
- package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
- package/plugins/acryl-router/tests/loader.spec.ts +3 -3
- package/plugins/acryl-router/tests/loopback-only.spec.ts +4 -4
- package/plugins/acryl-router/tests/no-dom/consumer.ts +11 -0
- package/plugins/acryl-router/tests/no-dom/tsconfig.json +13 -0
- package/plugins/acryl-router/tests/service.spec.ts +23 -23
- package/plugins/acryl-router/upstream.json +3 -3
- package/plugins/acryl-router-http/tests/adapter.spec.ts +3 -3
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +2 -2
- package/plugins/acryl-router-http/upstream.json +2 -2
- package/plugins/acryl-state/src/service.ts +2 -2
- package/plugins/acryl-state/upstream.json +2 -2
- package/plugins/builder/script.ts +2 -2
- package/plugins/extensions/resolve.ts +70 -10
- package/plugins/openai/prompt.ts +5 -5
- package/shared/AGENTS.core.md +16 -9
- package/templates/agent/src/routes.ts +2 -2
- package/templates/agent/test/model.test.ts +2 -2
- package/templates/agent/tsconfig.json +40 -0
- package/templates/blank/src/routes.ts +2 -2
- package/templates/todo/src/todo.ts +2 -2
- package/templates/todo/upstream.json +2 -2
- package/docs/framework/context.md +0 -129
- package/docs/framework/helpers.md +0 -118
- package/docs/framework/middleware.md +0 -154
- package/docs/framework/request.md +0 -106
- /package/docs/framework/{plugin-lifecycle.md → cordis/plugin-lifecycle.md} +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# Migration from 0.3
|
|
2
2
|
|
|
3
3
|
0.3.0 had the framework's own small router API: routes registered one at a time on `ctx.router`, and a `RouteContext` with a few
|
|
4
|
-
helpers. 0.4.0 replaces it with the ported application API: a plugin builds an `
|
|
4
|
+
helpers. 0.4.0 replaces it with the ported application API: a plugin builds an `Acryl` and mounts it. The old calls are removed, not kept
|
|
5
5
|
as aliases, so there is one way to write a route.
|
|
6
6
|
|
|
7
7
|
## Before and after
|
|
8
8
|
|
|
9
9
|
0.3.0:
|
|
10
10
|
|
|
11
|
-
```ts
|
|
12
|
-
import { HttpError } from '
|
|
13
|
-
import { readJson,
|
|
14
|
-
import {
|
|
11
|
+
```ts no-check
|
|
12
|
+
import { HttpError } from 'acrylblends'
|
|
13
|
+
import { readJson, valid, validate } from 'acrylblends'
|
|
14
|
+
import { streamSSE } from 'acrylblends/streaming'
|
|
15
|
+
import { bodyLimit } from 'acrylblends/body-limit'
|
|
15
16
|
|
|
16
17
|
export function apply(ctx: Context): void {
|
|
17
18
|
const api = ctx.router.group('/todo')
|
|
@@ -33,12 +34,14 @@ export function apply(ctx: Context): void {
|
|
|
33
34
|
0.4.0:
|
|
34
35
|
|
|
35
36
|
```ts
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
37
|
+
import { Acryl } from 'acrylblends'
|
|
38
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
39
|
+
import { schemaValidator } from 'acrylblends/schema-validator'
|
|
40
|
+
import { streamSSE } from 'acrylblends/streaming'
|
|
41
|
+
import { bodyLimit } from 'acrylblends/body-limit'
|
|
39
42
|
|
|
40
43
|
export function apply(ctx: Context): void {
|
|
41
|
-
const app = new
|
|
44
|
+
const app = new Acryl()
|
|
42
45
|
.use(bodyLimit({ maxSize: 16 * 1024 }))
|
|
43
46
|
.get('/todos/:id', c => {
|
|
44
47
|
const todo = find(c.req.param('id'))
|
|
@@ -63,16 +66,16 @@ Public paths do not change: the mount path plus the app's route path is the old
|
|
|
63
66
|
|
|
64
67
|
| 0.3.0 | 0.4.0 |
|
|
65
68
|
| --- | --- |
|
|
66
|
-
| `ctx.router.get/post/put/patch/delete/all/on(path, ...)` | the same methods on an `
|
|
69
|
+
| `ctx.router.get/post/put/patch/delete/all/on(path, ...)` | the same methods on an `Acryl`, then `ctx.router.route(prefix, app)` |
|
|
67
70
|
| `ctx.router.use(...)` | `app.use(...)` on the app it should wrap (or on an app mounted earlier, for a guard in front of other plugins) |
|
|
68
|
-
| `ctx.router.group(prefix, ...middleware)` | one `
|
|
71
|
+
| `ctx.router.group(prefix, ...middleware)` | one `Acryl` per group mounted at `prefix`, or `app.route(prefix, sub)` / `app.basePath(prefix)` |
|
|
69
72
|
| the remover each registration returned | the one remover `ctx.router.route` returns; the mount is owned by the calling plugin as before |
|
|
70
73
|
| `ctx.router.onError`, `ctx.router.notFound` | unchanged; an app's own `app.onError` now also applies to its routes |
|
|
71
74
|
| `c.param(name)`, `c.params` | `c.req.param(name)`, `c.req.param()` |
|
|
72
75
|
| `c.query(name)`, `c.query()` | `c.req.query(name)`, `c.req.query()` (and `c.req.queries(name)` for repeated values) |
|
|
73
76
|
| `c.url` | `new URL(c.req.url)`, or `c.req.path` |
|
|
74
77
|
| `c.req` (the Web `Request`) | `c.req.raw`; `c.req` is now the request wrapper |
|
|
75
|
-
| `c.connection` | `connectionOf(c)` (or `getConnInfo(c)`); type the app `new
|
|
78
|
+
| `c.connection` | `connectionOf(c)` (or `getConnInfo(c)`); type the app `new Acryl<RouterEnv>()` to read `c.env.connection` |
|
|
76
79
|
| `c.signal` | `c.req.raw.signal` |
|
|
77
80
|
| `c.waitUntil(task)` | `c.executionCtx.waitUntil(task)` |
|
|
78
81
|
| `c.get(key)` / `c.set(key, value)` | unchanged, typed by the app's `Variables` (`c.var.key` also works) |
|
|
@@ -86,7 +89,7 @@ Public paths do not change: the mount path plus the app's route path is the old
|
|
|
86
89
|
| the cleanup function `run` returned | `stream.onAbort(cleanup)`; the callback's end closes the stream |
|
|
87
90
|
| a 405 with `Allow` for a known path and another method | 404 by default; add the `methodNotAllowed` middleware for 405 |
|
|
88
91
|
| the `Middleware` type, `(c, next) => Response` | `MiddlewareHandler`, `async (c, next) => { await next() }`; change `c.res` after `next()` instead of returning it |
|
|
89
|
-
| `logger`, `cors`, `requestId`, `secureHeaders`, `loopbackOnly` | same names
|
|
92
|
+
| `logger`, `cors`, `requestId`, `secureHeaders`, `loopbackOnly` | same names, each from its own subpath (`acrylblends/logger`, `acrylblends/cors`, ...); the first four now take the core's options |
|
|
90
93
|
|
|
91
94
|
## Behavior changes to check
|
|
92
95
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Migration from 0.4
|
|
2
|
+
|
|
3
|
+
0.5 gives the framework its public names: the application class is `Acryl` and the request class is `AcrylRequest`, the upstream names
|
|
4
|
+
with the project renamed. Everything else (`Context`, `HTTPException`, the middleware, the helpers, `ctx.router.route(path, app)`) is
|
|
5
|
+
unchanged.
|
|
6
|
+
|
|
7
|
+
| 0.4 | 0.5 |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `import { App } from 'acryl-router'`, `new App()` | `import { Acryl } from 'acryl-router'`, `new Acryl()` |
|
|
10
|
+
| `AppRequest` | `AcrylRequest` |
|
|
11
|
+
|
|
12
|
+
`App` and `AppRequest` still work in 0.5, marked deprecated, and are removed in the next release. Code outside Cordis imports the same
|
|
13
|
+
names from the public package: `import { Acryl } from 'acrylblends'`, and each upstream subpath under it (`acrylblends/cors`,
|
|
14
|
+
`acrylblends/jwt`, `acrylblends/node-server`).
|
package/docs/framework/{migration-from-dsh-webserver.md → cordis/migration-from-dsh-webserver.md}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Migration from the DSH webServer
|
|
2
2
|
|
|
3
3
|
ACRYL plugins have registered HTTP routes directly with the DSH `webServer` (or the Host's `acrylWeb` wrapper of it): one Node-style handler
|
|
4
|
-
per exact path or prefix, with no middleware chain. The framework replaces that with an `
|
|
4
|
+
per exact path or prefix, with no middleware chain. The framework replaces that with an `Acryl`, a mount, and Web-standard requests.
|
|
5
5
|
|
|
6
6
|
## Before and after
|
|
7
7
|
|
|
@@ -24,12 +24,12 @@ export function apply(ctx: Context): void {
|
|
|
24
24
|
After, it builds an app, mounts it, and asks the Host adapter to serve the prefix:
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
import {
|
|
27
|
+
import { Acryl } from 'acrylblends'
|
|
28
28
|
|
|
29
29
|
export const inject = ['router']
|
|
30
30
|
|
|
31
31
|
export function apply(ctx: Context): void {
|
|
32
|
-
const app = new
|
|
32
|
+
const app = new Acryl().get('/health', c => c.json({ ok: true }))
|
|
33
33
|
ctx.router.route('/api', app)
|
|
34
34
|
ctx.inject(['routerHttp'], inner => { inner.routerHttp.mount('/api/health') })
|
|
35
35
|
}
|
|
@@ -42,8 +42,8 @@ the route in process (`ctx.router.request('/api/health')`), which the old handle
|
|
|
42
42
|
|
|
43
43
|
| webServer | Framework |
|
|
44
44
|
| --- | --- |
|
|
45
|
-
| `register({ kind: 'exact', path, handler })` | a route on an `
|
|
46
|
-
| `register({ kind: 'prefix', path, handler })` | an `
|
|
45
|
+
| `register({ kind: 'exact', path, handler })` | a route on an `Acryl`, mounted with `ctx.router.route`; served with `routerHttp.mount(path)` |
|
|
46
|
+
| `register({ kind: 'prefix', path, handler })` | an `Acryl` mounted at `path` with its own routes below it |
|
|
47
47
|
| `req.method` checks in the handler | `app.get`, `app.post`, ... (add `methodNotAllowed` for 405 answers) |
|
|
48
48
|
| `new URL(req.url, ...)` and manual parsing | `c.req.param`, `c.req.query`, `c.req.header` |
|
|
49
49
|
| reading the body stream by hand | `c.req.json()`, `c.req.parseBody()` behind `bodyLimit`, or a validator |
|
|
@@ -56,7 +56,7 @@ the route in process (`ctx.router.request('/api/health')`), which the old handle
|
|
|
56
56
|
|
|
57
57
|
Move one capability at a time:
|
|
58
58
|
|
|
59
|
-
1. Build an `
|
|
59
|
+
1. Build an `Acryl` for the capability and move its handlers into it without changing their public answers.
|
|
60
60
|
2. Mount it with `ctx.router.route` and serve the same prefix with `routerHttp.mount`.
|
|
61
61
|
3. Remove the direct `webServer` registration in the same change: the two cannot claim one prefix, `mount` throws.
|
|
62
62
|
4. Test the route through the router (`ctx.router.request`) and over the Host, and unload and reload the plugin to confirm the route goes
|
|
@@ -71,7 +71,7 @@ per domain folder (`filesRoutes`, `gitRoutes`, `ptyRoutes`, ...), composes them
|
|
|
71
71
|
result after the effect that builds its services, and serves the one prefix `/api/acryl-workspace`:
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
|
-
const built: { api?:
|
|
74
|
+
const built: { api?: Acryl } = {}
|
|
75
75
|
ctx.effect(() => {
|
|
76
76
|
// ... the PTY table, git, files, the project registry ...
|
|
77
77
|
built.api = privateApi(filesRoutes(files, report), gitRoutes(git, report) /* , ... */)
|
|
@@ -1,109 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Mounting and trailing slashes
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
1. Routes inside an `App`.
|
|
6
|
-
2. Cordis mounts that place an `App` in the host at a base path.
|
|
7
|
-
|
|
8
|
-
Keeping those levels separate lets application code stay portable while the host controls the public route space.
|
|
9
|
-
|
|
10
|
-
## HTTP methods
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
app.get('/todos', listTodos)
|
|
14
|
-
app.post('/todos', createTodo)
|
|
15
|
-
app.put('/todos/:id', replaceTodo)
|
|
16
|
-
app.patch('/todos/:id', updateTodo)
|
|
17
|
-
app.delete('/todos/:id', deleteTodo)
|
|
18
|
-
app.options('/todos', options)
|
|
19
|
-
app.all('/health', health) // any method
|
|
20
|
-
app.on('PURGE', '/cache', purge) // a custom method
|
|
21
|
-
app.on(['PUT', 'DELETE'], '/post', handler)
|
|
22
|
-
app.on('GET', ['/hello', '/en/hello'], handler)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`HEAD` is answered from the `GET` route with the body removed. A known path asked with another method answers 404 like an unknown path;
|
|
26
|
-
add the `methodNotAllowed` middleware to an app that should answer 405 with an `Allow` header instead.
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
import { methodNotAllowed } from 'acryl-router/middleware'
|
|
30
|
-
|
|
31
|
-
const app = new App()
|
|
32
|
-
app.use(methodNotAllowed({ app })) // POST /item -> 405, Allow: GET, HEAD
|
|
33
|
-
app.get('/item', c => c.text('item'))
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Routes can be chained, and a chained call without a path reuses the last one:
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
app
|
|
40
|
-
.get('/endpoint', c => c.text('GET /endpoint'))
|
|
41
|
-
.post(c => c.text('POST /endpoint'))
|
|
42
|
-
.delete(c => c.text('DELETE /endpoint'))
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Chaining also carries the route types, which the typed `testClient` and `acryl-router/client` read.
|
|
46
|
-
|
|
47
|
-
## Path parameters
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
app.get('/users/:userId/todos/:todoId', c => {
|
|
51
|
-
const { userId, todoId } = c.req.param()
|
|
52
|
-
return c.json({ userId, todoId })
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
app.get('/api/animal/:type?', c => c.text('Animal!')) // optional: /api/animal and /api/animal/cat
|
|
56
|
-
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', handler) // constrained by a regular expression
|
|
57
|
-
app.get('/posts/:filename{.+\\.png}', handler) // a parameter that includes slashes
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Prefer explicit routes over complex patterns when both express the same rule.
|
|
61
|
-
|
|
62
|
-
## Wildcards
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
app.use('/admin/*', requireAdmin)
|
|
66
|
-
app.get('/wild/*/card', c => c.text('GET /wild/*/card'))
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
A wildcard is appropriate for a prefix boundary. It should not hide distinct resources that callers need to address independently.
|
|
70
|
-
|
|
71
|
-
## Priority
|
|
72
|
-
|
|
73
|
-
Handlers and middleware run in registration order, and the first handler that returns a response ends the request:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
app.get('/book/a', c => c.text('a')) // GET /book/a -> a
|
|
77
|
-
app.get('/book/:slug', c => c.text('common')) // GET /book/b -> common
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Put middleware above the handlers it should wrap and a fallback such as `app.get('*', ...)` below the others.
|
|
81
|
-
|
|
82
|
-
## Grouping related routes
|
|
83
|
-
|
|
84
|
-
Build a smaller app for a feature, then place it inside another app:
|
|
85
|
-
|
|
86
|
-
```ts
|
|
87
|
-
const todos = new App()
|
|
88
|
-
todos.get('/', listTodos)
|
|
89
|
-
todos.post('/', createTodo)
|
|
90
|
-
todos.get('/:id', showTodo)
|
|
91
|
-
|
|
92
|
-
const app = new App()
|
|
93
|
-
app.route('/todos', todos)
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
`route` copies the routes the sub-app has at that moment. Register them first: a route added to `todos` after `app.route(...)` is not
|
|
97
|
-
part of `app`.
|
|
98
|
-
|
|
99
|
-
## Base paths
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const api = new App().basePath('/api/v1')
|
|
103
|
-
api.get('/todos', listTodos) // GET /api/v1/todos
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Do not repeat a mount prefix both in `basePath` and in the outer mount unless the doubled path is deliberate.
|
|
3
|
+
How a Cordis plugin serves an `Acryl` app, what happens to errors and unknown paths, and how trailing slashes behave. For routing itself (methods, parameters, wildcards, grouping) see the [routing API](../api/routing.md).
|
|
107
4
|
|
|
108
5
|
## Mounting from Cordis
|
|
109
6
|
|
|
@@ -164,15 +61,15 @@ What each choice does for an app mounted at `/orders` with routes `/` and `/:id`
|
|
|
164
61
|
| nothing (the default) | 200 | 404 | 200 | 404 |
|
|
165
62
|
| `.use(trimTrailingSlash())` in the app | 200 | 301 to `/orders` | 200 | 301 to `/orders/42` |
|
|
166
63
|
| `acryl-router` row config `trailingSlash: 'ignore'` | 200 | 200 | 200 | 200 |
|
|
167
|
-
| `new
|
|
64
|
+
| `new Acryl({ strict: false })` for the mounted app | 200 | 404 | 200 | 404 |
|
|
168
65
|
|
|
169
|
-
- **`trimTrailingSlash()`** (from `
|
|
66
|
+
- **`trimTrailingSlash()`** (from `acrylblends/trailing-slash`) is the choice a plugin makes for itself. It acts after the handlers: only
|
|
170
67
|
a GET or HEAD that would be a 404 and ends in `/` is redirected, so a path you did register with a trailing slash keeps working, and a
|
|
171
68
|
POST, PUT, PATCH or DELETE to `/orders/` stays a 404. `trimTrailingSlash({ alwaysRedirect: true })` redirects before the handlers run,
|
|
172
69
|
which a wildcard route (`/files/*`) needs. `appendTrailingSlash()` does the opposite, for an app whose canonical paths end in `/`.
|
|
173
70
|
- **`trailingSlash: 'ignore'`** is the router's own setting and applies to every mount at once, without a redirect. Set it in the
|
|
174
71
|
composition that owns the `acryl-router` row (the Blueprint or profile), not from one plugin.
|
|
175
|
-
- **`strict: false`** on an `
|
|
72
|
+
- **`strict: false`** on an `Acryl` is the core's option and works for an app you serve yourself (`app.fetch`, `app.request`). It has no
|
|
176
73
|
effect on a mounted app, because the router composes all mounts into one root app whose strictness comes from `trailingSlash`.
|
|
177
74
|
|
|
178
75
|
`examples/acryl-todo` uses `trimTrailingSlash()`: `/todo/` sends the browser to `/todo`, and the API keeps one path per route.
|
|
@@ -10,7 +10,7 @@ The server is a Cordis plugin that provides the `nodeServer` service:
|
|
|
10
10
|
```ts
|
|
11
11
|
import { Context } from '@deepseek-ai/cordis'
|
|
12
12
|
import * as router from 'acryl-router'
|
|
13
|
-
import * as nodeServer from '
|
|
13
|
+
import * as nodeServer from 'acrylblends/node-server'
|
|
14
14
|
import * as todo from './todo.ts'
|
|
15
15
|
|
|
16
16
|
const root = new Context()
|
|
@@ -39,16 +39,16 @@ Every request goes to `ctx.router.fetch`, so routes appear and disappear as plug
|
|
|
39
39
|
|
|
40
40
|
The adapter passes the socket's peer address and local port to the router as connection facts. Handlers read them through
|
|
41
41
|
`connectionOf(c)` or `getConnInfo(c)` and never touch Node socket objects, so the same handler runs in process and through the Host. See
|
|
42
|
-
[Context](
|
|
42
|
+
[Context](../api/context.md#transport-facts).
|
|
43
43
|
|
|
44
44
|
## Static files
|
|
45
45
|
|
|
46
46
|
The Node `serveStatic` reads files from disk:
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import { serveStatic } from '
|
|
49
|
+
import { serveStatic } from 'acrylblends/node-server'
|
|
50
50
|
|
|
51
|
-
const app = new
|
|
51
|
+
const app = new Acryl()
|
|
52
52
|
.use('/assets/*', serveStatic({ root: './public' }))
|
|
53
53
|
.get('/', serveStatic({ path: './public/index.html' }))
|
|
54
54
|
```
|
|
@@ -33,7 +33,7 @@ so a plugin cannot take over a Host route by mounting on it.
|
|
|
33
33
|
|
|
34
34
|
Desktop runs the same Host inside Electron, and its window talks to the Host's web server on a loopback port. A plugin's routes therefore
|
|
35
35
|
serve Desktop exactly as they serve Web, with no Desktop-specific code. Guard local control routes with `loopbackOnly` (see
|
|
36
|
-
[Middleware](
|
|
36
|
+
[Middleware](../concepts/middleware.md#loopbackonly)).
|
|
37
37
|
|
|
38
38
|
A transport without a port, handing `ctx.router.fetch` to Electron's `protocol.handle`, needs no change to plugins and is a later task
|
|
39
39
|
(`specs/047-framework-port/plan.md` T014).
|
|
@@ -57,7 +57,7 @@ host declares instead of assuming a network route exists.
|
|
|
57
57
|
|
|
58
58
|
| Concern | Owner |
|
|
59
59
|
| --- | --- |
|
|
60
|
-
| Route handlers and middleware | the feature plugin's `
|
|
60
|
+
| Route handlers and middleware | the feature plugin's `Acryl` |
|
|
61
61
|
| Mount path and lifetime | the feature plugin, through `ctx.router.route` |
|
|
62
62
|
| Socket and port | the Host (`acryl-router-http`) or `acryl-node-server` |
|
|
63
63
|
| Terminal rendering and input | the terminal surface |
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Transport facts and cancellation
|
|
2
|
+
|
|
3
|
+
`c.env` carries what the caller of the router supplied for this request. Under the router that is `{ connection }`: the socket's peer
|
|
4
|
+
address and the real local port, filled by the HTTP adapter and empty for an in-process call. Transport facts never travel in headers.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { Acryl } from 'acrylblends'
|
|
8
|
+
import { connectionOf, type RouterEnv } from 'acrylblends/conninfo'
|
|
9
|
+
import { getConnInfo } from 'acrylblends/conninfo'
|
|
10
|
+
|
|
11
|
+
const app = new Acryl<RouterEnv>() // types c.env.connection
|
|
12
|
+
app.get('/peer', c => c.json({
|
|
13
|
+
connection: connectionOf(c), // { remoteAddress?, localPort? }
|
|
14
|
+
info: getConnInfo(c), // the same peer in the core's conninfo shape, for middleware such as ipRestriction
|
|
15
|
+
}))
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The same handler then runs over HTTP, in process or in a test, and reads an empty connection where there is no socket. Guards such as
|
|
19
|
+
`loopbackOnly` refuse an empty one unless told otherwise.
|
|
20
|
+
|
|
21
|
+
## Cancellation and background work
|
|
22
|
+
|
|
23
|
+
- `c.req.raw.signal` aborts when the caller goes away, when the plugin whose code is serving the request unloads, and when the router is
|
|
24
|
+
disposed. Long work
|
|
25
|
+
should pass it on (`fetch(url, { signal: c.req.raw.signal })`) or check it.
|
|
26
|
+
- `c.executionCtx.waitUntil(promise)` keeps the request in flight until the promise settles, for work that continues after the response
|
|
27
|
+
is returned.
|
|
28
|
+
|
|
29
|
+
Both belong to the current request, and both end with their owner: see [Plugin lifecycle](./plugin-lifecycle.md#disposal-sequence).
|
|
30
|
+
|
|
31
|
+
## Errors and control flow
|
|
32
|
+
|
|
33
|
+
Return a response for expected failures, or throw an `HTTPException`, which becomes its own response:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
37
|
+
|
|
38
|
+
if (!todo) return c.json({ error: 'todo_not_found' }, 404)
|
|
39
|
+
if (!allowed) throw new HTTPException(403, { message: 'not yours' })
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Anything else that is thrown goes to the app's `onError`, or to the router's when the app has none. A middleware's `await next()` never
|
|
43
|
+
throws: the error has already become a response by then, and the error itself is in `c.error`:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
app.use(async (c, next) => {
|
|
47
|
+
await next()
|
|
48
|
+
if (c.error) report(c.error)
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Context rules
|
|
53
|
+
|
|
54
|
+
- Treat the context as request-local.
|
|
55
|
+
- Do not retain it after the handler returns.
|
|
56
|
+
- Do not put sockets, server objects or Cordis internals into context values for other plugins to discover accidentally.
|
|
57
|
+
- Put cross-plugin capabilities in a declared Cordis service, not in `c.set`.
|
|
@@ -7,15 +7,15 @@ Validate untrusted input at the route boundary. `schemaValidator` takes any [Sta
|
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
9
|
import z from '@deepseek-ai/schemastery'
|
|
10
|
-
import {
|
|
11
|
-
import { schemaValidator } from '
|
|
10
|
+
import { Acryl } from 'acrylblends'
|
|
11
|
+
import { schemaValidator } from 'acrylblends/schema-validator'
|
|
12
12
|
|
|
13
13
|
const NewTodo = z.object({
|
|
14
14
|
title: z.string().required(),
|
|
15
15
|
done: z.boolean().default(false),
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
const app = new
|
|
18
|
+
const app = new Acryl().post('/todos', schemaValidator('json', NewTodo), c => {
|
|
19
19
|
const todo = c.req.valid('json') // typed by the schema's output
|
|
20
20
|
return c.json({ todo }, 201)
|
|
21
21
|
})
|
|
@@ -60,7 +60,7 @@ If you see a field error for a field the request clearly carries, check the requ
|
|
|
60
60
|
explicitly can refuse other content types before the validator:
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
|
-
import type { MiddlewareHandler } from '
|
|
63
|
+
import type { MiddlewareHandler } from 'acrylblends'
|
|
64
64
|
|
|
65
65
|
const jsonOnly: MiddlewareHandler = async (c, next) => {
|
|
66
66
|
// The same types the json target reads: application/json and application/*+json, parameters allowed.
|
|
@@ -101,11 +101,11 @@ app.get('/todos/:id', schemaValidator('param', z.object({ id: z.natural() })), s
|
|
|
101
101
|
|
|
102
102
|
## The core validator
|
|
103
103
|
|
|
104
|
-
`validator` (
|
|
104
|
+
`validator` (from `acrylblends/validator`) takes a function instead of a schema. Return the value the handler should see, or a
|
|
105
105
|
response to refuse:
|
|
106
106
|
|
|
107
107
|
```ts
|
|
108
|
-
import { validator } from '
|
|
108
|
+
import { validator } from 'acrylblends/validator'
|
|
109
109
|
|
|
110
110
|
app.post('/posts', validator('form', (value, c) => {
|
|
111
111
|
const body = value['body']
|
|
@@ -119,15 +119,15 @@ app.post('/posts', validator('form', (value, c) => {
|
|
|
119
119
|
Validation reads the whole body. Put `bodyLimit` in front of a validator on any route open to untrusted callers:
|
|
120
120
|
|
|
121
121
|
```ts
|
|
122
|
-
import { bodyLimit } from '
|
|
122
|
+
import { bodyLimit } from 'acrylblends/body-limit'
|
|
123
123
|
|
|
124
124
|
app.post('/todos', bodyLimit({ maxSize: 16 * 1024 }), schemaValidator('json', NewTodo), createTodo)
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
## Typed clients
|
|
128
128
|
|
|
129
|
-
The validated input types flow into the route types, so the typed `testClient` and `
|
|
130
|
-
See [Helpers](
|
|
129
|
+
The validated input types flow into the route types, so the typed `testClient` and `acrylblends/client` check the request a caller sends.
|
|
130
|
+
See [Helpers](../guides/helpers.md#testing).
|
|
131
131
|
|
|
132
132
|
## What not to validate twice
|
|
133
133
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Using Acryl is easy. You set up the project, write code, develop with a local server, and run it. The same application code works on every verified runtime, with a different
|
|
4
|
+
entry point for each. This page shows the basic usage.
|
|
5
|
+
|
|
6
|
+
## Starter
|
|
7
|
+
|
|
8
|
+
Starter templates are available for each runtime. Use the "create-acrylblends" command.
|
|
9
|
+
|
|
10
|
+
**npm**
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm create acrylblends@latest my-app
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**yarn**
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
yarn create acrylblends my-app
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**pnpm**
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pnpm create acrylblends@latest my-app
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**bun**
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bun create acrylblends@latest my-app
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You are asked which template you want:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
? Which template do you want to use?
|
|
38
|
+
❯ nodejs
|
|
39
|
+
bun (experimental)
|
|
40
|
+
acryl-app
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`nodejs` and `bun` (experimental) are a minimal Acryl application for that runtime. `acryl-app` is a Cordis app built from plugins, with an agent that can extend it while it
|
|
44
|
+
runs (see [Acryl in a Cordis app](../cordis/acryl-in-a-cordis-app.md)). Pass `--template` to skip the question, see [create-acrylblends](../guides/create-acrylblends.md).
|
|
45
|
+
|
|
46
|
+
The template is created in `my-app`: go to it, install the dependencies and start the local server.
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
cd my-app
|
|
50
|
+
npm install
|
|
51
|
+
npm run dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Hello World
|
|
55
|
+
|
|
56
|
+
Write your first application in `src/index.ts`. The example below is a starter Acryl application.
|
|
57
|
+
|
|
58
|
+
The `import` and the final `export default` are the same on every runtime; what differs is how the runtime starts the app (see [Node.js](nodejs.md) and [Bun](bun.md)).
|
|
59
|
+
|
|
60
|
+
```ts twoslash
|
|
61
|
+
import { Acryl } from 'acrylblends'
|
|
62
|
+
|
|
63
|
+
const app = new Acryl()
|
|
64
|
+
|
|
65
|
+
app.get('/', (c) => {
|
|
66
|
+
return c.text('Hello Acryl!')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
export default app
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Start the development server and open the address it prints.
|
|
73
|
+
|
|
74
|
+
## Return JSON
|
|
75
|
+
|
|
76
|
+
Returning JSON is also easy. The following handles a GET request to `/api/hello` and returns an `application/json` response.
|
|
77
|
+
|
|
78
|
+
```ts twoslash
|
|
79
|
+
import { Acryl } from 'acrylblends'
|
|
80
|
+
|
|
81
|
+
const app = new Acryl()
|
|
82
|
+
|
|
83
|
+
app.get('/api/hello', (c) => {
|
|
84
|
+
return c.json({
|
|
85
|
+
ok: true,
|
|
86
|
+
message: 'Hello Acryl!',
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Request and Response
|
|
92
|
+
|
|
93
|
+
Getting a path parameter, a URL query value, and adding a response header is written as follows.
|
|
94
|
+
|
|
95
|
+
```ts twoslash
|
|
96
|
+
import { Acryl } from 'acrylblends'
|
|
97
|
+
|
|
98
|
+
const app = new Acryl()
|
|
99
|
+
|
|
100
|
+
app.get('/posts/:id', (c) => {
|
|
101
|
+
const page = c.req.query('page')
|
|
102
|
+
const id = c.req.param('id')
|
|
103
|
+
c.header('X-Message', 'Hi!')
|
|
104
|
+
return c.text(`You want to see ${page} of ${id}`)
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
POST, PUT and DELETE are handled the same way as GET.
|
|
109
|
+
|
|
110
|
+
```ts twoslash
|
|
111
|
+
import { Acryl } from 'acrylblends'
|
|
112
|
+
|
|
113
|
+
const app = new Acryl()
|
|
114
|
+
|
|
115
|
+
app.post('/posts', (c) => c.text('Created!', 201))
|
|
116
|
+
app.delete('/posts/:id', (c) => c.text(`${c.req.param('id')} is deleted!`))
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Return HTML
|
|
120
|
+
|
|
121
|
+
Write HTML with [the html helper](../helpers/html.md):
|
|
122
|
+
|
|
123
|
+
```ts twoslash
|
|
124
|
+
import { Acryl } from 'acrylblends'
|
|
125
|
+
import { html } from 'acrylblends/html'
|
|
126
|
+
|
|
127
|
+
const app = new Acryl()
|
|
128
|
+
|
|
129
|
+
app.get('/', (c) => {
|
|
130
|
+
const name = 'Acryl'
|
|
131
|
+
return c.html(html`<!doctype html><h1>Hello ${name}!</h1>`)
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
[JSX](../guides/jsx.md) is not yet available.
|
|
136
|
+
|
|
137
|
+
## Return raw Response
|
|
138
|
+
|
|
139
|
+
You can also return a raw Web Standard `Response`.
|
|
140
|
+
|
|
141
|
+
```ts twoslash
|
|
142
|
+
import { Acryl } from 'acrylblends'
|
|
143
|
+
|
|
144
|
+
const app = new Acryl()
|
|
145
|
+
|
|
146
|
+
app.get('/', () => {
|
|
147
|
+
return new Response('Good morning!')
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Using Middleware
|
|
152
|
+
|
|
153
|
+
Middleware can do the hard work for you. For example, add Basic Authentication.
|
|
154
|
+
|
|
155
|
+
```ts twoslash
|
|
156
|
+
import { Acryl } from 'acrylblends'
|
|
157
|
+
import { basicAuth } from 'acrylblends/basic-auth'
|
|
158
|
+
|
|
159
|
+
const app = new Acryl()
|
|
160
|
+
|
|
161
|
+
app.use(
|
|
162
|
+
'/admin/*',
|
|
163
|
+
basicAuth({
|
|
164
|
+
username: 'admin',
|
|
165
|
+
password: 'secret',
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
app.get('/admin', (c) => {
|
|
170
|
+
return c.text('You are authorized!')
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
There are useful middleware included, such as Bearer and JWT authentication, CORS and ETag. See [Built-in middleware](../middleware/builtin/basic-auth.md).
|
|
175
|
+
|
|
176
|
+
## Testing
|
|
177
|
+
|
|
178
|
+
An app is a function from `Request` to `Response`, so a test needs no server:
|
|
179
|
+
|
|
180
|
+
```ts twoslash
|
|
181
|
+
import { Acryl } from 'acrylblends'
|
|
182
|
+
|
|
183
|
+
const app = new Acryl()
|
|
184
|
+
app.get('/', (c) => c.text('Hello Acryl!'))
|
|
185
|
+
|
|
186
|
+
const response = await app.request('/')
|
|
187
|
+
console.log(response.status, await response.text())
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
See [Testing](../guides/testing.md).
|
|
191
|
+
|
|
192
|
+
## Next
|
|
193
|
+
|
|
194
|
+
- [Node.js](nodejs.md) and [Bun](bun.md) (experimental)
|
|
195
|
+
- [Routing](../api/routing.md), [Context](../api/context.md) and [Request](../api/request.md)
|
|
196
|
+
- [Validation](../guides/validation.md)
|