acrylblends 0.11.0 → 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.
Files changed (93) hide show
  1. package/README.md +131 -53
  2. package/dist/index.d.ts +29 -2
  3. package/dist/index.js +450 -69
  4. package/dist/index.js.map +1 -1
  5. package/docs/framework/README.md +5 -3
  6. package/docs/framework/agents/index.md +1 -1
  7. package/docs/framework/api/acryl.md +5 -17
  8. package/docs/framework/api/context.md +12 -61
  9. package/docs/framework/api/presets.md +1 -1
  10. package/docs/framework/api/request.md +3 -3
  11. package/docs/framework/api/summary.md +1 -1
  12. package/docs/framework/concepts/web-standard.md +6 -16
  13. package/docs/framework/cordis/acryl-in-a-cordis-app.md +13 -8
  14. package/docs/framework/cordis/migration-from-0-4.md +1 -1
  15. package/docs/framework/cordis/migration-to-1-0.md +45 -0
  16. package/docs/framework/cordis/node-server.md +2 -2
  17. package/docs/framework/getting-started/basic.md +15 -10
  18. package/docs/framework/getting-started/desktop.md +1 -1
  19. package/docs/framework/getting-started/other-runtimes.md +10 -14
  20. package/docs/framework/guides/create-acrylblends.md +4 -2
  21. package/docs/framework/guides/data.md +1 -1
  22. package/docs/framework/guides/faq.md +1 -1
  23. package/docs/framework/guides/jsx-dom.md +1 -1
  24. package/docs/framework/guides/production.md +43 -0
  25. package/docs/framework/guides/rpc.md +3 -11
  26. package/docs/framework/helpers/adapter.md +13 -43
  27. package/docs/framework/helpers/conninfo.md +1 -36
  28. package/docs/framework/helpers/streaming.md +1 -1
  29. package/docs/framework/helpers/websocket.md +4 -11
  30. package/docs/framework/middleware/builtin/basic-auth.md +1 -1
  31. package/docs/framework/middleware/builtin/cache.md +2 -22
  32. package/docs/framework/middleware/builtin/compress.md +1 -1
  33. package/docs/framework/middleware/builtin/context-storage.md +10 -14
  34. package/docs/framework/middleware/builtin/cors.md +1 -2
  35. package/docs/framework/middleware/builtin/ip-restriction.md +1 -1
  36. package/docs/framework/middleware/builtin/jsx-renderer.md +1 -1
  37. package/docs/framework/middleware/builtin/logger.md +1 -1
  38. package/docs/framework/middleware/builtin/loopback-only.md +1 -1
  39. package/docs/framework/middleware/builtin/mount.md +1 -1
  40. package/docs/framework/middleware/builtin/request-id.md +13 -20
  41. package/docs/framework/middleware/builtin/timing.md +1 -2
  42. package/lib/cordis/door.d.ts +5 -0
  43. package/lib/cordis/door.js +8 -4
  44. package/lib/cordis/inspect.js +1 -1
  45. package/lib/cordis/router-http.js +1 -1
  46. package/lib/cordis/router.d.ts +1 -2
  47. package/lib/cordis/router.js +2 -3
  48. package/lib/cordis/state.js +1 -1
  49. package/lib/door/host.js +1 -1
  50. package/lib/drivers.d.ts +66 -0
  51. package/lib/drivers.js +16 -0
  52. package/lib/index.d.ts +1 -2
  53. package/lib/index.js +1 -2
  54. package/lib/{snapshot-text-yaWlezbd.js → snapshot-text-DmE0YfeZ.js} +2 -2
  55. package/lib/{src-DNzw0Qsb.js → src-2Z4mJg0x.js} +0 -1
  56. package/package.json +5 -121
  57. package/plugins/acryl-door/src/index.ts +8 -0
  58. package/plugins/acryl-router/src/index.ts +0 -1
  59. package/plugins/agent-panel/drivers.ts +1 -16
  60. package/plugins/agent-panel/index.ts +1 -1
  61. package/plugins/agent-panel/registry.ts +16 -0
  62. package/plugins/driver-acp/README.md +53 -0
  63. package/plugins/driver-acp/chat.ts +258 -0
  64. package/plugins/driver-acp/connection.ts +239 -0
  65. package/plugins/driver-acp/index.ts +65 -0
  66. package/plugins/driver-acp/plugin.json +12 -0
  67. package/plugins/driver-acp/provenance.json +20 -0
  68. package/plugins/driver-pty/index.ts +1 -0
  69. package/shared/library-history.json +464 -0
  70. package/docs/framework/getting-started/aws-lambda.md +0 -115
  71. package/docs/framework/getting-started/cloudflare-pages.md +0 -53
  72. package/docs/framework/getting-started/cloudflare-workers.md +0 -67
  73. package/docs/framework/getting-started/lambda-edge.md +0 -41
  74. package/docs/framework/getting-started/netlify.md +0 -25
  75. package/docs/framework/getting-started/service-worker.md +0 -33
  76. package/docs/framework/getting-started/vercel.md +0 -28
  77. package/lib/aws-lambda.d.ts +0 -324
  78. package/lib/aws-lambda.js +0 -357
  79. package/lib/cloudflare-pages.d.ts +0 -58
  80. package/lib/cloudflare-pages.js +0 -78
  81. package/lib/cloudflare-workers.d.ts +0 -21
  82. package/lib/cloudflare-workers.js +0 -80
  83. package/lib/deprecated-BP4fPt38.js +0 -10
  84. package/lib/deprecated-CTy4N7ng.d.ts +0 -12
  85. package/lib/lambda-edge.d.ts +0 -92
  86. package/lib/lambda-edge.js +0 -105
  87. package/lib/netlify.d.ts +0 -29
  88. package/lib/netlify.js +0 -30
  89. package/lib/service-worker.d.ts +0 -48
  90. package/lib/service-worker.js +0 -38
  91. package/lib/vercel.d.ts +0 -10
  92. package/lib/vercel.js +0 -9
  93. package/plugins/acryl-router/src/deprecated.ts +0 -15
