@abide/abide 0.50.0 → 0.51.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/AGENTS.md +580 -459
- package/CHANGELOG.md +16 -0
- package/README.md +173 -205
- package/package.json +1 -1
- package/src/lib/ui/compile/compileShadow.ts +49 -11
- package/src/lib/ui/compile/lowerScript.ts +18 -0
- package/src/lib/ui/compile/wrapReactionCellSources.ts +85 -0
package/AGENTS.md
CHANGED
|
@@ -1,496 +1,617 @@
|
|
|
1
1
|
# AGENTS.md — abide complete surface map
|
|
2
2
|
|
|
3
|
-
> This file is the exhaustive
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
3
|
+
> This file is the exhaustive map of abide's public surface: every `exports` key
|
|
4
|
+
> grouped by namespace, with its import specifier and a one-line spec, so an
|
|
5
|
+
> agent can grasp the whole API in one read. For the curated three-primitive
|
|
6
|
+
> intro read `README.md`; for the domain glossary read `CONTEXT.md`; for the
|
|
7
|
+
> rationale behind a decision read `docs/adr/`.
|
|
8
|
+
>
|
|
9
|
+
> Ground rule — **no barrels**. Every public name is its own module path; there
|
|
10
|
+
> is no umbrella `index.ts`, so importing one name never drags side-effecting
|
|
11
|
+
> siblings into the bundle. The namespace marks the side a name runs on:
|
|
12
|
+
> `abide/server/*` server-side, `abide/ui/*` client-side, `abide/shared/*`
|
|
13
|
+
> isomorphic (same callable, same behaviour on both sides). The package is
|
|
14
|
+
> `@abide/abide` (Bun ≥ 1.3.0, one direct dependency — TypeScript). Every import
|
|
15
|
+
> specifier below is `@abide/abide<exports-key>`; the file path after it is the
|
|
16
|
+
> source, not an import target.
|
|
13
17
|
|
|
14
18
|
## The premise
|
|
15
19
|
|
|
16
|
-
One
|
|
20
|
+
One declared RPC fans out to five surfaces:
|
|
17
21
|
|
|
18
22
|
```text
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
└─ OpenAPI operation in /openapi.json
|
|
23
|
+
export const getMessages = GET(fn, { schemas })
|
|
24
|
+
│
|
|
25
|
+
┌───────────┬──────────────┼──────────────┬─────────────┐
|
|
26
|
+
▼ ▼ ▼ ▼ ▼
|
|
27
|
+
SSR call browser fetch MCP tool CLI subcmd OpenAPI op
|
|
28
|
+
(bare, (same call, (read-only abide-cli /openapi.json
|
|
29
|
+
in-proc) swap to fetch) from type) getMessages
|
|
27
30
|
```
|
|
28
31
|
|
|
29
|
-
A
|
|
30
|
-
|
|
31
|
-
(
|
|
32
|
-
|
|
32
|
+
A typed input unlocks the **CLI** on any RPC and **MCP** for read-only methods
|
|
33
|
+
(`GET`/`HEAD`): the handler's input type is projected to JSON Schema at build
|
|
34
|
+
(ADR-0030), so a plainly-typed handler auto-exposes with no hand-written
|
|
35
|
+
`schemas.input` (a declared `schemas.input` adds runtime validation on top, it
|
|
36
|
+
isn't what flips the surfaces on). A mutating method
|
|
37
|
+
(`POST`/`PUT`/`PATCH`/`DELETE`) never auto-exposes to MCP — it needs an explicit
|
|
38
|
+
`clients: { mcp: true }`. Explicit `clients` values always win; `browser`
|
|
39
|
+
defaults on. A socket with a `schema` auto-exposes to MCP and CLI regardless of
|
|
40
|
+
direction.
|
|
33
41
|
|
|
34
42
|
## File-based conventions
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
| `src/
|
|
42
|
-
| `src/server/
|
|
43
|
-
| `src/
|
|
44
|
-
| `src/
|
|
45
|
-
| `src/
|
|
46
|
-
| `src/
|
|
47
|
-
| `src/ui/
|
|
48
|
-
| `src
|
|
49
|
-
| `
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
side-crossing error.
|
|
44
|
+
The bundler and route resolver read these paths by convention (dir aliases
|
|
45
|
+
`$server`, `$ui`, `$shared`, `$mcp`, `$cli` point at the matching `src/` dirs):
|
|
46
|
+
|
|
47
|
+
| Path | Meaning |
|
|
48
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `src/server/rpc/<name>.ts` | One RPC per file; filename = export name = URL under `/rpc/`. The method helper picks the verb. Rewritten to `defineRpc` (server) / `remoteProxy` (client). |
|
|
50
|
+
| `src/server/sockets/<name>.ts` | One socket per file (`export const <name> = socket(...)`); path → socket name. Rewritten to `defineSocket` (server) / `socketProxy` (client). |
|
|
51
|
+
| `src/mcp/prompts/<name>.md` | Markdown MCP prompt: frontmatter (description + arguments) + `{{arg}}` template body, compiled to `definePrompt`. |
|
|
52
|
+
| `src/mcp/resources/*` | MCP resource files served by the generated MCP server. |
|
|
53
|
+
| `src/server/config.ts` | Optional typed-env module: `export const config = env(schema)` validates `Bun.env` at boot (or the floor `export const config = Bun.env`). Eager-imported; deletable. |
|
|
54
|
+
| `src/app.ts` | Optional app hooks (`AppModule` shape): `init` / `handle` / `handleError` / `health` / `forwardHeaders`. Deletable. |
|
|
55
|
+
| `src/ui/pages/**/page.abide` | Folder-based route: a folder's `page.abide` mounts at that folder's URL. `[name]` / `[[name]]` (optional) / `[...rest]` (catch-all) are dynamic segments → `page.params`. |
|
|
56
|
+
| `src/ui/pages/**/layout.abide` | Wraps every page at/below its folder; renders the page where it calls `{children()}`; kept mounted across navigation. |
|
|
57
|
+
| `src/ui/app.html`, `src/ui/app.css` | Custom document shell and root stylesheet. |
|
|
58
|
+
| `src/ui/public/` | Static assets, served at the site root (`/<file>`). |
|
|
59
|
+
| `src/bundle/window.ts` | Optional default-exported `BundleWindow` for the desktop bundle (plus optional `src/bundle/disconnected.abide` connect-screen override). |
|
|
60
|
+
| `src/cli/banner.txt`, `src/cli/footer.txt` | CLI help chrome. |
|
|
61
|
+
| `src/.abide/*.d.ts` | Generated ambient types (`rpc.d.ts`, `routes.d.ts`, `health.d.ts`, `publicAssets.d.ts`, `testRpc.d.ts`, `testSockets.d.ts`). Do not hand-edit. |
|
|
62
|
+
| `dist/` | Build output: `dist/_app/` (prod client) or `dist/_app.gen-<id>/` (dev), `dist/app` (compiled binary), `dist/cli-*`. |
|
|
56
63
|
|
|
57
64
|
## CLI
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
| `abide
|
|
64
|
-
| `abide
|
|
65
|
-
| `abide
|
|
66
|
-
| `abide
|
|
67
|
-
| `abide
|
|
68
|
-
| `abide
|
|
69
|
-
| `abide
|
|
70
|
-
| `abide
|
|
71
|
-
| `abide
|
|
66
|
+
`abide <command>` (the `abide` bin):
|
|
67
|
+
|
|
68
|
+
| Command | Does |
|
|
69
|
+
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `abide scaffold <name> [--no-install] [--no-dev]` | Scaffold a project from the bundled template, install it, and (TTY only) start dev. |
|
|
71
|
+
| `abide dev` | Dev orchestrator: build client, spawn the server child, watch `src/`, rebuild + restart on change, browser live-reload. |
|
|
72
|
+
| `abide build` | Single client build into `dist/_app/`, no server (CI / static deploys). |
|
|
73
|
+
| `abide start` | Run the production server against an already-built `dist/`. |
|
|
74
|
+
| `abide run <file> [args...]` | Run an arbitrary script under the abide preload (same runtime as the server); argv after the file is forwarded verbatim. |
|
|
75
|
+
| `abide compile [--target=<bun-…>] [--out=<path>]` | Build a standalone server executable. |
|
|
76
|
+
| `abide cli [--target=…] [--out=…] [--platforms=<a,b,c>]` | Build the thin CLI binary (manifest baked in, ships the compiled server beside it); `--platforms` cross-compiles into `dist/cli-thin/<platform>/`. |
|
|
77
|
+
| `abide bundle` | Assemble a self-contained desktop app bundle for the host platform (`.app` on macOS), unsigned. |
|
|
78
|
+
| `abide check` | Type-check every `.abide` component's template + props through its shadow program; non-zero on error. |
|
|
79
|
+
| `abide lsp` | Run the `.abide` language server over stdio (JSON-RPC) for editor diagnostics. |
|
|
80
|
+
| `abide init-agent` | Write/refresh the abide agent-guide pointer in the project root `CLAUDE.md`. |
|
|
72
81
|
|
|
73
82
|
For tests, add `preload = ["@abide/abide/preload"]` under `[test]` in
|
|
74
|
-
`bunfig.toml` and
|
|
83
|
+
`bunfig.toml` and run `bun test`.
|
|
75
84
|
|
|
76
85
|
## Authoring contracts
|
|
77
86
|
|
|
78
|
-
**RPC**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
`
|
|
85
|
-
|
|
86
|
-
`
|
|
87
|
-
`
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
non-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
`
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
|
193
|
-
| `
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
- `@abide/abide/server/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- `@abide/abide/server/
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
- `@abide/abide/server/
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
- `@abide/abide/
|
|
301
|
-
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
- `@abide/abide/shared/patch` — `patch(fn, args?, updater)` / `patch({
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
- `@abide/abide/shared/
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
- `@abide/abide/shared/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
- `@abide/abide/shared/
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
- `@abide/abide/shared/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
###
|
|
342
|
-
|
|
343
|
-
- `@abide/abide/shared/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
- `@abide/abide/
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
###
|
|
362
|
-
|
|
363
|
-
- `@abide/abide/
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
###
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
- `@abide/abide/ui/
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
- `@abide/abide/ui/
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
- `@abide/abide/ui/
|
|
384
|
-
|
|
385
|
-
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
- `@abide/abide/ui/
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
- `@abide/abide/ui/
|
|
396
|
-
-
|
|
397
|
-
- `@abide/abide/ui/
|
|
398
|
-
|
|
399
|
-
- `@abide/abide/ui/
|
|
400
|
-
-
|
|
401
|
-
- `@abide/abide/ui/
|
|
402
|
-
|
|
403
|
-
- `@abide/abide/ui/
|
|
404
|
-
-
|
|
405
|
-
|
|
406
|
-
- `@abide/abide/ui/
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
- `@abide/abide/ui/
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
- `@abide/abide/ui/
|
|
413
|
-
-
|
|
414
|
-
|
|
415
|
-
- `@abide/abide/ui/
|
|
416
|
-
|
|
417
|
-
- `@abide/abide/ui/
|
|
418
|
-
-
|
|
419
|
-
- `@abide/abide/ui/
|
|
420
|
-
|
|
421
|
-
- `@abide/abide/ui/
|
|
422
|
-
-
|
|
423
|
-
- `@abide/abide/ui/
|
|
424
|
-
|
|
425
|
-
- `@abide/abide/ui/
|
|
426
|
-
|
|
87
|
+
**RPC** (`src/server/rpc/<name>.ts`). `export const x = METHOD(handler, opts?)`.
|
|
88
|
+
The handler receives the validated args — `StandardSchemaV1.InferOutput<schemas.input>`
|
|
89
|
+
when a schema is present, otherwise its own declared first parameter (or `undefined`
|
|
90
|
+
for a nullary handler); you never pass `<Args, Return>` call generics. It reaches
|
|
91
|
+
request context via `request()` (the inbound `Request`) and `cookies()` (the jar),
|
|
92
|
+
and returns a `Response` — canonically `json(...)` (success body → the caller's
|
|
93
|
+
`Return`), `jsonl(...)`/`sse(...)` (streaming), `error(...)` / `error.typed(...)()`
|
|
94
|
+
(non-2xx, body typed `never`), `redirect(...)`, or a hand-built `Response`
|
|
95
|
+
(`Return` falls back to `unknown`). Typed errors are inferred from the
|
|
96
|
+
`error.typed(...)` branches a handler returns — there is no `errors:` option.
|
|
97
|
+
|
|
98
|
+
`opts` (`RpcSharedOpts`, all optional): `schemas: { input?, output?, files? }`
|
|
99
|
+
(the ADR-0020 namespace — `input` validates args and drives their type, `output`
|
|
100
|
+
is the success-body schema for OpenAPI 200 / MCP `outputSchema` and never drives
|
|
101
|
+
arg inference, `files` validates multipart File parts and merges them into the
|
|
102
|
+
args bag); `clients: { browser?, mcp?, cli? }` (surface-exposure flags);
|
|
103
|
+
`crossOrigin` (exempt a mutating RPC from the same-origin CSRF gate); `maxBodySize`
|
|
104
|
+
(pre-parse body-byte cap, 413 past it); `timeout` (per-RPC handler deadline in ms,
|
|
105
|
+
a 504 on every surface, composed into `request().signal`). Read helpers (GET/HEAD)
|
|
106
|
+
additionally accept `cache` (`ttl`/`tags`/`throttle`/`debounce`/`shared`) and
|
|
107
|
+
`stream` (replay depth); these are a compile error on the mutating helpers. There
|
|
108
|
+
is **no** `outbox` option. Query/path/form args auto-coerce from the endpoint's
|
|
109
|
+
typed shape (ADR-0028 build-time plan) — no `z.coerce` needed; a value that will
|
|
110
|
+
not parse stays a string so the schema raises an honest 422.
|
|
111
|
+
|
|
112
|
+
Consume forms (`RemoteFunction`): the bare `fn(args)` **is** the smart read —
|
|
113
|
+
cached, coalesced, SWR-reactive; decodes by Content-Type, throws `HttpError` on
|
|
114
|
+
non-2xx. There is no call-site options argument on the bare call. Members:
|
|
115
|
+
`fn.raw(args, opts?)` (raw `Response`, no decode/throw), `fn.refresh(args?)`
|
|
116
|
+
(refetch keeping the stale value visible), `fn.patch(...)` (in-place cache
|
|
117
|
+
mutation; fetch-only, absent on streaming RPCs), `fn.peek(args?)` (retained value,
|
|
118
|
+
sync), `fn.pending(args?)`, `fn.refreshing(args?)`, `fn.error(args?)` (this RPC's
|
|
119
|
+
last typed error), `fn.isError(caught, 'name')` (typed guard), and client-only
|
|
120
|
+
`fn.watch(handler)` / `fn.watch(args, handler)`. A streaming handler
|
|
121
|
+
(`jsonl`/`sse`) makes the bare call return a `NamedAsyncIterable<Frame>`
|
|
122
|
+
synchronously — `for await (… of fn(args))`, never `await`.
|
|
123
|
+
|
|
124
|
+
**Socket** (`src/server/sockets/<name>.ts`). `export const x = socket(opts?)`.
|
|
125
|
+
`opts` (`SocketOptions`): `tail` (retention count — kept frames for late joiners
|
|
126
|
+
/ reconnects; server default 1), `ttl` (evict retained frames older than N ms,
|
|
127
|
+
lazy), `clientPublish` (allow publishes over the wire; off by default), `schema`
|
|
128
|
+
(validate publish payloads; flips mcp/cli `clients` on), `clients`. A `Socket<T>`
|
|
129
|
+
extends `AsyncIterable<T>` (bare `for await` is the live stream, no replay) with
|
|
130
|
+
`publish(frame)`, `tail(count?)` (subscription seeded from the retained tail),
|
|
131
|
+
`peek()`, `pending()`/`refreshing()`/`done()`/`error()`, `refresh()`, and
|
|
132
|
+
`watch(handler)`. HTTP face at `/__abide/sockets/<name>`: `GET` reads the retained
|
|
133
|
+
tail, `POST` publishes (only when `clientPublish`).
|
|
134
|
+
|
|
135
|
+
**Page / layout** (`src/ui/pages/**`). `page` (isomorphic `PageSnapshot` proxy)
|
|
136
|
+
exposes `route`, `params`, `url` (browser-space `URL`, base-prefixed), and
|
|
137
|
+
`navigating`; read a field inside an effect/derived and it re-runs on navigation.
|
|
138
|
+
`url(path, params?, query?)` builds base-correct links; `navigate(path, …)`
|
|
139
|
+
performs typed in-app SPA navigation (params first for `[name]` routes, then
|
|
140
|
+
`{ replace?, keepScroll? }`). A layout renders the active page via `{children()}`.
|
|
141
|
+
|
|
142
|
+
**`app.ts` / `config.ts`.** `app.ts` default- or named-exports the `AppModule`
|
|
143
|
+
hooks (all optional; `init({ server })` may return a cleanup run on
|
|
144
|
+
SIGINT/SIGTERM; `handle(request, next)` is single middleware; `health(request)`
|
|
145
|
+
merges into `/__abide/health`, runs before `handle`, and is public). `config.ts`
|
|
146
|
+
exports `config` — `env(schema)` (validated, typed, throws on bad config at boot)
|
|
147
|
+
or the unvalidated `Bun.env` floor.
|
|
148
|
+
|
|
149
|
+
**The isomorphism move.** There is no `cache()` wrapper. A bare smart RPC call
|
|
150
|
+
read inline during SSR is captured in the per-request cache; the runtime
|
|
151
|
+
snapshots each settled entry into a wire-safe form serialized into the HTML, and
|
|
152
|
+
the client seeds its store from it on hydration — the same call hydrates warm
|
|
153
|
+
instead of re-firing. Streaming reads are snapshotted again after the stream
|
|
154
|
+
drains and seeded over the wire.
|
|
155
|
+
|
|
156
|
+
## .abide template grammar
|
|
157
|
+
|
|
158
|
+
A `.abide` component is HTML with a leading `<script>` (its component script);
|
|
159
|
+
`<script>` and `<style>` may also sit **inside a control-flow branch**, scoped to
|
|
160
|
+
that branch (a nested `<script>` declares branch-local `state`/`state.computed`/
|
|
161
|
+
`state.linked`, re-seeded per mount, and takes **no** module imports — imports
|
|
162
|
+
live only in the leading script; a nested `<style>` scopes to its sibling
|
|
163
|
+
subtree). A _root_ `<style>` is component-scoped. Reactive primitives are reached
|
|
164
|
+
through their own imported bindings (alias-safe) — `state` from `abide/ui/state`,
|
|
165
|
+
`watch` from `abide/ui/watch`, `html` from `abide/ui/html`, `snippet` from
|
|
166
|
+
`abide/shared/snippet`, `props` from `abide/ui/props` — never through `scope()`
|
|
167
|
+
(internal plumbing). Every one of these, `props()` included, is a required import:
|
|
168
|
+
a missing one surfaces as `Cannot find name '…'`.
|
|
169
|
+
|
|
170
|
+
Reactive state:
|
|
171
|
+
|
|
172
|
+
| Form | Meaning |
|
|
173
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
174
|
+
| `state(initial, transform?)` | Writable cell; read/write via `.value`; `transform(next, prev)` gates writes. |
|
|
175
|
+
| `state.computed(fn)` | Read-only cell derived from other cells (lazy, never serialized). |
|
|
176
|
+
| `state.linked(fn, transform?)` | Writable cell reseeded when the thunk's deps change. |
|
|
177
|
+
| `watch(source, handler)` | The single reaction primitive: over a cell, a cell array, a socket/stream, or an RPC; bare `watch(thunk)` is an auto-tracked effect. Client-only. |
|
|
178
|
+
| `props()` | Ambient prop reader: `const { name = fallback, ...rest } = props()`. |
|
|
179
|
+
|
|
180
|
+
Bindings and directives (attribute kinds `event` / `bind` / `class` / `style` /
|
|
181
|
+
`attach` / spread, plus plain `expression` / static):
|
|
182
|
+
|
|
183
|
+
| Form | Meaning |
|
|
184
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
185
|
+
| `{expr}` | Reactive text (escaped); an `html`-branded value inserts unescaped raw HTML. |
|
|
186
|
+
| `name={expr}` | Reactive attribute. |
|
|
187
|
+
| `on<event>={fn}` | Event listener (`onclick`, `oninput`, `onsubmit`, …). |
|
|
188
|
+
| `bind:value` / `bind:checked` / `bind:group` | Two-way form binds. |
|
|
189
|
+
| `bind:value={{ get, set }}` | Derived two-way binding. |
|
|
190
|
+
| `class:name={cond}` | Toggle a class. |
|
|
191
|
+
| `style:property={value}` | Set one style property. |
|
|
192
|
+
| `attach={fn}` | Run `fn(element)` at mount; its return is the teardown. |
|
|
193
|
+
| `{...spread}` | Spread an object's keys as attributes (element) or props (component). |
|
|
194
|
+
|
|
195
|
+
Control flow — mustache `{#…}` blocks (NOT `<template>`):
|
|
196
|
+
|
|
197
|
+
| Block | Form |
|
|
198
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
199
|
+
| Conditional | `{#if}` / `{:else if}` / `{:else}` / `{/if}` |
|
|
200
|
+
| Keyed list | `{#for item, i of list by key}` / `{/for}` |
|
|
201
|
+
| Async list | `{#for await item of source}` / `{/for}` (over an `AsyncIterable`) |
|
|
202
|
+
| Promise | bare peek `{fn()}` is the default (below); `{#await p}` / `{:then v}` / `{:catch e}` / `{:finally}` / `{/await}` is the explicit opt-in |
|
|
203
|
+
| Switch | `{#switch subject}` / `{:case v}` / `{:default}` / `{/switch}` |
|
|
204
|
+
| Error boundary | `{#try}` / `{:catch}` / `{:finally}` / `{/try}` |
|
|
205
|
+
| Snippet | `{#snippet name(args)}…{/snippet}`, called `{name(args)}` |
|
|
206
|
+
|
|
207
|
+
Reading async data does **not** require `{#await}`. A promise/async-cell-typed
|
|
208
|
+
`{expr}` is a **peek** — `undefined` while pending (auto-streamed on SSR), composing
|
|
209
|
+
with `?? fallback` / `?.` / `{#if}` / attributes in every position (ADR-0032), paired
|
|
210
|
+
with the `.pending()` / `.error()` probes for affordances; `{await expr}` blocks SSR
|
|
211
|
+
inline (the shorthand for a `{#await … then}` head). The `{#await}` block above is the
|
|
212
|
+
opt-in reserved for a distinct pending branch, a local `{:catch}`, or `{:then}`
|
|
213
|
+
type-narrowing — ADR-0019 narrowed its authored role to exactly that.
|
|
214
|
+
|
|
215
|
+
Components are capitalised tags; content nested in them renders where the
|
|
216
|
+
component calls `{children()}` (`{#if children}{children()}{:else}…{/if}` is the
|
|
217
|
+
fallback). The `<slot>` element, the `<template name>` snippet form, and
|
|
218
|
+
`<template if>` / `<template each>` / … control flow were **removed** — a bare
|
|
219
|
+
`<template>` is now an inert element, and any removed form throws a migration
|
|
220
|
+
error. The branch keyword is `{:else if}` (a space).
|
|
221
|
+
|
|
222
|
+
## Server surface — abide/server/*
|
|
223
|
+
|
|
224
|
+
### RPC helpers — @documentation rpc
|
|
225
|
+
|
|
226
|
+
- `@abide/abide/server/GET` — declares a read (GET) RPC; accepts `RpcReadOpts`
|
|
227
|
+
(shared opts + `cache`/`stream`); query args. Bundler-rewritten; calling the
|
|
228
|
+
bare helper throws.
|
|
229
|
+
- `@abide/abide/server/POST` — declares a mutating (POST) RPC; `RpcSharedOpts`
|
|
230
|
+
only (`cache`/`stream` are a compile error); JSON-body (or FormData) args.
|
|
231
|
+
- `@abide/abide/server/PUT` — mutating PUT RPC; body args; `RpcSharedOpts`.
|
|
232
|
+
- `@abide/abide/server/PATCH` — mutating PATCH RPC; body args; `RpcSharedOpts`.
|
|
233
|
+
- `@abide/abide/server/DELETE` — mutating DELETE RPC; query args; `RpcSharedOpts`.
|
|
234
|
+
- `@abide/abide/server/HEAD` — read HEAD RPC alongside GET; query args;
|
|
235
|
+
`RpcReadOpts`.
|
|
236
|
+
|
|
237
|
+
### Responses — @documentation response
|
|
238
|
+
|
|
239
|
+
- `@abide/abide/server/json` — `json(data, init?)`: JSON `TypedResponse<T>` with
|
|
240
|
+
RPC defaults (`no-store`), wire-encoding Set/Map/bigint/Date; `json(undefined)`
|
|
241
|
+
→ 204. `T` drives `Return` inference.
|
|
242
|
+
- `@abide/abide/server/jsonl` — `jsonl(iterable, init?)`: wraps an
|
|
243
|
+
`AsyncIterable<Frame>` as `application/jsonl` (one JSON value per line); a
|
|
244
|
+
generator error emits a final `{"$error":…}` line.
|
|
245
|
+
- `@abide/abide/server/sse` — `sse(iterable, init?)`: wraps an
|
|
246
|
+
`AsyncIterable<Frame>` as `text/event-stream` with 15s keepalive comments;
|
|
247
|
+
errors emit an `event: error` frame.
|
|
248
|
+
- `@abide/abide/server/error` — `error(status, message?, init?)`:
|
|
249
|
+
`text/plain` `TypedResponse<never>`. `error.typed(name, status, schema?)`
|
|
250
|
+
declares a reusable typed-error constructor driving `fn.isError(e, 'name')`.
|
|
251
|
+
- `@abide/abide/server/redirect` — `redirect(url, status=302, init?)`:
|
|
252
|
+
`TypedResponse<never>`, accepts relative URLs, `no-store`, status restricted to
|
|
253
|
+
301/302/303/307/308.
|
|
254
|
+
|
|
255
|
+
### Request scope — @documentation request-scope
|
|
256
|
+
|
|
257
|
+
- `@abide/abide/server/request` — `request(): Request` — the in-flight inbound
|
|
258
|
+
request (ALS-scoped); throws outside a request scope.
|
|
259
|
+
- `@abide/abide/server/cookies` — `cookies(): Bun.CookieMap` — the request's
|
|
260
|
+
cookie jar; reads parse `Cookie`, writes flush as `Set-Cookie` on return.
|
|
261
|
+
- `@abide/abide/server/server` — `server(): Bun.Server` — the active server; a
|
|
262
|
+
no-op in-process server for CLI/MCP/test dispatch; throws before init.
|
|
263
|
+
|
|
264
|
+
### Configuration — @documentation configuration
|
|
265
|
+
|
|
266
|
+
- `@abide/abide/server/env` — `env(schema)`: validate `Bun.env` against a Standard
|
|
267
|
+
Schema at module top level (synchronous; all issues at once) and return the
|
|
268
|
+
typed config; also registers the schema for the launcher setup form.
|
|
269
|
+
|
|
270
|
+
### Sockets — @documentation sockets
|
|
271
|
+
|
|
272
|
+
- `@abide/abide/server/socket` — `socket(opts?)` / `socket({ schema })` declares a
|
|
273
|
+
broadcast topic returning `Socket<T>`; opts `tail`/`ttl`/`clientPublish`/
|
|
274
|
+
`schema`/`clients` (server-only; the client stub discards them).
|
|
275
|
+
|
|
276
|
+
### Agent — @documentation agent
|
|
277
|
+
|
|
278
|
+
- `@abide/abide/server/agent` — `agent(engine, messages): AsyncIterable<AgentFrame>`
|
|
279
|
+
runs a provider `AgentEngine` against the current request's MCP surface
|
|
280
|
+
(forwarding caller auth); the handler picks transport via `jsonl`/`sse`. Exports
|
|
281
|
+
`NeutralMessage`, `AgentFrame`, `AgentSurface`, `AgentEngine` types.
|
|
282
|
+
|
|
283
|
+
### Server plumbing — @documentation plumbing
|
|
284
|
+
|
|
285
|
+
- `@abide/abide/server/AppModule` — type of the optional `src/app.ts` hooks
|
|
286
|
+
(`forwardHeaders`/`init`/`handle`/`handleError`/`health`).
|
|
287
|
+
- `@abide/abide/server/InspectorContext` — type of the capability object core
|
|
288
|
+
injects into `@abide/inspector` when `ABIDE_ENABLE_INSPECTOR=true`.
|
|
289
|
+
- `@abide/abide/server/rpc/defineRpc` — bundler-emitted RPC builder: resolves
|
|
290
|
+
`clients`, validates input/files, applies `timeout`, registers the entry.
|
|
291
|
+
- `@abide/abide/server/sockets/defineSocket` — bundler-emitted socket builder:
|
|
292
|
+
per-subscriber queue + retained tail, optional `ttl`/`schema`, Bun-native
|
|
293
|
+
fan-out.
|
|
294
|
+
- `@abide/abide/server/prompts/definePrompt` — resolver-emitted prompt builder
|
|
295
|
+
from `src/mcp/prompts/<name>.md`; registers with the MCP dispatcher.
|
|
296
|
+
- `@abide/abide/server/prompts/renderPromptTemplate` — substitutes `{{name}}`
|
|
297
|
+
placeholders in a prompt template body (missing args → empty string).
|
|
298
|
+
|
|
299
|
+
## Isomorphic surface — abide/shared/*
|
|
300
|
+
|
|
301
|
+
### RPC schema projection — @documentation rpc
|
|
302
|
+
|
|
303
|
+
- `@abide/abide/shared/withJsonSchema` — `withJsonSchema(schema, toJsonSchema)`
|
|
304
|
+
attaches a `toJSONSchema()` projection to a Standard Schema whose library lacks
|
|
305
|
+
one native (feeds OpenAPI / MCP / CLI / setup form).
|
|
306
|
+
|
|
307
|
+
### Error responses — @documentation response
|
|
308
|
+
|
|
309
|
+
- `@abide/abide/shared/HttpError` — the error class thrown by a remote call on
|
|
310
|
+
non-2xx: `status`, `statusText`, raw `response`, optional `kind`/`data` (set for
|
|
311
|
+
a typed error or a 422 validation failure).
|
|
312
|
+
- `@abide/abide/shared/ValidationErrorData` — type of `HttpError.data` when
|
|
313
|
+
`kind === 'validation'`: `{ issues, fields }` (raw Standard Schema issues + a
|
|
314
|
+
field→first-message map).
|
|
315
|
+
|
|
316
|
+
### Cache mutation — @documentation cache
|
|
317
|
+
|
|
318
|
+
- `@abide/abide/shared/patch` — `patch(fn, args?, updater)` / `patch({tags},
|
|
319
|
+
updater)`: reactively mutate the retained value of matching cached reads in
|
|
320
|
+
place, no network — the optimistic-update / real-time primitive.
|
|
321
|
+
- `@abide/abide/shared/refresh` — `refresh(selector?, args?)`: refetch every
|
|
322
|
+
matching cached read, keeping the stale value visible (`refreshing()` true)
|
|
323
|
+
until fresh swaps in.
|
|
324
|
+
|
|
325
|
+
### Page — @documentation page
|
|
326
|
+
|
|
327
|
+
- `@abide/abide/shared/page` — `page`: isomorphic reactive `PageSnapshot` proxy
|
|
328
|
+
(`route`/`params`/`url`/`navigating`); reading a field in a tracking scope
|
|
329
|
+
re-runs on navigation.
|
|
330
|
+
|
|
331
|
+
### Probes — @documentation probes
|
|
332
|
+
|
|
333
|
+
- `@abide/abide/shared/pending` — `pending(source?, args?): boolean` — reactive
|
|
334
|
+
"no value yet" over cached calls and tail streams.
|
|
335
|
+
- `@abide/abide/shared/peek` — `peek(source, args?)` — the currently-retained
|
|
336
|
+
value synchronously, triggering nothing; `undefined` when nothing retained.
|
|
337
|
+
- `@abide/abide/shared/refreshing` — `refreshing(source?, args?): boolean` —
|
|
338
|
+
reactive "holding a value while a fresher source is in flight".
|
|
339
|
+
- `@abide/abide/shared/done` — `done(subscribable): boolean` — reactive terminal
|
|
340
|
+
read: true once a stream closed.
|
|
341
|
+
- `@abide/abide/shared/online` — `online(): boolean` — reactive connectivity
|
|
342
|
+
probe (browser online/offline; server reflects the caller's reported state).
|
|
343
|
+
|
|
344
|
+
### URL — @documentation url
|
|
345
|
+
|
|
346
|
+
- `@abide/abide/shared/url` — `url(path, ...args): string` resolves any in-app URL
|
|
347
|
+
base-correctly (RPC query, page params, or asset); external paths pass through.
|
|
348
|
+
Exports `PathParams` and augmentable `RpcRoutes`/`PageRoutes`/`PublicAssets`.
|
|
349
|
+
|
|
350
|
+
### Templating — @documentation templating
|
|
351
|
+
|
|
352
|
+
- `@abide/abide/shared/snippet` — `snippet(payload)` brands a snippet payload so a
|
|
353
|
+
`{expr}` interpolation mounts it (the compiler wraps a `{#snippet}` body); also
|
|
354
|
+
exports `SnippetValue` and `Snippet<Args>` types.
|
|
355
|
+
|
|
356
|
+
### Observability — @documentation observability
|
|
357
|
+
|
|
358
|
+
- `@abide/abide/shared/health` — `health(): HealthState` — reactive backend-health
|
|
359
|
+
read (reachability + the app's `health()` fields), reader-driven poll of
|
|
360
|
+
`/__abide/health`; composes `navigator.onLine`.
|
|
361
|
+
- `@abide/abide/shared/log` — `log`: the unified request-scope-aware logger
|
|
362
|
+
(`log(...)`, `.warn`/`.error`/`.trace`, `.channel(name)` for a DEBUG-gated
|
|
363
|
+
channel); TSV by default, JSON under `ABIDE_LOG_FORMAT=json`.
|
|
364
|
+
- `@abide/abide/shared/reachable` — `reachable(host?): Promise<boolean>` —
|
|
365
|
+
isomorphic outbound reachability HEAD probe, cached per TTL; any response counts
|
|
366
|
+
reachable, only connection failure/timeout is not.
|
|
367
|
+
- `@abide/abide/shared/trace` — `trace(): string | undefined` — the current
|
|
368
|
+
request's W3C `traceparent` (server from ALS, browser from `__SSR__`).
|
|
369
|
+
|
|
370
|
+
### Isomorphic plumbing — @documentation plumbing
|
|
371
|
+
|
|
372
|
+
- `@abide/abide/shared/createSubscriber` — `createSubscriber(start)`: abide-ui
|
|
373
|
+
subscriber grounded in the signal core (open-on-first-tracked-read,
|
|
374
|
+
close-on-last-reader).
|
|
375
|
+
|
|
376
|
+
## UI surface — abide/ui/* (client-only)
|
|
377
|
+
|
|
378
|
+
### Reactive state — @documentation reactive-state
|
|
379
|
+
|
|
380
|
+
- `@abide/abide/ui/state` — `state(initial, transform?)` writable cell (read/reassigned as a plain variable; compiler desugars to `.value`);
|
|
381
|
+
members `state.computed(fn)` (read-only derived), `state.linked(fn, transform?)`
|
|
382
|
+
(writable, reseeded from a thunk), `state.share(key, value)` / `state.shared(key)`
|
|
383
|
+
(ambient scope context).
|
|
384
|
+
- `@abide/abide/ui/watch` — `watch(source, handler)` the single reaction primitive
|
|
385
|
+
(cell / cell array / socket-stream / RPC); bare `watch(thunk)` is an auto-tracked
|
|
386
|
+
effect; returns a scope-tied disposer; SSR-inert.
|
|
387
|
+
- `@abide/abide/ui/props` — `props()` prop reader (a required import, like the other
|
|
388
|
+
reactive names); compiler-lowered inside a component; throws if called directly.
|
|
389
|
+
|
|
390
|
+
### Templating — @documentation templating
|
|
391
|
+
|
|
392
|
+
- `@abide/abide/ui/html` — `html\`…\``(or`html(string)`) returns branded
|
|
393
|
+
**unescaped** raw HTML for `{expr}` insertion; interpolations are not
|
|
394
|
+
auto-escaped; nullish → empty.
|
|
395
|
+
|
|
396
|
+
### Navigate — @documentation navigate
|
|
397
|
+
|
|
398
|
+
- `@abide/abide/ui/navigate` — `navigate(path, ...rest)` typed in-app SPA
|
|
399
|
+
navigation (params first for `[name]` routes, then `{ replace?, keepScroll? }`);
|
|
400
|
+
builds through `url()`.
|
|
401
|
+
|
|
402
|
+
### UI plumbing — @documentation plumbing
|
|
403
|
+
|
|
404
|
+
- `@abide/abide/ui/effect` — `effect(fn)` internal reactive effect (tracks reads,
|
|
405
|
+
re-runs, returns a disposer); compiler-emitted — authors use `watch`.
|
|
406
|
+
- `@abide/abide/ui/currentScope` — `scope()` resolves the current lexical scope;
|
|
407
|
+
the internal lowering host the compiler targets.
|
|
408
|
+
- `@abide/abide/ui/enterRenderScope` — `enterScope()` establishes a fresh isolated
|
|
409
|
+
SSR-render scope, returning the previous.
|
|
410
|
+
- `@abide/abide/ui/exitRenderScope` — `exitScope(previous)` restores the scope
|
|
411
|
+
`enterScope` saved.
|
|
412
|
+
- `@abide/abide/ui/router` — `router(host, loaders, layoutLoaders?, probe?)` the
|
|
413
|
+
History-API client router: match, code-split, mount a diffed outlet chain, drive
|
|
414
|
+
SPA nav with scroll restoration.
|
|
415
|
+
- `@abide/abide/ui/startClient` — `startClient(routes, layoutRoutes?, target)` the
|
|
416
|
+
client entry: read `window.__SSR__`, seed cache/streamed/warm state, start the
|
|
417
|
+
router; returns a disposer.
|
|
418
|
+
- `@abide/abide/ui/renderToStream` — `renderToStream(render)` out-of-order SSR
|
|
419
|
+
streaming generator: shell first, then one `<abide-resolve>` fragment per
|
|
420
|
+
streaming `{#await}` in completion order.
|
|
421
|
+
- `@abide/abide/ui/remoteProxy` — `remoteProxy(method, url, options?)`
|
|
422
|
+
bundler-target client substitute for a server RPC (fetch over the network; does
|
|
423
|
+
the client pre-flight input validation; attaches the real `.watch`).
|
|
424
|
+
- `@abide/abide/ui/socketProxy` — `socketProxy(name)` bundler-target client
|
|
425
|
+
substitute for a server socket (subscribes over the multiplexed ws).
|
|
426
|
+
- `@abide/abide/ui/settleAsyncCells` — the SSR await-barrier draining the
|
|
427
|
+
request-scoped pending-cell list; client no-op.
|
|
428
|
+
- `@abide/abide/ui/flight` — server-only flight-starter hoisting a hoistable
|
|
429
|
+
await's promise into the sync prefix so independent flights overlap.
|
|
430
|
+
- `@abide/abide/ui/isolateCellBarrier` — runs a hoisted child render under its own
|
|
431
|
+
async-cell barrier so its cells isolate from siblings; client passthrough.
|
|
432
|
+
- `@abide/abide/ui/finalizeStreamedChildren` — the when-to-stream decision run
|
|
433
|
+
after a component body walk; fills each hoistable child's reserved output slot.
|
|
434
|
+
- `@abide/abide/ui/runtime/withPath` — pushes one escaped render-path segment for
|
|
435
|
+
the duration of a synchronous build.
|
|
436
|
+
- `@abide/abide/ui/runtime/renderPath` — composes a streamed child's ordinal
|
|
437
|
+
segment onto the ambient render path and returns the boundary id.
|
|
438
|
+
- `@abide/abide/ui/runtime/escapeKey` — escapes one key to an RFC 6901
|
|
439
|
+
JSON-Pointer token so a `/`-bearing key survives a `/`-joined render path.
|
|
440
|
+
- `@abide/abide/ui/runtime/nextBlockId` — the next await/try block id in the
|
|
441
|
+
current render pass, namespaced by render path.
|
|
442
|
+
- `@abide/abide/ui/runtime/blockId` — allocates a render-path-namespaced await/try
|
|
443
|
+
block id with a per-path document-order counter.
|
|
444
|
+
- `@abide/abide/ui/runtime/enterRenderPass` — marks entry into a render/mount;
|
|
445
|
+
depth 0 clears the per-path block-id counters.
|
|
446
|
+
- `@abide/abide/ui/runtime/exitRenderPass` — marks exit, unwinding the render-pass
|
|
447
|
+
depth.
|
|
448
|
+
- `@abide/abide/ui/dom/mount` — mounts a top-level page/layout into a host under an
|
|
449
|
+
ownership scope; returns a disposer.
|
|
450
|
+
- `@abide/abide/ui/dom/mountChild` — mounts a `<Child/>` as a marker-bounded range
|
|
451
|
+
(no wrapper element).
|
|
452
|
+
- `@abide/abide/ui/dom/mountStreamedChild` — client adopter for a hoistable child:
|
|
453
|
+
adopts an inlined range or a streamed boundary.
|
|
454
|
+
- `@abide/abide/ui/dom/mergeProps` — composes a child's props from ordered layers
|
|
455
|
+
(explicit runs, spreads, trailing `children`), last-wins per key.
|
|
456
|
+
- `@abide/abide/ui/dom/spreadProps` — wraps a `{...source}` spread layer so each
|
|
457
|
+
key resolves to a live value thunk.
|
|
458
|
+
- `@abide/abide/ui/dom/restProps` — the live `...rest` of a `props()` destructure.
|
|
459
|
+
- `@abide/abide/ui/dom/bindProp` — parent half of a component `bind:prop`
|
|
460
|
+
(annotates the prop thunk with a write-back channel).
|
|
461
|
+
- `@abide/abide/ui/dom/bindableProp` — child half of a two-way prop (the writable
|
|
462
|
+
cell the child writes/forwards).
|
|
463
|
+
- `@abide/abide/ui/dom/spreadAttrs` — spreads an object's keys onto a native
|
|
464
|
+
element (`on<event>` keys attach listeners, others bind as reactive attrs).
|
|
465
|
+
- `@abide/abide/ui/dom/readCall` — guarded method call on a reactive-document read
|
|
466
|
+
so throws name the authored scope path.
|
|
467
|
+
- `@abide/abide/ui/dom/readCell` — unified read for a `computed`/`linked`
|
|
468
|
+
reference (async peek / derive call / sync `.value`).
|
|
469
|
+
- `@abide/abide/ui/dom/cellPending` — whether a `{#if}`/`{#switch}` async subject
|
|
470
|
+
is still loading (render no branch while pending).
|
|
471
|
+
- `@abide/abide/ui/dom/mutateDocContainer` — in-place container mutation lowered to
|
|
472
|
+
clone-mutate-replace so a patch emits and readers wake.
|
|
473
|
+
- `@abide/abide/ui/dom/hydrate` — adopts server-rendered DOM in place with a claim
|
|
474
|
+
cursor (attach listeners/effects, no re-render); returns a disposer.
|
|
475
|
+
- `@abide/abide/ui/dom/appendText` — reactive `{expr}` interpolation (escaped text
|
|
476
|
+
/ snippet builder / `html\`\`` raw).
|
|
477
|
+
- `@abide/abide/ui/dom/appendTextAt` — reactive `{expr}` mounted at a skeleton
|
|
478
|
+
anchor comment, interleaved with element siblings.
|
|
479
|
+
- `@abide/abide/ui/dom/appendSnippet` — mounts a `{snippet(args)}` builder's nodes
|
|
480
|
+
in a marker-bounded range.
|
|
481
|
+
- `@abide/abide/ui/dom/appendStatic` — a static text node, created or claimed from
|
|
482
|
+
SSR text.
|
|
483
|
+
- `@abide/abide/ui/dom/cloneStatic` — appends a fully-static bindingless subtree via
|
|
484
|
+
one cached-template deep clone.
|
|
485
|
+
- `@abide/abide/ui/dom/skeleton` — clones a template and locates its bound
|
|
486
|
+
holes/anchors for a subtree carrying bindings/control-flow.
|
|
487
|
+
- `@abide/abide/ui/dom/anchorCursor` — positions a skeleton-anchored control-flow
|
|
488
|
+
block or slot at its `<!--a-->` anchor.
|
|
489
|
+
- `@abide/abide/ui/dom/mountSlot` — mounts a component's `{children()}` content
|
|
490
|
+
(parent children or fallback) as a marker-bounded range.
|
|
491
|
+
- `@abide/abide/ui/dom/outlet` — a layout's outlet boundary the router fills with
|
|
492
|
+
the next chain layer.
|
|
493
|
+
- `@abide/abide/ui/dom/attr` — binds an element attribute to `read()` via one
|
|
494
|
+
effect (`name={expr}`).
|
|
495
|
+
- `@abide/abide/ui/dom/on` — attaches an event listener pinned to the owning scope
|
|
496
|
+
(`on<event>`).
|
|
497
|
+
- `@abide/abide/ui/dom/attach` — runs an `attach={fn}` against an element and
|
|
498
|
+
registers its teardown.
|
|
499
|
+
- `@abide/abide/ui/dom/bindSelectValue` — two-way `bind:value` for `<select>`
|
|
500
|
+
(reactive selection + change write-back; `multiple` = array membership).
|
|
501
|
+
- `@abide/abide/ui/dom/each` — keyed list binding (`{#for … by key}`),
|
|
502
|
+
marker-range rows reconciled by key with minimal DOM moves.
|
|
503
|
+
- `@abide/abide/ui/dom/eachAsync` — async keyed list over an AsyncIterable
|
|
504
|
+
(`{#for await}`); rows append as it yields; SSR renders none.
|
|
505
|
+
- `@abide/abide/ui/dom/when` — conditional swappable range (`{#if}`/`{:else}`) with
|
|
506
|
+
an optional pending state.
|
|
507
|
+
- `@abide/abide/ui/dom/awaitBlock` — await-block runtime across
|
|
508
|
+
pending/resolved/error branches with SSR resume adoption (`{#await}`).
|
|
509
|
+
- `@abide/abide/ui/dom/tryBlock` — error-boundary block catching thrown/async-cell
|
|
510
|
+
errors into a catch branch (`{#try}`).
|
|
511
|
+
- `@abide/abide/ui/dom/switchBlock` — multi-branch swappable range (first matching
|
|
512
|
+
`{:case}` else `{:default}`; also backs `{:else if}` chains).
|
|
427
513
|
|
|
428
514
|
## Build / tooling
|
|
429
515
|
|
|
430
|
-
### Building —
|
|
431
|
-
|
|
432
|
-
- `@abide/abide/build` — `build(
|
|
433
|
-
|
|
516
|
+
### Building — @documentation building
|
|
517
|
+
|
|
518
|
+
- `@abide/abide/build` — `build(opts?)` builds the client bundle into `dist/` and
|
|
519
|
+
concurrently writes `src/.abide/*.d.ts`; never throws. Options `cwd`/`minify`/
|
|
520
|
+
`compress`/`clean`/`exitOnFailure`/`dev`.
|
|
521
|
+
- `@abide/abide/compile` — `compile(opts?)` produces a standalone Bun server
|
|
522
|
+
executable (runs the client build first to embed assets); returns the binary
|
|
523
|
+
path.
|
|
524
|
+
|
|
525
|
+
### Tooling — @documentation plumbing
|
|
526
|
+
|
|
527
|
+
- `@abide/abide/ui-plugin` — the `abide-ui` `BunPlugin` that loads and compiles
|
|
528
|
+
`.abide` components (and pulls scoped `<style>` into browser bundles).
|
|
529
|
+
- `@abide/abide/preload` — the Bun preload module registering the UI plugin, the
|
|
530
|
+
resolver plugin, and the `.css` no-op loader (used by `bunfig` `[test]` and the
|
|
531
|
+
CLI `--preload`).
|
|
532
|
+
- `@abide/abide/resolver-plugin` — `abideResolverPlugin({ cwd?, embedAssets?,
|
|
533
|
+
target? })` wiring every `abide:*` virtual module, the `$server`/`$ui`/`$shared`/
|
|
534
|
+
`$mcp`/`$cli` aliases, and the per-target RPC/socket rewrite (with the
|
|
535
|
+
client-side server-code-leak guard).
|
|
536
|
+
- `@abide/abide/tsconfig` — the base `tsconfig.app.json` for consuming apps to
|
|
537
|
+
`extends`.
|
|
538
|
+
|
|
539
|
+
## Desktop bundle — @documentation bundle
|
|
540
|
+
|
|
541
|
+
- `@abide/abide/server/appDataDir` — `appDataDir()` returns the bundle's per-user
|
|
542
|
+
data dir keyed by the injected program name; cwd-independent, pure.
|
|
543
|
+
- `@abide/abide/bundle/BundleWindow` — type of the default export from
|
|
544
|
+
`src/bundle/window.ts`: `{ title?, width?, height?, menu?, config? }`.
|
|
545
|
+
- `@abide/abide/bundle/BundleMenu` — `{ label, items: BundleMenuItem[] }`, a
|
|
546
|
+
top-level bundle menu.
|
|
547
|
+
- `@abide/abide/bundle/BundleMenuItem` — a menu entry: separator, or `emit` (a
|
|
548
|
+
page `abide:menu` event), or `navigate`.
|
|
549
|
+
- `@abide/abide/bundle/onMenu` — `onMenu(handler)` / `onMenu(name, handler)`
|
|
550
|
+
subscribes to bundle menu-click events; returns an unsubscribe; inert in a
|
|
551
|
+
plain tab / SSR.
|
|
552
|
+
- `@abide/abide/bundle/bundled` — `bundled(): boolean` — am I part of the abide
|
|
553
|
+
desktop bundle (client reads `window.__ABIDE_BUNDLE__`, server the parent-pid
|
|
554
|
+
env).
|
|
555
|
+
|
|
556
|
+
## MCP — @documentation mcp
|
|
557
|
+
|
|
558
|
+
- `@abide/abide/mcp/createMcpServer` — `createMcpServer(opts?)` constructs the
|
|
559
|
+
framework-generated MCP server bound to the RPC/socket registries; returns
|
|
560
|
+
`{ handle(request) }` (the `/__abide/mcp` handler). Tools derive from surfaces
|
|
561
|
+
with `clients.mcp`.
|
|
434
562
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
- `@abide/abide/preload` — the Bun preload installing the `.abide` loader, the virtual-module resolver, and a `.css` no-op loader — the same runtime for the server, scripts (`abide run`), and `bun test`.
|
|
438
|
-
- `@abide/abide/resolver-plugin` — the resolver plugin itself: `$`-alias + virtual-module (`abide:*`) resolution, rpc/socket module rewriting, side-crossing guards.
|
|
439
|
-
- `@abide/abide/ui-plugin` — the Bun plugin that compiles `.abide` single-file components to ES modules (layouts flagged by filename; scoped styles bundled into the entry stylesheet).
|
|
440
|
-
- `@abide/abide/tsconfig` — the base tsconfig apps extend (`bundler` resolution, strict, `types: ["bun"]`, erasable syntax only).
|
|
441
|
-
|
|
442
|
-
## Desktop bundle — `@documentation bundle`
|
|
443
|
-
|
|
444
|
-
- `@abide/abide/bundle/BundleWindow` — the type of `src/bundle/window.ts`'s default export: window title/size plus custom `menu` entries inserted between the standard Edit and Window menus.
|
|
445
|
-
- `@abide/abide/bundle/BundleMenu` — one top-level custom menu (`label` + `items`).
|
|
446
|
-
- `@abide/abide/bundle/BundleMenuItem` — one menu entry: a divider, an `emit` item dispatching an `abide:menu` CustomEvent into the page (optional Cmd `shortcut`), or a `navigate` item repointing the window itself.
|
|
447
|
-
- `@abide/abide/bundle/onMenu` — `onMenu(handler)` / `onMenu(name, handler)`: subscribes to bundle menu clicks; returns an unsubscribe; inert during SSR and in plain browser tabs.
|
|
448
|
-
- `@abide/abide/bundle/bundled` — `bundled()`: true inside the desktop bundle (client: webview init flag; server: launcher-spawned process), false in a plain browser tab or on a remote server.
|
|
449
|
-
- `@abide/abide/server/appDataDir` — `appDataDir()`: the running bundle's per-user data dir, keyed by the bundler-injected program name; pure path computation, cwd-independent (`ABIDE_DATA_DIR` overrides).
|
|
563
|
+
## Testing
|
|
450
564
|
|
|
451
|
-
|
|
565
|
+
### Testing — @documentation testing
|
|
452
566
|
|
|
453
|
-
- `@abide/abide/
|
|
567
|
+
- `@abide/abide/test/createTestApp` — `createTestApp()` boots the real app on an
|
|
568
|
+
ephemeral port; returns `{ origin, fetch, rpc, sockets, health, stop,
|
|
569
|
+
[Symbol.asyncDispose] }` (`rpc`/`sockets` typed by generated d.ts). Use
|
|
570
|
+
`await using`.
|
|
454
571
|
|
|
455
|
-
|
|
572
|
+
### Testing plumbing — @documentation plumbing
|
|
456
573
|
|
|
457
|
-
- `@abide/abide/test/
|
|
458
|
-
|
|
459
|
-
- `@abide/abide/test/assertAgentFrameConformance` —
|
|
574
|
+
- `@abide/abide/test/createScriptedSurface` — `createScriptedSurface(tools?)` a
|
|
575
|
+
scripted `AgentSurface` for engine tests; records every `call`.
|
|
576
|
+
- `@abide/abide/test/assertAgentFrameConformance` — collects an engine frame
|
|
577
|
+
stream and asserts the neutral `AgentFrame` contract (one terminal `done`; every
|
|
578
|
+
`tool_use` answered), throwing on violation.
|
|
460
579
|
|
|
461
580
|
## Generated machine surfaces
|
|
462
581
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
|
466
|
-
|
|
|
467
|
-
| `/
|
|
468
|
-
| `/__abide/
|
|
469
|
-
| `/__abide/
|
|
470
|
-
| `/__abide/
|
|
471
|
-
| `/__abide/
|
|
472
|
-
| `/__abide/
|
|
582
|
+
Runtime routes the framework serves:
|
|
583
|
+
|
|
584
|
+
| Route | Serves |
|
|
585
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
586
|
+
| `/openapi.json` | OpenAPI spec for the public `/rpc/*` surface, built lazily from the frozen RPC registry. |
|
|
587
|
+
| `/__abide/mcp` | MCP endpoint (POST → `mcp.handle`), through the app auth/CSRF pipeline; mounted when an MCP is configured. |
|
|
588
|
+
| `/__abide/health` | Health/identity probe answered ahead of app middleware (framework identity + `app.health()` fields), `no-store`. |
|
|
589
|
+
| `/__abide/identity` | Compatibility alias of the health payload, stamped `{ abide: true }` for legacy probers. |
|
|
590
|
+
| `/__abide/inspector` | Operator inspector UI + data/SSE routes, gated by `ABIDE_ENABLE_INSPECTOR` (optional `@abide/inspector`). |
|
|
591
|
+
| `/__abide/sockets` | WebSocket upgrade for the socket multiplex hub; `/__abide/sockets/<name>` is one socket's HTTP face (GET tail, POST publish). |
|
|
592
|
+
| `/__abide/cli` | GET returns the platform-detecting install script; `/__abide/cli/<platform>` streams the built CLI binary tarball. |
|
|
473
593
|
|
|
474
594
|
## Environment variables
|
|
475
595
|
|
|
476
|
-
| Variable
|
|
477
|
-
|
|
|
478
|
-
| `PORT`
|
|
479
|
-
| `APP_URL`
|
|
480
|
-
| `ABIDE_APP_URL`
|
|
481
|
-
| `ABIDE_APP_TOKEN`
|
|
482
|
-
| `
|
|
483
|
-
| `ABIDE_DATA_DIR`
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
486
|
-
| `
|
|
487
|
-
| `ABIDE_LOG_FORMAT`
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `DEBUG`
|
|
596
|
+
| Variable | Effect |
|
|
597
|
+
| ----------------------------- | -------------------------------------------------------------------------------------------- |
|
|
598
|
+
| `PORT` | Exact TCP port to bind; unset/invalid scans from 3000. |
|
|
599
|
+
| `APP_URL` | Derives the server's mount base path from its pathname (e.g. `/v2`). |
|
|
600
|
+
| `ABIDE_APP_URL` | Default server URL the CLI connects to; its pathname sets the mount base. |
|
|
601
|
+
| `ABIDE_APP_TOKEN` | Sent as `Authorization: Bearer <value>` on CLI→server requests. |
|
|
602
|
+
| `ABIDE_APP_DIR` | Overrides the dir the server serves chunks/shell/assets from (set per dev build generation). |
|
|
603
|
+
| `ABIDE_DATA_DIR` | Overrides the app data directory on all platforms, used as-is (no program-name suffix). |
|
|
604
|
+
| `ABIDE_CLIENT_TIMEOUT` | RPC client timeout in ms (1–600000), shipped to the browser transport. |
|
|
605
|
+
| `ABIDE_MAX_REQUEST_BODY_SIZE` | Server-wide max request body size. |
|
|
606
|
+
| `ABIDE_IDLE_TIMEOUT` | Bun per-connection idle timeout in seconds (default 10). |
|
|
607
|
+
| `ABIDE_LOG_FORMAT` | `json` renders log records as JSON instead of TSV. |
|
|
608
|
+
| `ABIDE_ENABLE_INSPECTOR` | `true` mounts the opt-in operator inspector UI/routes. |
|
|
609
|
+
| `ABIDE_INSPECT` | Enables webview devtools/inspect for desktop bundles. |
|
|
610
|
+
| `ABIDE_DEV_SURFACE` | `1` forces the worker to print its surface map (set by the dev orchestrator). |
|
|
611
|
+
| `DEBUG` | npm-debug-style channel gate for diagnostic log channels (e.g. `abide:cache`, `-abide`). |
|
|
492
612
|
|
|
493
613
|
---
|
|
494
614
|
|
|
495
|
-
|
|
496
|
-
|
|
615
|
+
Mirrors `package.json`'s `exports`; run
|
|
616
|
+
`bun run packages/abide/scripts/readmeSurfaces.ts` after adding or renaming an
|
|
617
|
+
export to keep this map honest.
|