Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ These reinforce devframe's positioning as "the container for one devtool integra
- **Headless by default.** No default startup banners, no opinionated logging to stdout, no default styling. Provide hooks (`onReady`, `cli.configure`, etc.); let the application print its own branding. Structured diagnostics via `nostics` are fine — ad-hoc `console.log`s baked into adapters are not.
- **Mount path depends on adapter context.** Given `id: 'foo'`, the default mount path is `/__foo/` for *hosted* adapters (`vite`, `embedded`) and `/` for *standalone* adapters (`cli`, `spa`, `build`). Authors override via `DevframeDefinition.basePath`. Don't hardcode mount paths in adapter code paths that may run standalone.
- **SPAs own their basePath at runtime.** Build SPAs with relative asset paths (`vite.base: './'`); discover the effective base in the browser from the executing script's location / `document.baseURI`. `createBuild` / `createSpa` copy SPA output verbatim — no HTML rewriting, no build-time `--base` injection. The client (`connectDevframe`) resolves `.connection.json` relative to the runtime base automatically.
- **CLI flags compose from both sides.** The `cac` instance backing `createCli` is exposed both to the `DevframeDefinition` (`cli.configure(cli)`) — for capabilities contributed by the tool itself — and to the `createCli` caller — for flags added at the final assembly stage. Parsed flag values are forwarded to `setup(ctx, { flags })`. Never hardcode domain-specific flags into `createCli`.
- **CLI flags compose from both sides.** The `cac` instance backing `createCac` is exposed both to the `DevframeDefinition` (`cli.configure(cli)`) — for capabilities contributed by the tool itself — and to the `createCac` caller — for flags added at the final assembly stage. Parsed flag values are forwarded to `setup(ctx, { flags })`. Never hardcode domain-specific flags into `createCac`.

## Structured Diagnostics (Error Codes)