@@ -25,7 +25,7 @@ with SQLite, small state on the `kv` port with one JSON file, the Node server, t
25
25
 
26
26
  ## What an agent can do inside a running app
27
27
 
28
- The `acryl-app` template (a Cordis app) also lets an agent change the app while it runs:
28
+ The `agent` template (a Cordis app; `acryl-app` is its old name and still works) also lets an agent change the app while it runs:
29
29
 
30
30
  1. The agent writes a plugin into `plugins/<name>/index.ts`. The plugin may import `acrylblends` and the other subpaths by name.
31
31
  2. The app installs it as a child plugin, runs its `check(ctx)` with the plugin's own context, and keeps the previous version if the new one fails to start or to pass its check.
@@ -1,4 +1,4 @@
1
- # App - Acryl
1
+ # Acryl
2
2
 
3
3
  `Acryl` is the primary object.
4
4
  It will be imported first and used until the end.
@@ -9,7 +9,7 @@ import { Acryl } from 'acrylblends'
9
9
  const app = new Acryl()
10
10
  //...
11
11
 
12
- export default app // for Cloudflare Workers or Bun
12
+ export default app // a Bun server, or a value a Cordis plugin mounts; see Node.js for the Node entry
13
13
  ```
14
14
 
15
15
  ## Methods
@@ -64,25 +64,13 @@ app.onError((err, c) => {
64
64
 
65
65
  ## fire()
66
66
 
67
- > **Warning.** **`app.fire()` is deprecated**. Use `fire()` from `acrylblends/service-worker` instead. See the Service Worker documentation for details.
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
- For Cloudflare Workers, you can use the following:
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 types of Cloudflare Workers Bindings and variables used in `c.set`/`c.get`.
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
- You can access Cloudflare Workers' specific [ExecutionContext](https://developers.cloudflare.com/workers/runtime-apis/context/).
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
- Bindings: {
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
- // Type definition to make type inference
407
- type Bindings = {
408
- MY_KV: KVNamespace
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
- In Cloudflare Workers Environment variables, secrets, KV namespaces, D1 database, R2 bucket etc. that are bound to a worker are known as bindings.
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
- // Type definition to make type inference
430
- type Bindings = {
431
- MY_KV: KVNamespace
432
- }
386
+ const app = new Acryl<{ Bindings: { REGION: string } }>()
433
387
 
434
- const app = new Acryl<{ Bindings: Bindings }>()
435
-
436
- // Environment object for Cloudflare Workers
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 servers built with **Deno**, **Bun**, or **Node.js**. It is also suitable for **Fastly Compute**, as route registration occurs during the app build phase on that platform. For environments such as **Cloudflare Workers**, **Deno Deploy**, where v8 isolates are utilized, this preset is suitable as well because the isolations persist for a certain amount of time after booting. |
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
- // For Cloudflare Workers
368
- app.post('/', async (c) => {
369
- const metadata = c.req.raw.cf?.hostMetadata?
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
  ```
