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
|
@@ -6,9 +6,10 @@ One page with the calls an ordinary app uses, each linked to the page that expla
|
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import type { Context } from '@deepseek-ai/cordis'
|
|
9
|
-
import {
|
|
10
|
-
import { trimTrailingSlash } from '
|
|
11
|
-
import { schemaValidator,
|
|
9
|
+
import { Acryl } from 'acrylblends'
|
|
10
|
+
import { trimTrailingSlash } from 'acrylblends/trailing-slash'
|
|
11
|
+
import { schemaValidator, type StandardSchema } from 'acrylblends/schema-validator'
|
|
12
|
+
import { streamSSE } from 'acrylblends/streaming'
|
|
12
13
|
import type {} from 'acryl-state'
|
|
13
14
|
|
|
14
15
|
export const name = 'orders' // the package name and the Loader row id
|
|
@@ -26,7 +27,7 @@ const NewOrder: StandardSchema<{ item: string }> = {
|
|
|
26
27
|
|
|
27
28
|
export function apply(ctx: Context): void {
|
|
28
29
|
const orders = ctx.state.cell<Order[]>('orders.list', [])
|
|
29
|
-
const app = new
|
|
30
|
+
const app = new Acryl()
|
|
30
31
|
.use(trimTrailingSlash())
|
|
31
32
|
.get('/', c => c.json(orders.get()))
|
|
32
33
|
.post('/', schemaValidator('json', NewOrder), c => {
|
|
@@ -45,11 +46,11 @@ export function apply(ctx: Context): void {
|
|
|
45
46
|
}
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
## `
|
|
49
|
+
## `Acryl` ([Routing](../api/routing.md))
|
|
49
50
|
|
|
50
51
|
| Call | Does |
|
|
51
52
|
| --- | --- |
|
|
52
|
-
| `new
|
|
53
|
+
| `new Acryl()` | an application: a route table plus middleware |
|
|
53
54
|
| `.get / .post / .put / .patch / .delete / .options (path, ...handlers)` | a route; handlers before the last are middleware for that route |
|
|
54
55
|
| `.all(path, ...)`, `.on(methods, path, ...)` | any method, or a list of methods |
|
|
55
56
|
| `.use([path], ...middleware)` | middleware for every route below `path` (all routes without it), registered before the routes it wraps |
|
|
@@ -60,9 +61,9 @@ export function apply(ctx: Context): void {
|
|
|
60
61
|
| `.fetch(request)` | the app as a fetch handler |
|
|
61
62
|
|
|
62
63
|
Paths: `/posts/:id` (parameter), `/posts/:id?` (optional), `/files/*` (wildcard), `/:name{[a-z]+}` (pattern). `/a` and `/a/` are different
|
|
63
|
-
paths: see [Trailing slashes](
|
|
64
|
+
paths: see [Trailing slashes](../api/routing.md#trailing-slashes).
|
|
64
65
|
|
|
65
|
-
## The handler context `c` ([Context](
|
|
66
|
+
## The handler context `c` ([Context](../api/context.md))
|
|
66
67
|
|
|
67
68
|
| Call | Does |
|
|
68
69
|
| --- | --- |
|
|
@@ -78,7 +79,7 @@ paths: see [Trailing slashes](./routing.md#trailing-slashes).
|
|
|
78
79
|
| `connectionOf(c)` / `getConnInfo(c)` | the caller's address and the local port, when a transport supplied them |
|
|
79
80
|
| `throw new HTTPException(status, { message })` | stop with that status from anywhere in the chain |
|
|
80
81
|
|
|
81
|
-
## The request `c.req` ([Request](
|
|
82
|
+
## The request `c.req` ([Request](../api/request.md))
|
|
82
83
|
|
|
83
84
|
| Call | Gives |
|
|
84
85
|
| --- | --- |
|
|
@@ -89,10 +90,11 @@ paths: see [Trailing slashes](./routing.md#trailing-slashes).
|
|
|
89
90
|
| `c.req.valid(target)` | the value a validator checked, typed by its schema |
|
|
90
91
|
| `c.req.method`, `c.req.path`, `c.req.url`, `c.req.raw` | the method, path, full URL and the Web `Request` |
|
|
91
92
|
|
|
92
|
-
## Validation ([Validation](
|
|
93
|
+
## Validation ([Validation](../cordis/validation.md))
|
|
93
94
|
|
|
94
95
|
```ts
|
|
95
|
-
import { schemaValidator
|
|
96
|
+
import { schemaValidator } from 'acrylblends/schema-validator'
|
|
97
|
+
import { validator } from 'acrylblends/validator'
|
|
96
98
|
app.post('/', schemaValidator('json', Schema), c => c.json(c.req.valid('json')))
|
|
97
99
|
```
|
|
98
100
|
|
|
@@ -101,9 +103,9 @@ app.post('/', schemaValidator('json', Schema), c => c.json(c.req.valid('json')))
|
|
|
101
103
|
- A failure is 400 `{ error: 'invalid <target>', issues: [{ message, path }] }`; `path` is dotted, and `""` when the issue has none.
|
|
102
104
|
- The `json` target reads the body only with a JSON `Content-Type`; otherwise the schema sees `{}`.
|
|
103
105
|
- `validator(target, (value, c) => valueOrResponse)` is the same middleware with a function instead of a schema.
|
|
104
|
-
- Put `bodyLimit({ maxSize })` from `
|
|
106
|
+
- Put `bodyLimit({ maxSize })` from `acrylblends/body-limit` in front on routes open to untrusted callers.
|
|
105
107
|
|
|
106
|
-
## Streaming ([Helpers](
|
|
108
|
+
## Streaming ([Helpers](../guides/helpers.md))
|
|
107
109
|
|
|
108
110
|
| Call | Does |
|
|
109
111
|
| --- | --- |
|
|
@@ -123,16 +125,16 @@ Do not start timers inside a handler for keep-alives; let the plugin own one tim
|
|
|
123
125
|
| `ctx.router.request(path, init?)`, `ctx.router.fetch(request)` | `router` | call the mounted routes in process (terminal UI, tests, other plugins) |
|
|
124
126
|
| `ctx.router.onError(handler)`, `ctx.router.notFound(handler)` | `router` | router-wide error and not-found answers, owned by the caller |
|
|
125
127
|
| `ctx.routerHttp.mount(prefix)` | `routerHttp` (`acryl-router-http`) | inside the ACRYL Host: serve a router prefix on the Host's web server (Web and Desktop). Use it through `ctx.inject(['routerHttp'], ...)` so the app also runs where there is no Host |
|
|
126
|
-
| `ctx.nodeServer.url`, `ctx.nodeServer.port` | `nodeServer` (`acryl-node-server`) | without the Host: one Node server for the whole router; options `hostname` (`127.0.0.1`), `port` (`0`), `scan` (`20`). See [Node server](
|
|
128
|
+
| `ctx.nodeServer.url`, `ctx.nodeServer.port` | `nodeServer` (`acryl-node-server`) | without the Host: one Node server for the whole router; options `hostname` (`127.0.0.1`), `port` (`0`), `scan` (`20`). See [Node server](../cordis/node-server.md) |
|
|
127
129
|
| `ctx.state.cell(key, initial)` | `state` (`acryl-state`) | a writable cell owned by the plugin: `get()`, `set(value)`, `update(fn)`, `subscribe(fn)` returning an unsubscribe |
|
|
128
130
|
| `ctx.state.derive(key, [cells], fn)` | `state` | a read-only cell computed from others |
|
|
129
131
|
| `GET /api/state/:key`, `GET /api/state/:key/events` | `state` | each cell over the router, read-only; `useCell(cell)` from `acryl-state/react` in a React page |
|
|
130
132
|
|
|
131
133
|
Everything a plugin registers this way (mounts, cells, handlers) goes away when the plugin unloads, and comes back once when it reloads:
|
|
132
|
-
see [Plugin lifecycle and disposal](
|
|
134
|
+
see [Plugin lifecycle and disposal](../cordis/plugin-lifecycle.md).
|
|
133
135
|
|
|
134
|
-
## Middleware ([Middleware](
|
|
136
|
+
## Middleware ([Middleware](../concepts/middleware.md))
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
Each from its own subpath (`acrylblends/body-limit`, `acrylblends/cors`, ...): `bodyLimit`, `cors`, `csrf`, `secureHeaders`, `etag`, `compress`, `logger`, `requestId`, `timeout`,
|
|
137
139
|
`bearerAuth`, `basicAuth`, `jwt`, `jwk`, `ipRestriction`, `methodNotAllowed`, `trimTrailingSlash`, `appendTrailingSlash`, `some` /
|
|
138
140
|
`every` / `except`, and ACRYL's `loopbackOnly` for private loopback routes.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
> **Not yet measured.** The routers in the framework are designed for different situations (see [Routers](routers.md)), but benchmark results for Acryl itself have not been
|
|
4
|
+
> produced yet, and this page does not quote anyone else's numbers as ours. When Acryl has its own reproducible benchmark suite, its scripts and results will be here.
|
|
5
|
+
|
|
6
|
+
To compare routers on your own workload today, register your routes on an `Acryl` app with each router and time `app.request()` for the paths you care about:
|
|
7
|
+
|
|
8
|
+
```ts twoslash
|
|
9
|
+
import { Acryl } from 'acrylblends'
|
|
10
|
+
import { RegExpRouter } from 'acrylblends/router/reg-exp-router'
|
|
11
|
+
import { TrieRouter } from 'acrylblends/router/trie-router'
|
|
12
|
+
|
|
13
|
+
const routes = ['/', '/users/:id', '/posts/:id/comments']
|
|
14
|
+
|
|
15
|
+
const apps = {
|
|
16
|
+
RegExpRouter: new Acryl({ router: new RegExpRouter() }),
|
|
17
|
+
TrieRouter: new Acryl({ router: new TrieRouter() }),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (const [name, app] of Object.entries(apps)) {
|
|
21
|
+
for (const path of routes) app.get(path, (c) => c.text('ok'))
|
|
22
|
+
const start = performance.now()
|
|
23
|
+
for (let i = 0; i < 100_000; i++) await app.request('/users/42')
|
|
24
|
+
console.log(name, Math.round(performance.now() - start), 'ms')
|
|
25
|
+
}
|
|
26
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Developer Experience
|
|
2
|
+
|
|
3
|
+
To create a great application, we need great development experience.
|
|
4
|
+
Fortunately, we can write applications for Node.js and Bun in TypeScript without having the need to transpile it to JavaScript.
|
|
5
|
+
Acryl is written in TypeScript and can make applications type-safe.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Middleware
|
|
2
|
+
|
|
3
|
+
We call the primitive that returns `Response` as "Handler".
|
|
4
|
+
"Middleware" is executed before and after the Handler and handles the `Request` and `Response`.
|
|
5
|
+
It's like an onion structure.
|
|
6
|
+
|
|
7
|
+
For example, we can write the middleware to add the "X-Response-Time" header as follows.
|
|
8
|
+
|
|
9
|
+
```ts twoslash
|
|
10
|
+
import { Acryl } from 'acrylblends'
|
|
11
|
+
const app = new Acryl()
|
|
12
|
+
// ---cut---
|
|
13
|
+
app.use(async (c, next) => {
|
|
14
|
+
const start = performance.now()
|
|
15
|
+
await next()
|
|
16
|
+
const end = performance.now()
|
|
17
|
+
c.res.headers.set('X-Response-Time', `${end - start}`)
|
|
18
|
+
})
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With this simple method, we can write our own custom middleware and we can use the built-in or third party middleware.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Philosophy
|
|
2
|
+
|
|
3
|
+
## Motivation
|
|
4
|
+
|
|
5
|
+
A web application should not be written twice because it runs in two places. Acryl builds on the Web Standard APIs (`Request`, `Response`, `fetch`, `URL`, streams), so a
|
|
6
|
+
handler written once runs on Node.js (and experimentally Bun), in a test with no socket, and inside a Cordis app.
|
|
7
|
+
|
|
8
|
+
Three ideas follow from that:
|
|
9
|
+
|
|
10
|
+
- **The handler is a function over Web Standards.** There is no runtime object to import, so there is nothing to replace when the runtime changes. See
|
|
11
|
+
[Web Standards](web-standard.md).
|
|
12
|
+
- **A plugin owns what it mounts.** Inside a Cordis app an application is mounted by a plugin and disappears when that plugin unloads, which is what lets an agent add,
|
|
13
|
+
update and remove features while the app runs. See [Plugin lifecycle](../cordis/plugin-lifecycle.md).
|
|
14
|
+
- **The documentation travels with the app.** A generated app contains these pages, so a person or a coding agent working in the folder reads the API from the folder.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Routers
|
|
2
|
+
|
|
3
|
+
The router decides which handler answers a request. Acryl has five routers behind one API, and an `Acryl` app uses `SmartRouter` unless you choose another.
|
|
4
|
+
|
|
5
|
+
| Router | Import | Built for |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `SmartRouter` | `acrylblends/router/smart-router` | the default: it picks from the routers it is given, by the routes registered |
|
|
8
|
+
| `RegExpRouter` | `acrylblends/router/reg-exp-router` | matching all routes with one regular expression built before dispatch |
|
|
9
|
+
| `TrieRouter` | `acrylblends/router/trie-router` | all route patterns, with a trie tree and no linear loop |
|
|
10
|
+
| `LinearRouter` | `acrylblends/router/linear-router` | applications that register routes at every start, because registration compiles nothing |
|
|
11
|
+
| `PatternRouter` | `acrylblends/router/pattern-router` | the smallest implementation |
|
|
12
|
+
|
|
13
|
+
## RegExpRouter
|
|
14
|
+
|
|
15
|
+
**RegExpRouter** turns the route patterns into one large regular expression and gets the result with one match. It is not the kind of "RegExp router" that tries each
|
|
16
|
+
route's expression in a loop, so matching does not repeat for every route.
|
|
17
|
+
|
|
18
|
+
It does not support every routing pattern, so it is normally combined with a router that does, through `SmartRouter`.
|
|
19
|
+
|
|
20
|
+
## TrieRouter
|
|
21
|
+
|
|
22
|
+
**TrieRouter** uses a trie tree, and like RegExpRouter it does not loop over the routes. It supports all patterns.
|
|
23
|
+
|
|
24
|
+
## SmartRouter
|
|
25
|
+
|
|
26
|
+
**SmartRouter** is for using several routers. It infers, from the routes registered, which of the routers it was given can serve them, and keeps using that one. An `Acryl`
|
|
27
|
+
app defaults to:
|
|
28
|
+
|
|
29
|
+
```ts twoslash
|
|
30
|
+
import { Acryl } from 'acrylblends'
|
|
31
|
+
import { RegExpRouter } from 'acrylblends/router/reg-exp-router'
|
|
32
|
+
import { SmartRouter } from 'acrylblends/router/smart-router'
|
|
33
|
+
import { TrieRouter } from 'acrylblends/router/trie-router'
|
|
34
|
+
|
|
35
|
+
const app = new Acryl({
|
|
36
|
+
router: new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }),
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## LinearRouter
|
|
41
|
+
|
|
42
|
+
**LinearRouter** registers a route without compiling anything, by keeping the routes in a list and matching them in order. Registration is cheap and matching is linear in
|
|
43
|
+
the number of routes, so it suits an environment that builds the application at every start and serves few requests.
|
|
44
|
+
|
|
45
|
+
```ts twoslash
|
|
46
|
+
import { Acryl } from 'acrylblends'
|
|
47
|
+
import { LinearRouter } from 'acrylblends/router/linear-router'
|
|
48
|
+
|
|
49
|
+
const app = new Acryl({ router: new LinearRouter() })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## PatternRouter
|
|
53
|
+
|
|
54
|
+
**PatternRouter** is the smallest of the routers: it adds a pattern and matches it, and nothing else. Choose it when size matters more than anything else.
|
|
55
|
+
|
|
56
|
+
```ts twoslash
|
|
57
|
+
import { Acryl } from 'acrylblends'
|
|
58
|
+
import { PatternRouter } from 'acrylblends/router/pattern-router'
|
|
59
|
+
|
|
60
|
+
const app = new Acryl({ router: new PatternRouter() })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
How the routers compare on your workload is for you to measure; see [Benchmarks](benchmarks.md).
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Acryl Stacks
|
|
2
|
+
|
|
3
|
+
Acryl makes easy things easy and hard things easy.
|
|
4
|
+
It is suitable for not just only returning JSON, but it's also great for building the full-stack application including REST API servers and the client.
|
|
5
|
+
|
|
6
|
+
## RPC
|
|
7
|
+
|
|
8
|
+
Acryl's RPC feature allows you to share API specs with little change to your code.
|
|
9
|
+
The client generated by `hc` will read the spec and access the endpoint type-safety.
|
|
10
|
+
|
|
11
|
+
The following libraries make it possible.
|
|
12
|
+
|
|
13
|
+
- Acryl - API Server
|
|
14
|
+
- [Zod](https://zod.dev) - Validator
|
|
15
|
+
- Zod Validator Middleware
|
|
16
|
+
- `hc` - HTTP Client
|
|
17
|
+
|
|
18
|
+
We can call the set of these components the **Acryl Stack**.
|
|
19
|
+
Now let's create an API server and a client with it.
|
|
20
|
+
|
|
21
|
+
## Writing API
|
|
22
|
+
|
|
23
|
+
First, write an endpoint that receives a GET request and returns JSON.
|
|
24
|
+
|
|
25
|
+
```ts twoslash
|
|
26
|
+
import { Acryl } from 'acrylblends'
|
|
27
|
+
|
|
28
|
+
const app = new Acryl()
|
|
29
|
+
|
|
30
|
+
app.get('/hello', (c) => {
|
|
31
|
+
return c.json({
|
|
32
|
+
message: `Hello!`,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Validation with Zod
|
|
38
|
+
|
|
39
|
+
Validate with Zod to receive the value of the query parameter.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { schemaValidator } from 'acrylblends/schema-validator'
|
|
43
|
+
import * as z from 'zod'
|
|
44
|
+
|
|
45
|
+
app.get(
|
|
46
|
+
'/hello',
|
|
47
|
+
schemaValidator(
|
|
48
|
+
'query',
|
|
49
|
+
z.object({
|
|
50
|
+
name: z.string(),
|
|
51
|
+
})
|
|
52
|
+
),
|
|
53
|
+
(c) => {
|
|
54
|
+
const { name } = c.req.valid('query')
|
|
55
|
+
return c.json({
|
|
56
|
+
message: `Hello! ${name}`,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Sharing the Types
|
|
63
|
+
|
|
64
|
+
To emit an endpoint specification, export its type.
|
|
65
|
+
|
|
66
|
+
> **Warning.**
|
|
67
|
+
> For the RPC to infer routes correctly, all included methods must be chained, and the endpoint or app type must be inferred from a declared variable. For more, see [Best Practices for RPC](../guides/best-practices.md#if-you-want-to-use-rpc-features).
|
|
68
|
+
>
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const route = app.get(
|
|
72
|
+
'/hello',
|
|
73
|
+
schemaValidator(
|
|
74
|
+
'query',
|
|
75
|
+
z.object({
|
|
76
|
+
name: z.string(),
|
|
77
|
+
})
|
|
78
|
+
),
|
|
79
|
+
(c) => {
|
|
80
|
+
const { name } = c.req.valid('query')
|
|
81
|
+
return c.json({
|
|
82
|
+
message: `Hello! ${name}`,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
export type AppType = typeof route
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Client
|
|
91
|
+
|
|
92
|
+
Next, The client-side implementation.
|
|
93
|
+
Create a client object by passing the `AppType` type to `hc` as generics.
|
|
94
|
+
Then, magically, completion works and the endpoint path and request type are suggested.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import type { AppType } from './server'
|
|
98
|
+
import { hc } from 'acrylblends/client'
|
|
99
|
+
|
|
100
|
+
const client = hc<AppType>('/api')
|
|
101
|
+
const res = await client.hello.$get({
|
|
102
|
+
query: {
|
|
103
|
+
name: 'Acryl',
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The `Response` is compatible with the fetch API, but the data that can be retrieved with `json()` has a type.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const data = await res.json()
|
|
112
|
+
console.log(`${data.message}`)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Sharing API specifications means that you can be aware of server-side changes.
|
|
116
|
+
|
|
117
|
+
## With React
|
|
118
|
+
|
|
119
|
+
You can create an application with a React front-end using the typed client.
|
|
120
|
+
|
|
121
|
+
The API server.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// src/index.ts
|
|
125
|
+
import { Acryl } from 'acrylblends'
|
|
126
|
+
import * as z from 'zod'
|
|
127
|
+
import { schemaValidator } from 'acrylblends/schema-validator'
|
|
128
|
+
|
|
129
|
+
const schema = z.object({
|
|
130
|
+
id: z.string(),
|
|
131
|
+
title: z.string(),
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
type Todo = z.infer<typeof schema>
|
|
135
|
+
|
|
136
|
+
const todos: Todo[] = []
|
|
137
|
+
|
|
138
|
+
const api = new Acryl()
|
|
139
|
+
.post('/todo', schemaValidator('form', schema), (c) => {
|
|
140
|
+
const todo = c.req.valid('form')
|
|
141
|
+
todos.push(todo)
|
|
142
|
+
return c.json({
|
|
143
|
+
message: 'created!',
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
.get('/todo', (c) => {
|
|
147
|
+
return c.json({
|
|
148
|
+
todos,
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
export type AppType = typeof api
|
|
153
|
+
|
|
154
|
+
const app = new Acryl()
|
|
155
|
+
app.route('/api', api)
|
|
156
|
+
|
|
157
|
+
export default app
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The client with React and React Query.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// src/App.tsx
|
|
164
|
+
import {
|
|
165
|
+
useQuery,
|
|
166
|
+
useMutation,
|
|
167
|
+
QueryClient,
|
|
168
|
+
QueryClientProvider,
|
|
169
|
+
} from '@tanstack/react-query'
|
|
170
|
+
import type { AppType } from '../functions/api/[[route]]'
|
|
171
|
+
import { hc, InferResponseType, InferRequestType } from 'acrylblends/client'
|
|
172
|
+
|
|
173
|
+
const queryClient = new QueryClient()
|
|
174
|
+
const client = hc<AppType>('/api')
|
|
175
|
+
|
|
176
|
+
export default function App() {
|
|
177
|
+
return (
|
|
178
|
+
<QueryClientProvider client={queryClient}>
|
|
179
|
+
<Todos />
|
|
180
|
+
</QueryClientProvider>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const Todos = () => {
|
|
185
|
+
const query = useQuery({
|
|
186
|
+
queryKey: ['todos'],
|
|
187
|
+
queryFn: async () => {
|
|
188
|
+
const res = await client.todo.$get()
|
|
189
|
+
return await res.json()
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const $post = client.todo.$post
|
|
194
|
+
|
|
195
|
+
const mutation = useMutation<
|
|
196
|
+
InferResponseType<typeof $post>,
|
|
197
|
+
Error,
|
|
198
|
+
InferRequestType<typeof $post>['form']
|
|
199
|
+
>({
|
|
200
|
+
mutationFn: async (todo) => {
|
|
201
|
+
const res = await $post({
|
|
202
|
+
form: todo,
|
|
203
|
+
})
|
|
204
|
+
return await res.json()
|
|
205
|
+
},
|
|
206
|
+
onSuccess: async () => {
|
|
207
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
208
|
+
},
|
|
209
|
+
onError: (error) => {
|
|
210
|
+
console.log(error)
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div>
|
|
216
|
+
<button
|
|
217
|
+
onClick={() => {
|
|
218
|
+
mutation.mutate({
|
|
219
|
+
id: Date.now().toString(),
|
|
220
|
+
title: 'Write code',
|
|
221
|
+
})
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
Add Todo
|
|
225
|
+
</button>
|
|
226
|
+
|
|
227
|
+
<ul>
|
|
228
|
+
{query.data?.todos.map((todo) => (
|
|
229
|
+
<li key={todo.id}>{todo.title}</li>
|
|
230
|
+
))}
|
|
231
|
+
</ul>
|
|
232
|
+
</div>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Web Standards
|
|
2
|
+
|
|
3
|
+
Acryl uses only **Web Standards** like Fetch.
|
|
4
|
+
They were originally used in the `fetch` function and consist of basic objects that handle HTTP requests and responses.
|
|
5
|
+
In addition to `Requests` and `Responses`, there are `URL`, `URLSearchParam`, `Headers` and others.
|
|
6
|
+
|
|
7
|
+
Cloudflare Workers, Deno, and Bun also are built upon Web Standards.
|
|
8
|
+
For example, a server that returns "Hello World" could be written as below. This could run on Cloudflare Workers and Bun.
|
|
9
|
+
|
|
10
|
+
```ts twoslash
|
|
11
|
+
export default {
|
|
12
|
+
async fetch() {
|
|
13
|
+
return new Response('Hello World')
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Acryl uses only Web Standards, which means that Acryl can run on any runtime that supports them.
|
|
19
|
+
In addition, we have a Node.js adapter. Acryl runs on these runtimes:
|
|
20
|
+
|
|
21
|
+
- Cloudflare Workers (`workerd`)
|
|
22
|
+
- Deno
|
|
23
|
+
- Bun
|
|
24
|
+
- Fastly Compute
|
|
25
|
+
- AWS Lambda
|
|
26
|
+
- Node.js
|
|
27
|
+
- Vercel (edge-light)
|
|
28
|
+
- WebAssembly (w/ [WebAssembly System Interface (WASI)][wasi] via [`wasi:http`][wasi-http])
|
|
29
|
+
|
|
30
|
+
It also works on Netlify and other platforms.
|
|
31
|
+
The same code runs on all platforms.
|
|
32
|
+
|
|
33
|
+
Cloudflare Workers, Deno, Shopify, and others launched [WinterCG](https://wintercg.org) to discuss the possibility of using the Web Standards to enable "web-interoperability".
|
|
34
|
+
Acryl will follow their steps and go for **the Standard of the Web Standards**.
|
|
35
|
+
|
|
36
|
+
[wasi]: https://github.com/WebAssembly/wasi
|
|
37
|
+
[wasi-http]: https://github.com/WebAssembly/wasi-http
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Acryl in a Cordis app
|
|
2
2
|
|
|
3
|
-
The ACRYL Framework puts its application model inside Cordis. You build an ordinary `
|
|
3
|
+
The ACRYL Framework puts its application model inside Cordis. You build an ordinary `Acryl`, register routes and middleware on it, then
|
|
4
4
|
mount that app from a Cordis plugin.
|
|
5
5
|
|
|
6
6
|
## The mental model
|
|
7
7
|
|
|
8
8
|
There are two layers:
|
|
9
9
|
|
|
10
|
-
1. **Application layer** - `
|
|
10
|
+
1. **Application layer** - `Acryl`, handlers, middleware, context, requests, responses, validation and helpers. This is the ACRYL
|
|
11
11
|
application API.
|
|
12
12
|
2. **Cordis layer** - plugins, services, injection, effects and disposal. This decides when an application exists and which surface can
|
|
13
13
|
reach it.
|
|
14
14
|
|
|
15
|
-
A route therefore has two owners. The `
|
|
15
|
+
A route therefore has two owners. The `Acryl` owns the route table. The plugin that mounted the app owns the mount for as long as its
|
|
16
16
|
Cordis fiber is active.
|
|
17
17
|
|
|
18
18
|
## A minimal plugin
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
import type { Context } from '@deepseek-ai/cordis'
|
|
22
|
-
import {
|
|
22
|
+
import { Acryl } from 'acrylblends'
|
|
23
23
|
|
|
24
24
|
export const name = 'todo' // equals the package name and the Loader row id
|
|
25
25
|
export const inject = ['router'] // PENDING until a router exists
|
|
26
26
|
|
|
27
27
|
export function apply(ctx: Context): void {
|
|
28
|
-
const app = new
|
|
28
|
+
const app = new Acryl()
|
|
29
29
|
.get('/', c => c.json({ ok: true }))
|
|
30
30
|
.get('/todos/:id', c => c.json({ id: c.req.param('id') }))
|
|
31
31
|
|
|
@@ -48,14 +48,14 @@ export function apply(ctx: Context): void {
|
|
|
48
48
|
`/todo/` is the first thing people type, and by default it is not found: the router treats `/todo` and `/todo/` as two paths. Choose
|
|
49
49
|
one of these, in this order of preference:
|
|
50
50
|
|
|
51
|
-
- **Redirect in your app** (what `examples/acryl-todo` does): add `trimTrailingSlash()` from `
|
|
51
|
+
- **Redirect in your app** (what `examples/acryl-todo` does): add `trimTrailingSlash()` from `acrylblends/trailing-slash` to the app.
|
|
52
52
|
A GET or HEAD that would be a 404 and ends in `/` gets a 301 to the same path without it, so `/todo/` reaches the page and every
|
|
53
53
|
route keeps one canonical path. Other methods are not redirected: `POST /todo/api/` stays a 404.
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
|
-
import { trimTrailingSlash } from '
|
|
56
|
+
import { trimTrailingSlash } from 'acrylblends/trailing-slash'
|
|
57
57
|
|
|
58
|
-
const app = new
|
|
58
|
+
const app = new Acryl()
|
|
59
59
|
.use(trimTrailingSlash())
|
|
60
60
|
.get('/', c => c.json({ ok: true }))
|
|
61
61
|
```
|
|
@@ -64,9 +64,9 @@ one of these, in this order of preference:
|
|
|
64
64
|
mounted app, with no redirect. It is the app's composition that sets it (the Blueprint or profile that owns the router row), not one
|
|
65
65
|
plugin, because it changes every other plugin's routes too.
|
|
66
66
|
|
|
67
|
-
`new
|
|
68
|
-
router's `trailingSlash` setting decides. `strict: false` applies to an `
|
|
69
|
-
and a measured table: [Routing and mounting](
|
|
67
|
+
`new Acryl({ strict: false })` does not help once the app is mounted: the router composes every mount into its own root app, and only the
|
|
68
|
+
router's `trailingSlash` setting decides. `strict: false` applies to an `Acryl` you serve yourself (`app.fetch`, `app.request`). Details
|
|
69
|
+
and a measured table: [Routing and mounting](../api/routing.md#trailing-slashes).
|
|
70
70
|
|
|
71
71
|
The mount belongs to the plugin that made it: `route` registers it as a Cordis effect of the caller, so when the plugin unloads the
|
|
72
72
|
routes disappear, and a reload mounts the app again without duplicating routes. `route` also returns a function that removes the mount
|
|
@@ -79,7 +79,7 @@ Finish building the app before you mount it: the router copies the app's routes
|
|
|
79
79
|
Middleware is registered on the app before the routes it should wrap:
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
|
-
const app = new
|
|
82
|
+
const app = new Acryl()
|
|
83
83
|
.use(async (c, next) => {
|
|
84
84
|
const started = Date.now()
|
|
85
85
|
await next()
|
|
@@ -89,7 +89,7 @@ const app = new App()
|
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
Use middleware for authentication, logging, CORS, compression, request IDs and other behavior that should wrap more than one handler.
|
|
92
|
-
The built-in set is listed in [Middleware](
|
|
92
|
+
The built-in set is listed in [Middleware](../concepts/middleware.md).
|
|
93
93
|
|
|
94
94
|
## Run the same app on a surface
|
|
95
95
|
|
|
@@ -141,6 +141,6 @@ expect(await response.json()).toEqual({ id: '42' })
|
|
|
141
141
|
## Next
|
|
142
142
|
|
|
143
143
|
- [Core concepts](./concepts.md)
|
|
144
|
-
- [Routing and mounting](
|
|
145
|
-
- [Context](
|
|
144
|
+
- [Routing and mounting](../api/routing.md)
|
|
145
|
+
- [Context](../api/context.md)
|
|
146
146
|
- [Plugin lifecycle and disposal](./plugin-lifecycle.md)
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
The framework's request-handling model is designed so every extension point belongs to a Cordis plugin. This page explains the pieces
|
|
4
4
|
before the API reference.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Acryl
|
|
7
7
|
|
|
8
|
-
An `
|
|
8
|
+
An `Acryl` is a self-contained ACRYL application. It owns:
|
|
9
9
|
|
|
10
10
|
- routes;
|
|
11
11
|
- middleware;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Middleware in Cordis
|
|
2
|
+
|
|
3
|
+
Middleware belongs to an `Acryl`; it is not a separate Cordis registration, and it disappears with the mount that serves it. A plugin may
|
|
4
|
+
build middleware from an injected service, but resources the middleware uses (timers, watchers, connections) are acquired in the plugin's
|
|
5
|
+
`ctx.effect()` and only the finished middleware goes into the app. Do not start timers, watchers or sockets from a handler.
|
|
6
|
+
|
|
7
|
+
Middleware an app registers with a path wraps the matching routes of every later mount too (an app mounted at `/` with
|
|
8
|
+
`app.use('/todo/*', ...)` wraps a plugin mounted at `/todo`), so a host can put a guard in front of other plugins by mounting first.
|