Skip to content

Commit 9916f10

Browse files
authored
docs: rework structure and flow around the standard-handler narrative (#272)
1 parent 3d473f3 commit 9916f10

71 files changed

Lines changed: 1248 additions & 2560 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/.vitepress/config.ts

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,44 @@ function listErrorCodes(prefix: string): string[] {
1919
function guideGroups(prefix: string) {
2020
return [
2121
{
22-
text: 'Fundamentals',
22+
text: 'Introduction',
2323
items: [
2424
{ text: 'Introduction', link: `${prefix}/guide/` },
25+
],
26+
},
27+
{
28+
text: 'Define your tool',
29+
items: [
2530
{ text: 'Devframe Definition', link: `${prefix}/guide/devframe-definition` },
2631
{ text: 'RPC', link: `${prefix}/guide/rpc` },
2732
{ text: 'Shared State', link: `${prefix}/guide/shared-state` },
28-
{ text: 'Client Assets', link: `${prefix}/guide/client-assets` },
29-
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
30-
{ text: 'Agent-Native', link: `${prefix}/guide/agent-native` },
31-
{ text: 'JSON-Render', link: `${prefix}/guide/json-render` },
3233
{ text: 'Streaming', link: `${prefix}/guide/streaming` },
34+
{ text: 'Client Assets', link: `${prefix}/guide/client-assets` },
3335
{ text: 'Scoped Context', link: `${prefix}/guide/scoped-context` },
34-
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
36+
{ text: 'JSON-Render', link: `${prefix}/guide/json-render` },
37+
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
38+
{ text: 'When Clauses', link: `${prefix}/guide/when-clauses` },
3539
],
3640
},
3741
{
38-
text: 'Client & Security',
42+
text: 'Mount anywhere',
3943
items: [
44+
{ text: 'The Standard Handler', link: `${prefix}/adapters/initiate` },
45+
{ text: 'Adapters', link: `${prefix}/adapters/` },
46+
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
4047
{ text: 'Client', link: `${prefix}/guide/client` },
4148
{ text: 'Transports', link: `${prefix}/guide/transports` },
4249
{ text: 'Security', link: `${prefix}/guide/security` },
4350
],
4451
},
4552
{
46-
text: 'Hub',
53+
text: 'Agentic',
54+
items: [
55+
{ text: 'Agent-Native', link: `${prefix}/guide/agent-native` },
56+
],
57+
},
58+
{
59+
text: 'Compose a hub',
4760
items: [
4861
{ text: 'Hub', link: `${prefix}/guide/hub` },
4962
{ text: 'Client Scripts & Context', link: `${prefix}/guide/client-context` },
@@ -54,17 +67,16 @@ function guideGroups(prefix: string) {
5467
],
5568
},
5669
{
57-
text: 'Customization',
70+
text: 'Customize the UI',
5871
items: [
5972
{ text: 'Build Your Own JSON-Render Frontend', link: `${prefix}/guide/build-your-own-json-render-frontend` },
6073
{ text: 'Build Your Own Hub UI', link: `${prefix}/guide/build-your-own-hub-ui` },
6174
],
6275
},
6376
{
64-
text: 'References',
77+
text: 'Ecosystem',
6578
items: [
66-
{ text: 'When Clauses', link: `${prefix}/guide/when-clauses` },
67-
{ text: 'Examples', link: `${prefix}/examples/` },
79+
{ text: 'Built with Devframe', link: `${prefix}/guide/built-with` },
6880
],
6981
},
7082
] satisfies { text: string, items: DefaultTheme.NavItemWithLink[] }[]
@@ -73,9 +85,9 @@ function guideGroups(prefix: string) {
7385
function adaptersItems(prefix: string) {
7486
return [
7587
{ text: 'Overview', link: `${prefix}/adapters/` },
76-
{ text: 'Initiate (middleware)', link: `${prefix}/adapters/initiate` },
77-
{ text: 'Dev', link: `${prefix}/adapters/dev` },
88+
{ text: 'The Standard Handler', link: `${prefix}/adapters/initiate` },
7889
{ text: 'CLI', link: `${prefix}/adapters/cac` },
90+
{ text: 'Dev', link: `${prefix}/adapters/dev` },
7991
{ text: 'Build', link: `${prefix}/adapters/build` },
8092
{ text: 'Vite DevTools', link: `${prefix}/adapters/vite` },
8193
{ text: 'Embedded', link: `${prefix}/adapters/embedded` },
@@ -115,27 +127,6 @@ function pluginsItems(prefix: string) {
115127
] satisfies DefaultTheme.NavItemWithLink[]
116128
}
117129

118-
function examplesItems(prefix: string) {
119-
return [
120-
{ text: 'Overview', link: `${prefix}/examples/` },
121-
{ text: 'Built with Devframe', link: `${prefix}/examples/built-with` },
122-
{ text: 'files-inspector', link: `${prefix}/examples/files-inspector` },
123-
{ text: 'json-render', link: `${prefix}/examples/json-render` },
124-
{ text: 'streaming-chat', link: `${prefix}/examples/streaming-chat` },
125-
{ text: 'next-runtime-snapshot', link: `${prefix}/examples/next-runtime-snapshot` },
126-
{ text: 'hub-vite', link: `${prefix}/examples/hub-vite` },
127-
{ text: 'hub-next', link: `${prefix}/examples/hub-next` },
128-
{ text: 'hub-vite-minimal', link: `${prefix}/examples/hub-vite-minimal` },
129-
{ text: 'hub-next-minimal', link: `${prefix}/examples/hub-next-minimal` },
130-
{ text: 'hub-nitro-minimal', link: `${prefix}/examples/hub-nitro-minimal` },
131-
{ text: 'hub-hono-minimal', link: `${prefix}/examples/hub-hono-minimal` },
132-
{ text: 'hub-fastify-minimal', link: `${prefix}/examples/hub-fastify-minimal` },
133-
{ text: 'hub-sveltekit-minimal', link: `${prefix}/examples/hub-sveltekit-minimal` },
134-
{ text: 'hub-deno-minimal', link: `${prefix}/examples/hub-deno-minimal` },
135-
{ text: 'hub-rsbuild-minimal', link: `${prefix}/examples/hub-rsbuild-minimal` },
136-
] satisfies DefaultTheme.NavItemWithLink[]
137-
}
138-
139130
export function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[] {
140131
return [
141132
{
@@ -159,10 +150,6 @@ export function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[] {
159150
text: 'Plugins',
160151
items: pluginsItems(prefix),
161152
},
162-
{
163-
text: 'Examples',
164-
items: examplesItems(prefix),
165-
},
166153
]
167154
}
168155

docs/adapters/build.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ outline: deep
44

55
# Build
66

7-
Produces a self-contained static deploy of a devframe:
7+
Produces a static deploy:
88

9-
1. Copies the author's SPA dist (`clientAssets` or `options.distDir`) into `<outDir>`.
9+
1. Copies the SPA dist into `<outDir>`.
1010
2. Runs `setup(ctx)` with `mode: 'build'`.
11-
3. Collects RPC dumps for every `'static'` function and any `'query'` function with `dump.inputs` / `snapshot: true`.
12-
4. Writes `<outDir>/__connection.json` (`{ backend: 'static' }`) and sharded dump files under `<outDir>/__rpc-dump/` — both at the SPA root so the deployed client discovers them via relative paths from `document.baseURI`.
11+
3. Collects RPC dumps for every `'static'` and `'query'` with `dump.inputs` / `snapshot: true`.
12+
4. Writes `__connection.json` (`{ backend: 'static' }`) and sharded dumps under `__rpc-dump/`.
1313

1414
```ts
1515
import { createBuild } from 'devframe/adapters/build'
@@ -22,10 +22,8 @@ await createBuild(devframe, {
2222

2323
| Option | Default | Description |
2424
|--------|---------|-------------|
25-
| `outDir` | `dist-static` | Output directory. Cleared on each build. |
26-
| `distDir` | `def.clientAssets` (falls back to deprecated `def.cli?.distDir`) | Override the SPA dist directory (a local path or a [remote assets](/guide/client-assets) package, materialized in full at build time). |
27-
| `pretty` | `false` | Pretty-print dump JSON (larger on disk). |
25+
| `outDir` | `dist-static` | Output directory (cleared). |
26+
| `distDir` | `def.clientAssets` | SPA dist override (or [remote assets](/guide/client-assets)). |
27+
| `pretty` | `false` | Pretty-print dump JSON. |
2828

29-
The resulting directory hosts on any static web server (`serve`, nginx, GitHub Pages, …). The client auto-detects `static` mode by resolving `./__connection.json` against `document.baseURI` and runs in read-only form.
30-
31-
`createBuild` copies the SPA verbatim, so deploying under a custom URL base just means building the SPA with relative asset paths (`vite.base: './'`) — the client discovers the effective base at runtime.
29+
The client runs read-only. For a custom URL base, build with relative asset paths (`vite.base: './'`).

docs/adapters/cac.md

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ outline: deep
44

55
# CLI (cac)
66

7-
The cac adapter wraps a `DevframeDefinition` in a [`cac`](https://github.com/cacjs/cac)-powered command-line interface. From one entry it spins up an `h3` dev server with WebSocket RPC, builds static snapshots, or starts an MCP server.
8-
9-
`cac` is an optional peer dependency, pulled in only through this adapter — install it alongside `devframe` to opt into `createCac`:
7+
A [`cac`](https://github.com/cacjs/cac) CLI around a `DevframeDefinition` with `dev`, `build`, and `mcp` commands. `cac` is an optional peer:
108

119
```sh
1210
npm install devframe cac
1311
```
1412

15-
Tools that assemble their own command-line shell from the [lower-level factories](#use-your-own-cli-framework) never import this adapter, so they run without `cac`.
13+
The [lower-level factories](#use-your-own-cli-framework) need no `cac`.
1614

1715
```ts
1816
import { defineDevframe } from 'devframe'
@@ -28,8 +26,6 @@ const devframe = defineDevframe({
2826
await createCac(devframe).parse()
2927
```
3028

31-
Running the resulting binary:
32-
3329
```sh
3430
my-devframe # dev server at http://localhost:9999/
3531
my-devframe --port 8080
@@ -38,19 +34,19 @@ my-devframe build --out-dir dist-static --base /devframe/
3834
my-devframe mcp # stdio MCP server
3935
```
4036

41-
Standalone CLI serves the SPA at `/` by default. The `/__devframe/` prefix is for *hosted* adapters where devframe mounts alongside an existing app — see [Mount paths](./#mount-paths).
37+
The SPA serves at `/` standalone, `/__devframe/` hosted ([Mount paths](./#mount-paths)).
4238

4339
## Options
4440

45-
`createCac(def, options?)` accepts:
41+
`createCac(def, options?)`:
4642

4743
| Option | Default | Description |
4844
|--------|---------|-------------|
49-
| `defaultPort` | `9999` (or `def.cli?.port`) | Port used by the dev command when `--port` isn't provided. |
50-
| `configureCli` || `(cli: CAC) => void`final hook to add commands/flags at the assembly stage, after the definition's `cli.configure` runs. |
51-
| `onReady` || `(info: { origin, port, app }) => void \| Promise<void>`called once the dev server is listening. Use this to print your own startup banner. |
45+
| `defaultPort` | `9999` (or `def.cli?.port`) | Dev port if `--port` unset. |
46+
| `configureCli` || `(cli: CAC) => void` — add commands/flags. |
47+
| `onReady` || `(info: { origin, port, app }) => void \| Promise<void>` — once listening. |
5248

53-
`createCac` returns a `CacHandle`:
49+
Returns a `CacHandle`:
5450

5551
```ts
5652
interface CacHandle {
@@ -59,8 +55,6 @@ interface CacHandle {
5955
}
6056
```
6157

62-
The `cli` property lets the caller add ad-hoc commands and flags right before `parse()` when a `configureCli` callback is inconvenient.
63-
6458
## Definition-level `cli` fields
6559

6660
```ts
@@ -87,11 +81,11 @@ defineDevframe({
8781
})
8882
```
8983

90-
The top-level [`clientAssets`](/guide/client-assets) supplies the SPA the dev/build commands serve; everything under `cli` has sensible defaults. The `configure` hook runs *before* the `configureCli` option passed to `createCac`, so the final tool author always has the last word on flags.
84+
`configure` runs *before* `createCac`'s `configureCli`.
9185

9286
## Headless logging
9387

94-
Devframe leaves startup output to the application. Wire `onReady` to print your own banner:
88+
Print a banner via `onReady`:
9589

9690
```ts
9791
await createCac(devframe, {
@@ -101,17 +95,15 @@ await createCac(devframe, {
10195
}).parse()
10296
```
10397

104-
Structured diagnostics (via `nostics`) continue to surface through their normal reporters.
105-
10698
## Use your own CLI framework
10799

108-
To integrate devframe into an existing commander / yargs program — or to expose a different command structure than `createCac`'s `dev` / `build` / `mcp` triplet — drop down to the peer factories. Same `DevframeDefinition`, different shell:
100+
Peer factories for a commander/yargs program:
109101

110102
| Building block | Entry | Purpose |
111103
|----------------|-------|---------|
112-
| [`createDevServer(def, opts?)`](./dev) | `devframe/adapters/dev` | h3 + WebSocket RPC + SPA mount |
113-
| [`createBuild(def, opts?)`](./build) | `devframe/adapters/build` | Static deploy |
114-
| [`createMcpServer(def, opts?)`](./mcp) | `devframe/adapters/mcp` | stdio MCP server |
115-
| `parseCliFlags(schema, raw)` | `devframe/adapters/cac` | Validate a flag bag against a `CliFlagsSchema` |
104+
| [`createDevServer()`](./dev) | `devframe/adapters/dev` | h3 + WebSocket RPC + SPA mount |
105+
| [`createBuild()`](./build) | `devframe/adapters/build` | Static deploy |
106+
| [`createMcpServer()`](./mcp) | `devframe/adapters/mcp` | stdio MCP server |
107+
| `parseCliFlags(schema, raw)` | `devframe/adapters/cac` | Validate flags (`CliFlagsSchema`) |
116108

117-
See the [Standalone CLI guide](/guide/standalone-cli#use-your-own-cli-framework) for a worked commander example.
109+
See the [Standalone CLI guide](/guide/standalone-cli#use-your-own-cli-framework).

docs/adapters/dev.md

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ outline: deep
44

55
# Dev
66

7-
The `dev` adapter is the building block `createCac` uses internally — h3 + WebSocket RPC + the author's SPA mounted at the resolved base path. Reach for it directly to mount the dev server inside an existing CLI program (commander, yargs, hand-rolled CAC) or to attach custom middleware to the underlying h3 app.
7+
`createCac`'s building block: h3 + WebSocket RPC + the SPA at the resolved base path.
88

99
```ts
1010
import { createDevServer } from 'devframe/adapters/dev'
@@ -19,48 +19,35 @@ const handle = await createDevServer(devframe, {
1919
process.on('SIGINT', () => handle.close().then(() => process.exit(0)))
2020
```
2121

22-
`createDevServer` returns the underlying `StartedServer` (origin, port, h3 app, WS server, RPC group, `close()`) so callers can integrate it into their own process lifecycle.
22+
Returns a `StartedServer`: origin, port, h3 app, WS server, RPC group, `close()`.
2323

2424
| Option | Default | Description |
2525
|--------|---------|-------------|
2626
| `host` | `def.cli?.host ?? 'localhost'` | Bind host. |
27-
| `port` | resolved via `resolveDevServerPort` | Port to listen on. |
28-
| `flags` | `{}` | Parsed flag bag forwarded to `setup(ctx, { flags })`. |
29-
| `distDir` | `def.clientAssets` (falls back to deprecated `def.cli?.distDir`) | SPA dist override. When unset the server runs in bridge mode (meta + WS only). |
30-
| `basePath` | `resolveBasePath(def, 'standalone')` | Mount path override. |
31-
| `app` | fresh h3 app | Pre-configured h3 app to mount onto (custom middleware, auth, extra static assets). |
32-
| `openBrowser` | resolves from `flags.open` / `def.cli?.open` | Explicit on/off override. `false` disables; a string opens that relative path. |
33-
| `ws` | `def.cli?.ws` | How the browser reaches the RPC WebSocket — see below. |
34-
| `onReady` || Callback when the WS server is bound. |
27+
| `port` | resolved via `resolveDevServerPort` | Listen port. |
28+
| `flags` | `{}` | To `setup(ctx, { flags })`. |
29+
| `distDir` | `def.clientAssets` (falls back to deprecated `def.cli?.distDir`) | SPA dist; unset = bridge mode. |
30+
| `basePath` | `resolveBasePath(def, 'standalone')` | Mount override. |
31+
| `app` | fresh h3 app | Mount onto. |
32+
| `openBrowser` | resolves from `flags.open` / `def.cli?.open` | `false` off; string opens a path. |
33+
| `ws` | `def.cli?.ws` | RPC WebSocket — see below. |
34+
| `onReady` || WS-bind callback. |
3535

3636
## WebSocket endpoint
3737

38-
By default the RPC socket shares the HTTP server's port and binds to the `__ws` route next to `__connection.json`. The descriptor advertises a *relative* path, so the client connects to its own origin — the link follows the page through a reverse proxy that rewrites the domain, port, or subpath. Configure the three connection scenarios via `def.cli.ws` (or the `ws` call-site option):
39-
40-
```ts
41-
defineDevframe({
42-
// 1. Same server, a custom route (default route is `__ws`):
43-
cli: { ws: { route: '__sockets' } },
44-
45-
// 2. A dedicated port on the same host:
46-
cli: { ws: { port: 9788 } },
47-
48-
// 3. A remote, fully-qualified endpoint (e.g. a tunnel/relay):
49-
cli: { ws: { url: 'wss://devtools.example.com/relay/__ws' } },
50-
})
51-
```
38+
The RPC socket shares the HTTP port at `__ws`, advertised *relative*. Configure `def.cli.ws`:
5239

5340
| Field | Scenario | Advertised `websocket` |
5441
|-------|----------|------------------------|
55-
| `route` | same server, different route | `{ path: <route> }` (same origin) |
56-
| `port` | different port | `{ port, path: <route> }` (page host) |
57-
| `url` | remote, different origin | the URL string, used verbatim |
42+
| `route` | same server, other route | `{ path: <route> }` |
43+
| `port` | different port | `{ port, path: <route> }` |
44+
| `url` | remote origin | URL verbatim |
5845

59-
Precedence is `url` > `port` > `route`. In the remote case the dev server still hosts the socket locally on `route`; point your tunnel at it.
46+
Precedence `url` > `port` > `route`; `url` keeps the socket local on `route`.
6047

6148
## Port resolution
6249

63-
`resolveDevServerPort(def, opts?)` resolves a port up-front (to print or log it) before the server starts:
50+
`resolveDevServerPort(def, opts?)` resolves a port before start.
6451

6552
```ts
6653
import { resolveDevServerPort } from 'devframe/adapters/dev'
@@ -71,5 +58,5 @@ const port = await resolveDevServerPort(devframe, { host: '127.0.0.1' })
7158

7259
| Option | Default | Description |
7360
|--------|---------|-------------|
74-
| `host` | `def.cli?.host ?? 'localhost'` | Bind host (passed to `get-port-please` for in-use detection). |
75-
| `defaultPort` | `def.cli?.port ?? 9999` | Override the preferred port. |
61+
| `host` | `def.cli?.host ?? 'localhost'` | Bind host (`get-port-please` detection). |
62+
| `defaultPort` | `def.cli?.port ?? 9999` | Preferred-port override. |

docs/adapters/embedded.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ outline: deep
44

55
# Embedded
66

7-
Register a devframe into an already-running context at runtime. Mirrors the [`vite`](./vite) adapter's plugin-scan, but for callers that need dynamic, post-startup registration. The host decides the mount path; `embedded` is a hosted adapter and inherits the `/__<id>/` default when one is needed.
7+
Register a devframe into an already-running context at runtime — dynamic, post-startup registration (unlike [`vite`](./vite)'s plugin-scan). Inherits the hosted `/__<id>/` default.
88

99
```ts
1010
import { createEmbedded } from 'devframe/adapters/embedded'
@@ -15,6 +15,4 @@ await createEmbedded(devframe, { ctx: existingCtx })
1515

1616
| Option | Required | Description |
1717
|--------|----------|-------------|
18-
| `ctx` || Target `DevframeNodeContext` the devframe is registered into. |
19-
20-
Useful when a host loads devframes based on runtime conditions (feature flags, user opt-in, dynamic discovery) rather than static config.
18+
| `ctx` || Target `DevframeNodeContext` to register into. |

0 commit comments

Comments
 (0)