Expand Down
1 change: 1 addition & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const alias = {
'devframe/utils/streaming-channel': r('devframe/src/utils/streaming-channel.ts'),
'devframe/utils/structured-clone': r('devframe/src/utils/structured-clone.ts'),
'devframe/utils/when': r('devframe/src/utils/when.ts'),
'devframe/adapters/cac': r('devframe/src/adapters/cac.ts'),
'devframe/adapters/cli': r('devframe/src/adapters/cli.ts'),
'devframe/adapters/dev': r('devframe/src/adapters/dev.ts'),
'devframe/adapters/build': r('devframe/src/adapters/build.ts'),
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function guideItems(prefix: string) {
function adaptersItems(prefix: string) {
return [
{ text: 'Overview', link: `${prefix}/adapters/` },
{ text: 'CLI', link: `${prefix}/adapters/cli` },
{ text: 'CLI (cac)', link: `${prefix}/adapters/cac` },
{ text: 'Dev', link: `${prefix}/adapters/dev` },
{ text: 'Build', link: `${prefix}/adapters/build` },
{ text: 'Vite', link: `${prefix}/adapters/vite` },
Expand Down
32 changes: 21 additions & 11 deletions docs/adapters/cli.md → docs/adapters/cac.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
outline: deep
---

# CLI
# CLI (cac)

The CLI adapter wraps a `DevframeDefinition` in a `cac`-powered command-line interface. From one entry it spins up an `h3` dev server with WebSocket RPC, builds static snapshots, builds SPA bundles, or starts an MCP server.
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, builds SPA bundles, or starts an MCP server.

`cac` is an optional peer dependency, pulled in only through this adapter — install it alongside `devframe` to opt into `createCac`:

```sh
npm install devframe cac
```

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`.

```ts
import { defineDevframe } from 'devframe'
import { createCli } from 'devframe/adapters/cli'
import { createCac } from 'devframe/adapters/cac'

const devframe = defineDevframe({
id: 'my-devframe',
Expand All @@ -17,9 +25,11 @@ const devframe = defineDevframe({
setup(ctx) { /* register docks, RPC, etc. */ },
})

await createCli(devframe).parse()
await createCac(devframe).parse()
```

The `devframe/adapters/cli` entry (`createCli`) remains as a deprecated alias for this module — new code should import `createCac` from `devframe/adapters/cac`.

Running the resulting binary:

```sh
Expand All @@ -34,18 +44,18 @@ Standalone CLI serves the SPA at `/` by default. The `/__devframe/` prefix is fo

## Options

`createCli(def, options?)` accepts:
`createCac(def, options?)` accepts:

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

`createCli` returns a `CliHandle`:
`createCac` returns a `CacHandle`:

```ts
interface CliHandle {
interface CacHandle {
cli: CAC // raw cac instance — mutate before calling parse()
parse: (argv?: string[]) => Promise<void>
}
Expand Down Expand Up @@ -80,14 +90,14 @@ defineDevframe({
})
```

`distDir` is the only required field; everything else has sensible defaults. The `configure` hook runs *before* the `configureCli` option passed to `createCli`, so the final tool author always has the last word on flags.
`distDir` is the only required field; everything else 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.

## Headless logging

Devframe leaves startup output to the application. Wire `onReady` to print your own banner:

```ts
await createCli(devframe, {
await createCac(devframe, {
onReady({ origin }) {
console.log(`ESLint Config Inspector ready at ${origin}`)
},
Expand All @@ -98,13 +108,13 @@ Structured diagnostics (via `nostics`) continue to surface through their normal

## Use your own CLI framework

To integrate devframe into an existing commander / yargs program — or to expose a different command structure than `createCli`'s `dev` / `build` / `mcp` triplet — drop down to the peer factories. Same `DevframeDefinition`, different shell:
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:

| Building block | Entry | Purpose |
|----------------|-------|---------|
| [`createDevServer(def, opts?)`](./dev) | `devframe/adapters/dev` | h3 + WebSocket RPC + SPA mount |
| [`createBuild(def, opts?)`](./build) | `devframe/adapters/build` | Static deploy |
| [`createMcpServer(def, opts?)`](./mcp) | `devframe/adapters/mcp` | stdio MCP server |
| `parseCliFlags(schema, raw)` | `devframe/adapters/cli` | Validate a flag bag against a `CliFlagsSchema` |
| `parseCliFlags(schema, raw)` | `devframe/adapters/cac` | Validate a flag bag against a `CliFlagsSchema` |

See the [Standalone CLI guide](/guide/standalone-cli#use-your-own-cli-framework) for a worked commander example.
2 changes: 1 addition & 1 deletion docs/adapters/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ outline: deep

# Dev

The `dev` adapter is the building block `createCli` 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.
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.

```ts
import { createDevServer } from 'devframe/adapters/dev'
Expand Down
4 changes: 2 additions & 2 deletions docs/adapters/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ outline: deep

An adapter takes a `DevframeDefinition` and deploys it into a specific runtime — a standalone CLI, a Vite plugin, a static snapshot, an embedded host, or an MCP server. Each adapter ships at its own entry point (`devframe/adapters/<name>`); the bundler pulls in only the ones you use.

Every adapter factory has the shape `createXxx(devframeDef, options?)`.
Every adapter factory has the shape `createXxx(devframeDef, options?)`. Some adapters draw on an optional peer dependency, installed only when you opt into that adapter: `cac` pulls in [`cac`](https://github.com/cacjs/cac), and `mcp` pulls in [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk).

## Comparison

| Adapter | Entry | Factory | Best for |
|---------|-------|---------|----------|
| [`cli`](./cli) | `devframe/adapters/cli` | `createCli(def, options?)` | Standalone tools run via `node ./my-tool.js` |
| [`cac`](./cac) | `devframe/adapters/cac` | `createCac(def, options?)` | Standalone tools run via `node ./my-tool.js` |
| [`dev`](./dev) | `devframe/adapters/dev` | `createDevServer(def, options?)` | Run the dev server programmatically — drive it from any CLI framework |
| [`build`](./build) | `devframe/adapters/build` | `createBuild(def, options?)` | Offline reports, CI artifacts, deployable SPA snapshots |
| [`vite`](./vite) | `@vitejs/devtools-kit/node` | `createPluginFromDevframe(def, options?)` | Mount the definition into Vite DevTools (or any compatible host) |
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The client runs in one of two modes depending on the backend advertised in `__de

| Backend | When | Capabilities |
|---------|------|--------------|
| `websocket` | Dev mode (`createCli`, Kit) | Full read/write, broadcasts, shared-state mutation. Requires auth. |
| `websocket` | Dev mode (`createCac`, Kit) | Full read/write, broadcasts, shared-state mutation. Requires auth. |
| `static` | Build / SPA output | Read-only — all calls resolve against the baked RPC dump. |

The client picks a mode automatically from the backend field. Mode-specific code paths like `broadcast` are scoped to `websocket`.
Expand Down
10 changes: 5 additions & 5 deletions docs/guide/devframe-definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ outline: deep

# Devframe Definition

Every Devframe tool starts with a single `defineDevframe` call. The returned `DevframeDefinition` is a portable value that any of the [adapters](/adapters/) can consume — the same definition runs under `createCli`, `createBuild`, `createMcpServer`, the `vite` adapter's `createPluginFromDevframe`, and so on.
Every Devframe tool starts with a single `defineDevframe` call. The returned `DevframeDefinition` is a portable value that any of the [adapters](/adapters/) can consume — the same definition runs under `createCac`, `createBuild`, `createMcpServer`, the `vite` adapter's `createPluginFromDevframe`, and so on.

## Minimal definition

Expand Down Expand Up @@ -51,7 +51,7 @@ export default defineDevframe({
| `basePath` | `string` | Optional mount path override. Defaults depend on the adapter: `/` for standalone (`cli` / `spa` / `build`), `/.<id>/` for hosted (`vite` / `embedded`). |
| `duplicationStrategy` | `'warn' \| 'silent' \| 'throw' \| 'duplicate'` | How a hub reacts when another devframe sharing this `id` is mounted onto the same hub. Defaults to `'warn'`. See [Hub](./hub). Hub adapters consult it; standalone adapters ignore it. |
| `capabilities` | `{ dev?, build?, spa? }` | Per-runtime feature flags. A `boolean` applies to the runtime as a whole; an object enables individual features. |
| `setup` | `(ctx, info?) => void \| Promise<void>` | **Required.** Server-side entry point. Runs in every runtime. The optional second argument carries runtime metadata — most notably the parsed CLI `flags` when running under `createCli`. |
| `setup` | `(ctx, info?) => void \| Promise<void>` | **Required.** Server-side entry point. Runs in every runtime. The optional second argument carries runtime metadata — most notably the parsed CLI `flags` when running under `createCac`. |
| `setupBrowser` | `(ctx) => void \| Promise<void>` | Browser-only entry used by the SPA adapter. |
| `cli` | `DevframeCliOptions` | Defaults for the CLI adapter. See [CLI options](#cli-options) below. |
| `spa` | `DevframeSpaOptions` | Defaults for the SPA adapter (`base`, `loader`). |
Expand Down Expand Up @@ -185,7 +185,7 @@ defineDevframe({
| `host` | `string` | Default bind host. |
| `open` | `boolean \| string` | `true` opens the origin, a string opens a specific path, `false` disables. Matches the `--open` / `--no-open` flags. |
| `auth` | `boolean` | Disable the WS trust flow when the tool is localhost-only and single-user. Default `true`. |
| `configure` | `(cli: CAC) => void` | Contribute capability flags/commands. Runs before `createCli`'s `configureCli` option so the final tool author always has the last word. |
| `configure` | `(cli: CAC) => void` | Contribute capability flags/commands. Runs before `createCac`'s `configureCli` option so the final tool author always has the last word. |

`setup(ctx, info)` receives `info.flags` populated from both devframe's built-in flags and any you declared via `configure` — saves duplicating flag parsing.

Expand All @@ -210,12 +210,12 @@ The definition is a plain value, so wire it into multiple adapters from the same
```ts
import { createPluginFromDevframe } from '@vitejs/devtools-kit/node'
import { createBuild } from 'devframe/adapters/build'
import { createCli } from 'devframe/adapters/cli'
import { createCac } from 'devframe/adapters/cac'

const devframe = defineDevframe({ id: 'my-devframe', name: 'My Devframe', setup() {} })

// 1. Standalone CLI:
await createCli(devframe).parse()
await createCac(devframe).parse()

// 2. Offline snapshot:
await createBuild(devframe, { outDir: 'dist-static' })
Expand Down
8 changes: 4 additions & 4 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Devframe keeps its surface focused on one tool, so the same definition stays por
- **App-owned file watching.** Wire your own watcher (chokidar, fs.watch, …) and signal change via `ctx.rpc.sharedState.set(...)` or event-typed RPCs.
- **Context-aware mount paths.** Standalone adapters (`cli`, `spa`, `build`) serve at `/` by default; hosted adapters (`vite`, `embedded`) serve at `/.<id>/`. Override via `DevframeDefinition.basePath`.
- **SPAs own their base at runtime.** Build with relative asset paths (`vite.base: './'`); `connectDevframe` discovers the effective base from the executing script's location.
- **CLI flags compose.** The `cac` instance is exposed to both the devframe (`cli.configure`) and the caller of `createCli`, so capability flags and app flags merge cleanly.
- **CLI flags compose.** The `cac` instance is exposed to both the devframe (`cli.configure`) and the caller of `createCac`, so capability flags and app flags merge cleanly.

## What Devframe provides

Expand Down Expand Up @@ -47,7 +47,7 @@ A minimal devframe with a CLI entry point:

```ts twoslash
import { defineDevframe, defineRpcFunction } from 'devframe'
import { createCli } from 'devframe/adapters/cli'
import { createCac } from 'devframe/adapters/cac'

const devframe = defineDevframe({
id: 'my-devframe',
Expand All @@ -70,7 +70,7 @@ const devframe = defineDevframe({
},
})

await createCli(devframe).parse()
await createCac(devframe).parse()
```

The same definition can also be deployed through any of the other adapters — for example, mounted into Vite DevTools via the [`vite` adapter](/adapters/vite).
Expand All @@ -91,7 +91,7 @@ Devframe deploys the same `DevframeDefinition` through one of these adapters:

| Adapter | Entry | Target |
|---------|-------|--------|
| `cli` | `createCli(d).parse()` | Standalone CLI with dev / build / mcp subcommands |
| `cli` | `createCac(d).parse()` | Standalone CLI with dev / build / mcp subcommands |
| `vite` | `createPluginFromDevframe(d, opts?)` *(from `@vitejs/devtools-kit/node`)* | Mount the devframe into Vite DevTools (or another compatible host) |
| `build` | `createBuild(d, opts?)` | Self-contained static deploy with baked RPC dumps |
| `embedded` | `createEmbedded(d, { ctx })` | Runtime registration into an existing host |
Expand Down
Loading
Loading