acrylblends 1.0.0-rc.1 → 1.0.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/README.md +130 -50
- package/docs/framework/api/acryl.md +3 -15
- package/docs/framework/api/context.md +12 -61
- package/docs/framework/api/presets.md +1 -1
- package/docs/framework/api/request.md +3 -3
- package/docs/framework/concepts/web-standard.md +2 -2
- package/docs/framework/guides/jsx-dom.md +1 -1
- package/docs/framework/guides/rpc.md +3 -11
- package/docs/framework/helpers/adapter.md +13 -43
- package/docs/framework/helpers/streaming.md +1 -1
- package/docs/framework/middleware/builtin/basic-auth.md +1 -1
- package/docs/framework/middleware/builtin/cache.md +2 -22
- package/docs/framework/middleware/builtin/compress.md +1 -1
- package/docs/framework/middleware/builtin/context-storage.md +10 -14
- package/docs/framework/middleware/builtin/cors.md +1 -2
- package/docs/framework/middleware/builtin/jsx-renderer.md +1 -1
- package/docs/framework/middleware/builtin/logger.md +1 -1
- package/docs/framework/middleware/builtin/mount.md +1 -1
- package/docs/framework/middleware/builtin/request-id.md +13 -20
- package/docs/framework/middleware/builtin/timing.md +1 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">acrylblends</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center"><strong>Acryl
|
|
8
|
-
One <code>Request</code> in, one <code>Response</code> out
|
|
7
|
+
<p align="center"><strong>Acryl: a small web framework on Web Standards, and whole apps built from Cordis plugins.</strong><br>
|
|
8
|
+
One <code>Request</code> in, one <code>Response</code> out - routing, middleware, validation, a typed client and a Node.js server, with no
|
|
9
|
+
dependencies - and a composition of plugins that runs in a browser or a desktop window.</p>
|
|
9
10
|
|
|
10
11
|
<p align="center">
|
|
11
|
-
<a href="https://github.com/
|
|
12
|
-
<a href="https://github.com/
|
|
13
|
-
<a href="https://github.com/
|
|
12
|
+
<a href="https://github.com/acrylblends/acrylblends/blob/main/docs/framework/README.md">Documentation</a> ·
|
|
13
|
+
<a href="https://github.com/acrylblends/acrylblends/blob/main/docs/framework/getting-started/basic.md">Getting started</a> ·
|
|
14
|
+
<a href="https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/index.md">API</a> ·
|
|
14
15
|
<a href="https://acrylblends.github.io">acrylblends.github.io</a> ·
|
|
15
16
|
<a href="https://acryl.dev">acryl.dev</a> ·
|
|
16
17
|
<a href="https://github.com/acrylblends/acrylblends">GitHub</a>
|
|
@@ -30,8 +31,12 @@ export default app
|
|
|
30
31
|
## Contents
|
|
31
32
|
|
|
32
33
|
- [Requirements](#requirements)
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
34
|
+
- [Start an app in five minutes](#start-an-app-in-five-minutes)
|
|
35
|
+
- [Run it in a browser or in a desktop window](#run-it-in-a-browser-or-in-a-desktop-window)
|
|
36
|
+
- [Start from the registry](#start-from-the-registry)
|
|
37
|
+
- [Publish your app, or your own starter](#publish-your-app-or-your-own-starter)
|
|
38
|
+
- [Keep the framework current](#keep-the-framework-current)
|
|
39
|
+
- [Use it as a library](#use-it-as-a-library)
|
|
35
40
|
- [A first API](#a-first-api)
|
|
36
41
|
- [Middleware](#middleware)
|
|
37
42
|
- [Validation](#validation)
|
|
@@ -39,7 +44,7 @@ export default app
|
|
|
39
44
|
- [Testing](#testing)
|
|
40
45
|
- [Serving on Node.js](#serving-on-nodejs)
|
|
41
46
|
- [What is in the package](#what-is-in-the-package)
|
|
42
|
-
- [Upgrading from
|
|
47
|
+
- [Upgrading from an older version](#upgrading-from-an-older-version)
|
|
43
48
|
- [The acrylblends command](#the-acrylblends-command)
|
|
44
49
|
- [Links](#links)
|
|
45
50
|
- [License](#license)
|
|
@@ -50,9 +55,93 @@ export default app
|
|
|
50
55
|
- **Bun** runs the same app (`export default app`) and is experimental.
|
|
51
56
|
- `acrylblends` has no dependencies. Its types need only Node's types (`@types/node`), not the DOM library.
|
|
52
57
|
|
|
53
|
-
##
|
|
58
|
+
## Start an app in five minutes
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
```sh
|
|
61
|
+
npm create acrylblends@latest my-app
|
|
62
|
+
cd my-app
|
|
63
|
+
npm install
|
|
64
|
+
npm run dev
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The starter asks which template you want; naming one skips the question (`npm create acrylblends@latest my-app notes`).
|
|
68
|
+
|
|
69
|
+
| Template | What you get |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `blank` | the router, the Node server and one route: the smallest app |
|
|
72
|
+
| `nodejs` | a page rendered with JSX and a client component, without a bundler |
|
|
73
|
+
| `notes` | a multi-user notes app on the `db` port: sign-up, sessions, migrations, search, a typed client |
|
|
74
|
+
| `chat` | an answer streamed over server-sent events from an OpenAI-compatible API, history in SQLite |
|
|
75
|
+
| `todo` | a to-do list on the router and live state |
|
|
76
|
+
| `agent` | the blank app with an agent that writes and installs plugins into the running app |
|
|
77
|
+
|
|
78
|
+
Every template is a Cordis app: `blend.yaml` lists its plugins, `plugins/` holds them as code you own, and the starter also writes
|
|
79
|
+
`docs/framework` (the pages this README links to), `AGENTS.md`, `CLAUDE.md`, one skill for a coding agent, and its own tests. `npm run dev`
|
|
80
|
+
serves it and prints the URL, `npm test` runs the tests, `npm run check` type-checks.
|
|
81
|
+
|
|
82
|
+
Adding a capability is one command: `npx acrylblends add <plugin>` copies a library plugin and what it needs into `plugins/`, prints the row to
|
|
83
|
+
add to `blend.yaml`, and records what it copied so `acrylblends upgrade` can move it forward later. `npx acrylblends plugins` lists the library.
|
|
84
|
+
|
|
85
|
+
## Run it in a browser or in a desktop window
|
|
86
|
+
|
|
87
|
+
The same app runs both ways; the routes, the plugins and the data are the ones you already wrote.
|
|
88
|
+
|
|
89
|
+
- **Browser**: what you get. `npm run dev` (or `npm start`) serves it on a loopback port and prints the URL.
|
|
90
|
+
- **Desktop**: add the shell and run it as a window on the app's own server.
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
npx acrylblends init my-app --desktop # create an app with the shell (--template picks the template)
|
|
94
|
+
# or, in an app you already have:
|
|
95
|
+
npx acrylblends add desktop
|
|
96
|
+
npm install # the shell needs electron
|
|
97
|
+
npm run desktop # an Electron window on the app's own server
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Every Electron call lives in `plugins/desktop`, so the app's own code never imports it. See
|
|
101
|
+
[Desktop](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/getting-started/desktop.md).
|
|
102
|
+
|
|
103
|
+
## Start from the registry
|
|
104
|
+
|
|
105
|
+
The registry is a set of starting points people published, browsable at [acrylblends.github.io/blends](https://acrylblends.github.io/blends)
|
|
106
|
+
and readable by any tool as plain JSON at <https://acrylblends.github.io/registry/index.json>. ACRYL's CLI pulls one by id, or from any git
|
|
107
|
+
repository:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
npm install -g acryl
|
|
111
|
+
acryl new my-app --from acryl.gtd # a starter from the registry
|
|
112
|
+
acryl new my-app --from github:you/starter # a repository with an acryl.template.json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Without `--from`, `acryl new` writes the blank app; `--blueprint` picks another built-in Blueprint. A team's own registry is the same layout in
|
|
116
|
+
its own repository: `--registry <git URL or folder>` reads starters from there instead of the public one.
|
|
117
|
+
|
|
118
|
+
## Publish your app, or your own starter
|
|
119
|
+
|
|
120
|
+
**Your app** belongs to you and lives in your repository: `npm run save` commits it and pushes to its remote. It refuses a file that looks like
|
|
121
|
+
a secret, and refuses to push a private app to a public remote.
|
|
122
|
+
|
|
123
|
+
Before an app goes on a server: with `NODE_ENV=production` the door, the agent panel and the PTY driver start nothing, but the `agent` template
|
|
124
|
+
still mounts its own agent, builder and extensions rows. Start from one of the other five templates, or remove those rows from its `blend.yaml`
|
|
125
|
+
- see [What runs in production](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/production.md).
|
|
126
|
+
|
|
127
|
+
**A starter** other people can pull is a pull request to the registry repository: add `registry/blends/<id>/` with a `blend.yaml` (`kind: Blueprint`,
|
|
128
|
+
an id you own, the license, `visibility`, a category and a one-line description), the plugins it adds under `plugins/`, and a `README.md`. CI
|
|
129
|
+
validates every entry and fails when `index.json` is stale, so what the site lists is what passed. The layout, and how to keep a starter private
|
|
130
|
+
without a registry at all, is in
|
|
131
|
+
[the registry README](https://github.com/acrylblends/acrylblends.github.io/tree/main/registry).
|
|
132
|
+
|
|
133
|
+
## Keep the framework current
|
|
134
|
+
|
|
135
|
+
An app carries its framework plugins as source, so a new release moves them in place and leaves your own code alone:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
npx acrylblends upgrade --dry-run # the plan: what would be replaced
|
|
139
|
+
npx acrylblends upgrade # an edited file stops it with the diff, unless --force
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Use it as a library
|
|
143
|
+
|
|
144
|
+
An Acryl app is also a plain function from `Request` to `Response`, which needs no scaffold. Create a folder and install the package:
|
|
56
145
|
|
|
57
146
|
```sh
|
|
58
147
|
mkdir my-app && cd my-app
|
|
@@ -90,21 +179,9 @@ Run it and open http://localhost:3000:
|
|
|
90
179
|
node server.ts
|
|
91
180
|
```
|
|
92
181
|
|
|
93
|
-
For Bun, `bun run index.ts` serves the same `export default app`.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
`create-acrylblends` creates a ready project with `dev`, `test` and `start` scripts:
|
|
98
|
-
|
|
99
|
-
```sh
|
|
100
|
-
npm create acrylblends@latest my-app
|
|
101
|
-
cd my-app
|
|
102
|
-
npm install
|
|
103
|
-
npm run dev
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
The templates are the same through `npm create acrylblends` and `acrylblends init`: `blank`, `nodejs`, `notes`, `chat`, `todo` and `agent` (a Cordis app with an agent that can extend it while it runs; `acryl-app` is its old name). See the
|
|
107
|
-
[create-acrylblends guide](https://github.com/acryldev/acryl/blob/main/docs/framework/guides/create-acrylblends.md).
|
|
182
|
+
For Bun, `bun run index.ts` serves the same `export default app`. The same app mounts inside a Cordis plugin with
|
|
183
|
+
`ctx.router.route(prefix, app)`, which is how a generated app is put together: see
|
|
184
|
+
[Acryl in a Cordis app](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/cordis/acryl-in-a-cordis-app.md).
|
|
108
185
|
|
|
109
186
|
## A first API
|
|
110
187
|
|
|
@@ -138,10 +215,10 @@ app.onError((error, c) => error instanceof HTTPException ? error.getResponse() :
|
|
|
138
215
|
export default app
|
|
139
216
|
```
|
|
140
217
|
|
|
141
|
-
Read more: [Routing](https://github.com/
|
|
142
|
-
[Context](https://github.com/
|
|
143
|
-
[Request](https://github.com/
|
|
144
|
-
[Exceptions](https://github.com/
|
|
218
|
+
Read more: [Routing](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/routing.md),
|
|
219
|
+
[Context](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/context.md),
|
|
220
|
+
[Request](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/request.md),
|
|
221
|
+
[Exceptions](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/exception.md).
|
|
145
222
|
|
|
146
223
|
## Middleware
|
|
147
224
|
|
|
@@ -165,8 +242,8 @@ app.use('/admin/*', bearerAuth({ token: process.env.ADMIN_TOKEN ?? '' }))
|
|
|
165
242
|
Built in: `basic-auth`, `bearer-auth`, `body-limit`, `combine`, `compress`, `context-storage`, `cors`, `csrf`, `etag`, `ip-restriction`,
|
|
166
243
|
`jwk`, `jwt`, `language`, `logger`, `method-not-allowed`, `method-override`, `powered-by`, `pretty-json`, `request-id`, `secure-headers`,
|
|
167
244
|
`serve-static`, `timeout`, `timing`, `trailing-slash`, plus ACRYL's `loopback-only`. Each one is documented under
|
|
168
|
-
[Middleware](https://github.com/
|
|
169
|
-
[middleware guide](https://github.com/
|
|
245
|
+
[Middleware](https://github.com/acrylblends/acrylblends/tree/main/docs/framework/middleware/builtin). To write your own, see the
|
|
246
|
+
[middleware guide](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/middleware.md).
|
|
170
247
|
|
|
171
248
|
## Validation
|
|
172
249
|
|
|
@@ -191,7 +268,7 @@ app.post(
|
|
|
191
268
|
```
|
|
192
269
|
|
|
193
270
|
`acrylblends/schema-validator` drives the same validator with any [Standard Schema](https://standardschema.dev) library (Zod, Valibot,
|
|
194
|
-
ArkType and others). See the [validation guide](https://github.com/
|
|
271
|
+
ArkType and others). See the [validation guide](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/validation.md).
|
|
195
272
|
|
|
196
273
|
## Typed client
|
|
197
274
|
|
|
@@ -216,7 +293,7 @@ const answer = await client.api.hello.$get()
|
|
|
216
293
|
console.log((await answer.json()).message)
|
|
217
294
|
```
|
|
218
295
|
|
|
219
|
-
See [RPC](https://github.com/
|
|
296
|
+
See [RPC](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/rpc.md).
|
|
220
297
|
|
|
221
298
|
## Testing
|
|
222
299
|
|
|
@@ -235,7 +312,7 @@ test('GET / says hello', async () => {
|
|
|
235
312
|
```
|
|
236
313
|
|
|
237
314
|
Run it with `node --test`. `acrylblends/testing` adds `testClient`, the typed client over `app.request()`. See the
|
|
238
|
-
[testing guide](https://github.com/
|
|
315
|
+
[testing guide](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/testing.md).
|
|
239
316
|
|
|
240
317
|
## Serving on Node.js
|
|
241
318
|
|
|
@@ -256,7 +333,7 @@ const server = serve({ fetch: app.fetch, port: 3000, hostname: '127.0.0.1' })
|
|
|
256
333
|
process.on('SIGTERM', () => { server.close() })
|
|
257
334
|
```
|
|
258
335
|
|
|
259
|
-
See [Node.js](https://github.com/
|
|
336
|
+
See [Node.js](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/getting-started/nodejs.md).
|
|
260
337
|
|
|
261
338
|
## What is in the package
|
|
262
339
|
|
|
@@ -266,20 +343,20 @@ See [Node.js](https://github.com/acryldev/acryl/blob/main/docs/framework/getting
|
|
|
266
343
|
| `acrylblends/<middleware>` | each built-in middleware, for example `acrylblends/cors` |
|
|
267
344
|
| `acrylblends/validator`, `acrylblends/schema-validator` | request validation |
|
|
268
345
|
| `acrylblends/client`, `acrylblends/testing` | the typed client and its in-process test version |
|
|
269
|
-
| `acrylblends/accepts`, `adapter`, `cookie`, `factory`, `html`, `route`, `streaming`, `ws`, `conninfo` | helpers ([helpers docs](https://github.com/
|
|
346
|
+
| `acrylblends/accepts`, `adapter`, `cookie`, `factory`, `html`, `route`, `streaming`, `ws`, `conninfo` | helpers ([helpers docs](https://github.com/acrylblends/acrylblends/tree/main/docs/framework/helpers)) |
|
|
270
347
|
| `acrylblends/http-exception` | `HTTPException` |
|
|
271
|
-
| `acrylblends/router/*` | the routers: `reg-exp-router`, `smart-router`, `trie-router`, `pattern-router`, `linear-router` ([routers](https://github.com/
|
|
272
|
-
| `acrylblends/tiny`, `acrylblends/quick` | the presets: `Acryl` with another router (`PatternRouter`; `LinearRouter` and `TrieRouter`) ([presets](https://github.com/
|
|
348
|
+
| `acrylblends/router/*` | the routers: `reg-exp-router`, `smart-router`, `trie-router`, `pattern-router`, `linear-router` ([routers](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/concepts/routers.md)) |
|
|
349
|
+
| `acrylblends/tiny`, `acrylblends/quick` | the presets: `Acryl` with another router (`PatternRouter`; `LinearRouter` and `TrieRouter`) ([presets](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/api/presets.md)) |
|
|
273
350
|
| `acrylblends/cache`, `acrylblends/mount` | the cache middleware and mounting another framework's fetch handler |
|
|
274
|
-
| `acrylblends/ssg` | static site generation: `toSSG` writes every route as a file ([SSG helper](https://github.com/
|
|
351
|
+
| `acrylblends/ssg` | static site generation: `toSSG` writes every route as a file ([SSG helper](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/helpers/ssg.md)) |
|
|
275
352
|
| `acrylblends/proxy`, `acrylblends/dev` | the proxy helper and the dev helpers (`showRoutes`, `inspectRoutes`, `getRouterName`) |
|
|
276
|
-
| `acrylblends/jsx` | JSX on the server: `jsxImportSource: 'acrylblends/jsx'`, `.tsx` files run on Node with `node --import acrylblends/node-server/register`, with `jsx/streaming`, and `acrylblends/jsx-renderer` for a layout behind `c.render` ([JSX guide](https://github.com/
|
|
277
|
-
| `acrylblends/jsx/dom` | JSX in the browser: hooks and `render` (needs the DOM library) ([client components](https://github.com/
|
|
353
|
+
| `acrylblends/jsx` | JSX on the server: `jsxImportSource: 'acrylblends/jsx'`, `.tsx` files run on Node with `node --import acrylblends/node-server/register`, with `jsx/streaming`, and `acrylblends/jsx-renderer` for a layout behind `c.render` ([JSX guide](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/jsx.md)) |
|
|
354
|
+
| `acrylblends/jsx/dom` | JSX in the browser: hooks and `render` (needs the DOM library) ([client components](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/jsx-dom.md)); on Node, `clientModules` serves them to the browser without a bundler ([guide](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/guides/client-modules.md)) |
|
|
278
355
|
| `acrylblends/drivers` | the driver contract of the agent panel (`ChatDriver`, `TerminalDriver`, `Drivers`), for plugins that add an agent to the panel |
|
|
279
|
-
| `acrylblends/css` | CSS in JSX: `css`, `cx`, `keyframes`, `Style` ([CSS helper](https://github.com/
|
|
356
|
+
| `acrylblends/css` | CSS in JSX: `css`, `cx`, `keyframes`, `Style` ([CSS helper](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/helpers/css.md)) |
|
|
280
357
|
| `acrylblends/node-server` | `serve`, `createAdaptorServer`, `getRequestListener`; with `node-server/serve-static`, `node-server/conninfo`, and `node-server/register` (compiles `.tsx` with the app's `typescript`); `clientModules` and `buildClientModules` (client components without a bundler) |
|
|
281
358
|
|
|
282
|
-
Every subpath in the table is listed in the package's `exports` and is covered by semver from 1.0, except the ones marked experimental: `acrylblends/bun` and the door (`door/*`, `cordis/door`), which are development tools. The ported core's utilities (`utils/*`) and the base-class chunk are internal and not exported. Node.js is the runtime the package is verified on; the upstream's adapters for AWS Lambda, Lambda@Edge, Cloudflare, Vercel, Netlify and Service Workers are not part of the package ([Other runtimes](https://github.com/
|
|
359
|
+
Every subpath in the table is listed in the package's `exports` and is covered by semver from 1.0, except the ones marked experimental: `acrylblends/bun` and the door (`door/*`, `cordis/door`), which are development tools. The ported core's utilities (`utils/*`) and the base-class chunk are internal and not exported. Node.js is the runtime the package is verified on; the upstream's adapters for AWS Lambda, Lambda@Edge, Cloudflare, Vercel, Netlify and Service Workers are not part of the package ([Other runtimes](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/getting-started/other-runtimes.md)).
|
|
283
360
|
|
|
284
361
|
To extend the context's typed variables, augment the root module:
|
|
285
362
|
|
|
@@ -291,11 +368,14 @@ declare module 'acrylblends' {
|
|
|
291
368
|
}
|
|
292
369
|
```
|
|
293
370
|
|
|
294
|
-
## Upgrading from
|
|
371
|
+
## Upgrading from an older version
|
|
372
|
+
|
|
373
|
+
An app made with any 0.9.1 or later release moves with `acrylblends upgrade` (above); its edited files are never replaced silently.
|
|
295
374
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
[Migration
|
|
375
|
+
Code that imports the old names moves by hand: `App` and `AppRequest`, the 0.4 aliases of `Acryl` and `AcrylRequest`, are removed in 1.0, and
|
|
376
|
+
the scaffolding API that was the package root in 0.4 is now `acrylblends/cli`. See
|
|
377
|
+
[Migration to 1.0](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/cordis/migration-to-1-0.md) and
|
|
378
|
+
[Migration from 0.4](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/cordis/migration-from-0-4.md).
|
|
299
379
|
|
|
300
380
|
## The acrylblends command
|
|
301
381
|
|
|
@@ -309,18 +389,18 @@ cd my-app && npm install && npm run dev
|
|
|
309
389
|
```
|
|
310
390
|
|
|
311
391
|
See the [ACRYLBLENDS documentation](https://github.com/acrylblends/acrylblends/blob/main/docs/README.md) and
|
|
312
|
-
[Acryl in a Cordis app](https://github.com/
|
|
392
|
+
[Acryl in a Cordis app](https://github.com/acrylblends/acrylblends/blob/main/docs/framework/cordis/acryl-in-a-cordis-app.md).
|
|
313
393
|
|
|
314
394
|
## Links
|
|
315
395
|
|
|
316
396
|
| | |
|
|
317
397
|
| --- | --- |
|
|
318
|
-
| Framework documentation | https://github.com/
|
|
398
|
+
| Framework documentation | https://github.com/acrylblends/acrylblends/blob/main/docs/framework/README.md |
|
|
319
399
|
| ACRYLBLENDS website | https://acrylblends.github.io |
|
|
320
400
|
| ACRYL website | https://acryl.dev |
|
|
321
401
|
| This package's source and issues | https://github.com/acrylblends/acrylblends ([issues](https://github.com/acrylblends/acrylblends/issues)) |
|
|
322
402
|
| The framework's source | https://github.com/acryldev/acryl (`plugins/acryl-router`, `plugins/acryl-node-server`) |
|
|
323
|
-
| For coding agents | https://github.com/
|
|
403
|
+
| For coding agents | https://github.com/acrylblends/acrylblends/blob/main/docs/framework/agents/index.md |
|
|
324
404
|
| npm | https://www.npmjs.com/package/acrylblends |
|
|
325
405
|
|
|
326
406
|
## License
|
|
@@ -64,25 +64,13 @@ app.onError((err, c) => {
|
|
|
64
64
|
|
|
65
65
|
## fire()
|
|
66
66
|
|
|
67
|
-
> **Warning.** **`app.fire()` is deprecated**, and the Service Worker adapter
|
|
68
|
-
|
|
69
|
-
`app.fire()` automatically adds a global `fetch` event listener.
|
|
70
|
-
|
|
71
|
-
This can be useful for environments that adhere to the [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), such as [non-ES module Cloudflare Workers](https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/).
|
|
72
|
-
|
|
73
|
-
`app.fire()` executes the following for you:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
addEventListener('fetch', (event: FetchEventLike): void => {
|
|
77
|
-
event.respondWith(this.dispatch(...))
|
|
78
|
-
})
|
|
79
|
-
```
|
|
67
|
+
> **Warning.** **`app.fire()` is deprecated**, and the Service Worker adapter is not part of the package in 1.0. It adds a global `fetch` event listener, which only a Service Worker-style host reads; use `app.fetch` in an entry point of your own instead.
|
|
80
68
|
|
|
81
69
|
## fetch()
|
|
82
70
|
|
|
83
71
|
`app.fetch` will be the entry point of your application.
|
|
84
72
|
|
|
85
|
-
|
|
73
|
+
As the `fetch` method of an object a host calls:
|
|
86
74
|
|
|
87
75
|
```ts twoslash
|
|
88
76
|
import { Acryl } from 'acrylblends'
|
|
@@ -230,7 +218,7 @@ const app = new Acryl({ router: new RegExpRouter() })
|
|
|
230
218
|
|
|
231
219
|
## Generics
|
|
232
220
|
|
|
233
|
-
You can pass Generics to specify the
|
|
221
|
+
You can pass Generics to specify the type of the environment the host passes and the variables used in `c.set`/`c.get`.
|
|
234
222
|
|
|
235
223
|
```ts twoslash
|
|
236
224
|
import { Acryl } from 'acrylblends'
|
|
@@ -362,81 +362,32 @@ app.get('/pages/my-hobbies', (c) => {
|
|
|
362
362
|
|
|
363
363
|
## executionCtx
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
A host that embeds the app can pass an execution context: `waitUntil(promise)` keeps work alive after the response has been sent, and `passThroughOnException()` lets the host take over when the handler throws. The Node server passes none, so read it only in an app whose host provides one.
|
|
366
366
|
|
|
367
367
|
```ts twoslash
|
|
368
368
|
import { Acryl } from 'acrylblends'
|
|
369
|
-
const app = new Acryl
|
|
370
|
-
|
|
371
|
-
KV: any
|
|
372
|
-
}
|
|
373
|
-
}>()
|
|
374
|
-
declare const key: string
|
|
375
|
-
declare const data: string
|
|
376
|
-
// ---cut---
|
|
377
|
-
// ExecutionContext object
|
|
378
|
-
app.get('/foo', async (c) => {
|
|
379
|
-
c.executionCtx.waitUntil(c.env.KV.put(key, data))
|
|
380
|
-
// ...
|
|
381
|
-
})
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
The `ExecutionContext` also has an [`exports`](https://developers.cloudflare.com/workers/runtime-apis/context/#exports) field. To get autocomplete with Wrangler's generated types, you can use module augmentation:
|
|
385
|
-
|
|
386
|
-
```ts
|
|
387
|
-
import 'acrylblends'
|
|
388
|
-
|
|
389
|
-
declare module 'acrylblends' {
|
|
390
|
-
interface ExecutionContext {
|
|
391
|
-
readonly exports: Cloudflare.Exports
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
## event
|
|
397
|
-
|
|
398
|
-
You can access Cloudflare Workers' specific `FetchEvent`. This was used in "Service Worker" syntax. But, it is not recommended now.
|
|
399
|
-
|
|
400
|
-
```ts twoslash
|
|
401
|
-
import { Acryl } from 'acrylblends'
|
|
402
|
-
declare const key: string
|
|
403
|
-
declare const data: string
|
|
404
|
-
type KVNamespace = any
|
|
369
|
+
const app = new Acryl()
|
|
370
|
+
declare const sendReceipt: Promise<void>
|
|
405
371
|
// ---cut---
|
|
406
|
-
//
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const app = new Acryl<{ Bindings: Bindings }>()
|
|
412
|
-
|
|
413
|
-
// FetchEvent object (only set when using Service Worker syntax)
|
|
414
|
-
app.get('/foo', async (c) => {
|
|
415
|
-
c.event.waitUntil(c.env.MY_KV.put(key, data))
|
|
416
|
-
// ...
|
|
372
|
+
// Work that must finish after the response, when the host provides a context
|
|
373
|
+
app.get('/orders/:id', async (c) => {
|
|
374
|
+
c.executionCtx.waitUntil(sendReceipt)
|
|
375
|
+
return c.json({ ok: true })
|
|
417
376
|
})
|
|
418
377
|
```
|
|
419
378
|
|
|
420
379
|
## env
|
|
421
380
|
|
|
422
|
-
|
|
423
|
-
Regardless of type, bindings are always available as global variables and can be accessed via the context `c.env.BINDING_KEY`.
|
|
381
|
+
`c.env` is the environment the host hands the app. The Node server passes `process.env`, so `c.env.MY_KEY` is the variable `MY_KEY`. A host that passes something else gives the app a type parameter, and then `c.env` is that shape.
|
|
424
382
|
|
|
425
383
|
```ts twoslash
|
|
426
384
|
import { Acryl } from 'acrylblends'
|
|
427
|
-
type KVNamespace = any
|
|
428
385
|
// ---cut---
|
|
429
|
-
|
|
430
|
-
type Bindings = {
|
|
431
|
-
MY_KV: KVNamespace
|
|
432
|
-
}
|
|
386
|
+
const app = new Acryl<{ Bindings: { REGION: string } }>()
|
|
433
387
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
app.get('/', async (c) => {
|
|
438
|
-
c.env.MY_KV.get('my-key')
|
|
439
|
-
// ...
|
|
388
|
+
app.get('/', (c) => {
|
|
389
|
+
const region = c.env.REGION
|
|
390
|
+
return c.text(region)
|
|
440
391
|
})
|
|
441
392
|
```
|
|
442
393
|
|
|
@@ -57,6 +57,6 @@ this.router = new PatternRouter()
|
|
|
57
57
|
|
|
58
58
|
| Preset | Suitable platforms |
|
|
59
59
|
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
-
| `acrylblends` | This is highly recommended for most use cases. Although the registration phase may be slower than `acrylblends/quick`, it exhibits high performance once booted. It's ideal for long-life
|
|
60
|
+
| `acrylblends` | This is highly recommended for most use cases. Although the registration phase may be slower than `acrylblends/quick`, it exhibits high performance once booted. It's ideal for a long-life server on **Node.js** (the verified runtime) or **Bun** (experimental). |
|
|
61
61
|
| `acrylblends/quick` | This preset is designed for environments where the application is initialized for every request. |
|
|
62
62
|
| `acrylblends/tiny` | This is the smallest router package and it's suitable for environments where resources are limited. |
|
|
@@ -364,9 +364,9 @@ app.get('/about/me', async (c) => {
|
|
|
364
364
|
The raw [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
|
|
365
365
|
|
|
366
366
|
```ts
|
|
367
|
-
//
|
|
368
|
-
app.post('/', async (c) => {
|
|
369
|
-
const
|
|
367
|
+
// Hand the raw Request to an API that takes one, such as a Web-standard parser
|
|
368
|
+
app.post('/upload', async (c) => {
|
|
369
|
+
const bytes = await c.req.raw.arrayBuffer()
|
|
370
370
|
// ...
|
|
371
371
|
})
|
|
372
372
|
```
|
|
@@ -4,8 +4,8 @@ Acryl uses only **Web Standards** like Fetch.
|
|
|
4
4
|
They were originally used in the `fetch` function and consist of basic objects that handle HTTP requests and responses.
|
|
5
5
|
In addition to `Requests` and `Responses`, there are `URL`, `URLSearchParam`, `Headers` and others.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
For example, a server that returns "Hello World"
|
|
7
|
+
Bun is built on Web Standards the same way.
|
|
8
|
+
For example, a server that returns "Hello World" can be written as below, and it runs on Node.js and Bun.
|
|
9
9
|
|
|
10
10
|
```ts twoslash
|
|
11
11
|
export default {
|
|
@@ -277,7 +277,7 @@ export default function App() {
|
|
|
277
277
|
On Node you can skip the bundler: [Client components without a bundler](client-modules.md) serves `.tsx` client modules to the browser with
|
|
278
278
|
this runtime and one script tag, in development and in production.
|
|
279
279
|
|
|
280
|
-
There is a small JSX Runtime for Client Components. Using this will result in smaller bundled results than using `acrylblends/jsx`. Specify `acrylblends/jsx/dom` in `tsconfig.json`.
|
|
280
|
+
There is a small JSX Runtime for Client Components. Using this will result in smaller bundled results than using `acrylblends/jsx`. Specify `acrylblends/jsx/dom` in `tsconfig.json`.
|
|
281
281
|
|
|
282
282
|
```json
|
|
283
283
|
{
|
|
@@ -584,21 +584,13 @@ const route = app.put(
|
|
|
584
584
|
|
|
585
585
|
## Custom `fetch` method
|
|
586
586
|
|
|
587
|
-
You can set the custom `fetch` method.
|
|
588
|
-
|
|
589
|
-
In the following example script for Cloudflare Worker, the Service Bindings' `fetch` method is used instead of the default `fetch`.
|
|
590
|
-
|
|
591
|
-
```toml
|
|
592
|
-
# wrangler.toml
|
|
593
|
-
services = [
|
|
594
|
-
{ binding = "AUTH", service = "auth-service" },
|
|
595
|
-
]
|
|
596
|
-
```
|
|
587
|
+
You can set the custom `fetch` method: anything with the `fetch` signature works in place of the global one, such as a client that adds a header or a test double.
|
|
597
588
|
|
|
598
589
|
```ts
|
|
599
590
|
// src/client.ts
|
|
600
591
|
const client = hc<CreateProfileType>('http://localhost', {
|
|
601
|
-
fetch:
|
|
592
|
+
fetch: (input, init) =>
|
|
593
|
+
fetch(input, { ...init, headers: { ...init?.headers, authorization: `Bearer ${token}` } }),
|
|
602
594
|
})
|
|
603
595
|
```
|
|
604
596
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Adapter Helper
|
|
2
2
|
|
|
3
|
-
The Adapter Helper
|
|
3
|
+
The Adapter Helper reads an app's environment the same way on every runtime it runs on.
|
|
4
4
|
|
|
5
5
|
## Import
|
|
6
6
|
|
|
@@ -11,50 +11,25 @@ import { env, getRuntimeKey } from 'acrylblends/adapter'
|
|
|
11
11
|
|
|
12
12
|
## `env()`
|
|
13
13
|
|
|
14
|
-
The `env()` function
|
|
14
|
+
The `env()` function returns the environment the host gives the app. On Node.js, the verified runtime, that is `process.env`; on Bun it is `Bun.env` (and `process.env`), and the two entries a `.env` file adds are there as well. A host that passes its own environment to `app.fetch(request, env)` gets that instead.
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
17
|
import { env } from 'acrylblends/adapter'
|
|
18
18
|
|
|
19
19
|
app.get('/env', (c) => {
|
|
20
|
-
// NAME is process.env.NAME on Node.js
|
|
21
|
-
// NAME is the value written in `wrangler.toml` on Cloudflare
|
|
20
|
+
// NAME is process.env.NAME on Node.js and Bun
|
|
22
21
|
const { NAME } = env<{ NAME: string }>(c)
|
|
23
22
|
return c.text(NAME)
|
|
24
23
|
})
|
|
25
24
|
```
|
|
26
25
|
|
|
27
|
-
Supported Runtimes, Serverless Platforms and Cloud Services:
|
|
28
|
-
|
|
29
|
-
- Cloudflare Workers
|
|
30
|
-
- `wrangler.toml`
|
|
31
|
-
- `wrangler.jsonc`
|
|
32
|
-
- Deno
|
|
33
|
-
- [`Deno.env`](https://docs.deno.com/runtime/manual/basics/env_variables)
|
|
34
|
-
- `.env` file
|
|
35
|
-
- Bun
|
|
36
|
-
- [`Bun.env`](https://bun.com/guides/runtime/set-env)
|
|
37
|
-
- `process.env`
|
|
38
|
-
- Node.js
|
|
39
|
-
- `process.env`
|
|
40
|
-
- Vercel
|
|
41
|
-
- [Environment Variables on Vercel](https://vercel.com/docs/projects/environment-variables)
|
|
42
|
-
- AWS Lambda
|
|
43
|
-
- [Environment Variables on AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/samples-blank.html#samples-blank-architecture)
|
|
44
|
-
- Lambda@Edge\
|
|
45
|
-
Environment Variables on Lambda are [not supported](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/add-origin-custom-headers.html) by Lambda@Edge, you need to use [Lambda@Edge event](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html) as an alternative.
|
|
46
|
-
- Fastly Compute\
|
|
47
|
-
On Fastly Compute, you can use the ConfigStore to manage user-defined data.
|
|
48
|
-
- Netlify\
|
|
49
|
-
On Netlify, you can use the [Netlify Contexts](https://docs.netlify.com/site-deploys/overview/#deploy-contexts) to manage user-defined data.
|
|
50
|
-
|
|
51
26
|
### Specify the runtime
|
|
52
27
|
|
|
53
|
-
|
|
28
|
+
`env()` reads the runtime itself. Pass the runtime key as the second argument when a host embeds the app and detects it differently, or in a test that fixes the key:
|
|
54
29
|
|
|
55
30
|
```ts
|
|
56
31
|
app.get('/env', (c) => {
|
|
57
|
-
const { NAME } = env<{ NAME: string }>(c, '
|
|
32
|
+
const { NAME } = env<{ NAME: string }>(c, 'node')
|
|
58
33
|
return c.text(NAME)
|
|
59
34
|
})
|
|
60
35
|
```
|
|
@@ -65,23 +40,18 @@ The `getRuntimeKey()` function returns the identifier of the current runtime.
|
|
|
65
40
|
|
|
66
41
|
```ts
|
|
67
42
|
app.get('/', (c) => {
|
|
68
|
-
if (getRuntimeKey() === '
|
|
69
|
-
return c.text('You are on Cloudflare')
|
|
70
|
-
} else if (getRuntimeKey() === 'bun') {
|
|
43
|
+
if (getRuntimeKey() === 'bun') {
|
|
71
44
|
return c.text('You are on Bun')
|
|
72
45
|
}
|
|
73
|
-
|
|
46
|
+
return c.text(`You are on ${getRuntimeKey()}`)
|
|
74
47
|
})
|
|
75
48
|
```
|
|
76
49
|
|
|
77
|
-
### Available
|
|
50
|
+
### Available runtime keys
|
|
78
51
|
|
|
79
|
-
|
|
52
|
+
The keys follow [WinterCG's Runtime Keys proposal](https://runtime-keys.proposal.wintercg.org/):
|
|
80
53
|
|
|
81
|
-
- `
|
|
82
|
-
- `
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
85
|
-
- `edge-light` - Vercel Edge Functions
|
|
86
|
-
- `fastly` - Fastly Compute
|
|
87
|
-
- `other` - Other unknown runtimes keys
|
|
54
|
+
- `node` - Node.js, the verified runtime
|
|
55
|
+
- `bun` - Bun, experimental
|
|
56
|
+
- `deno`, `workerd`, `edge-light`, `fastly` - keys the helper still recognizes for a host that embeds an app in that runtime; 1.0 ships no adapter for them (see [Other runtimes](../getting-started/other-runtimes.md))
|
|
57
|
+
- `other` - a runtime the helper does not recognize
|
|
@@ -47,7 +47,7 @@ app.get('/streamText', (c) => {
|
|
|
47
47
|
|
|
48
48
|
**Warning.**
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
A proxy or a host that compresses the response body can hold a stream back until it ends. If a stream arrives only when it is complete, send `Content-Encoding: Identity` for that route.
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
53
|
app.get('/streamText', (c) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Basic Auth Middleware
|
|
2
2
|
|
|
3
3
|
This middleware can apply Basic authentication to a specified path.
|
|
4
|
-
Implementing Basic authentication
|
|
4
|
+
Implementing Basic authentication by hand is more complicated than it seems, but with this middleware, it's a breeze.
|
|
5
5
|
|
|
6
6
|
For more information about how the Basic auth scheme works under the hood, see the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme).
|
|
7
7
|
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Cache middleware uses the Web Standards' [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Cloudflare Workers respects the `Cache-Control` header and return cached responses. For details, refer to [Cache on Cloudflare Docs](https://developers.cloudflare.com/workers/runtime-apis/cache/). Deno does not respect headers, so if you need to update the cache, you will need to implement your own mechanism.
|
|
8
|
-
|
|
9
|
-
See [Usage](#usage) below for instructions on each platform.
|
|
5
|
+
It needs a Cache API on the host: the middleware reads `globalThis.caches`. Node.js, the verified runtime, does not provide one, so on Node the middleware is not enabled and says so in one line; a host that embeds an app in a runtime with a Cache API (or provides `globalThis.caches` itself) gets it. `Cache-Control` handling and the QUERY-request cache below are implemented by the middleware, not by the platform.
|
|
10
6
|
|
|
11
7
|
## Import
|
|
12
8
|
|
|
@@ -17,28 +13,12 @@ import { cache } from 'acrylblends/cache'
|
|
|
17
13
|
|
|
18
14
|
## Usage
|
|
19
15
|
|
|
20
|
-
**Cloudflare Workers**
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
app.get(
|
|
24
|
-
'*',
|
|
25
|
-
cache({
|
|
26
|
-
cacheName: 'my-app',
|
|
27
|
-
cacheControl: 'max-age=3600',
|
|
28
|
-
})
|
|
29
|
-
)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Deno**
|
|
33
|
-
|
|
34
16
|
```ts
|
|
35
|
-
// Must use `wait: true` for the Deno runtime
|
|
36
17
|
app.get(
|
|
37
18
|
'*',
|
|
38
19
|
cache({
|
|
39
20
|
cacheName: 'my-app',
|
|
40
21
|
cacheControl: 'max-age=3600',
|
|
41
|
-
wait: true,
|
|
42
22
|
})
|
|
43
23
|
)
|
|
44
24
|
```
|
|
@@ -59,7 +39,7 @@ The name of the cache. Can be used to store multiple caches with different ident
|
|
|
59
39
|
|
|
60
40
|
### *(optional)* wait: `boolean`
|
|
61
41
|
|
|
62
|
-
A boolean indicating if Acryl should wait for the Promise of the `cache.put` function to resolve before continuing with the request.
|
|
42
|
+
A boolean indicating if Acryl should wait for the Promise of the `cache.put` function to resolve before continuing with the request. The default is `false`. A runtime whose Cache API only completes a write after the response has finished needs `true`, or the write is dropped.
|
|
63
43
|
|
|
64
44
|
### *(optional)* cacheControl: `string`
|
|
65
45
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This middleware compresses the response body, according to `Accept-Encoding` request header.
|
|
4
4
|
|
|
5
|
-
> **Note.**
|
|
5
|
+
> **Note.** Node.js and Bun do not compress a response on their own, so this middleware is what compresses it. A host that already compresses at its edge does not need it.
|
|
6
6
|
|
|
7
7
|
## Import
|
|
8
8
|
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The Context Storage Middleware stores the Acryl `Context` in the `AsyncLocalStorage`, to make it globally accessible.
|
|
4
4
|
|
|
5
|
-
> **Note.** This middleware uses `AsyncLocalStorage`.
|
|
6
|
-
>
|
|
7
|
-
> **Cloudflare Workers**: To enable `AsyncLocalStorage`, add the [`nodejs_compat` or `nodejs_als` flag](https://developers.cloudflare.com/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) to your `wrangler.toml` file.
|
|
5
|
+
> **Note.** This middleware uses `AsyncLocalStorage`. Node.js, the verified runtime, provides it; a host that embeds an app in a runtime without it cannot use this middleware.
|
|
8
6
|
|
|
9
7
|
## Import
|
|
10
8
|
|
|
@@ -47,22 +45,20 @@ app.get('/', (c) => {
|
|
|
47
45
|
})
|
|
48
46
|
```
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
Anything a handler puts on the context can be read further down the call stack, without passing `c` through every function:
|
|
51
49
|
|
|
52
50
|
```ts
|
|
53
|
-
|
|
54
|
-
Bindings: {
|
|
55
|
-
KV: KVNamespace
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const app = new Acryl<Env>()
|
|
51
|
+
const app = new Acryl()
|
|
60
52
|
|
|
61
53
|
app.use(contextStorage())
|
|
54
|
+
app.use(async (c, next) => {
|
|
55
|
+
c.set('user', 'ada')
|
|
56
|
+
await next()
|
|
57
|
+
})
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
app.get('/', (c) => c.text(`Hello ${getUser()}`))
|
|
60
|
+
|
|
61
|
+
const getUser = () => getContext().get('user')
|
|
66
62
|
```
|
|
67
63
|
|
|
68
64
|
## tryGetContext
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# CORS Middleware
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
For them we have to implement CORS, let's do this with middleware as well.
|
|
3
|
+
A web API is often called from a page served by another origin. For that we have to implement CORS, and this middleware does it.
|
|
5
4
|
|
|
6
5
|
## Import
|
|
7
6
|
|
|
@@ -224,7 +224,7 @@ app.get('/page/info', (c) => {
|
|
|
224
224
|
|
|
225
225
|
**Warning.**
|
|
226
226
|
|
|
227
|
-
You can't use `useRequestContext()` with the
|
|
227
|
+
You can't use `useRequestContext()` with a JSX option that precompiles the templates ahead of the build. Use `react-jsx`:
|
|
228
228
|
|
|
229
229
|
```json
|
|
230
230
|
"compilerOptions": {
|
|
@@ -32,7 +32,7 @@ By using the Logger Middleware, you can easily monitor the flow of requests and
|
|
|
32
32
|
You can also extend the middleware further by providing your own `PrintFunc` function for tailored logging behavior.
|
|
33
33
|
|
|
34
34
|
> **Tip.**
|
|
35
|
-
> To disable _status code coloring_, you can set a `NO_COLOR` environment variable. This is a common way to disable ANSI color escape codes in logging libraries, and is described at <https://no-color.org/>.
|
|
35
|
+
> To disable _status code coloring_, you can set a `NO_COLOR` environment variable. This is a common way to disable ANSI color escape codes in logging libraries, and is described at <https://no-color.org/>.
|
|
36
36
|
|
|
37
37
|
## PrintFunc
|
|
38
38
|
|
|
@@ -29,7 +29,7 @@ app.all('/itty-router/*', mount(ittyRouter.handle))
|
|
|
29
29
|
|
|
30
30
|
By default, the mounted application receives a new `Request` with the matched path prefix removed from its URL. A request to `/itty-router/hello` reaches itty-router as `/hello`.
|
|
31
31
|
|
|
32
|
-
The mounted application is called as `handler(request, c.env, c.executionCtx)`, the
|
|
32
|
+
The mounted application is called as `handler(request, c.env, c.executionCtx)`, the three arguments a `fetch`-style handler takes. Use `optionHandler` to pass different arguments.
|
|
33
33
|
|
|
34
34
|
## Options
|
|
35
35
|
|
|
@@ -72,23 +72,16 @@ The header name used for the request ID. The default is `X-Request-Id`.
|
|
|
72
72
|
|
|
73
73
|
The request ID generation function. By default, it uses `crypto.randomUUID()`.
|
|
74
74
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
Without
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- Cloudflare
|
|
89
|
-
- [Cloudflare Ray ID
|
|
90
|
-
](https://developers.cloudflare.com/fundamentals/reference/cloudflare-ray-id/)
|
|
91
|
-
- Deno
|
|
92
|
-
- [Request ID on the Deno Blog](https://deno.com/blog/zero-config-debugging-deno-opentelemetry#:~:text=s%20automatically%20have-,unique%20request%20IDs,-associated%20with%20them)
|
|
93
|
-
- Fastly
|
|
94
|
-
- [Fastly documentation: req.xid](https://www.fastly.com/documentation/reference/vcl/variables/client-request/req-xid/)
|
|
75
|
+
## An ID that came from earlier in the chain
|
|
76
|
+
|
|
77
|
+
A proxy, a load balancer or the host that starts the app may already put a request id on the request.
|
|
78
|
+
Without configuration this middleware is unaware of it and generates a new one, which makes the two logs hard to line up.
|
|
79
|
+
Use the `generator` function to read the incoming one and fall back to a fresh id:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
app.use(
|
|
83
|
+
requestId({
|
|
84
|
+
generator: (c) => c.req.header('X-Request-Id') ?? crypto.randomUUID(),
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
```
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
The [Server-Timing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing) Middleware provides
|
|
4
4
|
performance metrics in the response headers.
|
|
5
5
|
|
|
6
|
-
> **Note.**
|
|
7
|
-
> since [timers only show the time of last I/O](https://developers.cloudflare.com/workers/learning/security-model/#step-1-disallow-timers-and-multi-threading).
|
|
6
|
+
> **Note.** The metrics come from timers around the handler, so they measure wall-clock time on the host the app runs on.
|
|
8
7
|
|
|
9
8
|
## Import
|
|
10
9
|
|
package/package.json
CHANGED