@@ -10,7 +10,7 @@ import { Acryl } from 'acrylblends'
10
10
  import { trimTrailingSlash } from 'acrylblends/trailing-slash'
11
11
  import { schemaValidator, type StandardSchema } from 'acrylblends/schema-validator'
12
12
  import { streamSSE } from 'acrylblends/streaming'
13
- import type {} from 'acryl-state'
13
+ import type {} from 'acrylblends/cordis/state'
14
14
 
15
15
  export const name = 'orders' // the package name and the Loader row id
16
16
  export const inject = ['router', 'state'] // the plugin waits (PENDING) until both exist
@@ -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
- 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.
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 {
@@ -15,23 +15,13 @@ export default {
15
15
  }
16
16
  ```
17
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:
18
+ Acryl uses only Web Standards, so the same application code is expected to run wherever a `fetch`-style handler can run: a platform that can call `app.fetch(request)` can serve an Acryl app. In 1.0 the framework builds, exports and gates two runtimes:
20
19
 
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])
20
+ - **Node.js** (`acrylblends/node-server`): the verified runtime, on Node 22 and 24, on Linux, macOS and Windows.
21
+ - **Bun** (`acrylblends/bun`): experimental, with one smoke gate.
29
22
 
30
- It also works on Netlify and other platforms.
23
+ The adapters the upstream project had for Cloudflare Workers and Pages, Deno, Fastly Compute, AWS Lambda, Lambda@Edge, Vercel, Netlify and service workers are not part of the 1.0 package: no gate of ours deploys to them. Their ported sources stay in the repository and may return as separate plugins, each with its own gate. See [Other runtimes](../getting-started/other-runtimes.md).
31
24
  The same code runs on all platforms.
32
25
 
33
26
  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
27
  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
@@ -126,7 +126,7 @@ smallest one to copy is `examples/acryl-framework-starter`.
126
126
 
127
127
  ```ts
128
128
  import { Context } from '@deepseek-ai/cordis'
129
- import * as router from 'acryl-router'
129
+ import * as router from 'acrylblends/cordis/router'
130
130
  import * as todo from '../src/index.ts'
131
131
 
132
132
  const ctx = new Context()
@@ -139,13 +139,18 @@ expect(await response.json()).toEqual({ id: '42' })
139
139
 
140
140
  ## Package map
141
141
 
142
- | Package | Responsibility |
143
- | --- | --- |
144
- | `acryl-router` | the application API and the Cordis `router` service |
145
- | `acryl-router-http` | serving router prefixes on the ACRYL Host's web server |
146
- | `acryl-node-server` | serving the whole router from a standalone Node.js process |
147
- | `acryl-state` | shared application state across surfaces |
148
- | `acryl-inspect` | runtime inspection of mounts, routes and plugins |
142
+ The framework plugins are published as subpaths of the `acrylblends` package (the Cordis forms); an app made by `acrylblends init` carries their source under `plugins/` instead, with the same names.
143
+
144
+ | Plugin | Published as | Responsibility |
145
+ | --- | --- | --- |
146
+ | `acryl-router` | `acrylblends/cordis/router` | the application API and the Cordis `router` service |
147
+ | `acryl-router-http` | `acrylblends/cordis/router-http` | serving router prefixes on the ACRYL Host's web server, with WebSocket upgrades owned by the mounting plugin |
148
+ | `acryl-node-server` | `acrylblends/cordis/node-server` | serving the whole router from a standalone Node.js process |
149
+ | `acryl-state` | `acrylblends/cordis/state` | shared application state across surfaces |
150
+ | `acryl-inspect` | `acrylblends/cordis/inspect` | runtime inspection of mounts, routes and plugins |
151
+ | `acryl-db`, `acryl-db-sqlite` | `acrylblends/cordis/db`, `acrylblends/cordis/db-sqlite` | the `db` port and its SQLite provider |
152
+ | `acryl-kv`, `acryl-kv-json`, `acryl-kv-db` | `acrylblends/cordis/kv`, `.../kv-json`, `.../kv-db` | the `kv` port and its providers |
153
+ | `acryl-door` | `acrylblends/cordis/door` (experimental) | the door: an agent looks at the running app and acts on it, in development only |
149
154
 
150
155
  ## Next
151
156
 
@@ -6,7 +6,7 @@ unchanged.
6
6
 
7
7
  | 0.4 | 0.5 |
8
8
  | --- | --- |
9
- | `import { App } from 'acryl-router'`, `new App()` | `import { Acryl } from 'acryl-router'`, `new Acryl()` |
9
+ | `import { App } from 'acryl-router'`, `new App()` | `import { Acryl } from 'acrylblends/cordis/router'`, `new Acryl()` (`App` and `AppRequest` are removed in 1.0) |
10
10
  | `AppRequest` | `AcrylRequest` |
11
11
 
12
12
  `App` and `AppRequest` still work in 0.5, marked deprecated, and are removed in the next release. Code outside Cordis imports the same
@@ -0,0 +1,45 @@
1
+ # Migration to 1.0
2
+
3
+ 1.0 fixes the public surface of `acrylblends` and gives an app a way to move its copies of the framework plugins forward. This page is the one path from any 0.x app that was made by 0.9.1 or later; an older app is best made again with `npm create acrylblends@latest` and its own code moved over (its routes and plugins are plain `Acryl` code and Cordis plugins).
4
+
5
+ ## 1. Upgrade the app's copies of the framework plugins
6
+
7
+ An app made by `acrylblends init` carries copies of the framework plugins (`acryl-router`, `acryl-node-server`, `acryl-door`, `extensions`, ...) in `plugins/`. `acrylblends upgrade` brings them to the version of the installed package:
8
+
9
+ ```sh
10
+ npm install acrylblends@latest # in an app that depends on the package; the agent template does not
11
+ npx acrylblends upgrade --dry-run # what would change, and the diff of every file you edited
12
+ npx acrylblends upgrade # do it
13
+ npm install && npm run check && npm test
14
+ ```
15
+
16
+ - A file nobody edited is replaced. A file the app edited stops the upgrade: nothing is changed, the diff of what would be lost is shown, and the command exits 1. Move your change out of the plugin (a plugin of your own in `plugins/<name>/` is never touched), or run `npx acrylblends upgrade --force`, which saves the edited versions in `.acrylblends/upgrade-backup/<time>/` before replacing them.
17
+ - `--diff` prints the diff of every changed file, not only the edited ones. `acrylblends upgrade acryl-router extensions` upgrades only those plugins.
18
+ - A file of your own inside a library plugin folder (a note, a test) is kept and reported. A plugin that a newer version needs and the app lacks is added.
19
+ - How it tells an edit from an untouched file: since 1.0 `acrylblends init` and `add` record the hash of every copied file in `plugins/library.lock.json`; for an app made earlier, the package knows the hashes of the released versions (0.9.1, 0.9.2, 0.10.0, 0.11.0). A file that matches neither counts as edited, so a doubt always stops the upgrade instead of overwriting.
20
+ - `package.json` gets the new `acrylblends` range when it has one, and the dependencies the new plugin versions need (existing ones are kept; run `npm install`).
21
+
22
+ ## 2. What changed in the public surface
23
+
24
+ | Removed or changed | Do this |
25
+ | --- | --- |
26
+ | `App` and `AppRequest` (the 0.4 names, deprecated since 0.5) are removed | `Acryl` and `AcrylRequest`: `import { Acryl } from 'acrylblends'` |
27
+ | `acrylblends/utils/*` and `acrylblends/acryl-base` are internal, not exported | Use the public helpers (`acrylblends/cookie`, `acrylblends/http-exception`, ...); the utilities are not promised |
28
+ | The adapters for AWS Lambda, Lambda@Edge, Cloudflare Workers and Pages, Vercel, Netlify and Service Workers are not exported | An app is plain `fetch` code (`app.fetch(request)`), so a platform that calls a `fetch` handler can call it. Their ported sources stay in the repository; they may return as separate plugins, each with a gate. See [Other runtimes](../getting-started/other-runtimes.md) |
29
+ | `acrylblends/bun` is experimental | The one Bun gate runs it on Bun; no promise beyond that |
30
+ | The door (`door/*`, `cordis/door`), the agent panel and the PTY driver are experimental and development-only | They start nothing with `NODE_ENV=production` (the door plugin now checks it itself; `allowInProduction: true` for a host that means it); see [what runs in production](../guides/production.md) |
31
+ | `HTTPException` is `acrylblends/http-exception`, not the root | `import { HTTPException } from 'acrylblends/http-exception'` |
32
+ | `acrylblends/cordis/*` are the published forms of the framework plugins | In code that uses the package (not an app's own copies), import `acrylblends/cordis/router` where an old guide wrote `acryl-router` |
33
+ | One template list: `blank`, `nodejs`, `notes`, `chat`, `todo`, `agent` in both `create-acrylblends` and `acrylblends init` | `acryl-app` still means `agent` |
34
+
35
+ Node.js 22.19 or newer. Bun is optional and experimental; Deno is not supported.
36
+
37
+ ## 3. Check
38
+
39
+ ```sh
40
+ npx acrylblends check-deps
41
+ npm run check
42
+ npm test
43
+ ```
44
+
45
+ An app on the vendored plugins keeps working without the package's own newer subpaths; moving to the package's `acrylblends/cordis/*` forms in place of the copies is a separate, optional step.
@@ -9,8 +9,8 @@ The server is a Cordis plugin that provides the `nodeServer` service:
9
9
 
10
10
  ```ts
11
11
  import { Context } from '@deepseek-ai/cordis'
12
- import * as router from 'acryl-router'
13
- import * as nodeServer from 'acrylblends/node-server'
12
+ import * as router from 'acrylblends/cordis/router'
13
+ import * as nodeServer from 'acrylblends/cordis/node-server'
14
14
  import * as todo from './todo.ts'
15
15
 
16
16
  const root = new Context()
@@ -1,11 +1,11 @@
1
1
  # Getting Started
2
2
 
3
- Using Acryl is easy. You set up the project, write code, develop with a local server, and run it. The same application code works on every verified runtime, with a different
4
- entry point for each. This page shows the basic usage.
3
+ Using Acryl is easy. You set up the project, write code, develop with a local server, and run it. The same application code runs on Node.js, the verified runtime, and on Bun,
4
+ which is experimental; only the entry point differs. This page shows the basic usage.
5
5
 
6
6
  ## Starter
7
7
 
8
- Starter templates are available for each runtime. Use the "create-acrylblends" command.
8
+ A starter is a Cordis app that arrives already wired: `blend.yaml`, its plugins in `plugins/`, and a test. Use the "create-acrylblends" command.
9
9
 
10
10
  **npm**
11
11
 
@@ -34,12 +34,17 @@ bun create acrylblends@latest my-app
34
34
  You are asked which template you want:
35
35
 
36
36
  ```
37
- ? Which template do you want to use?
38
- ❯ nodejs
39
- acryl-app
37
+ Templates:
38
+ blank the router, a server and one route: the smallest app
39
+ nodejs a web app with a page and a client component
40
+ notes a multi-user notes app on the db port (sign-up, log-in, sessions)
41
+ chat a chat app that streams an answer over server-sent events
42
+ todo a to-do list on the router and state
43
+ agent the blank app with an offline agent that builds plugins into the running app
44
+ Which template do you want to use? [nodejs]:
40
45
  ```
41
46
 
42
- Every template is a Cordis app: `blend.yaml` lists its plugins and every plugin is a folder in `plugins/`. `nodejs` is a web app with a page and a client component, `notes` and `chat` add a database, and `acryl-app` adds an agent that can extend it while it
47
+ Every template is a Cordis app: `blend.yaml` lists its plugins and every plugin is a folder in `plugins/`. `blank` is the smallest app, `nodejs` is a web app with a page and a client component, `notes` and `chat` add a database, `todo` is a to-do list, and `agent` adds an agent that can extend the app while it
43
48
  runs (see [Acryl in a Cordis app](../cordis/acryl-in-a-cordis-app.md)). Pass `--template` to skip the question, see [create-acrylblends](../guides/create-acrylblends.md).
44
49
 
45
50
  The template is created in `my-app`: go to it, install the dependencies and start the local server.
@@ -52,9 +57,9 @@ npm run dev
52
57
 
53
58
  ## Hello World
54
59
 
55
- Write your first application in `src/index.ts`. The example below is a starter Acryl application.
60
+ In a generated app the routes live in `src/routes.ts`, which builds an `Acryl` app and mounts it with `ctx.router.route`; `src/main.ts` picks where the app lives and starts it. The app on its own is plain:
56
61
 
57
- The `import` and the final `export default` are the same on every runtime; what differs is how the runtime starts the app (see [Node.js](nodejs.md) and [Bun](bun.md)).
62
+ The `import` and the final `export default` are the same on Node.js and on Bun; what differs is how the runtime starts the app (see [Node.js](nodejs.md) and [Bun](bun.md)).
58
63
 
59
64
  ```ts twoslash
60
65
  import { Acryl } from 'acrylblends'
@@ -131,7 +136,7 @@ app.get('/', (c) => {
131
136
  })
132
137
  ```
133
138
 
134
- [JSX](../guides/jsx.md) is not yet available.
139
+ JSX works too: `acrylblends/jsx` renders on the server and in the browser, and the `nodejs` template ships a `.tsx` page. See [JSX](../guides/jsx.md).
135
140
 
136
141
  ## Return raw Response
137
142
 
@@ -1,7 +1,7 @@
1
1
  # Desktop
2
2
 
3
3
  An Acryl app also runs as a desktop app: an Electron window opens on the app's own server. It is the same app and the same code as `npm run dev`; nothing in the app changes. This page covers a plain Acryl app (the `nodejs`
4
- template) and a Blend app (the `acryl-app` template or `acrylblends init`). It runs from source today: packaging a distributable app (dmg, exe, deb) is not part of it yet.
4
+ template) and a Blend app (the `agent` template or any other `acrylblends init` template). It runs from source today: packaging a distributable app (dmg, exe, deb) is not part of it yet.
5
5
 
6
6
  ## Add it
7
7
 
@@ -2,24 +2,20 @@
2
2
 
3
3
  Acryl is built on Web Standards, so the same application code is expected to run wherever a `fetch`-style handler can run. Node.js ([guide](nodejs.md)) is the runtime the framework is verified on. Bun is experimental ([guide](bun.md)).
4
4
 
5
- ## Hosting platforms with an adapter
5
+ ## Runtimes with an adapter
6
6
 
7
- These platforms have an adapter in acrylblends, ported from the upstream adapters with their tests. Each is called with the platform's own events in the framework's release check; the ACRYL team has not deployed to these platforms, so try a first deploy on a test project.
7
+ | Runtime | Guide | Import | Status |
8
+ | --- | --- | --- | --- |
9
+ | Node.js | [Node.js](nodejs.md) | `acrylblends/node-server` | Primary. Verified on Node 22 and 24, on Linux, macOS and Windows. |
10
+ | Bun | [Bun](bun.md) | `acrylblends/bun` | Experimental. One smoke gate: the Bun adapter is run on Bun (a static file, the client address, a WebSocket echo). |
8
11
 
9
- | Platform | Guide | Import |
10
- | --- | --- | --- |
11
- | AWS Lambda (API Gateway, ALB, Function URLs, VPC Lattice) | [AWS Lambda](aws-lambda.md) | `acrylblends/aws-lambda` |
12
- | Lambda@Edge (CloudFront) | [Lambda@Edge](lambda-edge.md) | `acrylblends/lambda-edge` |
13
- | Cloudflare Workers | [Cloudflare Workers](cloudflare-workers.md) | `acrylblends/cloudflare-workers` |
14
- | Cloudflare Pages | [Cloudflare Pages](cloudflare-pages.md) | `acrylblends/cloudflare-pages` |
15
- | Vercel | [Vercel](vercel.md) | `acrylblends/vercel` |
16
- | Netlify | [Netlify](netlify.md) | `acrylblends/netlify` |
17
- | Service Worker | [Service Worker](service-worker.md) | `acrylblends/service-worker` |
18
- | Bun | [Bun](bun.md) | `acrylblends/bun` |
12
+ ## Removed in 1.0
13
+
14
+ The upstream also ships adapters for AWS Lambda, Lambda@Edge, Cloudflare Workers and Pages, Vercel, Netlify and Service Workers. They were part of 0.x, ported with their tests, but no gate of ours deploys to any of them, so a 1.0 package does not promise them: they are not built or exported. Their ported sources stay in `plugins/acryl-router/src/core/adapter/` in the repository and may come back as separate plugins, each with its own gate. An app written with `acrylblends` is still plain `fetch` code (`app.fetch(request)`), so a platform that calls a `fetch` handler can call it directly.
19
15
 
20
16
  ## Not yet verified
21
17
 
22
- The following runtimes and platforms have no adapter and no getting-started page yet:
18
+ The following runtimes and platforms have no adapter and no getting-started page:
23
19
 
24
20
  - Deno, and Deno Deploy
25
21
  - Fastly Compute
@@ -31,4 +27,4 @@ The following runtimes and platforms have no adapter and no getting-started page
31
27
  - CloudBase
32
28
  - WebAssembly (WASI)
33
29
 
34
- A page is added here, with a getting-started guide, when a platform has an adapter and a test that calls it.
30
+ A page is added here, with a getting-started guide, when a runtime has an adapter and a gate that runs it.
@@ -7,10 +7,12 @@
7
7
 
8
8
  | Template | What you get |
9
9
  | --- | --- |
10
+ | `blank` | the smallest app: the router, a server and one route, with a test |
10
11
  | `nodejs` | a web app: routes, a server-rendered JSX page and a client component, mounted by the app's own plugin; run with Node.js 22.19 or newer |
11
12
  | `notes` | a full-stack starter: accounts (scrypt, cookie session), notes in SQLite through the [`db` port](data.md) with numbered migrations, validated forms, search, a JSON API with a typed test client, csrf, a test for every route, a client-component counter |
12
13
  | `chat` | an AI chat starter: the answer streams token by token from an OpenAI-compatible API (DeepSeek by default, key only from `DEEPSEEK_API_KEY`), a client component with a Stop button that also stops the request to the model, history in SQLite through the [`db` port](data.md), tests with a mock model |
13
- | `acryl-app` | a Cordis app built from plugins, with an agent that can extend it while it runs (the same app `npx acrylblends init --template agent` creates) |
14
+ | `todo` | a to-do list on the router and state: CRUD routes, a page, tests |
15
+ | `agent` | the blank app with an offline agent that builds plugins into the running app, through the door (`acryl-app` is the old name for it and still works) |
14
16
 
15
17
  Every template is a Cordis app: `blend.yaml` lists the plugins that are mounted, every plugin is a folder in `plugins/`, and the app is started by `src/main.ts`. A bare template name after the directory is the same as `--template`: `npm create acrylblends@latest my-app notes`. `bun` is no longer a template; the framework still runs on Bun (see [Bun](../getting-started/bun.md)), but a new app is a Cordis app on Node.
16
18
 
@@ -51,7 +53,7 @@ bun create acrylblends@latest my-app --template nodejs
51
53
  | :-- | :-- | :-- |
52
54
  | `[directory]` | The folder to create. It must not exist or must be empty. Asked for when it is missing. | `my-app` |
53
55
  | `[source]` | A template from outside the package: `github:owner/repo[#ref]`, `@scope/name[@version]` or a packed `.tgz` file. See [Templates from a repository or a package](#templates-from-a-repository-or-a-package). | `github:acme/starter` |
54
- | `--template <template>` | One of `nodejs`, `notes`, `chat`, `acryl-app`. Skips the template question; a bare name after the directory means the same. | `--template notes` |
56
+ | `--template <template>` | One of `blank`, `nodejs`, `notes`, `chat`, `todo`, `agent` (the same list as `acrylblends init --template`; `acryl-app` is accepted as `agent`). Skips the template question; a bare name after the directory means the same. | `--template notes` |
55
57
  | `--install` | Install the dependencies with the package manager after creating the project. For a template from a source, the install scripts it will run are listed first. | `--install` |
56
58
  | `--pm <packageManager>` | The package manager for `--install`: `npm`, `pnpm`, `yarn` or `bun`. Defaults to the one that started the initializer. | `--pm pnpm` |
57
59
  | `--yes`, `-y` | Ask nothing: use `nodejs` unless `--template` is given, and the directory `acryl-app`. | `-y` |
@@ -289,7 +289,7 @@ applies the `migrations` folder, and provides `db`; a plugin declares `inject: [
289
289
 
290
290
  ```ts
291
291
  import type { Context } from '@deepseek-ai/cordis'
292
- import type {} from 'acryl-db'
292
+ import type {} from 'acrylblends/cordis/db'
293
293
 
294
294
  export const name = 'notes'
295
295
  export const inject = ['db']
@@ -12,7 +12,7 @@ A `json` validation target reads the body only when the request has a JSON `Cont
12
12
 
13
13
  ## Which runtimes are verified?
14
14
 
15
- Node.js. Bun is experimental. The hosting platforms with an adapter (AWS Lambda, Lambda@Edge, Cloudflare Workers and Pages, Vercel, Netlify, Service Worker) and the ones without are listed on [Other runtimes](../getting-started/other-runtimes.md).
15
+ Node.js. Bun is experimental. The other hosting platforms have no adapter in 1.0; they are listed on [Other runtimes](../getting-started/other-runtimes.md).
16
16
 
17
17
  ## Where are the parts marked "not yet available"?
18
18
 
@@ -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`. For Deno, modify the deno.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
  {
@@ -0,0 +1,43 @@
1
+ # What runs in production
2
+
3
+ `acrylblends` has two kinds of parts: the ones an app is built from, which run in production, and the development tools that let an agent (or you) work on the running app, which do not. This page says which is which, and exactly what the safety checks around live plugin install do and do not promise. A test (`plugins/driver-pty/test/production.test.ts`) mounts every development tool in one app and checks the table below.
4
+
5
+ ## The table
6
+
7
+ | Part | In development | With `NODE_ENV=production` |
8
+ | --- | --- | --- |
9
+ | Your routes and plugins, the router, the server, state, the `db` and `kv` ports and their providers | run | run |
10
+ | **The door** (`acryl-door`: `.acryl/bin/acryl`, the page script, the operator routes) | **experimental**: starts when the server is up | starts nothing: no service, no route, no `.acryl/` folder, no script in the page. `ACRYL_DOOR=off` also turns it off. A host that means to run it in production sets `allowInProduction: true` in the plugin's config |
11
+ | **The agent panel** (`agent-panel`) | **experimental**: a script tag on every HTML page and `/api/agent-panel/*` | does nothing (no tag, no routes). `ACRYL_DOOR=off` too |
12
+ | **The PTY driver** (`driver-pty`) | **experimental**: terminal drivers in the panel, `/api/driver-pty/*` | does nothing. `ACRYL_DOOR=off` too |
13
+ | **Live plugin install** (`extensions`, `builder`) | **experimental**: install, update, remove and reload plugins while the app runs | the `nodejs`, `notes` and `chat` templates leave the `extensions` row out; the `agent` template does not (see below) |
14
+ | `acrylblends/bun` | **experimental**: one smoke gate runs it on Bun | the same |
15
+
16
+ Experimental means: it works and is gated, its interface may still change in a minor version, and it is not for the public internet. The door and the panel are loopback and own-page only, with a per-run secret and an audit log; that is a convenience fence for a developer's machine, not an authentication system.
17
+
18
+ ## The `agent` template is a development app
19
+
20
+ The `agent` template mounts an agent behind `POST /ask` that can write plugins into the running app (`builder`) and install them (`extensions`). Only its door is left out in production. Do not deploy it as it is: remove the `agent`, `builder` and `extensions` rows from `blend.yaml` (or start from `blank`, `nodejs`, `notes`, `chat` or `todo`), and if live install is wanted anywhere shared, set `approval: true` so an install waits for `POST /extensions/approve`.
21
+
22
+ ## What live plugin install does and does not promise
23
+
24
+ Installing a plugin (`ctx.extensions.install`, the door's `plugin add`, the builder's `install_plugin`) is a **correctness check, not a security boundary**:
25
+
26
+ - It stages a content-hashed copy, imports it, mounts it, and reports the real state (`active`, `pending` with what it waits for, or `failed` with the error). If an update fails to start, the previous version is mounted again. A ledger line records every change.
27
+ - **The plugin's code runs inside the app's own process with the app's permissions**: it can read the app's files and environment, open sockets and start processes. Nothing in the install check stops that.
28
+ - At start-up the app reinstalls what was installed, and installs a new folder only from a source trusted to `install` (the app's own `plugins/` is). Code that arrived through a `git pull` into an untrusted source is listed, not run.
29
+ - `approval: true` holds an install until someone posts to `/extensions/approve`: it decides *whether* a plugin installs, not what an installed plugin can do.
30
+
31
+ ## What the sandbox does and does not stop
32
+
33
+ `ctx.sandbox` runs code you do not trust, such as a **tool** an agent just wrote, in a separate Node process with the permission model on (`node --permission`):
34
+
35
+ - It **stops**: reading any file outside the tool's own folder; writing files; starting child processes or workers; loading native addons; using the inspector. It gets an empty environment (no keys, no paths). It is killed after a time limit and capped in memory and output size. Calls are independent: nothing survives into the next one.
36
+ - It **does not stop**: network access (Node 24 has no network permission: restrict egress outside the process where that matters), and it does not apply to plugins installed live (those are not run in the sandbox).
37
+
38
+ ## Checklist for a deployment
39
+
40
+ 1. `NODE_ENV=production` (the process manager or the Dockerfile sets it; the templates' Dockerfile does).
41
+ 2. Start from a template that is not `agent`, or remove the agent rows.
42
+ 3. Do not set `allowInProduction` on the door unless the host is a single-user desktop app.
43
+ 4. Keep `ACRYL_DOOR=off` in any environment where an agent should not be able to see the page.
@@ -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: c.env.AUTH.fetch.bind(c.env.AUTH),
592
+ fetch: (input, init) =>
593
+ fetch(input, { ...init, headers: { ...init?.headers, authorization: `Bearer ${token}` } }),
602
594
  })
603
595
  ```
604
596