Skip to content

Commit 275e08d

Browse files
committed
refactor(vite): rename viteDevBridge, split static-mount vs RPC-bridge
viteDevBridge conflated two different Vite plugins behind one devMiddleware option. Split it into two purpose-named exports plus a convenience wrapper, all served as real Vite Plugin objects: - devframeVitePlugin(def, { base? }) — statically mounts the built SPA, no RPC server. - devframeViteBridge(def, { base?, port?, host?, flags?, auth?, mcp? }) — the RPC/WS bridge, options flattened (no more nested devMiddleware: boolean | {...}). - devframeVite(def, { bridge?, ...bridgeOptions }) — dispatches to one of the two above, the direct (renamed) successor to the old combined function. Updates every consumer: the 6 single-purpose plugin wrappers (inspect, messages, og, assets, data-inspector, a11y) switch to devframeVite; terminals/code-server (which always run both plugins together) call devframeViteBridge + devframeVitePlugin explicitly — this also fixes a latent bug where their bridge plugin silently dropped any port/host/flags override. @devframes/nuxt's always-bridge call site uses devframeViteBridge directly. Refreshes docs, JSDoc cross-references, and tsnapi API snapshots (allowing the intentional breaking rename — this package has not shipped yet). This PR was created with the help of an agent.
1 parent 87dac4e commit 275e08d

42 files changed

Lines changed: 357 additions & 242 deletions

