@abide/abide 0.40.0 → 0.40.1
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 +318 -146
- package/CHANGELOG.md +42 -0
- package/README.md +77 -79
- package/package.json +1 -1
- package/src/lib/shared/assertExhaustive.ts +14 -0
- package/src/lib/ui/compile/TS_PRINTER.ts +7 -0
- package/src/lib/ui/compile/analyzeComponent.ts +15 -16
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +66 -0
- package/src/lib/ui/compile/assertTranspiles.ts +19 -0
- package/src/lib/ui/compile/compileModule.ts +56 -34
- package/src/lib/ui/compile/compileSSR.ts +1 -3
- package/src/lib/ui/compile/desugarSignals.ts +168 -107
- package/src/lib/ui/compile/generateBuild.ts +8 -1
- package/src/lib/ui/compile/generateSSR.ts +7 -0
- package/src/lib/ui/compile/hoistCells.ts +2 -2
- package/src/lib/ui/compile/lowerContext.ts +23 -10
- package/src/lib/ui/compile/lowerDocAccess.ts +29 -3
- package/src/lib/ui/compile/lowerScript.ts +64 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +160 -102
- package/src/lib/ui/compile/stripEffects.ts +22 -17
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +5 -0
- package/src/lib/ui/dom/awaitBlock.ts +8 -1
- package/src/lib/ui/dom/each.ts +4 -0
- package/src/lib/ui/dom/switchBlock.ts +4 -0
- package/src/lib/ui/dom/when.ts +4 -0
- package/src/lib/ui/installInspectorBridge.ts +3 -1
- package/src/lib/ui/router.ts +107 -14
- package/src/lib/ui/runtime/NODE_STATE.ts +22 -0
- package/src/lib/ui/runtime/clientPage.ts +114 -9
- package/src/lib/ui/runtime/createComputedNode.ts +5 -3
- package/src/lib/ui/runtime/createEffectNode.ts +3 -1
- package/src/lib/ui/runtime/createSignalNode.ts +4 -2
- package/src/lib/ui/runtime/flushEffects.ts +8 -5
- package/src/lib/ui/runtime/historyEntries.ts +39 -1
- package/src/lib/ui/runtime/readNode.ts +8 -7
- package/src/lib/ui/runtime/runNode.ts +18 -2
- package/src/lib/ui/runtime/scope.ts +12 -1
- package/src/lib/ui/runtime/trigger.ts +40 -24
- package/src/lib/ui/runtime/types/ReactiveNode.ts +4 -1
- package/src/lib/ui/runtime/updateIfNecessary.ts +40 -0
package/AGENTS.md
CHANGED
|
@@ -3,228 +3,400 @@
|
|
|
3
3
|
> The exhaustive index of abide's public surface: every `exports` key appears once,
|
|
4
4
|
> grouped by namespace, with its import specifier and a one-line spec — so an agent
|
|
5
5
|
> grasps the whole API in one read and knows which file to open for depth. The README
|
|
6
|
-
> is the curated
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
> Package `@abide/abide`, runtime Bun `>= 1.3.0`, one dependency (`typescript`); the
|
|
14
|
-
> import specifier (`abide/server/GET`) is the `@abide/abide/...` `exports` key, not the
|
|
15
|
-
> on-disk file path.
|
|
6
|
+
> is the curated three-primitive intro (RPCs, sockets, components); `CONTEXT.md` is the
|
|
7
|
+
> glossary and `docs/adr/` the rationale. No barrels: every public name has its own
|
|
8
|
+
> module path, and the namespace marks the side it runs on — `abide/server/*` is
|
|
9
|
+
> server-only, `abide/ui/*` client-only, `abide/shared/*` isomorphic (same callable,
|
|
10
|
+
> same behaviour on both sides). Package `@abide/abide`, single Bun runtime (≥ 1.3.0),
|
|
11
|
+
> one direct dependency (`typescript`). Bullets show the import specifier; the file
|
|
12
|
+
> path it resolves to lives in `package.json` `exports`.
|
|
16
13
|
|
|
17
14
|
## The premise
|
|
18
15
|
|
|
16
|
+
One typed verb declaration fans out to five surfaces:
|
|
19
17
|
```text
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
cache(fn)() fetch (read-only) command operation
|
|
18
|
+
getMessages = GET(fn, { inputSchema })
|
|
19
|
+
│
|
|
20
|
+
┌───────────┬─────────────┼────────────┬──────────────┐
|
|
21
|
+
SSR call browser fetch MCP tool CLI command OpenAPI op
|
|
22
|
+
cache(fn)() typed proxy() (read-only) abide ... cli /openapi.json
|
|
26
23
|
```
|
|
27
24
|
|
|
28
|
-
A
|
|
25
|
+
A schema unlocks the CLI everywhere and MCP for read-only verbs; a mutating verb
|
|
26
|
+
never auto-exposes to MCP — it needs explicit `clients: { mcp: true }`.
|
|
29
27
|
|
|
30
28
|
## File-based conventions
|
|
31
29
|
|
|
32
|
-
The bundler reads these paths
|
|
33
|
-
|
|
34
|
-
| Path
|
|
35
|
-
|
|
|
36
|
-
| `src/server/rpc/<name>.ts`
|
|
37
|
-
| `src/server/sockets/<name>.ts` | One
|
|
38
|
-
| `src/
|
|
39
|
-
| `src/
|
|
40
|
-
| `src/
|
|
41
|
-
| `src/
|
|
42
|
-
| `src/
|
|
43
|
-
| `src/ui/pages
|
|
44
|
-
| `src/ui/
|
|
45
|
-
| `src
|
|
46
|
-
| `
|
|
47
|
-
|
|
48
|
-
|
|
30
|
+
The bundler reads these paths by convention:
|
|
31
|
+
|
|
32
|
+
| Path | Meaning |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `src/server/rpc/<name>.ts` | One HTTP verb per file; the path is the route, the schema projects MCP/CLI/OpenAPI |
|
|
35
|
+
| `src/server/sockets/<name>.ts` | One broadcast socket per file; file name is the topic |
|
|
36
|
+
| `src/server/prompts/<name>.md` | One MCP prompt per file; `{{name}}` placeholders, optional frontmatter |
|
|
37
|
+
| `src/server/config.ts` | Optional `env(schema)` validation, run at boot |
|
|
38
|
+
| `src/app.ts` | Optional `AppModule` lifecycle hooks (`init`/`handle`/`handleError`/`health`/`forwardHeaders`) |
|
|
39
|
+
| `src/bundle/window.ts` | Optional desktop `BundleWindow` config (title, size, menus, setup form) |
|
|
40
|
+
| `src/ui/pages/<path>/page.abide` | A route; folder path is the URL, `[id]` segments are params |
|
|
41
|
+
| `src/ui/pages/<path>/layout.abide` | A layout wrapping pages at or below its folder |
|
|
42
|
+
| `src/ui/public/` | Static assets served from `/`, embedded in the standalone binary |
|
|
43
|
+
| `src/.abide/*.d.ts` | Generated route/rpc/health types — do not hand-edit |
|
|
44
|
+
| `dist/_app/` | Generated client bundle (code-split, hashed chunks) |
|
|
45
|
+
|
|
46
|
+
Aliases: `$server` → `src/server/`, `$ui` → `src/ui/`, `$shared` → `src/shared/`,
|
|
47
|
+
`$mcp` → `src/mcp/`, `$cli` → `src/cli/`.
|
|
49
48
|
|
|
50
49
|
## CLI
|
|
51
50
|
|
|
52
|
-
| Command
|
|
53
|
-
|
|
|
54
|
-
| `abide scaffold <name>`
|
|
55
|
-
| `abide dev`
|
|
56
|
-
| `abide build`
|
|
57
|
-
| `abide
|
|
58
|
-
| `abide
|
|
59
|
-
| `abide
|
|
60
|
-
| `abide
|
|
61
|
-
| `abide
|
|
62
|
-
| `abide
|
|
63
|
-
| `abide
|
|
64
|
-
| `abide init-agent` | Write/refresh a `CLAUDE.md` pointer to this surface map |
|
|
51
|
+
| Command | Does |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `abide scaffold <name>` | Copy the template, install, start dev (`--no-install`, `--no-dev`) |
|
|
54
|
+
| `abide dev` | Build + run with hot reload; watches `src/`, rebuilds client, restarts server |
|
|
55
|
+
| `abide build` | One-shot client build into `dist/_app/` |
|
|
56
|
+
| `abide check` | Type-check `.abide` templates + props via the shadow language service |
|
|
57
|
+
| `abide start` | Run the production server against a pre-built `dist/` |
|
|
58
|
+
| `abide run <file> [args]` | Run a script under the abide preload (jobs, tests) |
|
|
59
|
+
| `abide compile` | Build a standalone server executable with embedded assets |
|
|
60
|
+
| `abide cli` | Build a thin CLI binary that ships the server (`--platforms` cross-compiles) |
|
|
61
|
+
| `abide bundle` | Build a movable, self-contained desktop app bundle for this platform |
|
|
62
|
+
| `abide init-agent` | Write/refresh a `CLAUDE.md` pointer to this surface map |
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Tests preload the framework: `preload = ["@abide/abide/preload"]` in `bunfig.toml`
|
|
65
|
+
registers `.abide` compilation so `bun test` runs the same runtime as the server.
|
|
67
66
|
|
|
68
|
-
##
|
|
67
|
+
## Authoring contracts
|
|
68
|
+
|
|
69
|
+
What you write into each convention path, and the contract the framework holds
|
|
70
|
+
you to. The per-export specs below are exhaustive; this is the shape of the code
|
|
71
|
+
that produces them.
|
|
72
|
+
|
|
73
|
+
### RPC verb — `src/server/rpc/<name>.ts`
|
|
74
|
+
|
|
75
|
+
- One `export const <name> = METHOD(handler, opts?)` per file (`METHOD` ∈ `GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`HEAD`); the file path is the route.
|
|
76
|
+
- The handler receives the **validated** input — `InferOutput<inputSchema>`, or `{}` when schemaless — and may read request scope via `request()` / `cookies()`. It returns `json()`, `jsonl()`, `sse()`, `error()`, `redirect()`, or a raw `Response`; the union of those branches is the verb's typed result.
|
|
77
|
+
- `opts`: `{ inputSchema?, outputSchema?, filesSchema?, clients?: { browser, mcp, cli }, crossOrigin?, maxBodySize?, timeout? }`. A Standard Schema flips MCP + CLI on automatically; a **mutating** verb still needs explicit `clients: { mcp: true }`. `crossOrigin: true` exempts a mutating verb from the same-origin CSRF check. `timeout` (ms) bounds the handler on every surface (504 past it). `filesSchema` validates multipart `File` parts (the caller sends `FormData`).
|
|
78
|
+
- Query args arrive as **strings** — validate with `z.coerce.*`.
|
|
79
|
+
- Consume four ways: `cache(verb)(args)` in-process (SSR, warm hydration), the same call over a swapped `fetch` in the browser, `verb.raw(args)` for the undecoded `Response`, `verb.stream(args)` for an iterable body view.
|
|
80
|
+
|
|
81
|
+
### Socket — `src/server/sockets/<name>.ts`
|
|
82
|
+
|
|
83
|
+
- One `export const <name> = socket({ schema, tail?, ttl?, clientPublish?, clients? })`; the file name is the topic.
|
|
84
|
+
- `.publish(msg)` validates then broadcasts; `.tail(n?)` replays the retained window then streams live; bare `for await` is live-only. `tail` = frames retained, `ttl` = frame expiry (ms), `clientPublish` = allow a browser `POST` (default off, so route writes through a verb that validates first).
|
|
85
|
+
- HTTP face `/__abide/sockets/<name>`: `GET` returns the tail, `POST` publishes (gated by `clientPublish`).
|
|
86
|
+
- Consume in a component with `tail(socket)` (latest-wins) or `tail(socket, { last: n })` (window).
|
|
87
|
+
|
|
88
|
+
### Page & layout — `src/ui/pages/**`
|
|
89
|
+
|
|
90
|
+
- `page.abide` is a route; the folder path is the URL. `[id]` segments become params read off the `page` proxy (`page.params.id`), typed via generated `src/.abide/routes.d.ts`, reactive across navigation.
|
|
91
|
+
- `layout.abide` wraps every page at or below its folder; its `<slot/>` (compiled to an outlet) holds the nested page or child layout.
|
|
92
|
+
- Build links with `url('/pages/product/[id]', { id })` and move with `navigate(href)`.
|
|
93
|
+
|
|
94
|
+
### App module & config — `src/app.ts`, `src/server/config.ts` (both optional)
|
|
95
|
+
|
|
96
|
+
- `src/app.ts` exports the `AppModule` hooks: `forwardHeaders`, `init`, `handle`, `handleError`, `health`.
|
|
97
|
+
- `src/server/config.ts` exports `env(schema)`, validated against `Bun.env` at boot; the typed result is your config and seeds the bundle launcher's setup form.
|
|
98
|
+
|
|
99
|
+
### The isomorphism move
|
|
100
|
+
|
|
101
|
+
Import a server verb (or socket) straight into a `.abide` and call it: during SSR
|
|
102
|
+
the bundler keeps the real handler (it runs in-process), in the browser it swaps
|
|
103
|
+
to a `fetch` proxy of the same signature, same name. Wrap reads in `cache()` so the
|
|
104
|
+
SSR pass serializes the value into the snapshot and the client hydrates warm
|
|
105
|
+
instead of refetching.
|
|
106
|
+
|
|
107
|
+
## .abide template grammar
|
|
108
|
+
|
|
109
|
+
A `.abide` file is one component: an optional `<script>` (module scope), HTML
|
|
110
|
+
markup with reactive interpolation and `<template>` control flow, and optional
|
|
111
|
+
component-scoped `<style>`. Ambient in `<script>` — no import — are `scope`,
|
|
112
|
+
`props`, `effect`, `html`, and `snippet`; everything else (`cache`, `page`,
|
|
113
|
+
`navigate`, `tail`, verbs, sockets, child components) is imported normally.
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<script>
|
|
117
|
+
import { cache } from '@abide/abide/shared/cache'
|
|
118
|
+
import { getThing } from '$server/rpc/getThing.ts'
|
|
119
|
+
import Child from '$ui/Child.abide'
|
|
120
|
+
|
|
121
|
+
const { title = 'untitled' } = props<{ title?: string }>() // reactive props
|
|
122
|
+
let count = scope().state(0) // writable cell
|
|
123
|
+
const doubled = scope().computed(() => count * 2) // read-only derived
|
|
124
|
+
effect(() => console.log(count)) // re-runs on change
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<h1>{title}: {doubled}</h1> <!-- {expr} interpolation -->
|
|
128
|
+
<button onclick={() => (count = count + 1)}>inc</button> <!-- on<event>={fn} -->
|
|
129
|
+
<Child label={title}><p>slot body</p></Child> <!-- caps tag = component -->
|
|
130
|
+
|
|
131
|
+
<style>h1 { font-weight: 600 }</style> <!-- component-scoped -->
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Reactive state — `scope()` is the only reactive surface:
|
|
135
|
+
|
|
136
|
+
| Form | Meaning |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| `scope().state(v)` | writable cell — read by name, assign with `=` |
|
|
139
|
+
| `scope().computed(fn)` | read-only derived; re-runs when its read cells change |
|
|
140
|
+
| `scope().linked(fn)` | local draft reseeded from upstream on source change |
|
|
141
|
+
| `effect(fn)` | side effect; re-runs on change; return a teardown |
|
|
142
|
+
| `props()` / `props<T>()` | destructure reactive props, defaults allowed |
|
|
143
|
+
|
|
144
|
+
Interpolation & bindings:
|
|
145
|
+
|
|
146
|
+
| Syntax | Effect |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `{expr}` | reactive text, HTML-escaped; `{html(s)}` inserts trusted raw HTML |
|
|
149
|
+
| `name={expr}` | reactive attribute (omitted when nullish) |
|
|
150
|
+
| `on<event>={fn}` | event listener — `onclick`, `onsubmit`, … |
|
|
151
|
+
| `bind:value={cell}` | two-way bind to input / textarea / select |
|
|
152
|
+
| `bind:checked={cell}` | checkbox boolean bind |
|
|
153
|
+
| `bind:group={cell}` | radio (single value) / checkbox group (array) |
|
|
154
|
+
| `bind:value={{ get, set }}` | writable computed — a lens over derived state |
|
|
155
|
+
|
|
156
|
+
Control flow — native `<template>`:
|
|
157
|
+
|
|
158
|
+
| Directive | Shape |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| `<template if={c}>` … `<template elseif={c2}>` … `<template else>` | conditional chain (`else` must be last) |
|
|
161
|
+
| `<template each={list} as="x" key="x.id">` | keyed list |
|
|
162
|
+
| `<template await={p}>` `<template then="v">` `<template catch="e">` `<template finally>` | promise (streams; branch value bound by `then`/`catch`) |
|
|
163
|
+
| `<template switch={s}>` `<template case={v}>` … `<template default>` | first strict-`===` match |
|
|
164
|
+
| `<template try>` `<template catch="e">` `<template finally>` | synchronous error boundary |
|
|
165
|
+
| `<template name="row" args={p}>` … `{row(p)}` | snippet — a named builder that closes over scope, rendered like a function and passable as a prop |
|
|
166
|
+
|
|
167
|
+
Components & slots — a capitalized tag is an imported component; its attributes
|
|
168
|
+
are props; child markup fills the default `<slot/>`, and `<el slot="footer">` fills
|
|
169
|
+
`<slot name="footer">`. A `then`/`catch` branch (and any nested `<script>` / `<style>`)
|
|
170
|
+
is its own lexical scope.
|
|
171
|
+
|
|
172
|
+
## Server surface — abide/server/*
|
|
69
173
|
|
|
70
174
|
### RPC verbs — @documentation rpc
|
|
71
175
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
176
|
+
- `@abide/abide/server/GET` — HTTP verb helper; the bundler rewrites `export const x = GET(fn, opts)` in `src/server/rpc/<file>.ts` to `defineVerb` server-side and `remoteProxy` client-side.
|
|
177
|
+
- `@abide/abide/server/POST` — as `GET`, for `POST`.
|
|
178
|
+
- `@abide/abide/server/PUT` — as `GET`, for `PUT`.
|
|
179
|
+
- `@abide/abide/server/PATCH` — as `GET`, for `PATCH`.
|
|
180
|
+
- `@abide/abide/server/DELETE` — as `GET`, for `DELETE`.
|
|
181
|
+
- `@abide/abide/server/HEAD` — as `GET`, for `HEAD`.
|
|
182
|
+
- `@abide/abide/server/rpc/defineVerb` — the verb factory (plumbing): handler + options (`inputSchema`, `outputSchema`, `filesSchema`, `clients`, `timeout`, `maxBodySize`, `crossOrigin`); returns a `RemoteFunction` — plain call decodes by Content-Type and throws `HttpError` on non-2xx, `.raw(args)` returns the `Response`, `.stream(args)` an iterable body view; auto-exposes to CLI/MCP per schema + method.
|
|
74
183
|
|
|
75
|
-
###
|
|
184
|
+
### Response helpers — @documentation response
|
|
76
185
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
- `abide/shared/HttpError` — `new HttpError(response)`: carries `status`/`statusText`/`response`; thrown by a proxy call on a non-2xx.
|
|
186
|
+
- `@abide/abide/server/json` — JSON `Response`, `Cache-Control: no-store`; `undefined` → 204; phantom `TypedResponse` brand carries the return type.
|
|
187
|
+
- `@abide/abide/server/jsonl` — wraps `AsyncIterable<Frame>` as JSON Lines; frame errors emit a `{"$error":...}` line; cancellation flows to `iter.return()`.
|
|
188
|
+
- `@abide/abide/server/sse` — wraps `AsyncIterable<Frame>` as Server-Sent Events; 15s keepalive comments, error frames as `event: error`.
|
|
189
|
+
- `@abide/abide/server/error` — plain-text error `Response(status, message?, init?)`; defaults to the standard status text; `TypedResponse<never>` so error branches union cleanly.
|
|
190
|
+
- `@abide/abide/server/redirect` — redirect `Response(url, status=302, init?)`; relative URLs allowed, 301/302/303/307/308, `TypedResponse<never>`.
|
|
83
191
|
|
|
84
192
|
### Request scope — @documentation request-scope
|
|
85
193
|
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
194
|
+
- `@abide/abide/server/request` — the inbound `Request` for the current SSR/RPC pass (AsyncLocalStorage); throws outside request scope.
|
|
195
|
+
- `@abide/abide/server/cookies` — `Bun.CookieMap` for the in-flight request; `.set()`/`.delete()` flush to `Set-Cookie`; throws outside scope.
|
|
196
|
+
- `@abide/abide/server/server` — the active `Bun.serve` instance (getter, shows in stack traces); in-process server inside scope when none is bound.
|
|
89
197
|
|
|
90
198
|
### Configuration — @documentation configuration
|
|
91
199
|
|
|
92
|
-
-
|
|
200
|
+
- `@abide/abide/server/env` — validate `Bun.env` against a Standard Schema, returns typed config; fails fast listing all issues; registers the schema for the bundle launcher's setup form.
|
|
201
|
+
|
|
202
|
+
### Reachability — @documentation observability
|
|
203
|
+
|
|
204
|
+
- `@abide/abide/server/reachable` — server-only probe of an external host: awaits a `HEAD` (`ABIDE_REACHABLE_TIMEOUT`), then background-polls at `ABIDE_REACHABLE_TTL`; returns a boolean, status-agnostic (any completed HTTP counts).
|
|
93
205
|
|
|
94
206
|
### Sockets — @documentation sockets
|
|
95
207
|
|
|
96
|
-
-
|
|
97
|
-
-
|
|
208
|
+
- `@abide/abide/server/socket` — `Socket<T>` declaration; the bundler placeholder throws if called outside `export const <file> = ...` in `src/server/sockets/`.
|
|
209
|
+
- `@abide/abide/server/sockets/defineSocket` — server socket factory (plumbing): `name` + `{ tail, ttl, schema, clientPublish, clients }`; `.publish(msg)` validates then broadcasts via `server.publish`, `.tail(count?, hooks?)` replays history then streams live, bare iteration is live-only; auto-MCP/CLI when a schema is present.
|
|
98
210
|
|
|
99
|
-
###
|
|
211
|
+
### Agent — @documentation agent
|
|
100
212
|
|
|
101
|
-
-
|
|
102
|
-
- `abide/server/agent` — `agent(engine, messages)`: run a model engine against the current request's MCP surface (caller auth forwarded into every tool call), yielding neutral `AgentFrame`s to wrap in `jsonl`/`sse`.
|
|
103
|
-
- `abide/server/InspectorContext` — the capability bundle (`app`, `loadSurface`, `cacheSnapshot`, `inFlightSnapshot`, `onRecord`) handed to `@abide/inspector` when `ABIDE_ENABLE_INSPECTOR=true`.
|
|
213
|
+
- `@abide/abide/server/agent` — run the AgentEngine (surface + messages → `AsyncIterable<AgentFrame>`) against the request's MCP surface; wrap the stream with `jsonl()`/`sse()`, auth forwarded per tool.
|
|
104
214
|
|
|
105
215
|
### Prompts — @documentation plumbing
|
|
106
216
|
|
|
107
|
-
-
|
|
108
|
-
-
|
|
217
|
+
- `@abide/abide/server/prompts/definePrompt` — prompt factory: `name` + `{ description, jsonSchema, render }`; registered for MCP enumeration (the resolver generates these from `src/server/prompts/*.md`).
|
|
218
|
+
- `@abide/abide/server/prompts/renderPromptTemplate` — substitute `{{name}}` placeholders in a markdown template from a `Record<string,string>`; missing args render empty.
|
|
109
219
|
|
|
110
|
-
|
|
220
|
+
### App + inspector hooks — @documentation plumbing
|
|
111
221
|
|
|
112
|
-
|
|
222
|
+
- `@abide/abide/server/AppModule` — the `src/app.ts` hook contract: `forwardHeaders`, `init(ctx)`, `handle(req, next)`, `handleError(err, req)`, `health(req)`.
|
|
223
|
+
- `@abide/abide/server/InspectorContext` — capabilities handed to `@abide/inspector` when enabled: identity, `loadSurface()`, `cacheSnapshot()`, `inFlightSnapshot()`, `onRecord(listener)`.
|
|
113
224
|
|
|
114
|
-
|
|
225
|
+
## Isomorphic surface — abide/shared/*
|
|
115
226
|
|
|
116
|
-
###
|
|
227
|
+
### RPC contract — @documentation rpc
|
|
228
|
+
|
|
229
|
+
- `@abide/abide/shared/withJsonSchema` — attach a `toJSONSchema()` to a Standard Schema, enabling projection to OpenAPI, MCP tools, CLI help, and bundle forms.
|
|
230
|
+
|
|
231
|
+
### Errors — @documentation response
|
|
232
|
+
|
|
233
|
+
- `@abide/abide/shared/HttpError` — `Error` subclass thrown by remote calls on non-2xx; carries `status`, `statusText`, and the raw `Response`.
|
|
234
|
+
|
|
235
|
+
### Cache — @documentation cache
|
|
117
236
|
|
|
118
|
-
-
|
|
119
|
-
- `abide/shared/refreshing` — `refreshing(fn?, args?)`: `true` while a matching entry holds a value but a fresher fetch is in flight.
|
|
120
|
-
- `abide/shared/online` — `online()`: `navigator.onLine` (client) / offline-header (SSR); reactive.
|
|
237
|
+
- `@abide/abide/shared/cache` — wrap a sync/async callable (remote or producer) with deduped, optional-TTL storage; SSR-aware streaming hydration, stale-while-revalidate, and optimistic mutation.
|
|
121
238
|
|
|
122
239
|
### Templating — @documentation templating
|
|
123
240
|
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
- `abide/shared/withJsonSchema` — `withJsonSchema(schema, toJsonSchema)`: attach a JSON Schema to a Standard Schema for OpenAPI/MCP/CLI/form projection.
|
|
241
|
+
- `@abide/abide/shared/html` — mark a string as trusted raw HTML inserted verbatim in interpolations; plain-call and tagged-template forms.
|
|
242
|
+
- `@abide/abide/shared/snippet` — brand a snippet payload (DOM builder on the client, HTML string on the server) so interpolations mount it instead of escaping.
|
|
127
243
|
|
|
128
|
-
### Page
|
|
244
|
+
### Page — @documentation page
|
|
129
245
|
|
|
130
|
-
-
|
|
131
|
-
|
|
246
|
+
- `@abide/abide/shared/page` — reactive page snapshot: matched route, decoded params, browser-space URL, and a `navigating` flag; isomorphic.
|
|
247
|
+
|
|
248
|
+
### Probes — @documentation probes
|
|
249
|
+
|
|
250
|
+
- `@abide/abide/shared/pending` — reactive: any cache call in-flight, any stream awaiting its first frame, or a tagged group pending.
|
|
251
|
+
- `@abide/abide/shared/refreshing` — reactive: a cache call holds a value while revalidating, or a stream reconnects with its window retained.
|
|
252
|
+
- `@abide/abide/shared/online` — reactive connectivity: `navigator.onLine` on the client, the request's `OFFLINE_HEADER` on the server.
|
|
253
|
+
- `@abide/abide/shared/health` — reactive backend health: a `reachable` flag polled from `/__abide/health` every 10s while visible, plus app `health()` fields; `navigator.onLine` composes at read.
|
|
254
|
+
|
|
255
|
+
### URL — @documentation url
|
|
256
|
+
|
|
257
|
+
- `@abide/abide/shared/url` — resolve in-app URLs to base-correct typed form: verb args serialize to query, page routes interpolate params, assets pass through; external URLs untouched.
|
|
132
258
|
|
|
133
259
|
### Observability — @documentation observability
|
|
134
260
|
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
- `abide/shared/trace` — `trace()`: the W3C `traceparent` for the current request, or `undefined` outside scope.
|
|
138
|
-
- `abide/shared/createSubscriber` — `createSubscriber(start)`: open-on-first-read / close-on-last-reader resource lifecycle for reactive sources.
|
|
139
|
-
- `abide/server/reachable` — `reachable(host)` (server-only): cached liveness probe; HEADs the origin and background-polls per `ABIDE_REACHABLE_TTL`, so reads resolve instantly off the warm value.
|
|
261
|
+
- `@abide/abide/shared/log` — unified logger with request-scope context (trace id, elapsed, verb+path); `log`/`warn`/`error`/`trace` on the always-on channel, `log.channel(name)` is `DEBUG`-gated.
|
|
262
|
+
- `@abide/abide/shared/trace` — the request's W3C `traceparent` string for propagation; `undefined` outside request scope.
|
|
140
263
|
|
|
141
|
-
|
|
264
|
+
### Subscribers — @documentation plumbing
|
|
265
|
+
|
|
266
|
+
- `@abide/abide/shared/createSubscriber` — lazy resource lifecycle: `start()` opens on first tracked read, close deferred to a microtask on last disposal, subscribers shared by reference.
|
|
267
|
+
|
|
268
|
+
## UI surface — abide/ui/* (client-only)
|
|
142
269
|
|
|
143
270
|
### Reactive state — @documentation reactive-state
|
|
144
271
|
|
|
145
|
-
-
|
|
272
|
+
- `@abide/abide/ui/scope` — the sole reactive surface: `scope()` resolves the current lexical scope (`scope('/')` the root); returned scope has `.state()` (writable cell), `.computed()` (read-only derived), `.linked()` (draft reseeded from upstream). A writable computed is expressed at the binding, `bind:value={{ get, set }}`.
|
|
146
273
|
|
|
147
|
-
###
|
|
274
|
+
### Effects — @documentation effect
|
|
148
275
|
|
|
149
|
-
-
|
|
276
|
+
- `@abide/abide/ui/effect` — run `fn` now and re-run when its read cells change; `fn` may return a teardown; captures lexical scope. Client-only.
|
|
150
277
|
|
|
151
|
-
### Tail
|
|
278
|
+
### Tail — @documentation tail
|
|
152
279
|
|
|
153
|
-
-
|
|
154
|
-
- `abide/ui/navigate` — `navigate(path, options?)`: client navigation through the router. `options` is `{ replace?, keepScroll? }` — `replace` swaps the current history entry, `keepScroll` preserves the live scroll offset across an in-page URL swap.
|
|
280
|
+
- `@abide/abide/ui/tail` — reactive stream consumer: latest-wins `T | undefined`, or a window `T[]` with `{ last: n }`; reconnect retains the window and flags `refreshing`; seeds from `Subscribable.tail(count)`.
|
|
155
281
|
|
|
156
|
-
###
|
|
282
|
+
### Navigation — @documentation navigate
|
|
157
283
|
|
|
158
|
-
-
|
|
284
|
+
- `@abide/abide/ui/navigate` — client navigation: push/replace a history entry, update the reactive route (re-mounts), bucket scroll per entry; `keepScroll` carries the live offset across.
|
|
159
285
|
|
|
160
|
-
###
|
|
286
|
+
### Outbox — @documentation ui
|
|
161
287
|
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
-
|
|
288
|
+
- `@abide/abide/ui/outbox` — durable FIFO mutation queue for local-first writes: enqueue appends and drains head-first while online, drops permanently-failed entries via `onDrop`, resumes on reconnect.
|
|
289
|
+
|
|
290
|
+
### Client glue — @documentation plumbing
|
|
291
|
+
|
|
292
|
+
- `@abide/abide/ui/router` — client router on the History API: matches patterns, imports page/layout chunks on demand, diffs and rebuilds divergent layers, restores scroll per entry, probes the server gate on SPA nav.
|
|
293
|
+
- `@abide/abide/ui/startClient` — the official client entry: seed cache from the SSR snapshot, install the base, start the router (adopts SSR DOM), returns a disposer.
|
|
294
|
+
- `@abide/abide/ui/renderToStream` — out-of-order SSR streaming: shell first, then resolved `{#await}` fragments in completion order with a resume seed; blocking awaits render inline.
|
|
295
|
+
- `@abide/abide/ui/remoteProxy` — client remote function: build the `Request` (base, mount, offline header, traceparent), decode by Content-Type, throw `HttpError` on non-2xx.
|
|
296
|
+
- `@abide/abide/ui/socketProxy` — client `Socket`: bare iteration is the live stream, `.tail(n)` seeds from the retained tail, `.publish` sends a server-validated frame; over the multiplexed ws.
|
|
297
|
+
- `@abide/abide/ui/enterScope` / `@abide/abide/ui/exitScope` — open and restore an isolated lexical scope around an SSR render so `scope()` and model state don't bleed.
|
|
298
|
+
|
|
299
|
+
### DOM runtime — @documentation plumbing
|
|
300
|
+
|
|
301
|
+
Compiler emit targets, not written by hand — the runtime the compiled `.abide`
|
|
302
|
+
output calls into to build, hydrate, and reconcile the DOM.
|
|
303
|
+
|
|
304
|
+
- `@abide/abide/ui/dom/mount` — mount a top-level page/layout into a host element under an ownership scope; returns a disposer.
|
|
305
|
+
- `@abide/abide/ui/dom/mountChild` — mount a child component as a marker-bounded range, with optional hot-reload support.
|
|
306
|
+
- `@abide/abide/ui/dom/mountSlot` — mount a slot's content as a marker-bounded range that renders once without re-render.
|
|
307
|
+
- `@abide/abide/ui/dom/outlet` — a layout's empty outlet boundary for the router to fill with the next chain layer.
|
|
308
|
+
- `@abide/abide/ui/dom/hydrate` — adopt existing server-rendered DOM instead of rebuilding, attaching listeners and effects to preserve focus/scroll.
|
|
309
|
+
- `@abide/abide/ui/dom/skeleton` — realize a compiled skeleton under a parent, returning the element and anchor holes for binding.
|
|
310
|
+
- `@abide/abide/ui/dom/cloneStatic` — append a fully-static subtree by cloning a cached template (create) or claiming server nodes (hydrate).
|
|
311
|
+
- `@abide/abide/ui/dom/appendStatic` — a static text node, claimed on hydrate or appended on create.
|
|
312
|
+
- `@abide/abide/ui/dom/anchorCursor` — position a control-flow block or slot by its skeleton anchor, returning the insertion reference.
|
|
313
|
+
- `@abide/abide/ui/dom/text` — a text node whose content tracks a reactive read, updating fine-grained on cell changes.
|
|
314
|
+
- `@abide/abide/ui/dom/appendText` — a reactive interpolation branching on value kind: escaped text, snippet builder, or raw HTML.
|
|
315
|
+
- `@abide/abide/ui/dom/appendTextAt` — as `appendText`, positioned at a skeleton anchor with live re-parsing.
|
|
316
|
+
- `@abide/abide/ui/dom/appendSnippet` — a snippet call that mounts/remounts its builder in a marker-bounded range on argument change.
|
|
317
|
+
- `@abide/abide/ui/dom/attr` — bind an element attribute to a reactive read with present/absent semantics.
|
|
318
|
+
- `@abide/abide/ui/dom/on` — attach an event listener registered with the ownership scope for cleanup on dispose.
|
|
319
|
+
- `@abide/abide/ui/dom/attach` — run an attachment at build time and register its optional teardown with the ownership scope.
|
|
320
|
+
- `@abide/abide/ui/dom/each` — keyed list binding with reconciliation by key, minimal DOM moves, optional hydration.
|
|
321
|
+
- `@abide/abide/ui/dom/eachAsync` — async keyed list appending rows as an iterator yields, reconciling by key with error handling.
|
|
322
|
+
- `@abide/abide/ui/dom/when` — conditional binding that swaps range content on condition flip; an unchanged condition is a no-op.
|
|
323
|
+
- `@abide/abide/ui/dom/awaitBlock` — async binding with pending/resolved/error branches, reactive re-run, and hydration by precedence.
|
|
324
|
+
- `@abide/abide/ui/dom/tryBlock` — synchronous error boundary building guarded content or the error branch on throw.
|
|
325
|
+
- `@abide/abide/ui/dom/switchBlock` — multi-branch binding picking the first case matching the subject (strict `===`).
|
|
326
|
+
- `@abide/abide/ui/dom/applyResolved` — bundle-side consumer of streamed resolution chunks, swapping pending DOM and seeding cache.
|
|
327
|
+
- `@abide/abide/ui/dom/mergeProps` — compose a child's props from layered sources (explicit, spreads, slot), last-writer-wins.
|
|
328
|
+
- `@abide/abide/ui/dom/spreadProps` — wrap a reactive spread layer so every key resolves to a live value thunk.
|
|
329
|
+
- `@abide/abide/ui/dom/restProps` — return unconsumed prop values as a live object, unwrapping thunks, excluding consumed/slot keys.
|
|
330
|
+
- `@abide/abide/ui/dom/spreadAttrs` — spread an object's keys onto a native element, binding event listeners and reactive attributes.
|
|
331
|
+
- `@abide/abide/ui/dom/readCall` — guard a method call on a reactive-document read, with friendly errors for nullish/non-callable members.
|
|
332
|
+
|
|
333
|
+
### Render-pass runtime — @documentation plumbing
|
|
334
|
+
|
|
335
|
+
- `@abide/abide/ui/runtime/escapeKey` — JSON-Pointer-escape reactive-doc path keys (`~`→`~0`, `/`→`~1`) so composite keys survive path joining.
|
|
336
|
+
- `@abide/abide/ui/runtime/nextBlockId` — next block id in the render pass (per await/try block).
|
|
337
|
+
- `@abide/abide/ui/runtime/enterRenderPass` — mark entry into a render/mount, resetting the block-id counter at depth 0.
|
|
338
|
+
- `@abide/abide/ui/runtime/exitRenderPass` — mark exit from a render/mount, unwinding the depth counter.
|
|
167
339
|
|
|
168
340
|
## Build / tooling — @documentation building
|
|
169
341
|
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
342
|
+
- `@abide/abide/build` — build the client bundle with optional gzip, via atomic directory swaps.
|
|
343
|
+
- `@abide/abide/compile` — produce a standalone Bun server executable with embedded assets.
|
|
344
|
+
- `@abide/abide/preload` — Bun preload that registers the `.abide` loader and resolver before builds.
|
|
345
|
+
- `@abide/abide/resolver-plugin` — Bun plugin resolving bare/extensionless paths, generating manifests, and handling virtual modules.
|
|
346
|
+
- `@abide/abide/ui-plugin` — Bun plugin that loads `.abide` single-file components, compiling them to ES modules with scoped styles.
|
|
347
|
+
- `@abide/abide/tsconfig` — the strict ESNext + bundler-resolution + Bun-types TypeScript config consumers extend.
|
|
176
348
|
|
|
177
349
|
## Desktop bundle — @documentation bundle
|
|
178
350
|
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
351
|
+
- `@abide/abide/server/appDataDir` — per-user app data directory keyed by the bundler-injected program name; cwd-independent, pure.
|
|
352
|
+
- `@abide/abide/bundle/BundleWindow` — type for the bundle window config: title, size, custom menus, setup form schema.
|
|
353
|
+
- `@abide/abide/bundle/BundleMenu` — type for a top-level menu inserted between the standard Edit and Window menus.
|
|
354
|
+
- `@abide/abide/bundle/BundleMenuItem` — type for one menu entry: separator, clickable emit, or navigate.
|
|
355
|
+
- `@abide/abide/bundle/onMenu` — subscribe to bundle menu clicks (optional filter); returns an unsubscribe.
|
|
356
|
+
- `@abide/abide/bundle/bundled` — true inside the desktop webview, false as a standalone web app.
|
|
184
357
|
|
|
185
358
|
## MCP — @documentation mcp
|
|
186
359
|
|
|
187
|
-
-
|
|
360
|
+
- `@abide/abide/mcp/createMcpServer` — build an MCP server bound to the project's RPC registry, deriving tools and sockets with auth.
|
|
188
361
|
|
|
189
362
|
## Testing — @documentation testing
|
|
190
363
|
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
364
|
+
- `@abide/abide/test/createTestApp` — build a test app with augmentable verb/socket maps and bundled virtual modules.
|
|
365
|
+
- `@abide/abide/test/createScriptedSurface` — a scripted `AgentSurface` with declarative tool stubs and recorded calls.
|
|
366
|
+
- `@abide/abide/test/assertAgentFrameConformance` — collect an engine's frame stream and validate it against the neutral `AgentFrame` contract.
|
|
194
367
|
|
|
195
368
|
## Generated machine surfaces
|
|
196
369
|
|
|
197
|
-
| Route
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
| `GET /__abide/inspector` | Opt-in inspector UI + data (when enabled and `@abide/inspector` installed) |
|
|
207
|
-
| `GET /__abide/hot/<id>` | Dev-only component hot-module fetch (`.abide` HMR) |
|
|
370
|
+
| Route | Serves |
|
|
371
|
+
| --- | --- |
|
|
372
|
+
| `/openapi.json` | OpenAPI 3.1 spec of every RPC verb (methods, params, schemas, operationIds) |
|
|
373
|
+
| `/__abide/health`, `/__abide/identity` | Health JSON: app `health()` fields merged with `{ abide: version, name }` |
|
|
374
|
+
| `/__abide/mcp` | MCP endpoint; delegates to `mcp.handle(request)`, publishing the surface on first access |
|
|
375
|
+
| `/__abide/sockets` | The single ws multiplex for every defined socket; per-name HTTP face for tail/publish |
|
|
376
|
+
| `/__abide/cli` | Platform-detecting shell installer that downloads and runs the CLI binary |
|
|
377
|
+
| `/__abide/inspector` | Inspector UI + data when `ABIDE_ENABLE_INSPECTOR=true` (operator-only) |
|
|
378
|
+
| `/__abide/hot/<moduleId>` | Dev-only HMR: compiles an edited `.abide` to importable JS; 404 falls back to reload |
|
|
208
379
|
|
|
209
380
|
## Environment variables
|
|
210
381
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
| `PORT`
|
|
214
|
-
| `APP_URL`
|
|
215
|
-
| `ABIDE_APP_URL`
|
|
216
|
-
| `ABIDE_APP_TOKEN`
|
|
217
|
-
| `ABIDE_CLIENT_TIMEOUT`
|
|
218
|
-
| `ABIDE_MAX_REQUEST_BODY_SIZE` | Server request
|
|
219
|
-
| `ABIDE_IDLE_TIMEOUT`
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `ABIDE_LOG_FORMAT`
|
|
224
|
-
| `DEBUG`
|
|
225
|
-
| `ABIDE_ENABLE_INSPECTOR`
|
|
226
|
-
| `ABIDE_INSPECT`
|
|
382
|
+
| Variable | Effect |
|
|
383
|
+
| --- | --- |
|
|
384
|
+
| `PORT` | Listener port (0–65535); honored exactly if set, else scans up from 3000 |
|
|
385
|
+
| `APP_URL` | Mount base path from the URL's pathname (`https://x/v2` → `/v2`); mounts server URLs + shell |
|
|
386
|
+
| `ABIDE_APP_URL` | App origin sent to the CLI download tarball's `.env` |
|
|
387
|
+
| `ABIDE_APP_TOKEN` | Bearer token forwarded into the CLI tarball `.env` when the request carries `Authorization` |
|
|
388
|
+
| `ABIDE_CLIENT_TIMEOUT` | Client RPC fetch timeout (ms, 1–600000); shipped to the browser via SSR |
|
|
389
|
+
| `ABIDE_MAX_REQUEST_BODY_SIZE` | Server-wide request body ceiling (bytes); default ~Bun default |
|
|
390
|
+
| `ABIDE_IDLE_TIMEOUT` | Per-connection idle timeout (s, 0–255); default 10 |
|
|
391
|
+
| `ABIDE_DATA_DIR` | Override the platform app-data dir (used as-is, no program name appended) |
|
|
392
|
+
| `ABIDE_REACHABLE_TIMEOUT` | Per-HEAD reachability probe timeout (ms, 100–60000); default 3000 |
|
|
393
|
+
| `ABIDE_REACHABLE_TTL` | Reachability poll cadence (ms, 1000–600000); default 30000 |
|
|
394
|
+
| `ABIDE_LOG_FORMAT` | `json` emits JSON log records to stdout instead of TSV |
|
|
395
|
+
| `DEBUG` | npm-debug channel selection (`abide`, `abide:*`, `-abide` to negate request-close logs) |
|
|
396
|
+
| `ABIDE_ENABLE_INSPECTOR` | `true` mounts `@abide/inspector` on `/__abide/inspector` |
|
|
397
|
+
| `ABIDE_INSPECT` | Enable the native webview inspector (right-click Inspect) in bundle windows |
|
|
227
398
|
|
|
228
399
|
---
|
|
229
400
|
|
|
230
|
-
|
|
401
|
+
Mirrors `package.json` `exports`. After adding or renaming an export, run
|
|
402
|
+
`bun run packages/abide/scripts/readmeSurfaces.ts` and update this map.
|