File tree

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function helpersItems(prefix: string) {
5959
return [
6060
{ text: 'Overview', link: `${prefix}/helpers/` },
6161
{ text: 'Utilities', link: `${prefix}/helpers/utilities` },
62-
{ text: 'Vite Bridge', link: `${prefix}/helpers/vite-bridge` },
62+
{ text: 'Vite Plugin', link: `${prefix}/helpers/vite-bridge` },
6363
{ text: 'Nuxt Module', link: `${prefix}/helpers/nuxt` },
6464
{ text: 'Next Helper', link: `${prefix}/helpers/next` },
6565
{ text: 'Common RPC Functions', link: `${prefix}/helpers/common-rpc-functions` },

‎docs/adapters/initiate.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,4 @@ The instance **gates by default** — a handler mounted inside an app server is
126126

127127
## Relation to the other adapters
128128

129-
`createDevServer`, `viteDevBridge`, and `@devframes/next` are assembled from this instance internally — the handler is the one wiring underneath every serving path. To host **many** devframes behind one namespace with shared transport and docks, use the hub's counterpart: [`initHub`](../guide/hub-initiate).
129+
`createDevServer`, `devframeViteBridge` (`@devframes/vite`), and `@devframes/next` are assembled from this instance internally — the handler is the one wiring underneath every serving path. To host **many** devframes behind one namespace with shared transport and docks, use the hub's counterpart: [`initHub`](../guide/hub-initiate).

‎docs/adapters/mcp.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ defineDevframe({
5151
Both hosted bridges forward the same option to their side-car dev server and advertise the endpoint (with its port) in the `__connection.json` they serve:
5252

5353
```ts
54-
// Vite
55-
viteDevBridge(devframe, { devMiddleware: true, mcp: true })
54+
// Vite (@devframes/vite)
55+
devframeViteBridge(devframe, { mcp: true })
5656

5757
// Next.js (@devframes/next)
5858
createDevframeNextHandler(devframe, { mcp: true })
@@ -90,6 +90,6 @@ It exposes two gateway tools (the wire names of the `devframe:connect:*` ids —
9090
- **`devframe_connect_list-instances`** — discover running devframe dev servers and list each one's MCP tools. Instances running without an MCP route are listed with a hint to restart with `--mcp`.
9191
- **`devframe_connect_call-tool`** — invoke one tool on one instance (`{ port, tool, args }`) over its Streamable-HTTP endpoint.
9292

93-
Discovery reads the **instance registry**: every `createDevServer` (CLI `dev`, `viteDevBridge`, `@devframes/next`'s handler) writes a record to `~/.devframe/instances/<pid>-<port>.json` on boot and removes it on close; readers prune records whose liveness probe fails. The connector dials each instance's endpoint with the instance's own loopback origin, so it clears the route's origin gate without any configuration. In-process hosts register explicitly with `registerDevframeInstance` from `devframe/node` — see `createDevframeNextHost().mountMcp` for serving MCP on a Next app's own origin. `--port <n>` probes an explicit port besides the registry; `DEVFRAME_INSTANCES_DIR` relocates the registry and `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` opts a server out.
93+
Discovery reads the **instance registry**: every `createDevServer` (CLI `dev`, `devframeViteBridge`, `@devframes/next`'s handler) writes a record to `~/.devframe/instances/<pid>-<port>.json` on boot and removes it on close; readers prune records whose liveness probe fails. The connector dials each instance's endpoint with the instance's own loopback origin, so it clears the route's origin gate without any configuration. In-process hosts register explicitly with `registerDevframeInstance` from `devframe/node` — see `createDevframeNextHost().mountMcp` for serving MCP on a Next app's own origin. `--port <n>` probes an explicit port besides the registry; `DEVFRAME_INSTANCES_DIR` relocates the registry and `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` opts a server out.
9494

9595
See the [Agent-Native](/guide/agent-native) page for the full API, safety model, and Claude Desktop integration example.

‎docs/errors/DF0033.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ outline: deep
1010
1111
## Cause
1212

13-
`viteDevBridge({ devMiddleware })` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA (Vite, Nuxt, Astro, etc.) with devframe's RPC backend. Common reasons:
13+
`devframeViteBridge()` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA (Vite, Nuxt, Astro, etc.) with devframe's RPC backend. Common reasons:
1414

1515
- The preferred port is in use and no fallback range was configured.
1616
- Calling `def.setup(ctx)` threw — the devframe's own setup logic surfaced an error.
@@ -20,10 +20,10 @@ This is a soft warning — the surrounding Vite dev server keeps running, but th
2020

2121
## Fix
2222

23-
- Pin a port via `cli.port` / `cli.portRange` on the devframe definition, or via `devMiddleware.port` on `viteDevBridge`.
23+
- Pin a port via `cli.port` / `cli.portRange` on the devframe definition, or via `port` on `devframeViteBridge`.
2424
- Inspect the `reason` (or the attached `cause`) for the underlying error — fix the setup function or free the port.
2525
- For Nuxt: pass `devMiddleware: { port: <free-port> }` to the `@devframes/nuxt` module.
2626

2727
## Source
2828

29-
- [`packages/vite/src/index.ts`](https://github.com/devframes/devframe/blob/main/packages/vite/src/index.ts) — `viteDevBridge({ devMiddleware })` logs `DF0033` when port resolution or `createDevServer` throws during `configureServer`.
29+
- [`packages/vite/src/index.ts`](https://github.com/devframes/devframe/blob/main/packages/vite/src/index.ts) — `devframeViteBridge()` logs `DF0033` when port resolution or `createDevServer` throws during `configureServer`.

‎docs/errors/DF0052.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The instance's side-car / shared-server transport binding tried to bind the HTTP
2121

2222
## Fix
2323

24-
- Free the port, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `devMiddleware.port` on `viteDevBridge`.
24+
- Free the port, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `port` on `devframeViteBridge` (`@devframes/vite`).
2525
- The original node error is available as `error.cause` — check `error.cause.code` (e.g. `'EADDRINUSE'`) to branch on the failure kind programmatically.
2626

2727
## Source

‎docs/guide/security.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Higher-level integrations can drive their own authentication UI instead: disable
9292
## Practices for tools built on devframe
9393

9494
- **Stay on loopback.** The default bind host is `localhost`. Bind to a routable address only when you intend to, and require authentication when you do.
95-
- **Keep `auth: false` local.** Reach for it only for single-user localhost tools; leave the default in place anywhere a connection could originate elsewhere. The hosted bridges (`viteDevBridge`, `@devframes/next`'s handler) gate their side-car by default too — a host that owns the trust boundary another way opts out with `auth: false` explicitly.
95+
- **Keep `auth: false` local.** Reach for it only for single-user localhost tools; leave the default in place anywhere a connection could originate elsewhere. The hosted bridges (`devframeViteBridge`, `@devframes/next`'s handler) gate their side-car by default too — a host that owns the trust boundary another way opts out with `auth: false` explicitly.
9696
- **The MCP route requires an origin.** Unlike the WS transport, the route-based MCP server rejects `Origin`-less requests (a request must carry a loopback or allow-listed `Origin`), so a route-based endpoint isn't reachable by an arbitrary local process — see [MCP](/adapters/mcp).
9797
- **Treat tokens as secrets.** Never log the bearer token or the one-time code, and never bake either into build output.
9898
- **Authorize every handler.** A registered function is callable by any trusted client. Validate inputs, and mark state-changing functions `type: 'destructive'` so MCP and agent clients prompt before invoking them.

‎docs/helpers/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Helpers are the optional, opt-in surface around the core `defineDevframe` API: s
99
| Helper | Entry | What it does |
1010
|--------|-------|--------------|
1111
| [Utilities](./utilities) | `devframe/utils/*` | Bundled small utilities — terminal colors, hashing, editor launch, structured-clone serialization, and more. |
12-
| [Vite Bridge](./vite-bridge) | `@devframes/vite` | Vite plugin for mounting a devframe inside any Vite-based host (Astro, SolidStart, plain Vite). |
12+
| [Vite Plugin](./vite-bridge) | `@devframes/vite` | Vite plugins for mounting a devframe inside any Vite-based host (Astro, SolidStart, plain Vite) — a static mount, an RPC bridge, or a convenience wrapper over both. |
1313
| [Nuxt Module](./nuxt) | `@devframes/nuxt` | Nuxt module that wires a Nuxt SPA as a devframe client and serves the dev-time RPC bridge. |
1414
| [Next Helper](./next) | `@devframes/next` | Route-handler host + React client for mounting devframes inside a Next.js App Router app (experimental). |
1515
| [Common RPC Functions](./common-rpc-functions) | `devframe/recipes/common-rpc-functions` | Prebuilt RPC actions for "open in editor" and "reveal in Finder". |

‎docs/helpers/vite-bridge.md‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,51 @@
22
outline: deep
33
---
44

5-
# Vite Bridge
5+
# @devframes/vite
66

7-
A thin Vite plugin for mounting a devframe inside an existing Vite dev server. Used by [`@devframes/nuxt`](./nuxt) and available for any Vite-based host (Astro, SolidStart, plain Vite apps).
7+
Two Vite plugins for mounting a devframe inside an existing Vite dev server — `devframeVitePlugin` (static mount) and `devframeViteBridge` (RPC bridge) — plus `devframeVite`, a convenience wrapper that picks between them. Used by [`@devframes/nuxt`](./nuxt) and available for any Vite-based host (Astro, SolidStart, plain Vite apps).
88

9-
This sits below the [`vite` adapter](/adapters/vite) on the abstraction ladder: the adapter targets the full Vite DevTools dock; the bridge is the lower-level Vite plugin you reach for when you want a devframe to ride along with an existing app's dev server without the DevTools dock.
9+
This sits below the [`vite` adapter](/adapters/vite) on the abstraction ladder: the adapter targets the full Vite DevTools dock; these are the lower-level Vite plugins you reach for when you want a devframe to ride along with an existing app's dev server without the DevTools dock.
1010

1111
```ts
12-
import { viteDevBridge } from '@devframes/vite'
12+
import { devframeViteBridge, devframeVitePlugin } from '@devframes/vite'
1313
import { defineConfig } from 'vite'
1414
import devframe from './devframe'
1515

1616
export default defineConfig({
17-
plugins: [viteDevBridge(devframe)],
17+
// Statically mounts the built SPA at `/__<id>/` — no RPC server:
18+
plugins: [devframeVitePlugin(devframe)],
19+
// Or bridge the RPC/WS backend into this dev server instead — the
20+
// host app owns the SPA:
21+
// plugins: [devframeViteBridge(devframe)],
1822
})
1923
```
2024

21-
## Modes
25+
## `devframeVitePlugin` — static mount
2226

23-
- **Static mount** (default) — mounts `def.cli.distDir` at `options.base` (`/__<id>/` by default). No RPC server. Useful when you only need the SPA bundle served from a known path.
24-
- **Bridge mode** (`devMiddleware: true | {…}`) — skips the static mount; the host app owns the SPA. Devframe spawns a separate RPC + WS server and registers Vite middleware at `<base>__connection.json` so the host-served SPA can discover the WS endpoint. The side-car listens on its own port, so the descriptor carries that port alongside the `/__ws` route.
27+
Mounts `def.cli.distDir` at `options.base` (`/__<id>/` by default) with SPA fallback. No RPC server is started — useful when you only need the SPA bundle served from a known path.
2528

26-
To mount the RPC socket onto the Vite server's own port instead of a side-car — so it shares the origin with the app and rides through a proxy — pass Vite's HTTP server to [`initDevframe`](/adapters/initiate) / `initHub` via the `server` option. Devframe binds only its own `<base>__ws` upgrade route and leaves the rest (Vite's HMR socket included) untouched.
29+
| Option | Default | Description |
30+
|--------|---------|-------------|
31+
| `base` | `def.basePath ?? '/__<id>/'` | Mount path inside the Vite dev server. |
32+
33+
## `devframeViteBridge` — RPC bridge
2734

28-
## Options
35+
Skips the static mount — the host app owns the SPA. Devframe spawns a separate RPC + WS server and registers Vite middleware at `<base>__connection.json` so the host-served SPA can discover the WS endpoint. The side-car listens on its own port unless it can share Vite's own HTTP server, so the descriptor carries that port alongside the `/__ws` route.
36+
37+
To mount the RPC socket onto the Vite server's own port instead of a side-car — so it shares the origin with the app and rides through a proxy — pass Vite's HTTP server to [`initDevframe`](/adapters/initiate) / `initHub` via the `server` option. Devframe binds only its own `<base>__ws` upgrade route and leaves the rest (Vite's HMR socket included) untouched.
2938

3039
| Option | Default | Description |
3140
|--------|---------|-------------|
3241
| `base` | `def.basePath ?? '/__<id>/'` | Mount path inside the Vite dev server. |
33-
| `devMiddleware` | `false` | `true` or `{ port?, host?, flags? }` to enable bridge mode. |
42+
| `port` | share Vite's HTTP server | Pin a side-car port for the RPC socket instead. |
43+
| `host` | `def.cli?.host ?? 'localhost'` | Bind host for a pinned side-car. |
44+
| `flags` | — | Forwarded to `def.setup(ctx, { flags })`. |
45+
| `auth` | gated (interactive OTP) | `false` to opt out for a single-user localhost host, or a `DevframeAuthHandler` for a custom scheme. |
46+
| `mcp` | `def.cli?.mcp` | `true` or `McpRouteOptions` to expose the route-based MCP server at `<base>__mcp`. |
47+
48+
`port` / `host` / `flags` mirror [`createDevServer`](/adapters/dev)'s options of the same name.
49+
50+
## `devframeVite` — convenience wrapper
3451

35-
When `devMiddleware` is an object, the inner fields mirror [`createDevServer`](/adapters/dev) — `port` pins the WS server port, `host` sets the bind host, and `flags` is forwarded to `def.setup(ctx, { flags })`.
52+
`devframeVite(def, { bridge, ...bridgeOptions })` forwards to `devframeViteBridge` when `bridge: true`, or `devframeVitePlugin` otherwise — handy when a single call site needs to switch between the two modes. Reach for the two plugins directly when a devframe needs both mounted at once (e.g. a bridge for RPC alongside a static mount serving its own bundled UI, as the built-in `terminals`/`code-server` plugins do).

‎packages/devframe/src/adapters/dev.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface CreateDevServerOptions {
3535
* `def.cli?.distDir` is set, the dev server runs in **bridge mode** —
3636
* only `__connection.json` and the WS endpoint are mounted; the SPA
3737
* is expected to be hosted elsewhere (e.g. by a parent Vite/Nuxt
38-
* dev server via `viteDevBridge({ devMiddleware })`).
38+
* dev server via `devframeViteBridge` from `@devframes/vite`).
3939
*/
4040
distDir?: string
4141
/**
@@ -74,7 +74,7 @@ export interface CreateDevServerOptions {
7474
* Override how authentication resolves, taking precedence over
7575
* `def.cli?.auth`. Pass `false` to skip the gate entirely (the standard
7676
* choice for a **hosted** deployment where the host manages auth — see
77-
* {@link viteDevBridge}); a {@link DevframeAuthHandler} to install a custom
77+
* {@link devframeViteBridge} from `@devframes/vite`); a {@link DevframeAuthHandler} to install a custom
7878
* scheme; or `true` to force devframe's interactive OTP gate on. When
7979
* omitted, auth resolves from `flags.auth` / `def.cli?.auth` (the standalone
8080
* default: gated). The `--no-auth` flag (`flags.auth === false`) still forces
@@ -115,7 +115,7 @@ export interface CreateDevServerOptions {
115115
* server runs in **bridge mode**: only `__connection.json` and the WS
116116
* endpoint are mounted, with no SPA mount. The SPA is expected to be
117117
* hosted elsewhere (e.g. by a parent Vite/Nuxt dev server) — see
118-
* `viteDevBridge({ devMiddleware })`.
118+
* `devframeViteBridge` from `@devframes/vite`.
119119
*
120120
* Returns the underlying {@link StartedServer} handle so callers can
121121
* close it gracefully (SIGINT, hot-reload, test teardown).

‎packages/devframe/src/node/diagnostics.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const diagnostics = defineDiagnostics({
6060
DF0033: {
6161
why: (p: { id: string, reason: string }) =>
6262
`Failed to start dev RPC bridge for "${p.id}": ${p.reason}`,
63-
fix: 'Verify the bridge port is free and the devframe setup function does not throw. Pin a port via `cli.port` / `cli.portRange` on the definition, or via `devMiddleware.port` on `viteDevBridge`.',
63+
fix: 'Verify the bridge port is free and the devframe setup function does not throw. Pin a port via `cli.port` / `cli.portRange` on the definition, or via `port` on `devframeViteBridge` (`@devframes/vite`).',
6464
},
6565
DF0034: {
6666
why: (p: { namespace: string, name: string }) =>
@@ -114,7 +114,7 @@ export const diagnostics = defineDiagnostics({
114114
},
115115
DF0052: {
116116
why: (p: { host: string, port: number, reason: string }) => `Failed to listen on ${p.host}:${p.port}: ${p.reason}`,
117-
fix: 'The port is likely already taken by another process (often a previous devframe instance). Free it, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `devMiddleware.port` on `viteDevBridge`. The original node error is available as `error.cause`.',
117+
fix: 'The port is likely already taken by another process (often a previous devframe instance). Free it, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `port` on `devframeViteBridge` (`@devframes/vite`). The original node error is available as `error.cause`.',
118118
},
119119
DF0054: {
120120
why: (p: { id: string }) => `connectionMeta() was called before initDevframe("${p.id}") finished initializing.`,

0 commit comments

Comments
 (0)