From 4b8c4be5b2e075912e8e0be1af1b4935c9f62426 Mon Sep 17 00:00:00 2001
From: Anthony Fu
Date: Fri, 22 May 2026 12:05:07 +0900
Subject: [PATCH 1/7] feat(hub): introduce @devframes/hub framework-neutral hub
layer
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Lifts the docks/terminals/messages/commands subsystems out of
@vitejs/devtools-kit into a framework-neutral package so any framework
(Vite, Next.js, etc.) can build a hub kit on the same infrastructure.
Adds `mountDevframe` as the framework-neutral mount primitive,
`HubHostCapabilities` for optional host capabilities (e.g. `openPath`),
and built-in RPCs `hub:open-path` / `hub:commands:execute`.
Reserves the `DF8xxx` diagnostic range for hub-side codes with
sub-ranges per subsystem. New `examples/minimal-vite-devtools-kit/`
acts as a protocol witness — a ~150-line Vite plugin + tiny DOM UI
that exercises every hub subsystem end to end.
---
AGENTS.md | 12 +
alias.ts | 5 +
docs/.vitepress/config.ts | 1 +
docs/errors/DF8100.md | 22 ++
docs/errors/DF8101.md | 22 ++
docs/errors/DF8102.md | 22 ++
docs/errors/DF8200.md | 22 ++
docs/errors/DF8201.md | 22 ++
docs/errors/DF8400.md | 22 ++
docs/errors/DF8401.md | 22 ++
docs/errors/DF8402.md | 23 ++
docs/errors/DF8500.md | 38 ++++
docs/guide/hub.md | 96 ++++++++
examples/minimal-vite-devtools-kit/README.md | 36 +++
examples/minimal-vite-devtools-kit/index.html | 44 ++++
.../minimal-vite-devtools-kit/package.json | 20 ++
.../src/client/main.ts | 109 +++++++++
.../src/client/style.css | 87 ++++++++
.../minimal-vite-devtools-kit/src/devframe.ts | 35 +++
.../src/minimal-hub-kit.ts | 175 +++++++++++++++
.../minimal-vite-devtools-kit/tsconfig.json | 12 +
.../minimal-vite-devtools-kit/vite.config.ts | 13 ++
packages/hub/LICENSE.md | 21 ++
packages/hub/package.json | 54 +++++
packages/hub/src/client/client-script.ts | 16 ++
packages/hub/src/client/context.ts | 12 +
packages/hub/src/client/docks.ts | 139 ++++++++++++
packages/hub/src/client/index.ts | 5 +
packages/hub/src/client/remote.ts | 122 ++++++++++
packages/hub/src/constants.ts | 24 ++
packages/hub/src/define.ts | 27 +++
packages/hub/src/index.ts | 2 +
packages/hub/src/node/context.ts | 152 +++++++++++++
packages/hub/src/node/diagnostics.ts | 48 ++++
packages/hub/src/node/host-commands.ts | 95 ++++++++
packages/hub/src/node/host-docks.ts | 209 ++++++++++++++++++
packages/hub/src/node/host-messages.ts | 134 +++++++++++
packages/hub/src/node/host-terminals.ts | 200 +++++++++++++++++
packages/hub/src/node/hub-builtins.ts | 36 +++
packages/hub/src/node/index.ts | 9 +
packages/hub/src/node/mount-devframe.ts | 52 +++++
packages/hub/src/node/rpc-builtins.ts | 30 +++
packages/hub/src/node/utils.ts | 17 ++
packages/hub/src/types/commands.ts | 130 +++++++++++
packages/hub/src/types/docks.ts | 167 ++++++++++++++
packages/hub/src/types/index.ts | 44 ++++
packages/hub/src/types/json-render.ts | 29 +++
packages/hub/src/types/messages.ts | 146 ++++++++++++
packages/hub/src/types/settings.ts | 11 +
packages/hub/src/types/terminals.ts | 48 ++++
.../hub/src/utils/diagnostics-reporter.ts | 12 +
packages/hub/tsconfig.json | 7 +
packages/hub/tsdown.config.ts | 28 +++
pnpm-lock.yaml | 50 +++++
pnpm-workspace.yaml | 1 +
.../@devframes/hub/client.snapshot.d.ts | 96 ++++++++
.../tsnapi/@devframes/hub/client.snapshot.js | 16 ++
.../@devframes/hub/constants.snapshot.d.ts | 11 +
.../@devframes/hub/constants.snapshot.js | 11 +
.../tsnapi/@devframes/hub/index.snapshot.d.ts | 93 ++++++++
.../tsnapi/@devframes/hub/index.snapshot.js | 9 +
.../tsnapi/@devframes/hub/node.snapshot.d.ts | 110 +++++++++
.../tsnapi/@devframes/hub/node.snapshot.js | 74 +++++++
.../tsnapi/@devframes/hub/types.snapshot.d.ts | 79 +++++++
.../tsnapi/@devframes/hub/types.snapshot.js | 4 +
tsconfig.base.json | 15 ++
turbo.json | 10 +
67 files changed, 3465 insertions(+)
create mode 100644 docs/errors/DF8100.md
create mode 100644 docs/errors/DF8101.md
create mode 100644 docs/errors/DF8102.md
create mode 100644 docs/errors/DF8200.md
create mode 100644 docs/errors/DF8201.md
create mode 100644 docs/errors/DF8400.md
create mode 100644 docs/errors/DF8401.md
create mode 100644 docs/errors/DF8402.md
create mode 100644 docs/errors/DF8500.md
create mode 100644 docs/guide/hub.md
create mode 100644 examples/minimal-vite-devtools-kit/README.md
create mode 100644 examples/minimal-vite-devtools-kit/index.html
create mode 100644 examples/minimal-vite-devtools-kit/package.json
create mode 100644 examples/minimal-vite-devtools-kit/src/client/main.ts
create mode 100644 examples/minimal-vite-devtools-kit/src/client/style.css
create mode 100644 examples/minimal-vite-devtools-kit/src/devframe.ts
create mode 100644 examples/minimal-vite-devtools-kit/src/minimal-hub-kit.ts
create mode 100644 examples/minimal-vite-devtools-kit/tsconfig.json
create mode 100644 examples/minimal-vite-devtools-kit/vite.config.ts
create mode 100644 packages/hub/LICENSE.md
create mode 100644 packages/hub/package.json
create mode 100644 packages/hub/src/client/client-script.ts
create mode 100644 packages/hub/src/client/context.ts
create mode 100644 packages/hub/src/client/docks.ts
create mode 100644 packages/hub/src/client/index.ts
create mode 100644 packages/hub/src/client/remote.ts
create mode 100644 packages/hub/src/constants.ts
create mode 100644 packages/hub/src/define.ts
create mode 100644 packages/hub/src/index.ts
create mode 100644 packages/hub/src/node/context.ts
create mode 100644 packages/hub/src/node/diagnostics.ts
create mode 100644 packages/hub/src/node/host-commands.ts
create mode 100644 packages/hub/src/node/host-docks.ts
create mode 100644 packages/hub/src/node/host-messages.ts
create mode 100644 packages/hub/src/node/host-terminals.ts
create mode 100644 packages/hub/src/node/hub-builtins.ts
create mode 100644 packages/hub/src/node/index.ts
create mode 100644 packages/hub/src/node/mount-devframe.ts
create mode 100644 packages/hub/src/node/rpc-builtins.ts
create mode 100644 packages/hub/src/node/utils.ts
create mode 100644 packages/hub/src/types/commands.ts
create mode 100644 packages/hub/src/types/docks.ts
create mode 100644 packages/hub/src/types/index.ts
create mode 100644 packages/hub/src/types/json-render.ts
create mode 100644 packages/hub/src/types/messages.ts
create mode 100644 packages/hub/src/types/settings.ts
create mode 100644 packages/hub/src/types/terminals.ts
create mode 100644 packages/hub/src/utils/diagnostics-reporter.ts
create mode 100644 packages/hub/tsconfig.json
create mode 100644 packages/hub/tsdown.config.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/client.snapshot.d.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/client.snapshot.js
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/constants.snapshot.d.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/constants.snapshot.js
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/index.snapshot.d.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/index.snapshot.js
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/node.snapshot.d.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/node.snapshot.js
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/types.snapshot.d.ts
create mode 100644 tests/__snapshots__/tsnapi/@devframes/hub/types.snapshot.js
diff --git a/AGENTS.md b/AGENTS.md
index 3daeef7..5f69e24 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,6 +4,8 @@
**`devframe`** is the framework-neutral container for one devtool integration, portable across viewers. Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/spa/embedded outputs) without caring how it'll be displayed. A devframe app runs standalone (CLI, static deploy, embedded SPA) just as well as it mounts inside a hub.
+**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. See `examples/minimal-vite-devtools-kit/` for a working ~120-line kit demonstrating the protocol end to end.
+
## Stack & Structure
ESM TypeScript library. Bundled with `tsdown`. Tested with `vitest`. pnpm workspaces with catalog dependencies (`pnpm-workspace.yaml`); workspace globs reserve `playground`, `docs`, `packages/*`, `examples/*` for future additions.
@@ -50,6 +52,16 @@ All node-side warnings and errors use structured diagnostics via [`nostics`](htt
Prefix: **`DF`**. Codes are sequential 4-digit numbers (e.g. `DF0033`). Check the existing diagnostics file to find the next available number.
+Range allocation:
+- `DF00xx–DF07xx` — `devframe` core (RPC, host, storage, streams, …)
+- `DF80xx–DF89xx` — `@devframes/hub`. Sub-ranges:
+ - `DF80xx` — hub context / lifecycle
+ - `DF81xx` — docks
+ - `DF82xx` — terminals
+ - `DF83xx` — messages
+ - `DF84xx` — commands
+ - `DF85xx` — built-in RPC commands
+
### Adding a new error
1. **Define the code** in the appropriate `diagnostics.ts`:
diff --git a/alias.ts b/alias.ts
index d0cb424..6388c5c 100644
--- a/alias.ts
+++ b/alias.ts
@@ -36,6 +36,11 @@ export const alias = {
'devframe/helpers/vite': r('devframe/src/helpers/vite.ts'),
'devframe/adapters/embedded': r('devframe/src/adapters/embedded.ts'),
'devframe/adapters/mcp': r('devframe/src/adapters/mcp/index.ts'),
+ '@devframes/hub/client': r('hub/src/client/index.ts'),
+ '@devframes/hub/constants': r('hub/src/constants.ts'),
+ '@devframes/hub/node': r('hub/src/node/index.ts'),
+ '@devframes/hub/types': r('hub/src/types/index.ts'),
+ '@devframes/hub': r('hub/src/index.ts'),
'@devframes/nuxt/runtime/plugin.client': r('nuxt/src/runtime/plugin.client.ts'),
'@devframes/nuxt': r('nuxt/src/index.ts'),
'devframe/recipes/open-helpers': r('devframe/src/recipes/open-helpers.ts'),
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index d129870..a834804 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -28,6 +28,7 @@ function guideItems(prefix: string): DefaultTheme.NavItemWithLink[] {
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
{ text: 'Client', link: `${prefix}/guide/client` },
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
+ { text: 'Hub (multi-tool)', link: `${prefix}/guide/hub` },
{ text: 'Agent-Native (experimental)', link: `${prefix}/guide/agent-native` },
]
}
diff --git a/docs/errors/DF8100.md b/docs/errors/DF8100.md
new file mode 100644
index 0000000..d8e3945
--- /dev/null
+++ b/docs/errors/DF8100.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8100: Dock Already Registered
+
+## Message
+
+> Dock with id "`{id}`" is already registered
+
+## Cause
+
+`ctx.docks.register(view)` was called with an `id` that another dock already owns. Dock ids are unique per hub context, so the second registration would silently override the first if allowed.
+
+## Fix
+
+- Pick a different dock id (best practice: namespace under your tool's id, e.g. `my-tool:overview`).
+- If overwriting is intentional (e.g. a hot-reload scenario), pass `force: true`: `ctx.docks.register(view, true)`.
+
+## Source
+
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.register()` throws when `views.has(view.id) && !force`.
diff --git a/docs/errors/DF8101.md b/docs/errors/DF8101.md
new file mode 100644
index 0000000..8135814
--- /dev/null
+++ b/docs/errors/DF8101.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8101: Cannot Change Dock Id
+
+## Message
+
+> Cannot change the id of a dock. Use register() to add new docks.
+
+## Cause
+
+The `update` handle returned by `ctx.docks.register(view)` received a patch whose `id` differs from the original. Dock ids are immutable post-registration — they key the dock list and any shared-state references.
+
+## Fix
+
+- Drop `id` from your patch; only pass the fields you actually want to mutate.
+- To replace one dock with a different one: call `ctx.docks.register(newView)` (or `register(newView, true)` to overwrite an existing id).
+
+## Source
+
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.register()` returns an `update` callable that throws this when the patch carries a different `id`.
diff --git a/docs/errors/DF8102.md b/docs/errors/DF8102.md
new file mode 100644
index 0000000..bc5ba85
--- /dev/null
+++ b/docs/errors/DF8102.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8102: Dock Not Registered
+
+## Message
+
+> Dock with id "`{id}`" is not registered. Use register() to add new docks.
+
+## Cause
+
+`ctx.docks.update(view)` was called with an `id` that has no prior registration. `update` is for mutating existing entries; new entries must go through `register`.
+
+## Fix
+
+- Use `ctx.docks.register(view)` for new entries.
+- Verify the id matches a previously registered dock — typos / case mismatches are the usual cause.
+
+## Source
+
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.update()` throws when `views.has(view.id) === false`.
diff --git a/docs/errors/DF8200.md b/docs/errors/DF8200.md
new file mode 100644
index 0000000..726b7b0
--- /dev/null
+++ b/docs/errors/DF8200.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8200: Terminal Session Already Registered
+
+## Message
+
+> Terminal session with id "`{id}`" already registered
+
+## Cause
+
+`ctx.terminals.register(session)` or `ctx.terminals.startChildProcess(opts, terminal)` was called with an `id` that another session already owns. Terminal ids are unique per hub context.
+
+## Fix
+
+- Pick a different session id (namespace under your tool: `my-tool:build-server`, `my-tool:tests`, …).
+- If you want to reuse an existing session's slot, `ctx.terminals.remove(session)` first.
+
+## Source
+
+- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevToolsTerminalHost.register()` and `startChildProcess()` throw when the id is already taken.
diff --git a/docs/errors/DF8201.md b/docs/errors/DF8201.md
new file mode 100644
index 0000000..cfabfa4
--- /dev/null
+++ b/docs/errors/DF8201.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8201: Terminal Session Not Registered
+
+## Message
+
+> Terminal session with id "`{id}`" not registered
+
+## Cause
+
+`ctx.terminals.update(patch)` was called with an `id` for which no session has been registered.
+
+## Fix
+
+- Use `ctx.terminals.register(session)` to add new sessions.
+- Verify the id matches an existing session — common cause is updating after `remove()`.
+
+## Source
+
+- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevToolsTerminalHost.update()` throws when `sessions.has(patch.id) === false`.
diff --git a/docs/errors/DF8400.md b/docs/errors/DF8400.md
new file mode 100644
index 0000000..fd093e6
--- /dev/null
+++ b/docs/errors/DF8400.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8400: Command Already Registered
+
+## Message
+
+> Command "`{id}`" is already registered
+
+## Cause
+
+`ctx.commands.register(command)` was called with an `id` that another command already owns. Command ids are unique per hub context.
+
+## Fix
+
+- Pick a different command id (namespace under your tool: `my-tool:reload`, `my-tool:open-settings`, …).
+- To replace, call `ctx.commands.unregister(id)` first.
+
+## Source
+
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.register()` throws when `commands.has(command.id)`.
diff --git a/docs/errors/DF8401.md b/docs/errors/DF8401.md
new file mode 100644
index 0000000..8fa88b1
--- /dev/null
+++ b/docs/errors/DF8401.md
@@ -0,0 +1,22 @@
+---
+outline: deep
+---
+
+# DF8401: Cannot Change Command Id
+
+## Message
+
+> Cannot change the id of a command. Use register() to add new commands.
+
+## Cause
+
+The `update` handle returned by `ctx.commands.register(cmd)` received a patch with an `id` key. Command ids are immutable post-registration.
+
+## Fix
+
+- Drop `id` from the patch object.
+- To register a new command, call `ctx.commands.register(newCmd)`.
+
+## Source
+
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.register()` returns a `update` callable that throws when `'id' in patch`.
diff --git a/docs/errors/DF8402.md b/docs/errors/DF8402.md
new file mode 100644
index 0000000..1fa3991
--- /dev/null
+++ b/docs/errors/DF8402.md
@@ -0,0 +1,23 @@
+---
+outline: deep
+---
+
+# DF8402: Command Not Registered
+
+## Message
+
+> Command "`{id}`" is not registered
+
+## Cause
+
+`ctx.commands.execute(id, …)` (or the `update()` handle returned by `register()`) was called with an `id` that has no registration. The command was either never registered or has already been `unregister()`'d.
+
+## Fix
+
+- Register the command first via `ctx.commands.register({ id, title, handler })`.
+- Verify the id — typos and stale references are the usual cause.
+- If invoking client-side, register the command on the client via `ctx.commands.register` in the dock client script.
+
+## Source
+
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.execute()` and the `update` handle throw when the id is missing.
diff --git a/docs/errors/DF8500.md b/docs/errors/DF8500.md
new file mode 100644
index 0000000..e03f9d3
--- /dev/null
+++ b/docs/errors/DF8500.md
@@ -0,0 +1,38 @@
+---
+outline: deep
+---
+
+# DF8500: Built-in Command Requires Host Capability
+
+## Message
+
+> Built-in command "`{id}`" requires a host capability that this host does not implement.
+
+## Cause
+
+A hub built-in command (e.g. `hub:open-path`) was invoked, but the host implementation passed to `createHubContext` did not implement the matching capability. The hub exposes these built-ins uniformly across framework kits — but the underlying capability is host-specific (e.g. `openPath` needs a launch-editor binding the host can call).
+
+## Fix
+
+Implement the matching capability on the `DevToolsHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`:
+
+```ts
+import type { HubHostCapabilities } from '@devframes/hub/node'
+import type { DevToolsHost } from 'devframe/types'
+import { launchEditor } from 'devframe/utils/launch-editor'
+
+const host: DevToolsHost & HubHostCapabilities = {
+ // … existing DevToolsHost methods …
+ async openPath(filepath, line, column) {
+ const target = line ? `${filepath}:${line}${column ? `:${column}` : ''}` : filepath
+ launchEditor(target)
+ return true
+ },
+}
+```
+
+See the [Hub guide](/guide/hub#host-capabilities) for the full capability surface.
+
+## Source
+
+- [`packages/hub/src/node/hub-builtins.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/hub-builtins.ts) — `registerHubBuiltins()` registers `hub:open-path`, whose handler throws this when `context.host.openPath` is undefined.
diff --git a/docs/guide/hub.md b/docs/guide/hub.md
new file mode 100644
index 0000000..aee1744
--- /dev/null
+++ b/docs/guide/hub.md
@@ -0,0 +1,96 @@
+---
+outline: deep
+---
+
+# Hub (multi-tool)
+
+`@devframes/hub` extends devframe with the orchestration features that only make sense when many devtools share a UI: a dock registry, terminal aggregation, message/toast queue, and a command palette. It does not ship UI — each framework kit (e.g. `@vitejs/devtools-kit`) provides its own UI on top of the hub's RPC + shared-state protocol.
+
+> [!WARNING] Experimental
+> The hub API surface is still being refined. Names may change before 1.0.
+
+## What the hub adds
+
+A hub-aware node context (`HubNodeContext`) extends `DevToolsNodeContext` with four subsystems:
+
+| Subsystem | Surface | Purpose |
+|---|---|---|
+| `ctx.docks` | `register / update / values` | Multi-tool dock entries (iframes, launchers, json-render, custom-render). |
+| `ctx.terminals` | `register / startChildProcess` | Aggregate terminal sessions, stream output over a well-known channel. |
+| `ctx.messages` | `add / update / remove / clear` | Server-side toast/notification queue (FIFO, capped at 1000). |
+| `ctx.commands` | `register / execute / list` | Hierarchical command palette with keybindings and `when` clauses. |
+
+Plus a `createJsonRenderer(spec)` factory for building remote-UI panels via the framework-neutral json-render DSL.
+
+## Built-in RPC
+
+Every hub context auto-registers these RPC functions so framework kits don't reimplement them:
+
+- `hub:commands:execute` — invoke a registered server command by id. `await rpc.call('hub:commands:execute', 'my-tool:do-thing', ...args)`.
+- `hub:open-path` — registered as a command, delegates to `host.openPath()` (see [Host capabilities](#host-capabilities)).
+
+## Host capabilities
+
+A hub host implements the same `DevToolsHost` interface as devframe, plus optional capabilities the hub knows how to delegate to:
+
+```ts
+interface HubHostCapabilities {
+ /** Open a file in the user's editor. Backs the built-in `hub:open-path` command. */
+ openPath?: (filepath: string, line?: number, column?: number) => boolean | Promise
+}
+```
+
+A framework kit's host implementation looks like this:
+
+```ts
+import type { HubHostCapabilities } from '@devframes/hub/node'
+import type { DevToolsHost } from 'devframe/types'
+import { launchEditor } from 'devframe/utils/launch-editor'
+
+const host: DevToolsHost & HubHostCapabilities = {
+ mountStatic(base, distDir) { /* … */ },
+ resolveOrigin() { /* … */ },
+ getStorageDir(scope) { /* … */ },
+ async openPath(filepath, line, column) {
+ const target = line ? `${filepath}:${line}${column ? `:${column}` : ''}` : filepath
+ launchEditor(target)
+ return true
+ },
+}
+```
+
+When a framework kit omits `openPath`, the `hub:open-path` command throws [`DF8500`](/errors/DF8500) instead of silently failing.
+
+## Mounting a devframe into a hub
+
+`mountDevframe(ctx, def)` is the framework-neutral primitive that registers any `DevframeDefinition` as a dock and runs its `setup(ctx)`:
+
+```ts
+import { createHubContext, mountDevframe } from '@devframes/hub/node'
+
+const ctx = await createHubContext({ cwd, host, mode: 'dev' })
+await mountDevframe(ctx, myDevframe)
+```
+
+Framework kits typically wrap this in a plugin shell. `@vitejs/devtools-kit`'s `createPluginFromDevframe` returns a Vite `Plugin` whose `devtools.setup` calls into `mountDevframe`.
+
+## The protocol — what the UI sees
+
+A hub-aware UI doesn't import any hub classes; it reads three shared-state keys and one RPC method:
+
+| Channel | Type | What it carries |
+|---|---|---|
+| `devframe:docks` shared state | `DevToolsDockEntry[]` | The full dock list, including the hub's `~terminals` / `~messages` / `~settings` builtins. |
+| `devframe:commands` shared state | `DevToolsServerCommandEntry[]` | Serializable command list (handlers stripped). |
+| `devframe:user-settings` shared state | `DevToolsDocksUserSettings` | Persisted per-workspace hub settings. |
+| `hub:commands:execute` RPC | `(id, ...args) => unknown` | Server-side command dispatch. |
+
+Plus broadcast notifications (`devframe:terminals:updated`, `devframe:messages:updated`) that a UI can subscribe to via `rpc.client.register(...)`.
+
+## Example
+
+See [`examples/minimal-vite-devtools-kit/`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devtools-kit) for a ~120-line Vite plugin that wires the hub end to end with a vanilla DOM UI. Every framework's hub kit follows the same shape: a thin layer that adapts the framework's dev server to the hub.
+
+## Diagnostics
+
+Hub-side diagnostic codes live in the `DF8xxx` range. See the [error reference](/errors/) for the full list.
diff --git a/examples/minimal-vite-devtools-kit/README.md b/examples/minimal-vite-devtools-kit/README.md
new file mode 100644
index 0000000..b29fba9
--- /dev/null
+++ b/examples/minimal-vite-devtools-kit/README.md
@@ -0,0 +1,36 @@
+# Minimal Vite DevTools Kit
+
+A protocol-witness example. The `src/minimal-hub-kit.ts` file is the entire "kit" — about 120 lines of Vite plugin code that wires `@devframes/hub` into a Vite dev server. Every framework's hub kit (`@vitejs/devtools-kit`, future `@next/devtools-kit`, etc.) is the same shape.
+
+## Run it
+
+```sh
+pnpm install
+pnpm --filter minimal-vite-devtools-kit-example dev
+```
+
+Open the printed URL. You should see:
+
+- A status line showing the RPC backend
+- A **Docks** list — one entry per `mountDevframe` call, plus the hub's built-in `~terminals` / `~messages` / `~settings` panels
+- A **Commands** list — one entry per `commands.register()`
+- A **Messages** list — populated via `messages.add()` on the server
+- A **Terminals** list — empty unless a devframe registers one
+- A button that exercises `hub:open-path` (opens this README in your editor)
+
+## What the example proves
+
+- `createHubContext()` boots a hub without any Vite-specific code path
+- A `DevToolsHost & HubHostCapabilities` impl plugs framework specifics (`openPath`, storage paths) into the hub uniformly
+- `mountDevframe(ctx, def)` registers any `DevframeDefinition` as a dock
+- Hub built-in RPCs (`hub:open-path`, `hub:commands:execute`) work regardless of how the host was constructed
+- The browser-side `connectDevframe({ baseURL: '/__hub/' })` discovers the WS endpoint via the kit's `__connection.json` middleware
+
+## Files
+
+| File | Role |
+|---|---|
+| `src/minimal-hub-kit.ts` | The Vite plugin — creates hub context, mounts middleware, side-car WS |
+| `src/devframe.ts` | A sample `DevframeDefinition` that plugs into the kit |
+| `src/client/main.ts` | The browser-side UI that consumes the hub protocol |
+| `index.html` | The UI shell |
diff --git a/examples/minimal-vite-devtools-kit/index.html b/examples/minimal-vite-devtools-kit/index.html
new file mode 100644
index 0000000..b9dfe77
--- /dev/null
+++ b/examples/minimal-vite-devtools-kit/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+ Minimal Vite DevTools Kit
+
+
+
+
+
Minimal Vite DevTools Kit
+
Protocol witness — verifies @devframes/hub end to end.
+
+ )
+}
diff --git a/examples/minimal-next-devtools-hub/src/client/devtools/demo-devframe.ts b/examples/minimal-next-devtools-hub/src/client/devtools/demo-devframe.ts
new file mode 100644
index 0000000..8f9f932
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/src/client/devtools/demo-devframe.ts
@@ -0,0 +1,26 @@
+import type { HubNodeContext } from '@devframes/hub/node'
+import { defineDevframe } from 'devframe/types'
+
+export default defineDevframe({
+ id: 'next-demo-tool',
+ name: 'Next Demo Tool',
+ icon: 'ph:rocket-duotone',
+ basePath: '/__next-demo-tool/',
+ async setup(rawCtx) {
+ const ctx = rawCtx as unknown as HubNodeContext
+
+ ctx.commands.register({
+ id: 'next-demo-tool:say-hello',
+ title: 'Next Demo Tool: Say Hello',
+ icon: 'ph:hand-waving-duotone',
+ category: 'demo',
+ handler: () => 'Hello from the Next demo command!',
+ })
+
+ await ctx.messages.add({
+ level: 'info',
+ message: 'Next demo devframe loaded',
+ description: 'Registered via mountDevframe() from the Next host.',
+ })
+ },
+})
diff --git a/examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts b/examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts
new file mode 100644
index 0000000..842626b
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts
@@ -0,0 +1,145 @@
+import type { HubHostCapabilities, HubNodeContext } from '@devframes/hub/node'
+import type { StartedServer } from 'devframe/node'
+import type { ConnectionMeta, DevframeDefinition, DevToolsHost } from 'devframe/types'
+import { homedir } from 'node:os'
+import process from 'node:process'
+import { defineRpcFunction } from '@devframes/hub'
+import { createHubContext, mountDevframe } from '@devframes/hub/node'
+import { startHttpAndWs } from 'devframe/node'
+import { launchEditor } from 'devframe/utils/launch-editor'
+import { getPort } from 'get-port-please'
+import { join } from 'pathe'
+import demoDevframe from './demo-devframe'
+
+export interface MinimalNextDevToolsHubOptions {
+ /** Preferred port for the side-car RPC/WS server. Default: a free port near 9877. */
+ port?: number
+ /** Hostname for the side-car server. Default: `localhost`. */
+ host?: string
+ /** Workspace root used by hub host capabilities. Default: `process.cwd()`. */
+ cwd?: string
+ /** Devframes to mount as docks. */
+ devframes?: DevframeDefinition[]
+}
+
+export interface StartedMinimalNextDevToolsHub extends StartedServer {
+ context: HubNodeContext
+ connectionMeta: ConnectionMeta & { backend: 'websocket', websocket: number }
+}
+
+const minimalNextHubMessagesList = defineRpcFunction({
+ name: 'minimal-next-devtools-hub:messages:list',
+ type: 'static',
+ jsonSerializable: true,
+ setup: (ctx: HubNodeContext) => ({
+ async handler() {
+ return Array.from(ctx.messages.entries.values())
+ },
+ }),
+})
+
+const minimalNextHubTerminalsList = defineRpcFunction({
+ name: 'minimal-next-devtools-hub:terminals:list',
+ type: 'static',
+ jsonSerializable: true,
+ setup: (ctx: HubNodeContext) => ({
+ async handler() {
+ return Array.from(ctx.terminals.sessions.values()).map(s => ({
+ id: s.id,
+ title: s.title,
+ description: s.description,
+ status: s.status,
+ }))
+ },
+ }),
+})
+
+export async function minimalNextDevToolsHub(
+ options: MinimalNextDevToolsHubOptions = {},
+): Promise {
+ const cwd = options.cwd ?? process.cwd()
+ const hostName = options.host ?? 'localhost'
+
+ const host: DevToolsHost & HubHostCapabilities = {
+ mountStatic() {
+ // Static mounting for devframe SPAs would route through Next middleware
+ // in a fuller host. This minimal example keeps mounted devframes headless.
+ },
+ resolveOrigin() {
+ return `http://${hostName}:3000`
+ },
+ getStorageDir(scope) {
+ return scope === 'workspace'
+ ? join(cwd, 'node_modules/.minimal-next-devtools-hub')
+ : join(homedir(), '.minimal-next-devtools-hub')
+ },
+ async openPath(filepath, line, column) {
+ const absolute = join(cwd, filepath)
+ const target = line
+ ? `${absolute}:${line}${column ? `:${column}` : ''}`
+ : absolute
+ launchEditor(target)
+ return true
+ },
+ }
+
+ const port = options.port ?? await getPort({ host: hostName, port: 9877, random: false })
+
+ const context = await createHubContext({
+ cwd,
+ workspaceRoot: cwd,
+ mode: 'dev',
+ host,
+ builtinRpcDeclarations: [
+ minimalNextHubMessagesList,
+ minimalNextHubTerminalsList,
+ ],
+ })
+
+ context.commands.register({
+ id: 'minimal-next-devtools-hub:ping',
+ title: 'Next Hub: Ping',
+ icon: 'ph:bell-duotone',
+ category: 'hub',
+ handler: () => 'pong',
+ })
+
+ await context.messages.add({
+ level: 'success',
+ message: 'Minimal Next DevTools Hub started',
+ description: `Side-car WS on port ${port}. ${options.devframes?.length ?? 1} devframe(s) registered.`,
+ })
+
+ for (const def of options.devframes ?? [demoDevframe]) {
+ await mountDevframe(context, def)
+ }
+
+ const started = await startHttpAndWs({
+ context,
+ host: hostName,
+ port,
+ auth: false,
+ })
+
+ return Object.assign(started, {
+ context,
+ connectionMeta: {
+ backend: 'websocket' as const,
+ websocket: started.port,
+ },
+ })
+}
+
+const GLOBAL_KEY = '__minimalNextDevToolsHub'
+
+type GlobalWithHub = typeof globalThis & {
+ [GLOBAL_KEY]?: Promise
+}
+
+export function ensureMinimalNextDevToolsHub(
+ options: MinimalNextDevToolsHubOptions = {},
+): Promise {
+ const globalHub = globalThis as GlobalWithHub
+ globalHub[GLOBAL_KEY] ??= minimalNextDevToolsHub(options)
+ return globalHub[GLOBAL_KEY]
+}
diff --git a/examples/minimal-next-devtools-hub/src/client/next.config.mjs b/examples/minimal-next-devtools-hub/src/client/next.config.mjs
new file mode 100644
index 0000000..d551674
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/src/client/next.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ images: { unoptimized: true },
+ // The workspace typecheck owns source-level project references.
+ typescript: { ignoreBuildErrors: true },
+}
+
+export default nextConfig
diff --git a/examples/minimal-next-devtools-hub/src/client/tsconfig.json b/examples/minimal-next-devtools-hub/src/client/tsconfig.json
new file mode 100644
index 0000000..62aa7e6
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/src/client/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx"
+ ],
+ "exclude": [
+ "node_modules",
+ ".next",
+ "out"
+ ]
+}
diff --git a/examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts b/examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts
new file mode 100644
index 0000000..ca99995
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts
@@ -0,0 +1,58 @@
+import { createRpcClient } from 'devframe/rpc/client'
+import { createWsRpcChannel } from 'devframe/rpc/transports/ws-client'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { WebSocket } from 'ws'
+import { minimalNextDevToolsHub } from '../src/client/devtools/minimal-next-devtools-hub'
+
+vi.stubGlobal('WebSocket', WebSocket)
+
+function bootRpc(port: number) {
+ const channel = createWsRpcChannel({ url: `ws://127.0.0.1:${port}` })
+ return createRpcClient({}, { channel })
+}
+
+describe('minimal-next-devtools-hub (example)', () => {
+ let server: Awaited> | undefined
+
+ afterEach(async () => {
+ await server?.close()
+ server = undefined
+ })
+
+ it('returns connection meta pointing at the WS backend', async () => {
+ server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+
+ expect(server.connectionMeta).toEqual({
+ backend: 'websocket',
+ websocket: server.port,
+ })
+ })
+
+ it('registers hub built-in docks and the mounted demo devframe', async () => {
+ server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+
+ const dockIds = server.context.docks.values().map(d => d.id)
+ expect(dockIds).toContain('next-demo-tool')
+ expect(dockIds).toContain('~terminals')
+ expect(dockIds).toContain('~messages')
+ expect(dockIds).toContain('~settings')
+ })
+
+ it('lists startup and demo messages through the kit-local RPC', async () => {
+ server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+
+ const rpc = bootRpc(server.port)
+ const messages = await rpc.$call('minimal-next-devtools-hub:messages:list') as { message: string }[]
+ expect(messages.map(m => m.message)).toContain('Minimal Next DevTools Hub started')
+ expect(messages.map(m => m.message)).toContain('Next demo devframe loaded')
+ })
+
+ it('executes the ping command through the hub command RPC', async () => {
+ server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+
+ const rpc = bootRpc(server.port)
+ await expect(
+ rpc.$call('hub:commands:execute', 'minimal-next-devtools-hub:ping'),
+ ).resolves.toBe('pong')
+ })
+})
diff --git a/examples/minimal-next-devtools-hub/tsconfig.json b/examples/minimal-next-devtools-hub/tsconfig.json
new file mode 100644
index 0000000..77efbe1
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "incremental": true,
+ "jsx": "preserve",
+ "lib": ["ESNext", "DOM"],
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "allowJs": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "isolatedDeclarations": false,
+ "plugins": [{ "name": "next" }]
+ },
+ "include": [
+ "src",
+ "tests",
+ "vitest.config.ts"
+ ],
+ "exclude": [
+ "dist",
+ ".next",
+ "out"
+ ]
+}
diff --git a/examples/minimal-next-devtools-hub/vitest.config.ts b/examples/minimal-next-devtools-hub/vitest.config.ts
new file mode 100644
index 0000000..7fd3b94
--- /dev/null
+++ b/examples/minimal-next-devtools-hub/vitest.config.ts
@@ -0,0 +1,11 @@
+import { defineConfig } from 'vitest/config'
+import { alias } from '../../alias'
+
+export default defineConfig({
+ resolve: {
+ alias,
+ },
+ test: {
+ environment: 'node',
+ },
+})
diff --git a/examples/minimal-vite-devtools-kit/README.md b/examples/minimal-vite-devtools-hub/README.md
similarity index 73%
rename from examples/minimal-vite-devtools-kit/README.md
rename to examples/minimal-vite-devtools-hub/README.md
index b29fba9..1295270 100644
--- a/examples/minimal-vite-devtools-kit/README.md
+++ b/examples/minimal-vite-devtools-hub/README.md
@@ -1,12 +1,12 @@
-# Minimal Vite DevTools Kit
+# Minimal Vite DevTools Hub
-A protocol-witness example. The `src/minimal-hub-kit.ts` file is the entire "kit" — about 120 lines of Vite plugin code that wires `@devframes/hub` into a Vite dev server. Every framework's hub kit (`@vitejs/devtools-kit`, future `@next/devtools-kit`, etc.) is the same shape.
+A protocol-witness example. The `src/minimal-vite-devtools-hub.ts` file is the entire Vite host — about 120 lines of Vite plugin code that wires `@devframes/hub` into a Vite dev server. Every framework's DevTools Hub host follows the same shape.
## Run it
```sh
pnpm install
-pnpm --filter minimal-vite-devtools-kit-example dev
+pnpm --filter minimal-vite-devtools-hub dev
```
Open the printed URL. You should see:
@@ -30,7 +30,7 @@ Open the printed URL. You should see:
| File | Role |
|---|---|
-| `src/minimal-hub-kit.ts` | The Vite plugin — creates hub context, mounts middleware, side-car WS |
+| `src/minimal-vite-devtools-hub.ts` | The Vite plugin — creates hub context, mounts middleware, side-car WS |
| `src/devframe.ts` | A sample `DevframeDefinition` that plugs into the kit |
| `src/client/main.ts` | The browser-side UI that consumes the hub protocol |
| `index.html` | The UI shell |
diff --git a/examples/minimal-vite-devtools-kit/index.html b/examples/minimal-vite-devtools-hub/index.html
similarity index 93%
rename from examples/minimal-vite-devtools-kit/index.html
rename to examples/minimal-vite-devtools-hub/index.html
index b9dfe77..b51934a 100644
--- a/examples/minimal-vite-devtools-kit/index.html
+++ b/examples/minimal-vite-devtools-hub/index.html
@@ -3,12 +3,12 @@
- Minimal Vite DevTools Kit
+ Minimal Vite DevTools Hub
-
Minimal Vite DevTools Kit
+
Minimal Vite DevTools Hub
Protocol witness — verifies @devframes/hub end to end.
diff --git a/examples/minimal-vite-devtools-kit/package.json b/examples/minimal-vite-devtools-hub/package.json
similarity index 64%
rename from examples/minimal-vite-devtools-kit/package.json
rename to examples/minimal-vite-devtools-hub/package.json
index d4023ed..26b159a 100644
--- a/examples/minimal-vite-devtools-kit/package.json
+++ b/examples/minimal-vite-devtools-hub/package.json
@@ -1,9 +1,9 @@
{
- "name": "minimal-vite-devtools-kit-example",
+ "name": "minimal-vite-devtools-hub",
"type": "module",
"version": "0.4.1",
"private": true,
- "description": "Protocol-witness example — a tiny Vite plugin built on @devframes/hub that exercises every hub subsystem end-to-end.",
+ "description": "Protocol-witness example — a tiny Vite DevTools Hub built on @devframes/hub that exercises every hub subsystem end-to-end.",
"scripts": {
"dev": "vite",
"build": "vite build"
diff --git a/examples/minimal-vite-devtools-kit/src/client/main.ts b/examples/minimal-vite-devtools-hub/src/client/main.ts
similarity index 97%
rename from examples/minimal-vite-devtools-kit/src/client/main.ts
rename to examples/minimal-vite-devtools-hub/src/client/main.ts
index 4854970..d25ccc6 100644
--- a/examples/minimal-vite-devtools-kit/src/client/main.ts
+++ b/examples/minimal-vite-devtools-hub/src/client/main.ts
@@ -62,7 +62,7 @@ async function main() {
// to refresh on broadcast; this minimal example polls instead.
const refreshMessages = async () => {
const entries = await rpc.call(
- 'minimal-hub-kit:messages:list' as any,
+ 'minimal-vite-devtools-hub:messages:list' as any,
) as DevToolsMessageEntry[]
renderList(messagesEl, entries, m =>
`
[${m.level}] ${m.message}
`)
@@ -72,7 +72,7 @@ async function main() {
// 4. Terminals — same pattern as messages.
const refreshTerminals = async () => {
const sessions = await rpc.call(
- 'minimal-hub-kit:terminals:list' as any,
+ 'minimal-vite-devtools-hub:terminals:list' as any,
) as Pick[]
renderList(terminalsEl, sessions, t =>
`
${t.title}${t.id} · ${t.status}
`)
diff --git a/examples/minimal-vite-devtools-kit/src/client/style.css b/examples/minimal-vite-devtools-hub/src/client/style.css
similarity index 100%
rename from examples/minimal-vite-devtools-kit/src/client/style.css
rename to examples/minimal-vite-devtools-hub/src/client/style.css
diff --git a/examples/minimal-vite-devtools-kit/src/devframe.ts b/examples/minimal-vite-devtools-hub/src/devframe.ts
similarity index 100%
rename from examples/minimal-vite-devtools-kit/src/devframe.ts
rename to examples/minimal-vite-devtools-hub/src/devframe.ts
diff --git a/examples/minimal-vite-devtools-kit/src/minimal-hub-kit.ts b/examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts
similarity index 80%
rename from examples/minimal-vite-devtools-kit/src/minimal-hub-kit.ts
rename to examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts
index faf8108..6a720c1 100644
--- a/examples/minimal-vite-devtools-kit/src/minimal-hub-kit.ts
+++ b/examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts
@@ -10,8 +10,8 @@ import { launchEditor } from 'devframe/utils/launch-editor'
import { getPort } from 'get-port-please'
import { join } from 'pathe'
-export interface MinimalHubKitOptions {
- /** Mount path for the kit's connection-meta endpoint. Default: `/__hub/`. */
+export interface MinimalViteDevToolsHubOptions {
+ /** Mount path for the hub's connection-meta endpoint. Default: `/__hub/`. */
base?: string
/** Preferred port for the side-car RPC/WS server. Default: a free port near 9777. */
port?: number
@@ -19,10 +19,10 @@ export interface MinimalHubKitOptions {
devframes?: DevframeDefinition[]
}
-// Minimal kit-local RPCs — used by the UI for read-side data. A more
-// ambitious kit might hoist these into `@devframes/hub` itself.
-const minimalKitMessagesList = defineRpcFunction({
- name: 'minimal-hub-kit:messages:list',
+// Minimal hub-local RPCs — used by the UI for read-side data. A more
+// ambitious hub host might hoist these into `@devframes/hub` itself.
+const minimalViteHubMessagesList = defineRpcFunction({
+ name: 'minimal-vite-devtools-hub:messages:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -32,8 +32,8 @@ const minimalKitMessagesList = defineRpcFunction({
}),
})
-const minimalKitTerminalsList = defineRpcFunction({
- name: 'minimal-hub-kit:terminals:list',
+const minimalViteHubTerminalsList = defineRpcFunction({
+ name: 'minimal-vite-devtools-hub:terminals:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -54,17 +54,16 @@ const minimalKitTerminalsList = defineRpcFunction({
* (`openPath` via launch-editor), and exposes the side-car WS endpoint
* to the browser via Vite middleware at `__connection.json`.
*
- * This file is the entire "kit" — every other framework's hub kit
- * (`@vitejs/devtools-kit`, future `@next/devtools-kit`, …) is the same
- * shape: a thin layer that adapts a framework's dev server to the hub.
+ * This file is the entire Vite host — every other framework's hub host is
+ * the same shape: a thin layer that adapts a framework's dev server to the hub.
*/
-export function minimalHubKit(options: MinimalHubKitOptions = {}): Plugin {
+export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions = {}): Plugin {
const base = normalizeBase(options.base ?? '/__hub/')
let viteConfig: ResolvedConfig | undefined
let started: { close: () => Promise } | undefined
return {
- name: 'minimal-vite-devtools-kit',
+ name: 'minimal-vite-devtools-hub',
apply: 'serve',
configResolved(config) {
@@ -91,8 +90,8 @@ export function minimalHubKit(options: MinimalHubKitOptions = {}): Plugin {
},
getStorageDir(scope) {
return scope === 'workspace'
- ? join(cwd, 'node_modules/.minimal-hub-kit')
- : join(homedir(), '.minimal-hub-kit')
+ ? join(cwd, 'node_modules/.minimal-vite-devtools-hub')
+ : join(homedir(), '.minimal-vite-devtools-hub')
},
async openPath(filepath, line, column) {
const absolute = join(cwd, filepath)
@@ -112,28 +111,28 @@ export function minimalHubKit(options: MinimalHubKitOptions = {}): Plugin {
mode: 'dev',
host,
builtinRpcDeclarations: [
- // The minimal kit ships its own `messages:list` and `terminals:list`
+ // The minimal hub ships its own `messages:list` and `terminals:list`
// RPCs so the UI has something to read. A full hub kit would
// likely standardise these (this is why hub-level RPC built-ins
// exist — see hub:open-path / hub:commands:execute) but for the
// demo we keep them kit-local.
- minimalKitMessagesList,
- minimalKitTerminalsList,
+ minimalViteHubMessagesList,
+ minimalViteHubTerminalsList,
],
})
- // Seed a sample terminal + command directly on the kit so the UI
+ // Seed a sample command directly on the hub so the UI
// shows something even without any plugged-in devframes.
context.commands.register({
- id: 'minimal-hub-kit:ping',
- title: 'Kit · Ping',
+ id: 'minimal-vite-devtools-hub:ping',
+ title: 'Vite Hub · Ping',
icon: 'ph:bell-duotone',
category: 'kit',
handler: () => 'pong',
})
await context.messages.add({
level: 'success',
- message: 'Minimal hub kit started',
+ message: 'Minimal Vite DevTools Hub started',
description: `Side-car WS on port ${port}. ${options.devframes?.length ?? 0} devframe(s) registered.`,
})
diff --git a/examples/minimal-vite-devtools-kit/tsconfig.json b/examples/minimal-vite-devtools-hub/tsconfig.json
similarity index 100%
rename from examples/minimal-vite-devtools-kit/tsconfig.json
rename to examples/minimal-vite-devtools-hub/tsconfig.json
diff --git a/examples/minimal-vite-devtools-kit/vite.config.ts b/examples/minimal-vite-devtools-hub/vite.config.ts
similarity index 69%
rename from examples/minimal-vite-devtools-kit/vite.config.ts
rename to examples/minimal-vite-devtools-hub/vite.config.ts
index c08e65a..5ab3361 100644
--- a/examples/minimal-vite-devtools-kit/vite.config.ts
+++ b/examples/minimal-vite-devtools-hub/vite.config.ts
@@ -1,12 +1,12 @@
import { defineConfig } from 'vite'
import { alias } from '../../alias'
import demoDevframe from './src/devframe'
-import { minimalHubKit } from './src/minimal-hub-kit'
+import { minimalViteDevToolsHub } from './src/minimal-vite-devtools-hub'
export default defineConfig({
resolve: { alias },
plugins: [
- minimalHubKit({
+ minimalViteDevToolsHub({
devframes: [demoDevframe],
}),
],
diff --git a/packages/devframe/src/node/server.ts b/packages/devframe/src/node/server.ts
index b32dfa8..4c81177 100644
--- a/packages/devframe/src/node/server.ts
+++ b/packages/devframe/src/node/server.ts
@@ -8,6 +8,7 @@ import { createRpcServer } from 'devframe/rpc/server'
import { attachWsRpcTransport } from 'devframe/rpc/transports/ws-server'
import { H3, toNodeHandler } from 'h3'
import { WebSocketServer as WSServer } from 'ws'
+import { getInternalContext } from './internal/context'
export interface StartHttpAndWsOptions {
context: DevToolsNodeContext
@@ -125,14 +126,21 @@ export async function startHttpAndWs(options: StartHttpAndWsOptions): Promise resolveListen())
})
- const origin = `http://${bindHost}:${port}`
+ const address = httpServer.address()
+ const resolvedPort = typeof address === 'object' && address ? address.port : port
+ const origin = `http://${bindHost}:${resolvedPort}`
+ const internal = getInternalContext(context)
+ const wsUrl = origin.replace(/^http/, 'ws')
+ internal.wsEndpoint = {
+ url: wsUrl,
+ }
if (options.onReady)
- await options.onReady({ origin, port, app })
+ await options.onReady({ origin, port: resolvedPort, app })
return {
origin,
- port,
+ port: resolvedPort,
app,
wss,
rpcGroup,
@@ -144,6 +152,8 @@ export async function startHttpAndWs(options: StartHttpAndWsOptions): Promise(r => wss.close(() => r()))
await new Promise(r => httpServer.close(() => r()))
+ if (getInternalContext(context).wsEndpoint?.url === wsUrl)
+ getInternalContext(context).wsEndpoint = undefined
},
}
}
diff --git a/packages/hub/src/client/remote.ts b/packages/hub/src/client/remote.ts
index fdf1811..5d3d1f2 100644
--- a/packages/hub/src/client/remote.ts
+++ b/packages/hub/src/client/remote.ts
@@ -19,6 +19,14 @@ function extractKeyFromFragment(hash: string): string | null {
if (!hash)
return null
const raw = hash.startsWith('#') ? hash.slice(1) : hash
+ const queryIdx = raw.indexOf('?')
+ if (queryIdx !== -1) {
+ const params = new URLSearchParams(raw.slice(queryIdx + 1))
+ const value = params.get(REMOTE_CONNECTION_KEY)
+ if (value)
+ return value
+ }
+
for (const part of raw.split('&')) {
const [k, v = ''] = part.split('=')
if (k === REMOTE_CONNECTION_KEY)
diff --git a/packages/hub/src/node/__tests__/context.test.ts b/packages/hub/src/node/__tests__/context.test.ts
new file mode 100644
index 0000000..394c623
--- /dev/null
+++ b/packages/hub/src/node/__tests__/context.test.ts
@@ -0,0 +1,58 @@
+import { mkdtempSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+import { describe, expect, it } from 'vitest'
+import { createHostContext, startHttpAndWs } from '../../../../devframe/src/node'
+import { getInternalContext } from '../../../../devframe/src/node/internal'
+import { createHubContext } from '../context'
+
+function createHost(storageDir = mkdtempSync(join(tmpdir(), 'devframe-hub-context-'))) {
+ return {
+ mountStatic: () => {},
+ resolveOrigin: () => 'http://localhost:5173',
+ getStorageDir: () => storageDir,
+ }
+}
+
+describe('createHubContext shared state', () => {
+ it('seeds built-in docks and commands immediately', async () => {
+ const context = await createHubContext({
+ cwd: process.cwd(),
+ mode: 'build',
+ host: createHost(),
+ })
+
+ const docks = await context.rpc.sharedState.get('devframe:docks')
+ expect(docks.value().map(dock => dock.id)).toEqual([
+ '~terminals',
+ '~messages',
+ '~settings',
+ ])
+
+ const commands = await context.rpc.sharedState.get('devframe:commands')
+ expect(commands.value().map(command => command.id)).toContain('hub:open-path')
+ })
+})
+
+describe('startHttpAndWs remote endpoint metadata', () => {
+ it('sets and clears the internal websocket endpoint', async () => {
+ const context = await createHostContext({
+ cwd: process.cwd(),
+ mode: 'dev',
+ host: createHost(),
+ })
+
+ const started = await startHttpAndWs({
+ context,
+ host: '127.0.0.1',
+ port: 0,
+ })
+
+ expect(getInternalContext(context).wsEndpoint).toEqual({
+ url: `ws://127.0.0.1:${started.port}`,
+ })
+
+ await started.close()
+ expect(getInternalContext(context).wsEndpoint).toBeUndefined()
+ })
+})
diff --git a/packages/hub/src/node/__tests__/host-commands.test.ts b/packages/hub/src/node/__tests__/host-commands.test.ts
new file mode 100644
index 0000000..ccc4e56
--- /dev/null
+++ b/packages/hub/src/node/__tests__/host-commands.test.ts
@@ -0,0 +1,63 @@
+import type { HubNodeContext } from '../context'
+import { describe, expect, it } from 'vitest'
+import { DevToolsCommandsHost } from '../host-commands'
+
+describe('devToolsCommandsHost command id validation', () => {
+ it('rejects duplicate ids inside one command tree', () => {
+ const host = new DevToolsCommandsHost({} as HubNodeContext)
+
+ expect(() => host.register({
+ id: 'tool:parent',
+ title: 'Parent',
+ children: [
+ { id: 'tool:child', title: 'Child' },
+ { id: 'tool:child', title: 'Duplicate child' },
+ ],
+ })).toThrow('Command id "tool:child" is already used')
+ })
+
+ it('rejects child ids that collide with existing command trees', () => {
+ const host = new DevToolsCommandsHost({} as HubNodeContext)
+ host.register({
+ id: 'tool:parent',
+ title: 'Parent',
+ children: [
+ { id: 'tool:child', title: 'Child' },
+ ],
+ })
+
+ expect(() => host.register({
+ id: 'other:parent',
+ title: 'Other parent',
+ children: [
+ { id: 'tool:child', title: 'Duplicate child' },
+ ],
+ })).toThrow('Command id "tool:child" is already used')
+
+ expect(() => host.register({
+ id: 'tool:child',
+ title: 'Top-level collision',
+ })).toThrow('Command id "tool:child" is already used')
+ })
+
+ it('validates updated children against other command trees', () => {
+ const host = new DevToolsCommandsHost({} as HubNodeContext)
+ host.register({
+ id: 'other:parent',
+ title: 'Other parent',
+ children: [
+ { id: 'other:child', title: 'Other child' },
+ ],
+ })
+ const handle = host.register({
+ id: 'tool:parent',
+ title: 'Parent',
+ })
+
+ expect(() => handle.update({
+ children: [
+ { id: 'other:child', title: 'Duplicate child' },
+ ],
+ })).toThrow('Command id "other:child" is already used')
+ })
+})
diff --git a/packages/hub/src/node/__tests__/host-docks.test.ts b/packages/hub/src/node/__tests__/host-docks.test.ts
new file mode 100644
index 0000000..c230ac4
--- /dev/null
+++ b/packages/hub/src/node/__tests__/host-docks.test.ts
@@ -0,0 +1,78 @@
+import type { HubNodeContext } from '../context'
+import { mkdtempSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+import { REMOTE_CONNECTION_KEY } from 'devframe/constants'
+import { getInternalContext } from 'devframe/node/internal'
+import { describe, expect, it } from 'vitest'
+import { parseRemoteConnection } from '../../client/remote'
+import { DevToolsDockHost } from '../host-docks'
+
+function createContext(): HubNodeContext {
+ const storageDir = mkdtempSync(join(tmpdir(), 'devframe-hub-docks-'))
+ return {
+ host: {
+ mountStatic: () => {},
+ resolveOrigin: () => 'http://localhost:5173',
+ getStorageDir: () => storageDir,
+ },
+ } as unknown as HubNodeContext
+}
+
+describe('devToolsDockHost remote URL enrichment', () => {
+ it('preserves hash routes and replaces existing remote descriptors', () => {
+ const context = createContext()
+ getInternalContext(context).wsEndpoint = { url: 'ws://localhost:4173' }
+ const host = new DevToolsDockHost(context)
+
+ host.register({
+ type: 'iframe',
+ id: 'remote',
+ title: 'Remote',
+ url: 'https://remote.test/app#/inspect?tab=state',
+ remote: true,
+ })
+
+ const first = host.values({ includeBuiltin: false })[0]
+ expect(first.type).toBe('iframe')
+ const firstUrl = first.type === 'iframe' ? first.url : ''
+ expect(firstUrl).toContain(`#/inspect?tab=state&${REMOTE_CONNECTION_KEY}=`)
+ expect(parseRemoteConnection(firstUrl)).toMatchObject({
+ backend: 'websocket',
+ websocket: 'ws://localhost:4173',
+ origin: 'http://localhost:5173',
+ })
+
+ host.update({
+ type: 'iframe',
+ id: 'remote',
+ title: 'Remote',
+ url: firstUrl,
+ remote: true,
+ })
+
+ const second = host.values({ includeBuiltin: false })[0]
+ const secondUrl = second.type === 'iframe' ? second.url : ''
+ expect(secondUrl.match(new RegExp(REMOTE_CONNECTION_KEY, 'g'))).toHaveLength(1)
+ expect(secondUrl).toContain('#/inspect?tab=state&')
+ })
+
+ it('preserves non-route fragments with the ampersand descriptor form', () => {
+ const context = createContext()
+ getInternalContext(context).wsEndpoint = { url: 'ws://localhost:4173' }
+ const host = new DevToolsDockHost(context)
+
+ host.register({
+ type: 'iframe',
+ id: 'remote',
+ title: 'Remote',
+ url: 'https://remote.test/app#section',
+ remote: true,
+ })
+
+ const entry = host.values({ includeBuiltin: false })[0]
+ const url = entry.type === 'iframe' ? entry.url : ''
+ expect(url).toContain(`#section&${REMOTE_CONNECTION_KEY}=`)
+ expect(parseRemoteConnection(url)?.websocket).toBe('ws://localhost:4173')
+ })
+})
diff --git a/packages/hub/src/node/__tests__/host-messages.test.ts b/packages/hub/src/node/__tests__/host-messages.test.ts
new file mode 100644
index 0000000..f4857a1
--- /dev/null
+++ b/packages/hub/src/node/__tests__/host-messages.test.ts
@@ -0,0 +1,23 @@
+import type { HubNodeContext } from '../context'
+import { describe, expect, it } from 'vitest'
+import { DevToolsMessagesHost } from '../host-messages'
+
+describe('devToolsMessagesHost', () => {
+ it('caps removal history', async () => {
+ const host = new DevToolsMessagesHost({} as HubNodeContext)
+
+ for (let i = 0; i < 1005; i++) {
+ const id = `message:${i}`
+ await host.add({
+ id,
+ level: 'info',
+ message: id,
+ })
+ await host.remove(id)
+ }
+
+ expect(host.removals).toHaveLength(1000)
+ expect(host.removals[0].id).toBe('message:5')
+ expect(host.removals.at(-1)?.id).toBe('message:1004')
+ })
+})
diff --git a/packages/hub/src/node/__tests__/host-terminals.test.ts b/packages/hub/src/node/__tests__/host-terminals.test.ts
new file mode 100644
index 0000000..fc70198
--- /dev/null
+++ b/packages/hub/src/node/__tests__/host-terminals.test.ts
@@ -0,0 +1,134 @@
+import type { DevToolsTerminalSession } from '../../types/terminals'
+import type { HubNodeContext } from '../context'
+import process from 'node:process'
+import { describe, expect, it, vi } from 'vitest'
+import { DevToolsTerminalHost } from '../host-terminals'
+
+interface FakeSink {
+ write: ReturnType
+ close: ReturnType
+ error: ReturnType
+ readonly closed: boolean
+}
+
+function createTerminalHost() {
+ const sinks = new Map()
+ const context = {
+ rpc: {
+ streaming: {
+ create: () => ({
+ start: ({ id }: { id: string }) => {
+ let closed = false
+ const sink: FakeSink = {
+ write: vi.fn(),
+ close: vi.fn(() => {
+ closed = true
+ }),
+ error: vi.fn(() => {
+ closed = true
+ }),
+ get closed() {
+ return closed
+ },
+ }
+ sinks.set(id, sink)
+ return sink
+ },
+ }),
+ },
+ },
+ } as unknown as HubNodeContext
+
+ return {
+ host: new DevToolsTerminalHost(context),
+ sinks,
+ }
+}
+
+async function waitUntil(assertion: () => void): Promise {
+ const deadline = Date.now() + 1000
+ let lastError: unknown
+ while (Date.now() < deadline) {
+ try {
+ assertion()
+ return
+ }
+ catch (error) {
+ lastError = error
+ await new Promise(resolve => setTimeout(resolve, 10))
+ }
+ }
+ throw lastError
+}
+
+describe('devToolsTerminalHost stream lifecycle', () => {
+ it('cancels a bound stream when a session is removed', async () => {
+ const { host, sinks } = createTerminalHost()
+ let controller: ReadableStreamDefaultController
+ let cancelled = false
+ const stream = new ReadableStream({
+ start(_controller) {
+ controller = _controller
+ },
+ cancel() {
+ cancelled = true
+ },
+ })
+ const session: DevToolsTerminalSession = {
+ id: 'terminal',
+ title: 'Terminal',
+ status: 'running',
+ stream,
+ }
+
+ host.register(session)
+ controller!.enqueue('hello')
+
+ await waitUntil(() => {
+ expect(session.buffer).toEqual(['hello'])
+ })
+
+ host.remove(session)
+
+ await waitUntil(() => {
+ expect(cancelled).toBe(true)
+ })
+ expect(sinks.get('terminal')?.closed).toBe(true)
+ })
+
+ it('closes child-process streams after natural process exit', async () => {
+ const { host, sinks } = createTerminalHost()
+
+ const session = await host.startChildProcess({
+ command: process.execPath,
+ args: ['-e', 'process.stdout.write("done")'],
+ }, {
+ id: 'child',
+ title: 'Child',
+ })
+
+ await waitUntil(() => {
+ expect(sinks.get('child')?.closed).toBe(true)
+ })
+ expect(session.buffer?.join('')).toContain('done')
+ })
+
+ it('closes child-process streams on terminate', async () => {
+ const { host, sinks } = createTerminalHost()
+
+ const session = await host.startChildProcess({
+ command: process.execPath,
+ args: ['-e', 'setInterval(() => {}, 1000)'],
+ }, {
+ id: 'child',
+ title: 'Child',
+ })
+
+ await session.terminate()
+
+ await waitUntil(() => {
+ expect(sinks.get('child')?.closed).toBe(true)
+ })
+ expect(session.getChildProcess()).toBeUndefined()
+ })
+})
diff --git a/packages/hub/src/node/context.ts b/packages/hub/src/node/context.ts
index eddcf62..cd08d05 100644
--- a/packages/hub/src/node/context.ts
+++ b/packages/hub/src/node/context.ts
@@ -117,6 +117,7 @@ export async function createHubContext(options: CreateHubContextOptions): Promis
docksSharedState.mutate(() => docks.values())
}, debounceMs)
docks.events.on('dock:entry:updated', refreshDocks)
+ docksSharedState.mutate(() => docks.values())
const broadcastTerminals = debounce(() => {
context.rpc.broadcast({
@@ -147,6 +148,7 @@ export async function createHubContext(options: CreateHubContextOptions): Promis
commands.events.on('command:unregistered', syncCommands)
registerHubBuiltins(context)
+ commandsSharedState.mutate(() => commands.list())
return context
}
diff --git a/packages/hub/src/node/diagnostics.ts b/packages/hub/src/node/diagnostics.ts
index c85e7a6..94a5fdd 100644
--- a/packages/hub/src/node/diagnostics.ts
+++ b/packages/hub/src/node/diagnostics.ts
@@ -40,6 +40,10 @@ export const diagnostics = defineDiagnostics({
DF8402: {
why: (p: { id: string }) => `Command "${p.id}" is not registered`,
},
+ DF8403: {
+ why: (p: { id: string }) => `Command id "${p.id}" is already used by another command or child command`,
+ fix: 'Use globally unique command ids for top-level commands and all child commands.',
+ },
DF8500: {
why: (p: { id: string }) => `Built-in command "${p.id}" requires a host capability that this host does not implement.`,
fix: 'Implement the matching capability on the `DevToolsHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`.',
diff --git a/packages/hub/src/node/host-commands.ts b/packages/hub/src/node/host-commands.ts
index 86b7c69..fb48dd7 100644
--- a/packages/hub/src/node/host-commands.ts
+++ b/packages/hub/src/node/host-commands.ts
@@ -8,6 +8,48 @@ import type { HubNodeContext } from './context'
import { createEventEmitter } from 'devframe/utils/events'
import { diagnostics } from './diagnostics'
+function findChildCommand(command: DevToolsServerCommandInput, id: string): DevToolsServerCommandInput | undefined {
+ for (const child of command.children ?? []) {
+ if (child.id === id)
+ return child
+ const nested = findChildCommand(child, id)
+ if (nested)
+ return nested
+ }
+ return undefined
+}
+
+function collectCommandIds(command: DevToolsServerCommandInput, ids: string[] = []): string[] {
+ ids.push(command.id)
+ for (const child of command.children ?? [])
+ collectCommandIds(child, ids)
+ return ids
+}
+
+function validateCommandIds(
+ commands: Map,
+ command: DevToolsServerCommandInput,
+ ignoreTopLevelId?: string,
+): void {
+ const ids = collectCommandIds(command)
+ const seen = new Set()
+ for (const id of ids) {
+ if (seen.has(id))
+ throw diagnostics.DF8403({ id })
+ seen.add(id)
+ }
+
+ for (const [registeredId, registered] of commands) {
+ if (registeredId === ignoreTopLevelId)
+ continue
+ const registeredIds = new Set(collectCommandIds(registered))
+ for (const id of ids) {
+ if (registeredIds.has(id))
+ throw diagnostics.DF8403({ id })
+ }
+ }
+}
+
export class DevToolsCommandsHost implements DevToolsCommandsHostType {
public readonly commands: DevToolsCommandsHostType['commands'] = new Map()
public readonly events: DevToolsCommandsHostType['events'] = createEventEmitter()
@@ -20,6 +62,7 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
if (this.commands.has(command.id)) {
throw diagnostics.DF8400({ id: command.id })
}
+ validateCommandIds(this.commands, command)
this.commands.set(command.id, command)
this.events.emit('command:registered', this.toSerializable(command))
@@ -33,6 +76,12 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
if (!existing) {
throw diagnostics.DF8402({ id: command.id })
}
+ const next = {
+ ...existing,
+ ...patch,
+ id: existing.id,
+ }
+ validateCommandIds(this.commands, next, existing.id)
Object.assign(existing, patch)
this.events.emit('command:registered', this.toSerializable(existing))
},
@@ -71,11 +120,9 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
// Search children
for (const cmd of this.commands.values()) {
- if (cmd.children) {
- const child = cmd.children.find((c: DevToolsServerCommandInput) => c.id === id)
- if (child)
- return child
- }
+ const child = findChildCommand(cmd, id)
+ if (child)
+ return child
}
return undefined
diff --git a/packages/hub/src/node/host-docks.ts b/packages/hub/src/node/host-docks.ts
index f18a058..584c341 100644
--- a/packages/hub/src/node/host-docks.ts
+++ b/packages/hub/src/node/host-docks.ts
@@ -46,12 +46,32 @@ function buildRemoteUrl(baseUrl: string, payload: RemoteConnectionInfo, transpor
const encoded = base64UrlEncode(JSON.stringify(payload))
const param = `${REMOTE_CONNECTION_KEY}=${encoded}`
if (transport === 'fragment') {
- // Replace any existing fragment bearing our key; otherwise append.
+ // Replace any existing fragment/query descriptor bearing our key; otherwise append.
const hashIdx = baseUrl.indexOf('#')
if (hashIdx === -1)
return `${baseUrl}#${param}`
const before = baseUrl.slice(0, hashIdx)
- return `${before}#${param}`
+ const rawHash = baseUrl.slice(hashIdx + 1)
+ if (!rawHash)
+ return `${before}#${param}`
+ const routeQueryIdx = rawHash.indexOf('?')
+ if (routeQueryIdx !== -1) {
+ const beforeQuery = rawHash.slice(0, routeQueryIdx + 1)
+ const params = new URLSearchParams(rawHash.slice(routeQueryIdx + 1))
+ params.set(REMOTE_CONNECTION_KEY, encoded)
+ return `${before}#${beforeQuery}${params.toString()}`
+ }
+
+ const parts = rawHash.split('&')
+ const existingIdx = parts.findIndex((part) => {
+ const [key] = part.split('=')
+ return key === REMOTE_CONNECTION_KEY
+ })
+ if (existingIdx >= 0) {
+ parts[existingIdx] = param
+ return `${before}#${parts.join('&')}`
+ }
+ return `${before}#${rawHash}&${param}`
}
// query
const qIdx = baseUrl.indexOf('?')
diff --git a/packages/hub/src/node/host-messages.ts b/packages/hub/src/node/host-messages.ts
index 2b9a1d3..4086818 100644
--- a/packages/hub/src/node/host-messages.ts
+++ b/packages/hub/src/node/host-messages.ts
@@ -9,6 +9,17 @@ import { createEventEmitter } from 'devframe/utils/events'
import { nanoid } from 'devframe/utils/nanoid'
const MAX_ENTRIES = 1000
+const MAX_REMOVALS = 1000
+
+function recordRemoval(
+ removals: Array<{ id: string, time: number }>,
+ id: string,
+ time: number,
+): void {
+ removals.push({ id, time })
+ if (removals.length > MAX_REMOVALS)
+ removals.splice(0, removals.length - MAX_REMOVALS)
+}
export class DevToolsMessagesHost implements DevToolsMessagesHostType {
public readonly entries: DevToolsMessagesHostType['entries'] = new Map()
@@ -105,7 +116,7 @@ export class DevToolsMessagesHost implements DevToolsMessagesHostType {
}
this.entries.delete(id)
this.lastModified.delete(id)
- this.removals.push({ id, time: this._tick() })
+ recordRemoval(this.removals, id, this._tick())
this.events.emit('message:removed', id)
}
@@ -115,7 +126,7 @@ export class DevToolsMessagesHost implements DevToolsMessagesHostType {
this._autoDeleteTimers.clear()
const tick = this._tick()
for (const id of this.entries.keys())
- this.removals.push({ id, time: tick })
+ recordRemoval(this.removals, id, tick)
this.entries.clear()
this.lastModified.clear()
this.events.emit('message:cleared')
diff --git a/packages/hub/src/node/host-terminals.ts b/packages/hub/src/node/host-terminals.ts
index bb62af4..df492c6 100644
--- a/packages/hub/src/node/host-terminals.ts
+++ b/packages/hub/src/node/host-terminals.ts
@@ -104,25 +104,43 @@ export class DevToolsTerminalHost implements DevToolsTerminalHostType {
// `devframe:terminals:list`.
const sink = channel?.start({ id: session.id })
- const writer = new WritableStream({
- write(chunk) {
- // Mirror to the legacy session.buffer used by `terminals:read` —
- // unbounded history kept for the snapshot endpoint.
- sessionBuffer.push(chunk)
- sink?.write(chunk)
- },
- close() {
- sink?.close()
- },
- abort(reason) {
- sink?.error(reason)
- },
- })
- session.stream.pipeTo(writer).catch(() => {
- // pipeTo rejection surfaces via writer.abort -> sink.error already.
- })
+ const reader = session.stream.getReader()
+ let disposed = false
+ ;(async () => {
+ try {
+ while (true) {
+ if (disposed)
+ break
+ const result = await reader.read()
+ if (disposed)
+ break
+ if (result.done)
+ break
+ // Mirror to the legacy session.buffer used by `terminals:read` —
+ // unbounded history kept for the snapshot endpoint.
+ sessionBuffer.push(result.value)
+ sink?.write(result.value)
+ }
+ if (!disposed && sink && !sink.closed)
+ sink.close()
+ }
+ catch (error) {
+ if (!disposed && sink && !sink.closed)
+ sink.error(error)
+ }
+ finally {
+ try {
+ reader.releaseLock()
+ }
+ catch {
+ // Already released by the stream implementation.
+ }
+ }
+ })()
this._boundStreams.set(session.id, {
dispose: () => {
+ disposed = true
+ reader.cancel('terminal stream disposed').catch(() => {})
if (sink && !sink.closed)
sink.close()
},
@@ -140,13 +158,47 @@ export class DevToolsTerminalHost implements DevToolsTerminalHostType {
const { exec } = await import('tinyexec')
let controller: ReadableStreamDefaultController | undefined
+ let cp: TinyExecResult | undefined
+ let runId = 0
+ let streamClosed = false
+
+ const closeStream = () => {
+ if (streamClosed)
+ return
+ streamClosed = true
+ try {
+ controller?.close()
+ }
+ catch {
+ // The stream may already be closed by cancellation.
+ }
+ }
+
+ const errorStream = (error: unknown) => {
+ if (streamClosed)
+ return
+ streamClosed = true
+ try {
+ controller?.error(error)
+ }
+ catch {
+ // The stream may already be closed by cancellation.
+ }
+ }
+
const stream = new ReadableStream({
start(_controller) {
controller = _controller
},
+ cancel() {
+ cp?.kill()
+ cp = undefined
+ closeStream()
+ },
})
function createChildProcess() {
+ const currentRun = ++runId
const cp = exec(
executeOptions.command,
executeOptions.args || [],
@@ -164,15 +216,25 @@ export class DevToolsTerminalHost implements DevToolsTerminalHostType {
)
;(async () => {
- for await (const chunk of cp) {
- controller?.enqueue(chunk)
+ try {
+ for await (const chunk of cp) {
+ if (streamClosed || currentRun !== runId)
+ return
+ controller?.enqueue(chunk)
+ }
+ if (currentRun === runId)
+ closeStream()
+ }
+ catch (error) {
+ if (currentRun === runId)
+ errorStream(error)
}
})()
return cp
}
- let cp: TinyExecResult | undefined = createChildProcess()
+ cp = createChildProcess()
const restart = async () => {
cp?.kill()
@@ -181,6 +243,7 @@ export class DevToolsTerminalHost implements DevToolsTerminalHostType {
const terminate = async () => {
cp?.kill()
cp = undefined
+ closeStream()
}
const session: DevToolsChildProcessTerminalSession = {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0f1064f..03058b2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -288,7 +288,44 @@ importers:
specifier: catalog:deps
version: 8.20.0
- examples/minimal-vite-devtools-kit:
+ examples/minimal-next-devtools-hub:
+ dependencies:
+ '@devframes/hub':
+ specifier: workspace:*
+ version: link:../../packages/hub
+ devframe:
+ specifier: workspace:*
+ version: link:../../packages/devframe
+ next:
+ specifier: catalog:frontend
+ version: 16.2.6(@playwright/test@1.60.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react:
+ specifier: catalog:frontend
+ version: 19.2.6
+ react-dom:
+ specifier: catalog:frontend
+ version: 19.2.6(react@19.2.6)
+ devDependencies:
+ '@types/react':
+ specifier: catalog:types
+ version: 19.2.15
+ '@types/react-dom':
+ specifier: catalog:types
+ version: 19.2.3(@types/react@19.2.15)
+ get-port-please:
+ specifier: catalog:deps
+ version: 3.2.0
+ pathe:
+ specifier: catalog:deps
+ version: 2.0.3
+ vitest:
+ specifier: catalog:testing
+ version: 4.1.6(@types/node@25.7.0)(vite@8.0.12(@types/node@25.7.0)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))
+ ws:
+ specifier: catalog:deps
+ version: 8.20.0
+
+ examples/minimal-vite-devtools-hub:
dependencies:
'@devframes/hub':
specifier: workspace:*
diff --git a/tests/__snapshots__/tsnapi/devframe/node/internal.snapshot.js b/tests/__snapshots__/tsnapi/devframe/node/internal.snapshot.js
index 235aba6..73cbb22 100644
--- a/tests/__snapshots__/tsnapi/devframe/node/internal.snapshot.js
+++ b/tests/__snapshots__/tsnapi/devframe/node/internal.snapshot.js
@@ -1,15 +1,9 @@
/**
* Generated by tsnapi — public API snapshot of `devframe/node/internal`
*/
-// #region Functions
-export function getInternalContext(_) {}
-// #endregion
-
-// #region Variables
-export var internalContextMap /* const */
-// #endregion
-
// #region Other
+export { getInternalContext }
+export { internalContextMap }
export { normalizeBasePath }
export { resolveBasePath }
// #endregion
\ No newline at end of file
diff --git a/turbo.json b/turbo.json
index 8f119f0..80cfa16 100644
--- a/turbo.json
+++ b/turbo.json
@@ -15,7 +15,12 @@
"outputLogs": "new-only",
"outputs": ["dist/**"]
},
- "minimal-vite-devtools-kit-example#build": {
+ "minimal-vite-devtools-hub#build": {
+ "outputLogs": "new-only",
+ "dependsOn": ["@devframes/hub#build", "devframe#build"],
+ "outputs": ["dist/**"]
+ },
+ "minimal-next-devtools-hub#build": {
"outputLogs": "new-only",
"dependsOn": ["@devframes/hub#build", "devframe#build"],
"outputs": ["dist/**"]
diff --git a/vitest.config.ts b/vitest.config.ts
index 391bf30..7143519 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -1,12 +1,18 @@
import { defineConfig } from 'vitest/config'
+import { alias } from './alias'
export default defineConfig({
+ resolve: {
+ alias,
+ },
test: {
projects: [
'packages/devframe',
+ 'packages/hub',
'examples/files-inspector',
'examples/streaming-chat',
'examples/next-runtime-snapshot',
+ 'examples/minimal-next-devtools-hub',
{
test: {
name: 'tests',
From 4dffab8be713754cdddf11c896804e3caeee6f7a Mon Sep 17 00:00:00 2001
From: Anthony Fu
Date: Fri, 22 May 2026 15:32:51 +0900
Subject: [PATCH 3/7] refactor: rename devtools APIs to devframe
---
.github/workflows/ecosystem-ci.yml | 2 +-
AGENTS.md | 2 +-
README.md | 2 +-
docs/.vitepress/config.ts | 2 +-
docs/adapters/cli.md | 4 +-
docs/adapters/embedded.md | 2 +-
docs/errors/DF0006.md | 2 +-
docs/errors/DF0007.md | 4 +-
docs/errors/DF0008.md | 4 +-
docs/errors/DF0012.md | 2 +-
docs/errors/DF0013.md | 2 +-
docs/errors/DF0014.md | 2 +-
docs/errors/DF0015.md | 2 +-
docs/errors/DF0016.md | 2 +-
docs/errors/DF0017.md | 2 +-
docs/errors/DF0019.md | 2 +-
docs/errors/DF0020.md | 2 +-
docs/errors/DF0021.md | 2 +-
docs/errors/DF0022.md | 2 +-
docs/errors/DF0023.md | 2 +-
docs/errors/DF0024.md | 4 +-
docs/errors/DF0025.md | 2 +-
docs/errors/DF0026.md | 2 +-
docs/errors/DF0027.md | 2 +-
docs/errors/DF0028.md | 2 +-
docs/errors/DF0029.md | 2 +-
docs/errors/DF0030.md | 2 +-
docs/errors/DF0031.md | 2 +-
docs/errors/DF0032.md | 2 +-
docs/errors/DF0033.md | 2 +-
docs/errors/DF8100.md | 2 +-
docs/errors/DF8101.md | 2 +-
docs/errors/DF8102.md | 2 +-
docs/errors/DF8200.md | 2 +-
docs/errors/DF8201.md | 2 +-
docs/errors/DF8400.md | 2 +-
docs/errors/DF8401.md | 2 +-
docs/errors/DF8402.md | 2 +-
docs/errors/DF8403.md | 2 +-
docs/errors/DF8500.md | 8 +-
docs/guide/agent-native.md | 4 +-
docs/guide/built-with.md | 2 +-
docs/guide/client.md | 10 +-
docs/guide/devframe-definition.md | 12 +-
docs/guide/diagnostics.md | 8 +-
docs/guide/hub.md | 16 +--
docs/guide/index.md | 2 +-
docs/guide/rpc.md | 16 +--
docs/guide/shared-state.md | 4 +-
docs/helpers/nuxt.md | 2 +-
docs/index.md | 2 +-
examples/files-inspector/src/client/app.tsx | 4 +-
.../src/client/routes/about.tsx | 4 +-
.../src/client/routes/home.tsx | 4 +-
examples/files-inspector/tests/_utils.ts | 8 +-
.../tests/static-build.test.ts | 14 +--
.../tests/static-serve.test.ts | 10 +-
.../.gitignore | 0
.../README.md | 12 +-
.../package.json | 4 +-
.../app/%5F_hub/%5F_connection.json/route.ts | 4 +-
.../src/client/app/globals.css | 0
.../src/client/app/layout.tsx | 2 +-
.../src/client/app/page.tsx | 34 +++---
.../src/client/devframe}/demo-devframe.ts | 0
.../devframe/minimal-next-devframe-hub.ts} | 38 +++----
.../src/client/next.config.mjs | 0
.../src/client/tsconfig.json | 0
.../tests/minimal-next-devframe-hub.test.ts} | 20 ++--
.../tsconfig.json | 0
.../vitest.config.ts | 0
.../README.md | 10 +-
.../index.html | 4 +-
.../package.json | 4 +-
.../src/client/main.ts | 20 ++--
.../src/client/style.css | 0
.../src/devframe.ts | 0
.../src/minimal-vite-devframe-hub.ts} | 26 ++---
.../tsconfig.json | 0
.../vite.config.ts | 4 +-
.../src/client/app/components/connect.tsx | 4 +-
.../next-runtime-snapshot/tests/_utils.ts | 8 +-
examples/streaming-chat/README.md | 2 +-
examples/streaming-chat/src/client/app.tsx | 4 +-
examples/streaming-chat/src/devframe.ts | 2 +-
examples/streaming-chat/tests/_utils.ts | 12 +-
.../tests/streaming-chat.test.ts | 6 +-
package.json | 14 +--
packages/devframe/README.md | 2 +-
packages/devframe/package.json | 2 +-
packages/devframe/src/adapters/build.ts | 20 ++--
packages/devframe/src/adapters/dev.ts | 8 +-
packages/devframe/src/adapters/embedded.ts | 4 +-
.../adapters/mcp/__tests__/mcp-server.test.ts | 4 +-
.../devframe/src/adapters/mcp/build-server.ts | 24 ++--
packages/devframe/src/client/index.ts | 4 +-
.../devframe/src/client/rpc-shared-state.ts | 4 +-
packages/devframe/src/client/rpc-static.ts | 8 +-
packages/devframe/src/client/rpc-streaming.ts | 4 +-
packages/devframe/src/client/rpc-ws.ts | 16 +--
packages/devframe/src/client/rpc.ts | 64 +++++------
.../devframe/src/client/static-rpc.test.ts | 12 +-
packages/devframe/src/client/static-rpc.ts | 8 +-
packages/devframe/src/constants.ts | 20 ++--
packages/devframe/src/define.ts | 4 +-
packages/devframe/src/helpers/vite.ts | 4 +-
.../src/node/__tests__/host-agent.test.ts | 12 +-
.../src/node/__tests__/host-functions.test.ts | 8 +-
.../__tests__/rpc-agent-introspection.test.ts | 4 +-
.../src/node/__tests__/rpc-streaming.test.ts | 12 +-
.../src/node/__tests__/storage.test.ts | 2 +-
packages/devframe/src/node/auth/revoke.ts | 6 +-
packages/devframe/src/node/auth/state.ts | 4 +-
packages/devframe/src/node/context.ts | 24 ++--
packages/devframe/src/node/diagnostics.ts | 2 +-
packages/devframe/src/node/host-agent.ts | 16 +--
.../devframe/src/node/host-diagnostics.ts | 12 +-
packages/devframe/src/node/host-functions.ts | 30 ++---
packages/devframe/src/node/host-h3.ts | 14 +--
packages/devframe/src/node/host-views.ts | 6 +-
.../devframe/src/node/internal/context.ts | 10 +-
packages/devframe/src/node/internal/index.ts | 2 +-
.../devframe/src/node/rpc-shared-state.ts | 12 +-
packages/devframe/src/node/rpc-streaming.ts | 14 +--
packages/devframe/src/node/rpc/index.ts | 2 +-
packages/devframe/src/node/server.ts | 16 +--
.../src/rpc/dump/__tests__/static.test.ts | 18 +--
packages/devframe/src/rpc/dump/static.ts | 8 +-
.../devframe/src/rpc/transports/ws-client.ts | 2 +-
.../devframe/src/rpc/transports/ws-server.ts | 8 +-
.../devframe/src/rpc/transports/ws.test.ts | 2 +-
packages/devframe/src/types/agent.ts | 14 +--
packages/devframe/src/types/context.ts | 20 ++--
packages/devframe/src/types/devframe.ts | 4 +-
packages/devframe/src/types/diagnostics.ts | 12 +-
packages/devframe/src/types/host.ts | 16 +--
packages/devframe/src/types/rpc-augments.ts | 6 +-
packages/devframe/src/types/rpc.ts | 34 +++---
packages/devframe/src/types/views.ts | 2 +-
packages/devframe/test/dts-dedupe.test.ts | 6 +-
packages/hub/src/client/client-script.ts | 4 +-
packages/hub/src/client/context.ts | 6 +-
packages/hub/src/client/docks.ts | 36 +++---
packages/hub/src/client/remote.ts | 20 ++--
packages/hub/src/constants.ts | 8 +-
packages/hub/src/define.ts | 12 +-
.../src/node/__tests__/host-commands.test.ts | 8 +-
.../hub/src/node/__tests__/host-docks.test.ts | 6 +-
.../src/node/__tests__/host-messages.test.ts | 4 +-
.../src/node/__tests__/host-terminals.test.ts | 8 +-
packages/hub/src/node/context.ts | 32 +++---
packages/hub/src/node/diagnostics.ts | 2 +-
packages/hub/src/node/host-commands.ts | 34 +++---
packages/hub/src/node/host-docks.ts | 40 +++----
packages/hub/src/node/host-messages.ts | 24 ++--
packages/hub/src/node/host-terminals.ts | 32 +++---
packages/hub/src/node/mount-devframe.ts | 6 +-
packages/hub/src/types/commands.ts | 44 ++++----
packages/hub/src/types/docks.ts | 48 ++++----
packages/hub/src/types/index.ts | 20 ++--
packages/hub/src/types/messages.ts | 42 +++----
packages/hub/src/types/settings.ts | 6 +-
packages/hub/src/types/terminals.ts | 34 +++---
packages/nuxt/src/runtime/plugin.client.ts | 2 +-
packages/nuxt/src/runtime/types.d.ts | 6 +-
packages/nuxt/tsdown.config.ts | 4 +-
pnpm-lock.yaml | 64 +++++------
pnpm-workspace.yaml | 16 +--
scripts/ecosystem-ci.ts | 2 +-
skills/devframe/SKILL.md | 10 +-
.../@devframes/hub/client.snapshot.d.ts | 36 +++---
.../tsnapi/@devframes/hub/client.snapshot.js | 4 +-
.../@devframes/hub/constants.snapshot.d.ts | 2 +-
.../tsnapi/@devframes/hub/index.snapshot.d.ts | 106 +++++++++---------
.../tsnapi/@devframes/hub/node.snapshot.d.ts | 50 ++++-----
.../tsnapi/@devframes/hub/node.snapshot.js | 8 +-
.../tsnapi/@devframes/hub/types.snapshot.d.ts | 100 ++++++++---------
.../devframe/adapters/embedded.snapshot.d.ts | 2 +-
.../tsnapi/devframe/client.snapshot.d.ts | 46 ++++----
.../tsnapi/devframe/client.snapshot.js | 2 +-
.../tsnapi/devframe/constants.snapshot.d.ts | 16 +--
.../tsnapi/devframe/constants.snapshot.js | 16 +--
.../tsnapi/devframe/index.snapshot.d.ts | 32 +++---
.../tsnapi/devframe/node.snapshot.d.ts | 48 ++++----
.../tsnapi/devframe/node.snapshot.js | 8 +-
.../tsnapi/devframe/node/auth.snapshot.d.ts | 6 +-
.../devframe/node/internal.snapshot.d.ts | 2 +-
.../rpc/transports/ws-server.snapshot.d.ts | 2 +-
.../tsnapi/devframe/types.snapshot.d.ts | 30 ++---
turbo.json | 4 +-
vitest.config.ts | 2 +-
191 files changed, 1062 insertions(+), 1062 deletions(-)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/.gitignore (100%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/README.md (77%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/package.json (87%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/app/%5F_hub/%5F_connection.json/route.ts (51%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/app/globals.css (100%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/app/layout.tsx (90%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/app/page.tsx (86%)
rename examples/{minimal-next-devtools-hub/src/client/devtools => minimal-next-devframe-hub/src/client/devframe}/demo-devframe.ts (100%)
rename examples/{minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts => minimal-next-devframe-hub/src/client/devframe/minimal-next-devframe-hub.ts} (77%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/next.config.mjs (100%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/src/client/tsconfig.json (100%)
rename examples/{minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts => minimal-next-devframe-hub/tests/minimal-next-devframe-hub.test.ts} (71%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/tsconfig.json (100%)
rename examples/{minimal-next-devtools-hub => minimal-next-devframe-hub}/vitest.config.ts (100%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/README.md (81%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/index.html (93%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/package.json (68%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/src/client/main.ts (88%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/src/client/style.css (100%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/src/devframe.ts (100%)
rename examples/{minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts => minimal-vite-devframe-hub/src/minimal-vite-devframe-hub.ts} (87%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/tsconfig.json (100%)
rename examples/{minimal-vite-devtools-hub => minimal-vite-devframe-hub}/vite.config.ts (69%)
diff --git a/.github/workflows/ecosystem-ci.yml b/.github/workflows/ecosystem-ci.yml
index 5a6dd64..17df18a 100644
--- a/.github/workflows/ecosystem-ci.yml
+++ b/.github/workflows/ecosystem-ci.yml
@@ -27,7 +27,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- run: pnpm test:ecosystem
env:
- ECOSYSTEM_DEVTOOLS_REF: ${{ inputs.ref }}
+ ECOSYSTEM_DEVFRAME_REF: ${{ inputs.ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: failure()
uses: actions/upload-artifact@v4
diff --git a/AGENTS.md b/AGENTS.md
index 3b20b60..2fb3123 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,7 +4,7 @@
**`devframe`** is the framework-neutral container for one devtool integration, portable across viewers. Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/spa/embedded outputs) without caring how it'll be displayed. A devframe app runs standalone (CLI, static deploy, embedded SPA) just as well as it mounts inside a hub.
-**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. See `examples/minimal-vite-devtools-hub/` for a working ~120-line Vite host demonstrating the protocol end to end.
+**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. See `examples/minimal-vite-devframe-hub/` for a working ~120-line Vite host demonstrating the protocol end to end.
## Stack & Structure
diff --git a/README.md b/README.md
index 1c4cbb9..c505d82 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
-Framework-neutral foundation for building generic DevTools.
+Framework-neutral foundation for building devframes.
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index a834804..034b98b 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -99,7 +99,7 @@ export function devframeNav(prefix = ''): DefaultTheme.NavItem[] {
export default withMermaid(defineConfig({
title: 'Devframe',
- description: 'Framework-neutral foundation for building generic DevTools — RPC layer, hosts, and adapters.',
+ description: 'Framework-neutral foundation for building generic devframes — RPC layer, hosts, and adapters.',
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],
['link', { rel: 'apple-touch-icon', href: '/logo.svg' }],
diff --git a/docs/adapters/cli.md b/docs/adapters/cli.md
index 076108c..3e363f8 100644
--- a/docs/adapters/cli.md
+++ b/docs/adapters/cli.md
@@ -26,11 +26,11 @@ Running the resulting binary:
my-devframe # dev server at http://localhost:9999/
my-devframe --port 8080
my-devframe build --out-dir dist-static
-my-devframe build --out-dir dist-static --base /devtools/
+my-devframe build --out-dir dist-static --base /devframe/
my-devframe mcp # stdio MCP server (experimental)
```
-Standalone CLI serves the SPA at `/` by default. The `/__devtools/` prefix is for *hosted* adapters where devframe mounts alongside an existing app — see [Mount paths](./#mount-paths).
+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).
## Options
diff --git a/docs/adapters/embedded.md b/docs/adapters/embedded.md
index 5d2ae0b..ef9bf64 100644
--- a/docs/adapters/embedded.md
+++ b/docs/adapters/embedded.md
@@ -15,6 +15,6 @@ await createEmbedded(devframe, { ctx: existingCtx })
| Option | Required | Description |
|--------|----------|-------------|
-| `ctx` | ✓ | Target `DevToolsNodeContext` the devframe is registered into. |
+| `ctx` | ✓ | Target `DevframeNodeContext` the devframe is registered into. |
Useful when a host loads devframes based on runtime conditions (feature flags, user opt-in, dynamic discovery) rather than static config.
diff --git a/docs/errors/DF0006.md b/docs/errors/DF0006.md
index fa848d0..d4d6076 100644
--- a/docs/errors/DF0006.md
+++ b/docs/errors/DF0006.md
@@ -18,4 +18,4 @@ Register the function with `ctx.rpc.register(defineRpcFunction({ name }))` befor
## Source
-- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `RpcFunctionsHost.invokeLocal()` throws `DF0006` when the requested method has not been registered on this host.
+- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `RpcFunctionsHost.invokeLocal()` throws `DF0006` when the requested method has not been registered on this host.
diff --git a/docs/errors/DF0007.md b/docs/errors/DF0007.md
index 7cc5e40..d95e9cd 100644
--- a/docs/errors/DF0007.md
+++ b/docs/errors/DF0007.md
@@ -6,7 +6,7 @@ outline: deep
## Message
-> AsyncLocalStorage is not set, it likely to be an internal bug of the DevTools foundation
+> AsyncLocalStorage is not set, it likely to be an internal bug of the Devframe foundation
## Cause
@@ -18,4 +18,4 @@ Only call `getCurrentRpcSession()` from RPC handlers executed by the server. Rep
## Source
-- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `getCurrentRpcSession()` throws `DF0007` when called outside the RPC dispatch async context.
+- [`packages/devframe/src/node/host-functions.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-functions.ts) — `getCurrentRpcSession()` throws `DF0007` when called outside the RPC dispatch async context.
diff --git a/docs/errors/DF0008.md b/docs/errors/DF0008.md
index 57db674..b423901 100644
--- a/docs/errors/DF0008.md
+++ b/docs/errors/DF0008.md
@@ -10,7 +10,7 @@ outline: deep
## Cause
-`DevToolsViewHost.hostStatic()` was asked to mount a directory that doesn't exist on disk.
+`DevframeViewHost.hostStatic()` was asked to mount a directory that doesn't exist on disk.
## Fix
@@ -18,4 +18,4 @@ Verify the `distDir` path resolves correctly (run your SPA build first, and chec
## Source
-- [`packages/devframe/src/node/host-views.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-views.ts) — `DevToolsViewHost.hostStatic()` throws `DF0008` when the resolved `distDir` does not exist on disk.
+- [`packages/devframe/src/node/host-views.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-views.ts) — `DevframeViewHost.hostStatic()` throws `DF0008` when the resolved `distDir` does not exist on disk.
diff --git a/docs/errors/DF0012.md b/docs/errors/DF0012.md
index d4f5ae6..7706726 100644
--- a/docs/errors/DF0012.md
+++ b/docs/errors/DF0012.md
@@ -18,4 +18,4 @@ Delete the file to reset to defaults, or investigate how it became malformed.
## Source
-- [`packages/devframe/src/node/storage.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/storage.ts) — `createStorage()` catches `JSON.parse` errors and logs `DF0012` (with the cause attached) before falling back to `initialValue`.
+- [`packages/devframe/src/node/storage.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/storage.ts) — `createStorage()` catches `JSON.parse` errors and logs `DF0012` (with the cause attached) before falling back to `initialValue`.
diff --git a/docs/errors/DF0013.md b/docs/errors/DF0013.md
index 00a94fd..661b593 100644
--- a/docs/errors/DF0013.md
+++ b/docs/errors/DF0013.md
@@ -18,4 +18,4 @@ Pass `initialValue` on the first call: `ctx.rpc.sharedState.get(key, { initialVa
## Source
-- [`packages/devframe/src/node/rpc-shared-state.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/rpc-shared-state.ts) — `RpcSharedStateHost.get()` throws `DF0013` when neither an existing entry nor an `initialValue` is provided for a key.
+- [`packages/devframe/src/node/rpc-shared-state.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/rpc-shared-state.ts) — `RpcSharedStateHost.get()` throws `DF0013` when neither an existing entry nor an `initialValue` is provided for a key.
diff --git a/docs/errors/DF0014.md b/docs/errors/DF0014.md
index bef3792..4798c8e 100644
--- a/docs/errors/DF0014.md
+++ b/docs/errors/DF0014.md
@@ -50,4 +50,4 @@ If you didn't intend for this function to be agent-exposed, remove the `agent` f
## Source
-- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.
+- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.
diff --git a/docs/errors/DF0015.md b/docs/errors/DF0015.md
index 35ae3e5..26562df 100644
--- a/docs/errors/DF0015.md
+++ b/docs/errors/DF0015.md
@@ -34,4 +34,4 @@ ctx.agent.registerTool({ id: 'my-tool', /* new config */ })
## Source
-- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerTool()` throws `DF0015` when the tool id collides with an existing agent tool or an RPC function's `agent` exposure.
+- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerTool()` throws `DF0015` when the tool id collides with an existing agent tool or an RPC function's `agent` exposure.
diff --git a/docs/errors/DF0016.md b/docs/errors/DF0016.md
index 1fce205..de41517 100644
--- a/docs/errors/DF0016.md
+++ b/docs/errors/DF0016.md
@@ -22,4 +22,4 @@ Pick a distinct id or unregister the existing resource first via the handle retu
## Source
-- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerResource()` throws `DF0016` when the resource id is already registered on the host.
+- [`packages/devframe/src/node/host-agent.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/host-agent.ts) — `ctx.agent.registerResource()` throws `DF0016` when the resource id is already registered on the host.
diff --git a/docs/errors/DF0017.md b/docs/errors/DF0017.md
index 38aa747..8d60b7c 100644
--- a/docs/errors/DF0017.md
+++ b/docs/errors/DF0017.md
@@ -28,4 +28,4 @@ The agent-native surface is experimental and may change without a major version
## Source
-- [`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/adapters/mcp/build-server.ts) — `createMcpServer()` throws `DF0017` when the requested transport is unsupported or when the underlying transport fails to `connect()`.
+- [`packages/devframe/src/adapters/mcp/build-server.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/adapters/mcp/build-server.ts) — `createMcpServer()` throws `DF0017` when the requested transport is unsupported or when the underlying transport fails to `connect()`.
diff --git a/docs/errors/DF0019.md b/docs/errors/DF0019.md
index e084aaf..ec950ab 100644
--- a/docs/errors/DF0019.md
+++ b/docs/errors/DF0019.md
@@ -49,4 +49,4 @@ defineRpcFunction({
## Source
-- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.
+- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.
diff --git a/docs/errors/DF0020.md b/docs/errors/DF0020.md
index 36395f2..09a883b 100644
--- a/docs/errors/DF0020.md
+++ b/docs/errors/DF0020.md
@@ -53,4 +53,4 @@ Or convert the payload to a JSON-safe shape (e.g. an array of entries, an ISO st
## Source
-- [`packages/devframe/src/rpc/serialization.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/serialization.ts) — the strict JSON serializer throws `DF0020` (with the offending path and runtime type) when a `jsonSerializable: true` payload contains a non-JSON value.
+- [`packages/devframe/src/rpc/serialization.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/serialization.ts) — the strict JSON serializer throws `DF0020` (with the offending path and runtime type) when a `jsonSerializable: true` payload contains a non-JSON value.
diff --git a/docs/errors/DF0021.md b/docs/errors/DF0021.md
index 47159f9..eac91e2 100644
--- a/docs/errors/DF0021.md
+++ b/docs/errors/DF0021.md
@@ -22,4 +22,4 @@ ctx.rpc.register(defineRpcFunction({ name: 'my-plugin:fn', handler: () => 1 }),
## Source
-- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0021` when an RPC name is already registered and `force` is not set.
+- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0021` when an RPC name is already registered and `force` is not set.
diff --git a/docs/errors/DF0022.md b/docs/errors/DF0022.md
index 5941f87..47cc11a 100644
--- a/docs/errors/DF0022.md
+++ b/docs/errors/DF0022.md
@@ -18,4 +18,4 @@ Call `ctx.rpc.register()` first, or pass `force: true` to `update()` to register
## Source
-- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.update()` throws `DF0022` when the named function was never registered.
+- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.update()` throws `DF0022` when the named function was never registered.
diff --git a/docs/errors/DF0023.md b/docs/errors/DF0023.md
index 5937e06..975972e 100644
--- a/docs/errors/DF0023.md
+++ b/docs/errors/DF0023.md
@@ -18,4 +18,4 @@ Confirm the function name matches a registration. RPC names are namespaced — t
## Source
-- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — collector `get()`/lookup paths throw `DF0023` when consumers ask for a function that has not been registered.
+- [`packages/devframe/src/rpc/collector.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/collector.ts) — collector `get()`/lookup paths throw `DF0023` when consumers ask for a function that has not been registered.
diff --git a/docs/errors/DF0024.md b/docs/errors/DF0024.md
index 07ca9b7..4ecb3cb 100644
--- a/docs/errors/DF0024.md
+++ b/docs/errors/DF0024.md
@@ -18,5 +18,5 @@ Add either `handler: ...` directly on the definition, or `setup: ctx => ({ handl
## Source
-- [`packages/devframe/src/rpc/handler.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/handler.ts) — invocation throws `DF0024` when neither `handler` nor a `setup` returning `{ handler }` is provided.
-- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — dump generation also requires a handler and throws `DF0024` if the definition is incomplete.
+- [`packages/devframe/src/rpc/handler.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/handler.ts) — invocation throws `DF0024` when neither `handler` nor a `setup` returning `{ handler }` is provided.
+- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — dump generation also requires a handler and throws `DF0024` if the definition is incomplete.
diff --git a/docs/errors/DF0025.md b/docs/errors/DF0025.md
index fa8285b..e0af4b0 100644
--- a/docs/errors/DF0025.md
+++ b/docs/errors/DF0025.md
@@ -18,4 +18,4 @@ Re-run `createBuild` to regenerate the dump, or check that the call site uses th
## Source
-- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0025` when a client calls a function name that is not present in the baked dump store.
+- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0025` when a client calls a function name that is not present in the baked dump store.
diff --git a/docs/errors/DF0026.md b/docs/errors/DF0026.md
index cccd666..f7f4627 100644
--- a/docs/errors/DF0026.md
+++ b/docs/errors/DF0026.md
@@ -28,4 +28,4 @@ defineRpcFunction({
## Source
-- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0026` when none of the pre-computed inputs matches the call's args and no `fallback` was configured.
+- [`packages/devframe/src/rpc/dump/index.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/dump/index.ts) — the static-mode dump resolver throws `DF0026` when none of the pre-computed inputs matches the call's args and no `fallback` was configured.
diff --git a/docs/errors/DF0027.md b/docs/errors/DF0027.md
index 099ed35..d9da4c3 100644
--- a/docs/errors/DF0027.md
+++ b/docs/errors/DF0027.md
@@ -18,4 +18,4 @@ Drop the `dump` field, or change the function `type` to `'static'` / `'query'` i
## Source
-- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0027` when a `dump` field is attached to an `'action'` or `'event'` function.
+- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0027` when a `dump` field is attached to an `'action'` or `'event'` function.
diff --git a/docs/errors/DF0028.md b/docs/errors/DF0028.md
index a0f840e..d9758fd 100644
--- a/docs/errors/DF0028.md
+++ b/docs/errors/DF0028.md
@@ -18,4 +18,4 @@ Remove `snapshot: true`, or change the function `type` to `'query'`.
## Source
-- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0028` when `snapshot: true` is set on a function whose type is not `'query'`.
+- [`packages/devframe/src/rpc/validation.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/rpc/validation.ts) — definition validation throws `DF0028` when `snapshot: true` is set on a function whose type is not `'query'`.
diff --git a/docs/errors/DF0029.md b/docs/errors/DF0029.md
index a4111b4..f5c41fc 100644
--- a/docs/errors/DF0029.md
+++ b/docs/errors/DF0029.md
@@ -22,4 +22,4 @@ This is a soft warning — the stream keeps running and remaining chunks still f
## Source
-- [`packages/devframe/src/client/rpc-streaming.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/client/rpc-streaming.ts) — the client subscription queue logs `DF0029` (with the dropped chunk count) when buffered chunks exceed `highWaterMark`.
+- [`packages/devframe/src/client/rpc-streaming.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/client/rpc-streaming.ts) — the client subscription queue logs `DF0029` (with the dropped chunk count) when buffered chunks exceed `highWaterMark`.
diff --git a/docs/errors/DF0030.md b/docs/errors/DF0030.md
index b7d0d28..2065fcd 100644
--- a/docs/errors/DF0030.md
+++ b/docs/errors/DF0030.md
@@ -20,4 +20,4 @@ A client subscribed to a stream id that the server-side channel doesn't know abo
## Source
-- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — the streaming subscribe/unsubscribe paths log `DF0030` when a client references an `id` that no producer has started (and no replay buffer covers).
+- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — the streaming subscribe/unsubscribe paths log `DF0030` when a client references an `id` that no producer has started (and no replay buffer covers).
diff --git a/docs/errors/DF0031.md b/docs/errors/DF0031.md
index 7a80000..6e9eb10 100644
--- a/docs/errors/DF0031.md
+++ b/docs/errors/DF0031.md
@@ -33,4 +33,4 @@ catch (err) {
## Source
-- [`packages/devframe/src/utils/streaming-channel.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/utils/streaming-channel.ts) — `stream.write()` throws `DF0031` when called after the stream has been closed, errored, or aborted by the consumer.
+- [`packages/devframe/src/utils/streaming-channel.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/utils/streaming-channel.ts) — `stream.write()` throws `DF0031` when called after the stream has been closed, errored, or aborted by the consumer.
diff --git a/docs/errors/DF0032.md b/docs/errors/DF0032.md
index 109fcca..4171fee 100644
--- a/docs/errors/DF0032.md
+++ b/docs/errors/DF0032.md
@@ -19,4 +19,4 @@ Two calls to `ctx.rpc.streaming.create(name, ...)` used the same channel name. E
## Source
-- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — `ctx.rpc.streaming.create()` throws `DF0032` when the requested channel name is already registered on the context.
+- [`packages/devframe/src/node/rpc-streaming.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/node/rpc-streaming.ts) — `ctx.rpc.streaming.create()` throws `DF0032` when the requested channel name is already registered on the context.
diff --git a/docs/errors/DF0033.md b/docs/errors/DF0033.md
index 8bee827..e2468e4 100644
--- a/docs/errors/DF0033.md
+++ b/docs/errors/DF0033.md
@@ -26,4 +26,4 @@ This is a soft warning — the surrounding Vite dev server keeps running, but th
## Source
-- [`packages/devframe/src/helpers/vite.ts`](https://github.com/vitejs/devtools/blob/main/devframe/packages/devframe/src/helpers/vite.ts) — `viteDevBridge({ devMiddleware })` logs `DF0033` when port resolution or `createDevServer` throws during `configureServer`.
+- [`packages/devframe/src/helpers/vite.ts`](https://github.com/vitejs/devframe/blob/main/devframe/packages/devframe/src/helpers/vite.ts) — `viteDevBridge({ devMiddleware })` logs `DF0033` when port resolution or `createDevServer` throws during `configureServer`.
diff --git a/docs/errors/DF8100.md b/docs/errors/DF8100.md
index d8e3945..5b82136 100644
--- a/docs/errors/DF8100.md
+++ b/docs/errors/DF8100.md
@@ -19,4 +19,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.register()` throws when `views.has(view.id) && !force`.
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevframeDockHost.register()` throws when `views.has(view.id) && !force`.
diff --git a/docs/errors/DF8101.md b/docs/errors/DF8101.md
index 8135814..af2fcb9 100644
--- a/docs/errors/DF8101.md
+++ b/docs/errors/DF8101.md
@@ -19,4 +19,4 @@ The `update` handle returned by `ctx.docks.register(view)` received a patch whos
## Source
-- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.register()` returns an `update` callable that throws this when the patch carries a different `id`.
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevframeDockHost.register()` returns an `update` callable that throws this when the patch carries a different `id`.
diff --git a/docs/errors/DF8102.md b/docs/errors/DF8102.md
index bc5ba85..e9d0b41 100644
--- a/docs/errors/DF8102.md
+++ b/docs/errors/DF8102.md
@@ -19,4 +19,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevToolsDockHost.update()` throws when `views.has(view.id) === false`.
+- [`packages/hub/src/node/host-docks.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-docks.ts) — `DevframeDockHost.update()` throws when `views.has(view.id) === false`.
diff --git a/docs/errors/DF8200.md b/docs/errors/DF8200.md
index 726b7b0..56d775b 100644
--- a/docs/errors/DF8200.md
+++ b/docs/errors/DF8200.md
@@ -19,4 +19,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevToolsTerminalHost.register()` and `startChildProcess()` throw when the id is already taken.
+- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevframeTerminalHost.register()` and `startChildProcess()` throw when the id is already taken.
diff --git a/docs/errors/DF8201.md b/docs/errors/DF8201.md
index cfabfa4..5d4c1d5 100644
--- a/docs/errors/DF8201.md
+++ b/docs/errors/DF8201.md
@@ -19,4 +19,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevToolsTerminalHost.update()` throws when `sessions.has(patch.id) === false`.
+- [`packages/hub/src/node/host-terminals.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-terminals.ts) — `DevframeTerminalHost.update()` throws when `sessions.has(patch.id) === false`.
diff --git a/docs/errors/DF8400.md b/docs/errors/DF8400.md
index fd093e6..c6f7257 100644
--- a/docs/errors/DF8400.md
+++ b/docs/errors/DF8400.md
@@ -19,4 +19,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.register()` throws when `commands.has(command.id)`.
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevframeCommandsHost.register()` throws when `commands.has(command.id)`.
diff --git a/docs/errors/DF8401.md b/docs/errors/DF8401.md
index 8fa88b1..c6b9a09 100644
--- a/docs/errors/DF8401.md
+++ b/docs/errors/DF8401.md
@@ -19,4 +19,4 @@ The `update` handle returned by `ctx.commands.register(cmd)` received a patch wi
## Source
-- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.register()` returns a `update` callable that throws when `'id' in patch`.
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevframeCommandsHost.register()` returns a `update` callable that throws when `'id' in patch`.
diff --git a/docs/errors/DF8402.md b/docs/errors/DF8402.md
index 1fa3991..cc1e3e3 100644
--- a/docs/errors/DF8402.md
+++ b/docs/errors/DF8402.md
@@ -20,4 +20,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.execute()` and the `update` handle throw when the id is missing.
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevframeCommandsHost.execute()` and the `update` handle throw when the id is missing.
diff --git a/docs/errors/DF8403.md b/docs/errors/DF8403.md
index 2685a95..9310fd1 100644
--- a/docs/errors/DF8403.md
+++ b/docs/errors/DF8403.md
@@ -20,4 +20,4 @@ outline: deep
## Source
-- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevToolsCommandsHost.register()` and command handle `update()` throw when a command id is duplicated.
+- [`packages/hub/src/node/host-commands.ts`](https://github.com/devframes/devframe/blob/main/packages/hub/src/node/host-commands.ts) — `DevframeCommandsHost.register()` and command handle `update()` throw when a command id is duplicated.
diff --git a/docs/errors/DF8500.md b/docs/errors/DF8500.md
index e03f9d3..48dec37 100644
--- a/docs/errors/DF8500.md
+++ b/docs/errors/DF8500.md
@@ -14,15 +14,15 @@ A hub built-in command (e.g. `hub:open-path`) was invoked, but the host implemen
## Fix
-Implement the matching capability on the `DevToolsHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`:
+Implement the matching capability on the `DevframeHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`:
```ts
import type { HubHostCapabilities } from '@devframes/hub/node'
-import type { DevToolsHost } from 'devframe/types'
+import type { DevframeHost } from 'devframe/types'
import { launchEditor } from 'devframe/utils/launch-editor'
-const host: DevToolsHost & HubHostCapabilities = {
- // … existing DevToolsHost methods …
+const host: DevframeHost & HubHostCapabilities = {
+ // … existing DevframeHost methods …
async openPath(filepath, line, column) {
const target = line ? `${filepath}:${line}${column ? `:${column}` : ''}` : filepath
launchEditor(target)
diff --git a/docs/guide/agent-native.md b/docs/guide/agent-native.md
index 3049c7f..ade90e6 100644
--- a/docs/guide/agent-native.md
+++ b/docs/guide/agent-native.md
@@ -5,7 +5,7 @@ outline: deep
# Agent-Native Devframe
::: warning Experimental
-The agent-native surface (`agent` field on `defineRpcFunction`, `DevToolsAgentHost`, and the `devframe/adapters/mcp` adapter) is experimental and may change without a major version bump until it stabilizes.
+The agent-native surface (`agent` field on `defineRpcFunction`, `DevframeAgentHost`, and the `devframe/adapters/mcp` adapter) is experimental and may change without a major version bump until it stabilizes.
:::
Devframe can expose the same surface a browser UI consumes — RPC functions, resources, and shared state — to coding agents (Claude Desktop / Cursor / Zed / Claude Code, or any MCP-speaking client). Agent exposure is opt-in per function; functions stay private by default.
@@ -15,7 +15,7 @@ Devframe can expose the same surface a browser UI consumes — RPC functions, re
Three building blocks:
1. **An `agent` field on `defineRpcFunction`.** Add `agent: { description, ... }` to opt a function in. Functions without the field stay private.
-2. **`ctx.agent`** — a host exposed on `DevToolsNodeContext`. Plugins register tools that aren't backed by an RPC, and expose readable resources (e.g. a Markdown build summary).
+2. **`ctx.agent`** — a host exposed on `DevframeNodeContext`. Plugins register tools that aren't backed by an RPC, and expose readable resources (e.g. a Markdown build summary).
3. **The MCP adapter** (`devframe/adapters/mcp`) — translates the agent host into a [Model Context Protocol](https://modelcontextprotocol.io) server, currently over `stdio`.
## Exposing an RPC function
diff --git a/docs/guide/built-with.md b/docs/guide/built-with.md
index 7c8372b..45f7b8d 100644
--- a/docs/guide/built-with.md
+++ b/docs/guide/built-with.md
@@ -4,7 +4,7 @@ outline: deep
# Built with Devframe
-Real-world devtools shipping on Devframe:
+Real-world devframes:
- [**Vite DevTools**](https://devtools.vite.dev/) — the host that bundles multiple devframes into one UI (docks, command palette, terminals). Mount your own definition into it via the [`vite` adapter](/adapters/vite).
- [**ESLint Config Inspector**](https://github.com/eslint/config-inspector) — official ESLint tool for inspecting flat configs.
diff --git a/docs/guide/client.md b/docs/guide/client.md
index 215db22..7d3a286 100644
--- a/docs/guide/client.md
+++ b/docs/guide/client.md
@@ -8,7 +8,7 @@ The browser-side client is how a dock iframe, remote-hosted page, or standalone
## Connecting
-`devframe/client` exports `connectDevframe` (an alias of `getDevToolsRpcClient`) — use either name:
+`devframe/client` exports `connectDevframe` (an alias of `getDevframeRpcClient`) — use either name:
```ts
import { connectDevframe } from 'devframe/client'
@@ -18,7 +18,7 @@ const rpc = await connectDevframe()
const modules = await rpc.call('my-devframe:get-modules', { limit: 10 })
```
-`connectDevframe` auto-detects the backend via `__devtools/__connection.json`, with a sequence of base URLs as fallback. No arguments are needed when the client is hosted from the default mount path.
+`connectDevframe` auto-detects the backend via `__devframe/__connection.json`, with a sequence of base URLs as fallback. No arguments are needed when the client is hosted from the default mount path.
### Runtime basePath discovery
@@ -46,7 +46,7 @@ await connectDevframe({
| Option | Description |
|--------|-------------|
-| `baseURL` | Mount path to probe for `__connection.json`. Accepts an array for fallback. Default: `'./'` — resolved relative to `document.baseURI` so the SPA finds its meta wherever it was deployed. Pass an explicit absolute path (e.g. `'/__devtools/'`) when calling from outside the SPA — say, an embedded webcomponent injected into a host app. |
+| `baseURL` | Mount path to probe for `__connection.json`. Accepts an array for fallback. Default: `'./'` — resolved relative to `document.baseURI` so the SPA finds its meta wherever it was deployed. Pass an explicit absolute path (e.g. `'/__devframe/'`) when calling from outside the SPA — say, an embedded webcomponent injected into a host app. |
| `authToken` | Override the auth token. Defaults to a locally-persisted human-readable id. |
| `cacheOptions` | `true` to enable caching with defaults, or an options object. |
| `wsOptions` | Forwarded to the WebSocket transport (reconnect, heartbeat, etc.). |
@@ -55,7 +55,7 @@ await connectDevframe({
## Modes
-The client runs in one of two modes depending on the backend advertised in `__devtools/__connection.json`:
+The client runs in one of two modes depending on the backend advertised in `__devframe/__connection.json`:
| Backend | When | Capabilities |
|---------|------|--------------|
@@ -89,7 +89,7 @@ const ok = await rpc.requestTrustWithToken('another-token')
### Broadcast-channel sync
-`connectDevframe` listens on a shared `BroadcastChannel` (named `vite-devtools-auth` for cross-tab handshake interop with Vite DevTools' auth page) for `auth-update` messages. When an auth page in another tab announces a new token, every open client requests trust with it automatically — no reload required.
+`connectDevframe` listens on a shared `BroadcastChannel` (named `devframe-auth` for cross-tab handshake interop with Vite DevTools' auth page) for `auth-update` messages. When an auth page in another tab announces a new token, every open client requests trust with it automatically — no reload required.
## Calling functions
diff --git a/docs/guide/devframe-definition.md b/docs/guide/devframe-definition.md
index 27cddc8..8a51903 100644
--- a/docs/guide/devframe-definition.md
+++ b/docs/guide/devframe-definition.md
@@ -68,19 +68,19 @@ The CLI dev server sets `mode: 'dev'`; `createBuild` sets `mode: 'build'`.
## The setup context
-`setup(ctx)` receives a `DevToolsNodeContext`:
+`setup(ctx)` receives a `DevframeNodeContext`:
```ts
-interface DevToolsNodeContext {
+interface DevframeNodeContext {
readonly cwd: string
readonly workspaceRoot: string
readonly mode: 'dev' | 'build'
- host: DevToolsHost // runtime abstraction (mountStatic / resolveOrigin / getStorageDir)
+ host: DevframeHost // runtime abstraction (mountStatic / resolveOrigin / getStorageDir)
rpc: RpcFunctionsHost // register + broadcast + sharedState
- views: DevToolsViewHost // static file hosting (`hostStatic`)
- diagnostics: DevToolsDiagnosticsHost
- agent: DevToolsAgentHost // experimental
+ views: DevframeViewHost // static file hosting (`hostStatic`)
+ diagnostics: DevframeDiagnosticsHost
+ agent: DevframeAgentHost // experimental
}
```
diff --git a/docs/guide/diagnostics.md b/docs/guide/diagnostics.md
index bdbed29..5775225 100644
--- a/docs/guide/diagnostics.md
+++ b/docs/guide/diagnostics.md
@@ -4,7 +4,7 @@ outline: deep
# Structured Diagnostics
-`ctx.diagnostics` is a thin layer over [`nostics`](https://www.npmjs.com/package/nostics) that lets integrations register coded errors and warnings into a shared lookup without depending on `nostics` directly. Use it for author-defined coded diagnostics — errors, warnings, deprecations — with a stable code, a documentation URL, and a structured payload. For free-form runtime output that should appear in the DevTools UI, use [`ctx.messages`](https://devtools.vite.dev/kit/messages).
+`ctx.diagnostics` is a thin layer over [`nostics`](https://www.npmjs.com/package/nostics) that lets integrations register coded errors and warnings into a shared lookup without depending on `nostics` directly. Use it for author-defined coded diagnostics — errors, warnings, deprecations — with a stable code, a documentation URL, and a structured payload. For free-form runtime output that should appear in the Devframe UI, use [`ctx.messages`](https://devtools.vite.dev/kit/messages).
| Surface | Purpose | Example |
|---------|---------|---------|
@@ -14,7 +14,7 @@ outline: deep
## Shape
```ts
-interface DevToolsDiagnosticsHost {
+interface DevframeDiagnosticsHost {
/** Proxy-backed lookup over every registered code. */
readonly logger: Record
@@ -135,6 +135,6 @@ Each page covers the message, cause, example, and fix — see any [DF code page]
## When to use what
- **`ctx.diagnostics`** — coded conditions worth looking up: misconfiguration, deprecations, validation failures, internal invariants. Always docs-backed. Often thrown.
-- **`ctx.messages`** — user-facing activity surfaces in the DevTools UI: progress indicators, audit results, "URL copied" toasts. Just a message and a level.
+- **`ctx.messages`** — user-facing activity surfaces in the Devframe UI: progress indicators, audit results, "URL copied" toasts. Just a message and a level.
-Diagnostics target tool authors and CI; messages target the human in front of the DevTools panel.
+Diagnostics target tool authors and CI; messages target the human in front of the Devframe panel.
diff --git a/docs/guide/hub.md b/docs/guide/hub.md
index d045d03..ccead1a 100644
--- a/docs/guide/hub.md
+++ b/docs/guide/hub.md
@@ -11,7 +11,7 @@ outline: deep
## What the hub adds
-A hub-aware node context (`HubNodeContext`) extends `DevToolsNodeContext` with four subsystems:
+A hub-aware node context (`HubNodeContext`) extends `DevframeNodeContext` with four subsystems:
| Subsystem | Surface | Purpose |
|---|---|---|
@@ -31,7 +31,7 @@ Every hub context auto-registers these RPC functions so framework kits don't rei
## Host capabilities
-A hub host implements the same `DevToolsHost` interface as devframe, plus optional capabilities the hub knows how to delegate to:
+A hub host implements the same `DevframeHost` interface as devframe, plus optional capabilities the hub knows how to delegate to:
```ts
interface HubHostCapabilities {
@@ -44,10 +44,10 @@ A framework kit's host implementation looks like this:
```ts
import type { HubHostCapabilities } from '@devframes/hub/node'
-import type { DevToolsHost } from 'devframe/types'
+import type { DevframeHost } from 'devframe/types'
import { launchEditor } from 'devframe/utils/launch-editor'
-const host: DevToolsHost & HubHostCapabilities = {
+const host: DevframeHost & HubHostCapabilities = {
mountStatic(base, distDir) { /* … */ },
resolveOrigin() { /* … */ },
getStorageDir(scope) { /* … */ },
@@ -80,16 +80,16 @@ A hub-aware UI doesn't import any hub classes; it reads three shared-state keys
| Channel | Type | What it carries |
|---|---|---|
-| `devframe:docks` shared state | `DevToolsDockEntry[]` | The full dock list, including the hub's `~terminals` / `~messages` / `~settings` builtins. |
-| `devframe:commands` shared state | `DevToolsServerCommandEntry[]` | Serializable command list (handlers stripped). |
-| `devframe:user-settings` shared state | `DevToolsDocksUserSettings` | Persisted per-workspace hub settings. |
+| `devframe:docks` shared state | `DevframeDockEntry[]` | The full dock list, including the hub's `~terminals` / `~messages` / `~settings` builtins. |
+| `devframe:commands` shared state | `DevframeServerCommandEntry[]` | Serializable command list (handlers stripped). |
+| `devframe:user-settings` shared state | `DevframeDocksUserSettings` | Persisted per-workspace hub settings. |
| `hub:commands:execute` RPC | `(id, ...args) => unknown` | Server-side command dispatch. |
Plus broadcast notifications (`devframe:terminals:updated`, `devframe:messages:updated`) that a UI can subscribe to via `rpc.client.register(...)`.
## Example
-See [`examples/minimal-vite-devtools-hub/`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devtools-hub) for a ~120-line Vite plugin that wires the hub end to end with a vanilla DOM UI. Every framework's hub host follows the same shape: a thin layer that adapts the framework's dev server to the hub.
+See [`examples/minimal-vite-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devframe-hub) for a ~120-line Vite plugin that wires the hub end to end with a vanilla DOM UI. Every framework's hub host follows the same shape: a thin layer that adapts the framework's dev server to the hub.
## Diagnostics
diff --git a/docs/guide/index.md b/docs/guide/index.md
index 1c830e3..871eea0 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -104,7 +104,7 @@ Devframe has zero dependencies on Vite or any `@vitejs/*` package — the same d
## What's next
-- [Devframe Definition](./devframe-definition) — understand `defineDevframe` and the `DevToolsNodeContext`
+- [Devframe Definition](./devframe-definition) — understand `defineDevframe` and the `DevframeNodeContext`
- [Adapters](/adapters/) — pick the right deployment target for your tool
- [RPC](./rpc) — define type-safe server functions your client can call
- [Agent-Native](./agent-native) — expose your devframe to Claude Desktop, Cursor, or any MCP client
diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md
index 9bb164f..100eec3 100644
--- a/docs/guide/rpc.md
+++ b/docs/guide/rpc.md
@@ -31,7 +31,7 @@ export const getModules = defineRpcFunction({
returns: v.array(v.object({ id: v.string(), size: v.number() })),
setup: ctx => ({
handler: async ({ limit }) => {
- // `ctx` is the DevToolsNodeContext.
+ // `ctx` is the DevframeNodeContext.
return loadModules().slice(0, limit)
},
}),
@@ -93,7 +93,7 @@ Prefer a single object argument (`args: [v.object({ ... })]`) over positional ar
Two ways to wire a handler:
-- **`setup(ctx)`** — receives the `DevToolsNodeContext` and returns `{ handler, dump? }`. Use this when you need the context (shared state, logs, `ctx.mode`, etc.).
+- **`setup(ctx)`** — receives the `DevframeNodeContext` and returns `{ handler, dump? }`. Use this when you need the context (shared state, logs, `ctx.mode`, etc.).
- **`handler(...)`** — shorthand when the handler is pure and doesn't touch the context.
```ts
@@ -165,7 +165,7 @@ const modules = await ctx.rpc.invokeLocal('my-devframe:get-modules', { limit: 10
## Client-side calls
-From the browser, [`connectDevframe`](./client) (or `getDevToolsRpcClient`) returns a client for calling registered functions:
+From the browser, [`connectDevframe`](./client) (or `getDevframeRpcClient`) returns a client for calling registered functions:
```ts
import { connectDevframe } from 'devframe/client'
@@ -179,7 +179,7 @@ Client-side registration (for server→client calls) goes through `rpc.client.re
## Type-safe client registry
-Devframe exposes two augmentable interfaces — `DevToolsRpcServerFunctions` (client→server calls) and `DevToolsRpcClientFunctions` (server→client calls) — so each registered RPC name shows up on the typed client. Augment them once per devframe via `declare module 'devframe'`.
+Devframe exposes two augmentable interfaces — `DevframeRpcServerFunctions` (client→server calls) and `DevframeRpcClientFunctions` (server→client calls) — so each registered RPC name shows up on the typed client. Augment them once per devframe via `declare module 'devframe'`.
The recommended pattern collects every server-side definition into a const array and feeds it through `RpcDefinitionsToFunctions`:
@@ -190,7 +190,7 @@ import { getFile, getModules } from './rpc'
const serverFunctions = [getModules, getFile] as const
declare module 'devframe' {
- interface DevToolsRpcServerFunctions
+ interface DevframeRpcServerFunctions
extends RpcDefinitionsToFunctions {}
}
```
@@ -211,13 +211,13 @@ For one-off augmentations, declare a single key with `RpcFunctionDefinitionToFun
import type { RpcFunctionDefinitionToFunction } from 'devframe/rpc'
declare module 'devframe' {
- interface DevToolsRpcServerFunctions {
+ interface DevframeRpcServerFunctions {
'my-devframe:get-modules': RpcFunctionDefinitionToFunction
}
}
```
-For server→client calls invoked via `ctx.rpc.broadcast`, augment `DevToolsRpcClientFunctions` the same way.
+For server→client calls invoked via `ctx.rpc.broadcast`, augment `DevframeRpcClientFunctions` the same way.
## Static dumps
@@ -262,7 +262,7 @@ Devframe's WS transport ships payloads using one of two encoders, picked per RPC
| `false` (default) | `structured-clone-es` | `s:` | `Map`, `Set`, `Date`, `BigInt`, cycles, class instances |
| `true` (opt-in) | strict `JSON.stringify` | _(unprefixed)_ | JSON-only |
-The wire stays plain JSON when every participating function is JSON-flagged — debuggable in DevTools, friendly to MCP, and a good default for tools that already speak JSON.
+The wire stays plain JSON when every participating function is JSON-flagged — debuggable in Devframe, friendly to MCP, and a good default for tools that already speak JSON.
### Discovering shape errors during dev
diff --git a/docs/guide/shared-state.md b/docs/guide/shared-state.md
index ed01a75..074d9f2 100644
--- a/docs/guide/shared-state.md
+++ b/docs/guide/shared-state.md
@@ -139,11 +139,11 @@ Protocol adapters (the [MCP adapter](./agent-native), for example) use this to s
## Type-safe keys
-Augment `DevToolsRpcSharedStates` to type each shared-state key once, then both server and client lookups stay typed without per-call generics:
+Augment `DevframeRpcSharedStates` to type each shared-state key once, then both server and client lookups stay typed without per-call generics:
```ts
declare module 'devframe' {
- interface DevToolsRpcSharedStates {
+ interface DevframeRpcSharedStates {
'my-devframe:state': {
count: number
items: { id: string, name: string }[]
diff --git a/docs/helpers/nuxt.md b/docs/helpers/nuxt.md
index fb484d8..e70da89 100644
--- a/docs/helpers/nuxt.md
+++ b/docs/helpers/nuxt.md
@@ -11,7 +11,7 @@ It handles the four things every Nuxt-powered standalone devtool needs:
1. **Base-agnostic assets.** Sets `app.baseURL: './'` and `vite.base: './'` so the same production build works at `/`, `/tool/`, and any other deployment path without build-time URL rewriting.
2. **Runtime RPC connection.** Adds a client plugin that calls [`connectDevframe()`](/guide/client) once on page load and provides the result as `$rpc` on the Nuxt app.
3. **Dev-time RPC bridge.** When you pass `devframe`, `nuxt dev` spins up a separate WebSocket RPC server and serves `__connection.json` so the SPA can reach it — no hand-rolled Vite plugin required.
-4. **TypeScript augmentation.** `useNuxtApp().$rpc` is typed as `DevToolsRpcClient` out of the box.
+4. **TypeScript augmentation.** `useNuxtApp().$rpc` is typed as `DevframeRpcClient` out of the box.
## Install
diff --git a/docs/index.md b/docs/index.md
index cf5494f..c5d9e19 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,7 +3,7 @@ layout: home
hero:
name: Devframe
- text: Framework-neutral foundation for DevTools
+ text: Framework-neutral foundation for Devframe
tagline: One devframe definition, adapters to different environments. Managed communication layer, agent-native.
image:
src: /logo.svg
diff --git a/examples/files-inspector/src/client/app.tsx b/examples/files-inspector/src/client/app.tsx
index 59cfcd4..e2d8afa 100644
--- a/examples/files-inspector/src/client/app.tsx
+++ b/examples/files-inspector/src/client/app.tsx
@@ -1,4 +1,4 @@
-import type { DevToolsRpcClient } from 'devframe/client'
+import type { DevframeRpcClient } from 'devframe/client'
import { connectDevframe } from 'devframe/client'
import { useEffect, useState } from 'preact/hooks'
import { About } from './routes/about'
@@ -19,7 +19,7 @@ function getRoute(basePath: string): string {
export function App() {
const basePath = getBasePath()
const [route, setRoute] = useState(getRoute(basePath))
- const [rpc, setRpc] = useState(null)
+ const [rpc, setRpc] = useState(null)
useEffect(() => {
let cancelled = false
diff --git a/examples/files-inspector/src/client/routes/about.tsx b/examples/files-inspector/src/client/routes/about.tsx
index f6cfd0c..26ae30b 100644
--- a/examples/files-inspector/src/client/routes/about.tsx
+++ b/examples/files-inspector/src/client/routes/about.tsx
@@ -1,7 +1,7 @@
-import type { DevToolsRpcClient } from 'devframe/client'
+import type { DevframeRpcClient } from 'devframe/client'
import { useEffect, useState } from 'preact/hooks'
-export function About({ rpc, basePath }: { rpc: DevToolsRpcClient, basePath: string }) {
+export function About({ rpc, basePath }: { rpc: DevframeRpcClient, basePath: string }) {
const [cwd, setCwd] = useState('')
useEffect(() => {
diff --git a/examples/files-inspector/src/client/routes/home.tsx b/examples/files-inspector/src/client/routes/home.tsx
index 5f7dd5e..d10ea2b 100644
--- a/examples/files-inspector/src/client/routes/home.tsx
+++ b/examples/files-inspector/src/client/routes/home.tsx
@@ -1,7 +1,7 @@
-import type { DevToolsRpcClient } from 'devframe/client'
+import type { DevframeRpcClient } from 'devframe/client'
import { useEffect, useState } from 'preact/hooks'
-export function Home({ rpc }: { rpc: DevToolsRpcClient }) {
+export function Home({ rpc }: { rpc: DevframeRpcClient }) {
const [files, setFiles] = useState([])
const [loading, setLoading] = useState(true)
diff --git a/examples/files-inspector/tests/_utils.ts b/examples/files-inspector/tests/_utils.ts
index 4e1f170..269a609 100644
--- a/examples/files-inspector/tests/_utils.ts
+++ b/examples/files-inspector/tests/_utils.ts
@@ -5,10 +5,10 @@ import os from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import {
- DEVTOOLS_CONNECTION_META_FILENAME,
+ DEVFRAME_CONNECTION_META_FILENAME,
} from 'devframe/constants'
import {
- createH3DevToolsHost,
+ createH3DevframeHost,
createHostContext,
startHttpAndWs,
} from 'devframe/node'
@@ -68,7 +68,7 @@ export async function startInspectorServer(
const app = new H3()
const origin = `http://${host}:${port}`
- const h3Host = createH3DevToolsHost({
+ const h3Host = createH3DevframeHost({
origin,
appName: devframe.id,
mount: (base, dir) => {
@@ -79,7 +79,7 @@ export async function startInspectorServer(
const ctx = await createHostContext({ cwd, mode: 'dev', host: h3Host })
await devframe.setup(ctx)
- const metaPath = `${basePath}${DEVTOOLS_CONNECTION_META_FILENAME}`
+ const metaPath = `${basePath}${DEVFRAME_CONNECTION_META_FILENAME}`
app.use(metaPath, () => ({ backend: 'websocket', websocket: port }))
mountStaticHandler(app, basePath, resolve(distDir))
diff --git a/examples/files-inspector/tests/static-build.test.ts b/examples/files-inspector/tests/static-build.test.ts
index 4179485..97302ca 100644
--- a/examples/files-inspector/tests/static-build.test.ts
+++ b/examples/files-inspector/tests/static-build.test.ts
@@ -4,8 +4,8 @@ import os from 'node:os'
import path from 'node:path'
import { createBuild } from 'devframe/adapters/build'
import {
- DEVTOOLS_CONNECTION_META_FILENAME,
- DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME,
+ DEVFRAME_CONNECTION_META_FILENAME,
+ DEVFRAME_RPC_DUMP_MANIFEST_FILENAME,
} from 'devframe/constants'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import devframe from '../src/devframe'
@@ -53,24 +53,24 @@ describe('static build (CLI build surface)', () => {
})
it('writes a static-backend connection meta next to index.html', async () => {
- // The meta sits at the SPA root (not under `__devtools/`) so any
+ // The meta sits at the SPA root (not under `__devframe/`) so any
// generic static file server (`serve`, `nginx`, `python -m http.server`)
// can serve it as a flat tree without nested-dir exclusions.
const meta = JSON.parse(
await readFile(
- path.join(outBuild, DEVTOOLS_CONNECTION_META_FILENAME),
+ path.join(outBuild, DEVFRAME_CONNECTION_META_FILENAME),
'utf-8',
),
) as { backend: string }
expect(meta).toMatchObject({ backend: 'static' })
- // Guard the design: nothing should land under a `__devtools/` subdir.
- expect(existsSync(path.join(outBuild, '__devtools'))).toBe(false)
+ // Guard the design: nothing should land under a `__devframe/` subdir.
+ expect(existsSync(path.join(outBuild, '__devframe'))).toBe(false)
})
it('dumps both RPC functions into the manifest', async () => {
const manifest = JSON.parse(
await readFile(
- path.join(outBuild, DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME),
+ path.join(outBuild, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME),
'utf-8',
),
) as DumpManifest
diff --git a/examples/files-inspector/tests/static-serve.test.ts b/examples/files-inspector/tests/static-serve.test.ts
index 143fb83..c35da41 100644
--- a/examples/files-inspector/tests/static-serve.test.ts
+++ b/examples/files-inspector/tests/static-serve.test.ts
@@ -88,8 +88,8 @@ describe('static serve (deployed SPA contract)', () => {
// This is the path the SPA fetches via relative `./__connection.json`
// resolved against `document.baseURI`. The 404 the user originally
// reported with `serve dist-static` was caused by the old layout
- // putting this file under `/__devtools/__connection.json`, which a
- // SPA at any non-`/__devtools/` mount could not discover.
+ // putting this file under `/__devframe/__connection.json`, which a
+ // SPA at any non-`/__devframe/` mount could not discover.
const res = await fetch(`${server.origin}${mountBase}__connection.json`)
expect(res.status).toBe(200)
const meta = await res.json() as { backend: string }
@@ -120,11 +120,11 @@ describe('static serve (deployed SPA contract)', () => {
expect(record.output).toEqual(['README.md', 'package.json', 'sample.txt'])
})
- it('does not expose a stray `__devtools/` directory at the SPA root', async () => {
+ it('does not expose a stray `__devframe/` directory at the SPA root', async () => {
// Regression guard: the build output is intentionally flat —
- // re-introducing a `__devtools/` subdir would create a nested
+ // re-introducing a `__devframe/` subdir would create a nested
// path the relative-base discovery in the SPA cannot reach.
- const res = await fetch(`${server.origin}${mountBase}__devtools/__connection.json`)
+ const res = await fetch(`${server.origin}${mountBase}__devframe/__connection.json`)
expect(res.status).toBe(404)
})
})
diff --git a/examples/minimal-next-devtools-hub/.gitignore b/examples/minimal-next-devframe-hub/.gitignore
similarity index 100%
rename from examples/minimal-next-devtools-hub/.gitignore
rename to examples/minimal-next-devframe-hub/.gitignore
diff --git a/examples/minimal-next-devtools-hub/README.md b/examples/minimal-next-devframe-hub/README.md
similarity index 77%
rename from examples/minimal-next-devtools-hub/README.md
rename to examples/minimal-next-devframe-hub/README.md
index d0fd459..5f6be28 100644
--- a/examples/minimal-next-devtools-hub/README.md
+++ b/examples/minimal-next-devframe-hub/README.md
@@ -1,12 +1,12 @@
-# Minimal Next DevTools Hub
+# Minimal Next Devframe Hub
-A protocol-witness example. The `src/client/devtools/minimal-next-devtools-hub.ts` file wires `@devframes/hub` into a Next.js App Router app by lazily starting a side-car RPC/WS server from a Node route handler.
+A protocol-witness example. The `src/client/devframe/minimal-next-devframe-hub.ts` file wires `@devframes/hub` into a Next.js App Router app by lazily starting a side-car RPC/WS server from a Node route handler.
## Run it
```sh
pnpm install
-pnpm --filter minimal-next-devtools-hub dev
+pnpm --filter minimal-next-devframe-hub dev
```
Open the printed URL. You should see:
@@ -21,7 +21,7 @@ Open the printed URL. You should see:
## What the example proves
- `createHubContext()` boots a hub without any Vite-specific code path
-- A `DevToolsHost & HubHostCapabilities` impl plugs Next host specifics into the hub uniformly
+- A `DevframeHost & HubHostCapabilities` impl plugs Next host specifics into the hub uniformly
- `mountDevframe(ctx, def)` registers any `DevframeDefinition` as a dock
- Hub built-in RPCs (`hub:open-path`, `hub:commands:execute`) work regardless of how the host was constructed
- The browser-side `connectDevframe({ baseURL: '/__hub/' })` discovers the WS endpoint via the Next route handler at `/__hub/__connection.json`
@@ -30,7 +30,7 @@ Open the printed URL. You should see:
| File | Role |
|---|---|
-| `src/client/devtools/minimal-next-devtools-hub.ts` | The Next host — creates hub context and side-car WS |
+| `src/client/devframe/minimal-next-devframe-hub.ts` | The Next host — creates hub context and side-car WS |
| `src/client/app/%5F_hub/%5F_connection.json/route.ts` | Connection-meta endpoint for `/__hub/__connection.json` that starts the singleton host |
-| `src/client/devtools/demo-devframe.ts` | A sample `DevframeDefinition` that plugs into the host |
+| `src/client/devframe/demo-devframe.ts` | A sample `DevframeDefinition` that plugs into the host |
| `src/client/app/page.tsx` | The browser-side UI that consumes the hub protocol |
diff --git a/examples/minimal-next-devtools-hub/package.json b/examples/minimal-next-devframe-hub/package.json
similarity index 87%
rename from examples/minimal-next-devtools-hub/package.json
rename to examples/minimal-next-devframe-hub/package.json
index d5f7997..f2ef7e8 100644
--- a/examples/minimal-next-devtools-hub/package.json
+++ b/examples/minimal-next-devframe-hub/package.json
@@ -1,9 +1,9 @@
{
- "name": "minimal-next-devtools-hub",
+ "name": "minimal-next-devframe-hub",
"type": "module",
"version": "0.4.1",
"private": true,
- "description": "Protocol-witness example — a tiny Next.js DevTools Hub built on @devframes/hub that exercises every hub subsystem end-to-end.",
+ "description": "Protocol-witness example — a tiny Next.js Devframe Hub built on @devframes/hub that exercises every hub subsystem end-to-end.",
"scripts": {
"dev": "next dev src/client",
"build": "next build src/client",
diff --git a/examples/minimal-next-devtools-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts b/examples/minimal-next-devframe-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts
similarity index 51%
rename from examples/minimal-next-devtools-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts
rename to examples/minimal-next-devframe-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts
index 610ffd5..96724f7 100644
--- a/examples/minimal-next-devtools-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts
+++ b/examples/minimal-next-devframe-hub/src/client/app/%5F_hub/%5F_connection.json/route.ts
@@ -1,9 +1,9 @@
-import { ensureMinimalNextDevToolsHub } from '../../../devtools/minimal-next-devtools-hub'
+import { ensureMinimalNextDevframeHub } from '../../../devframe/minimal-next-devframe-hub'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export async function GET() {
- const hub = await ensureMinimalNextDevToolsHub()
+ const hub = await ensureMinimalNextDevframeHub()
return Response.json(hub.connectionMeta)
}
diff --git a/examples/minimal-next-devtools-hub/src/client/app/globals.css b/examples/minimal-next-devframe-hub/src/client/app/globals.css
similarity index 100%
rename from examples/minimal-next-devtools-hub/src/client/app/globals.css
rename to examples/minimal-next-devframe-hub/src/client/app/globals.css
diff --git a/examples/minimal-next-devtools-hub/src/client/app/layout.tsx b/examples/minimal-next-devframe-hub/src/client/app/layout.tsx
similarity index 90%
rename from examples/minimal-next-devtools-hub/src/client/app/layout.tsx
rename to examples/minimal-next-devframe-hub/src/client/app/layout.tsx
index 37e8bdc..5090a20 100644
--- a/examples/minimal-next-devtools-hub/src/client/app/layout.tsx
+++ b/examples/minimal-next-devframe-hub/src/client/app/layout.tsx
@@ -3,7 +3,7 @@ import type { ReactNode } from 'react'
import './globals.css'
export const metadata: Metadata = {
- title: 'Minimal Next DevTools Hub',
+ title: 'Minimal Next Devframe Hub',
description: 'A Next.js host for the @devframes/hub protocol.',
}
diff --git a/examples/minimal-next-devtools-hub/src/client/app/page.tsx b/examples/minimal-next-devframe-hub/src/client/app/page.tsx
similarity index 86%
rename from examples/minimal-next-devtools-hub/src/client/app/page.tsx
rename to examples/minimal-next-devframe-hub/src/client/app/page.tsx
index 6dccb94..c981000 100644
--- a/examples/minimal-next-devtools-hub/src/client/app/page.tsx
+++ b/examples/minimal-next-devframe-hub/src/client/app/page.tsx
@@ -1,11 +1,11 @@
'use client'
-import type { DevToolsRpcClient } from '@devframes/hub/client'
+import type { DevframeRpcClient } from '@devframes/hub/client'
import type {
- DevToolsCommandEntry,
- DevToolsDockEntry,
- DevToolsMessageEntry,
- DevToolsTerminalSession,
+ DevframeCommandEntry,
+ DevframeDockEntry,
+ DevframeMessageEntry,
+ DevframeTerminalSession,
} from '@devframes/hub/types'
import type { ReactNode } from 'react'
import { connectDevframe } from '@devframes/hub/client'
@@ -18,17 +18,17 @@ interface Status {
kind?: 'ready' | 'error'
}
-type TerminalSummary = Pick
+type TerminalSummary = Pick
export default function Page() {
const [status, setStatus] = useState({ text: 'Connecting...' })
- const [docks, setDocks] = useState([])
- const [commands, setCommands] = useState([])
- const [messages, setMessages] = useState([])
+ const [docks, setDocks] = useState([])
+ const [commands, setCommands] = useState([])
+ const [messages, setMessages] = useState([])
const [terminals, setTerminals] = useState([])
const [openPathResult, setOpenPathResult] = useState('Test hub:open-path on this README')
const [pingResult, setPingResult] = useState('Run ping')
- const rpcRef = useRef(null)
+ const rpcRef = useRef(null)
useEffect(() => {
let cancelled = false
@@ -43,11 +43,11 @@ export default function Page() {
rpcRef.current = rpc
setStatus({ text: `Connected: backend=${rpc.connectionMeta.backend}`, kind: 'ready' })
- const docksState = await rpc.sharedState.get(
+ const docksState = await rpc.sharedState.get(
'devframe:docks',
{ initialValue: [] },
)
- const commandsState = await rpc.sharedState.get(
+ const commandsState = await rpc.sharedState.get(
'devframe:commands',
{ initialValue: [] },
)
@@ -61,15 +61,15 @@ export default function Page() {
const refreshMessages = async () => {
const entries = await rpc.call(
- 'minimal-next-devtools-hub:messages:list' as any,
- ) as DevToolsMessageEntry[]
+ 'minimal-next-devframe-hub:messages:list' as any,
+ ) as DevframeMessageEntry[]
if (!cancelled)
setMessages(entries)
}
const refreshTerminals = async () => {
const sessions = await rpc.call(
- 'minimal-next-devtools-hub:terminals:list' as any,
+ 'minimal-next-devframe-hub:terminals:list' as any,
) as TerminalSummary[]
if (!cancelled)
setTerminals(sessions)
@@ -122,7 +122,7 @@ export default function Page() {
try {
const result = await rpcRef.current.call(
'hub:commands:execute' as any,
- 'minimal-next-devtools-hub:ping',
+ 'minimal-next-devframe-hub:ping',
)
setPingResult(`Ping returned ${JSON.stringify(result)}`)
}
@@ -134,7 +134,7 @@ export default function Page() {
return (
-
Minimal Next DevTools Hub
+
Minimal Next Devframe Hub
Protocol witness: verifies
{' '}
diff --git a/examples/minimal-next-devtools-hub/src/client/devtools/demo-devframe.ts b/examples/minimal-next-devframe-hub/src/client/devframe/demo-devframe.ts
similarity index 100%
rename from examples/minimal-next-devtools-hub/src/client/devtools/demo-devframe.ts
rename to examples/minimal-next-devframe-hub/src/client/devframe/demo-devframe.ts
diff --git a/examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts b/examples/minimal-next-devframe-hub/src/client/devframe/minimal-next-devframe-hub.ts
similarity index 77%
rename from examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts
rename to examples/minimal-next-devframe-hub/src/client/devframe/minimal-next-devframe-hub.ts
index 842626b..897c13b 100644
--- a/examples/minimal-next-devtools-hub/src/client/devtools/minimal-next-devtools-hub.ts
+++ b/examples/minimal-next-devframe-hub/src/client/devframe/minimal-next-devframe-hub.ts
@@ -1,6 +1,6 @@
import type { HubHostCapabilities, HubNodeContext } from '@devframes/hub/node'
import type { StartedServer } from 'devframe/node'
-import type { ConnectionMeta, DevframeDefinition, DevToolsHost } from 'devframe/types'
+import type { ConnectionMeta, DevframeDefinition, DevframeHost } from 'devframe/types'
import { homedir } from 'node:os'
import process from 'node:process'
import { defineRpcFunction } from '@devframes/hub'
@@ -11,7 +11,7 @@ import { getPort } from 'get-port-please'
import { join } from 'pathe'
import demoDevframe from './demo-devframe'
-export interface MinimalNextDevToolsHubOptions {
+export interface MinimalNextDevframeHubOptions {
/** Preferred port for the side-car RPC/WS server. Default: a free port near 9877. */
port?: number
/** Hostname for the side-car server. Default: `localhost`. */
@@ -22,13 +22,13 @@ export interface MinimalNextDevToolsHubOptions {
devframes?: DevframeDefinition[]
}
-export interface StartedMinimalNextDevToolsHub extends StartedServer {
+export interface StartedMinimalNextDevframeHub extends StartedServer {
context: HubNodeContext
connectionMeta: ConnectionMeta & { backend: 'websocket', websocket: number }
}
const minimalNextHubMessagesList = defineRpcFunction({
- name: 'minimal-next-devtools-hub:messages:list',
+ name: 'minimal-next-devframe-hub:messages:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -39,7 +39,7 @@ const minimalNextHubMessagesList = defineRpcFunction({
})
const minimalNextHubTerminalsList = defineRpcFunction({
- name: 'minimal-next-devtools-hub:terminals:list',
+ name: 'minimal-next-devframe-hub:terminals:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -54,13 +54,13 @@ const minimalNextHubTerminalsList = defineRpcFunction({
}),
})
-export async function minimalNextDevToolsHub(
- options: MinimalNextDevToolsHubOptions = {},
-): Promise {
+export async function minimalNextDevframeHub(
+ options: MinimalNextDevframeHubOptions = {},
+): Promise {
const cwd = options.cwd ?? process.cwd()
const hostName = options.host ?? 'localhost'
- const host: DevToolsHost & HubHostCapabilities = {
+ const host: DevframeHost & HubHostCapabilities = {
mountStatic() {
// Static mounting for devframe SPAs would route through Next middleware
// in a fuller host. This minimal example keeps mounted devframes headless.
@@ -70,8 +70,8 @@ export async function minimalNextDevToolsHub(
},
getStorageDir(scope) {
return scope === 'workspace'
- ? join(cwd, 'node_modules/.minimal-next-devtools-hub')
- : join(homedir(), '.minimal-next-devtools-hub')
+ ? join(cwd, 'node_modules/.minimal-next-devframe-hub')
+ : join(homedir(), '.minimal-next-devframe-hub')
},
async openPath(filepath, line, column) {
const absolute = join(cwd, filepath)
@@ -97,7 +97,7 @@ export async function minimalNextDevToolsHub(
})
context.commands.register({
- id: 'minimal-next-devtools-hub:ping',
+ id: 'minimal-next-devframe-hub:ping',
title: 'Next Hub: Ping',
icon: 'ph:bell-duotone',
category: 'hub',
@@ -106,7 +106,7 @@ export async function minimalNextDevToolsHub(
await context.messages.add({
level: 'success',
- message: 'Minimal Next DevTools Hub started',
+ message: 'Minimal Next Devframe Hub started',
description: `Side-car WS on port ${port}. ${options.devframes?.length ?? 1} devframe(s) registered.`,
})
@@ -130,16 +130,16 @@ export async function minimalNextDevToolsHub(
})
}
-const GLOBAL_KEY = '__minimalNextDevToolsHub'
+const GLOBAL_KEY = '__minimalNextDevframeHub'
type GlobalWithHub = typeof globalThis & {
- [GLOBAL_KEY]?: Promise
+ [GLOBAL_KEY]?: Promise
}
-export function ensureMinimalNextDevToolsHub(
- options: MinimalNextDevToolsHubOptions = {},
-): Promise {
+export function ensureMinimalNextDevframeHub(
+ options: MinimalNextDevframeHubOptions = {},
+): Promise {
const globalHub = globalThis as GlobalWithHub
- globalHub[GLOBAL_KEY] ??= minimalNextDevToolsHub(options)
+ globalHub[GLOBAL_KEY] ??= minimalNextDevframeHub(options)
return globalHub[GLOBAL_KEY]
}
diff --git a/examples/minimal-next-devtools-hub/src/client/next.config.mjs b/examples/minimal-next-devframe-hub/src/client/next.config.mjs
similarity index 100%
rename from examples/minimal-next-devtools-hub/src/client/next.config.mjs
rename to examples/minimal-next-devframe-hub/src/client/next.config.mjs
diff --git a/examples/minimal-next-devtools-hub/src/client/tsconfig.json b/examples/minimal-next-devframe-hub/src/client/tsconfig.json
similarity index 100%
rename from examples/minimal-next-devtools-hub/src/client/tsconfig.json
rename to examples/minimal-next-devframe-hub/src/client/tsconfig.json
diff --git a/examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts b/examples/minimal-next-devframe-hub/tests/minimal-next-devframe-hub.test.ts
similarity index 71%
rename from examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts
rename to examples/minimal-next-devframe-hub/tests/minimal-next-devframe-hub.test.ts
index ca99995..7bd797a 100644
--- a/examples/minimal-next-devtools-hub/tests/minimal-next-devtools-hub.test.ts
+++ b/examples/minimal-next-devframe-hub/tests/minimal-next-devframe-hub.test.ts
@@ -2,7 +2,7 @@ import { createRpcClient } from 'devframe/rpc/client'
import { createWsRpcChannel } from 'devframe/rpc/transports/ws-client'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { WebSocket } from 'ws'
-import { minimalNextDevToolsHub } from '../src/client/devtools/minimal-next-devtools-hub'
+import { minimalNextDevframeHub } from '../src/client/devframe/minimal-next-devframe-hub'
vi.stubGlobal('WebSocket', WebSocket)
@@ -11,8 +11,8 @@ function bootRpc(port: number) {
return createRpcClient({}, { channel })
}
-describe('minimal-next-devtools-hub (example)', () => {
- let server: Awaited> | undefined
+describe('minimal-next-devframe-hub (example)', () => {
+ let server: Awaited> | undefined
afterEach(async () => {
await server?.close()
@@ -20,7 +20,7 @@ describe('minimal-next-devtools-hub (example)', () => {
})
it('returns connection meta pointing at the WS backend', async () => {
- server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+ server = await minimalNextDevframeHub({ host: '127.0.0.1' })
expect(server.connectionMeta).toEqual({
backend: 'websocket',
@@ -29,7 +29,7 @@ describe('minimal-next-devtools-hub (example)', () => {
})
it('registers hub built-in docks and the mounted demo devframe', async () => {
- server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+ server = await minimalNextDevframeHub({ host: '127.0.0.1' })
const dockIds = server.context.docks.values().map(d => d.id)
expect(dockIds).toContain('next-demo-tool')
@@ -39,20 +39,20 @@ describe('minimal-next-devtools-hub (example)', () => {
})
it('lists startup and demo messages through the kit-local RPC', async () => {
- server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+ server = await minimalNextDevframeHub({ host: '127.0.0.1' })
const rpc = bootRpc(server.port)
- const messages = await rpc.$call('minimal-next-devtools-hub:messages:list') as { message: string }[]
- expect(messages.map(m => m.message)).toContain('Minimal Next DevTools Hub started')
+ const messages = await rpc.$call('minimal-next-devframe-hub:messages:list') as { message: string }[]
+ expect(messages.map(m => m.message)).toContain('Minimal Next Devframe Hub started')
expect(messages.map(m => m.message)).toContain('Next demo devframe loaded')
})
it('executes the ping command through the hub command RPC', async () => {
- server = await minimalNextDevToolsHub({ host: '127.0.0.1' })
+ server = await minimalNextDevframeHub({ host: '127.0.0.1' })
const rpc = bootRpc(server.port)
await expect(
- rpc.$call('hub:commands:execute', 'minimal-next-devtools-hub:ping'),
+ rpc.$call('hub:commands:execute', 'minimal-next-devframe-hub:ping'),
).resolves.toBe('pong')
})
})
diff --git a/examples/minimal-next-devtools-hub/tsconfig.json b/examples/minimal-next-devframe-hub/tsconfig.json
similarity index 100%
rename from examples/minimal-next-devtools-hub/tsconfig.json
rename to examples/minimal-next-devframe-hub/tsconfig.json
diff --git a/examples/minimal-next-devtools-hub/vitest.config.ts b/examples/minimal-next-devframe-hub/vitest.config.ts
similarity index 100%
rename from examples/minimal-next-devtools-hub/vitest.config.ts
rename to examples/minimal-next-devframe-hub/vitest.config.ts
diff --git a/examples/minimal-vite-devtools-hub/README.md b/examples/minimal-vite-devframe-hub/README.md
similarity index 81%
rename from examples/minimal-vite-devtools-hub/README.md
rename to examples/minimal-vite-devframe-hub/README.md
index 1295270..d03f983 100644
--- a/examples/minimal-vite-devtools-hub/README.md
+++ b/examples/minimal-vite-devframe-hub/README.md
@@ -1,12 +1,12 @@
-# Minimal Vite DevTools Hub
+# Minimal Vite Devframe Hub
-A protocol-witness example. The `src/minimal-vite-devtools-hub.ts` file is the entire Vite host — about 120 lines of Vite plugin code that wires `@devframes/hub` into a Vite dev server. Every framework's DevTools Hub host follows the same shape.
+A protocol-witness example. The `src/minimal-vite-devframe-hub.ts` file is the entire Vite host — about 120 lines of Vite plugin code that wires `@devframes/hub` into a Vite dev server. Every framework's Devframe Hub host follows the same shape.
## Run it
```sh
pnpm install
-pnpm --filter minimal-vite-devtools-hub dev
+pnpm --filter minimal-vite-devframe-hub dev
```
Open the printed URL. You should see:
@@ -21,7 +21,7 @@ Open the printed URL. You should see:
## What the example proves
- `createHubContext()` boots a hub without any Vite-specific code path
-- A `DevToolsHost & HubHostCapabilities` impl plugs framework specifics (`openPath`, storage paths) into the hub uniformly
+- A `DevframeHost & HubHostCapabilities` impl plugs framework specifics (`openPath`, storage paths) into the hub uniformly
- `mountDevframe(ctx, def)` registers any `DevframeDefinition` as a dock
- Hub built-in RPCs (`hub:open-path`, `hub:commands:execute`) work regardless of how the host was constructed
- The browser-side `connectDevframe({ baseURL: '/__hub/' })` discovers the WS endpoint via the kit's `__connection.json` middleware
@@ -30,7 +30,7 @@ Open the printed URL. You should see:
| File | Role |
|---|---|
-| `src/minimal-vite-devtools-hub.ts` | The Vite plugin — creates hub context, mounts middleware, side-car WS |
+| `src/minimal-vite-devframe-hub.ts` | The Vite plugin — creates hub context, mounts middleware, side-car WS |
| `src/devframe.ts` | A sample `DevframeDefinition` that plugs into the kit |
| `src/client/main.ts` | The browser-side UI that consumes the hub protocol |
| `index.html` | The UI shell |
diff --git a/examples/minimal-vite-devtools-hub/index.html b/examples/minimal-vite-devframe-hub/index.html
similarity index 93%
rename from examples/minimal-vite-devtools-hub/index.html
rename to examples/minimal-vite-devframe-hub/index.html
index b51934a..23910a4 100644
--- a/examples/minimal-vite-devtools-hub/index.html
+++ b/examples/minimal-vite-devframe-hub/index.html
@@ -3,12 +3,12 @@
- Minimal Vite DevTools Hub
+ Minimal Vite Devframe Hub
-
Minimal Vite DevTools Hub
+
Minimal Vite Devframe Hub
Protocol witness — verifies @devframes/hub end to end.
diff --git a/examples/minimal-vite-devtools-hub/package.json b/examples/minimal-vite-devframe-hub/package.json
similarity index 68%
rename from examples/minimal-vite-devtools-hub/package.json
rename to examples/minimal-vite-devframe-hub/package.json
index 26b159a..7afa00a 100644
--- a/examples/minimal-vite-devtools-hub/package.json
+++ b/examples/minimal-vite-devframe-hub/package.json
@@ -1,9 +1,9 @@
{
- "name": "minimal-vite-devtools-hub",
+ "name": "minimal-vite-devframe-hub",
"type": "module",
"version": "0.4.1",
"private": true,
- "description": "Protocol-witness example — a tiny Vite DevTools Hub built on @devframes/hub that exercises every hub subsystem end-to-end.",
+ "description": "Protocol-witness example — a tiny Vite Devframe Hub built on @devframes/hub that exercises every hub subsystem end-to-end.",
"scripts": {
"dev": "vite",
"build": "vite build"
diff --git a/examples/minimal-vite-devtools-hub/src/client/main.ts b/examples/minimal-vite-devframe-hub/src/client/main.ts
similarity index 88%
rename from examples/minimal-vite-devtools-hub/src/client/main.ts
rename to examples/minimal-vite-devframe-hub/src/client/main.ts
index d25ccc6..a5afe25 100644
--- a/examples/minimal-vite-devtools-hub/src/client/main.ts
+++ b/examples/minimal-vite-devframe-hub/src/client/main.ts
@@ -1,8 +1,8 @@
import type {
- DevToolsCommandEntry,
- DevToolsDockEntry,
- DevToolsMessageEntry,
- DevToolsTerminalSession,
+ DevframeCommandEntry,
+ DevframeDockEntry,
+ DevframeMessageEntry,
+ DevframeTerminalSession,
} from '@devframes/hub/types'
import { connectDevframe } from '@devframes/hub/client'
@@ -36,7 +36,7 @@ async function main() {
setStatus(`Connected · backend=${rpc.connectionMeta.backend}`, 'ready')
// 1. Docks — read from `devframe:docks` shared state.
- const docks = await rpc.sharedState.get(
+ const docks = await rpc.sharedState.get(
'devframe:docks',
{ initialValue: [] },
)
@@ -48,7 +48,7 @@ async function main() {
renderDocks()
// 2. Commands — read from `devframe:commands` shared state.
- const commands = await rpc.sharedState.get(
+ const commands = await rpc.sharedState.get(
'devframe:commands',
{ initialValue: [] },
)
@@ -62,8 +62,8 @@ async function main() {
// to refresh on broadcast; this minimal example polls instead.
const refreshMessages = async () => {
const entries = await rpc.call(
- 'minimal-vite-devtools-hub:messages:list' as any,
- ) as DevToolsMessageEntry[]
+ 'minimal-vite-devframe-hub:messages:list' as any,
+ ) as DevframeMessageEntry[]
renderList(messagesEl, entries, m =>
`
[${m.level}] ${m.message}
`)
}
@@ -72,8 +72,8 @@ async function main() {
// 4. Terminals — same pattern as messages.
const refreshTerminals = async () => {
const sessions = await rpc.call(
- 'minimal-vite-devtools-hub:terminals:list' as any,
- ) as Pick[]
+ 'minimal-vite-devframe-hub:terminals:list' as any,
+ ) as Pick[]
renderList(terminalsEl, sessions, t =>
`
${t.title}${t.id} · ${t.status}
`)
}
diff --git a/examples/minimal-vite-devtools-hub/src/client/style.css b/examples/minimal-vite-devframe-hub/src/client/style.css
similarity index 100%
rename from examples/minimal-vite-devtools-hub/src/client/style.css
rename to examples/minimal-vite-devframe-hub/src/client/style.css
diff --git a/examples/minimal-vite-devtools-hub/src/devframe.ts b/examples/minimal-vite-devframe-hub/src/devframe.ts
similarity index 100%
rename from examples/minimal-vite-devtools-hub/src/devframe.ts
rename to examples/minimal-vite-devframe-hub/src/devframe.ts
diff --git a/examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts b/examples/minimal-vite-devframe-hub/src/minimal-vite-devframe-hub.ts
similarity index 87%
rename from examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts
rename to examples/minimal-vite-devframe-hub/src/minimal-vite-devframe-hub.ts
index 6a720c1..b5a4969 100644
--- a/examples/minimal-vite-devtools-hub/src/minimal-vite-devtools-hub.ts
+++ b/examples/minimal-vite-devframe-hub/src/minimal-vite-devframe-hub.ts
@@ -1,16 +1,16 @@
import type { HubHostCapabilities, HubNodeContext } from '@devframes/hub/node'
-import type { DevframeDefinition, DevToolsHost } from 'devframe/types'
+import type { DevframeDefinition, DevframeHost } from 'devframe/types'
import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
import { homedir } from 'node:os'
import { defineRpcFunction } from '@devframes/hub'
import { createHubContext, mountDevframe } from '@devframes/hub/node'
-import { DEVTOOLS_CONNECTION_META_FILENAME } from 'devframe/constants'
+import { DEVFRAME_CONNECTION_META_FILENAME } from 'devframe/constants'
import { startHttpAndWs } from 'devframe/node'
import { launchEditor } from 'devframe/utils/launch-editor'
import { getPort } from 'get-port-please'
import { join } from 'pathe'
-export interface MinimalViteDevToolsHubOptions {
+export interface MinimalViteDevframeHubOptions {
/** Mount path for the hub's connection-meta endpoint. Default: `/__hub/`. */
base?: string
/** Preferred port for the side-car RPC/WS server. Default: a free port near 9777. */
@@ -22,7 +22,7 @@ export interface MinimalViteDevToolsHubOptions {
// Minimal hub-local RPCs — used by the UI for read-side data. A more
// ambitious hub host might hoist these into `@devframes/hub` itself.
const minimalViteHubMessagesList = defineRpcFunction({
- name: 'minimal-vite-devtools-hub:messages:list',
+ name: 'minimal-vite-devframe-hub:messages:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -33,7 +33,7 @@ const minimalViteHubMessagesList = defineRpcFunction({
})
const minimalViteHubTerminalsList = defineRpcFunction({
- name: 'minimal-vite-devtools-hub:terminals:list',
+ name: 'minimal-vite-devframe-hub:terminals:list',
type: 'static',
jsonSerializable: true,
setup: (ctx: HubNodeContext) => ({
@@ -57,13 +57,13 @@ const minimalViteHubTerminalsList = defineRpcFunction({
* This file is the entire Vite host — every other framework's hub host is
* the same shape: a thin layer that adapts a framework's dev server to the hub.
*/
-export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions = {}): Plugin {
+export function minimalViteDevframeHub(options: MinimalViteDevframeHubOptions = {}): Plugin {
const base = normalizeBase(options.base ?? '/__hub/')
let viteConfig: ResolvedConfig | undefined
let started: { close: () => Promise } | undefined
return {
- name: 'minimal-vite-devtools-hub',
+ name: 'minimal-vite-devframe-hub',
apply: 'serve',
configResolved(config) {
@@ -78,7 +78,7 @@ export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions =
const cwd = viteConfig!.root
- const host: DevToolsHost & HubHostCapabilities = {
+ const host: DevframeHost & HubHostCapabilities = {
mountStatic() {
// Static mounting for devframe SPAs would route through Vite's
// middleware in a fuller kit. This minimal example doesn't
@@ -90,8 +90,8 @@ export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions =
},
getStorageDir(scope) {
return scope === 'workspace'
- ? join(cwd, 'node_modules/.minimal-vite-devtools-hub')
- : join(homedir(), '.minimal-vite-devtools-hub')
+ ? join(cwd, 'node_modules/.minimal-vite-devframe-hub')
+ : join(homedir(), '.minimal-vite-devframe-hub')
},
async openPath(filepath, line, column) {
const absolute = join(cwd, filepath)
@@ -124,7 +124,7 @@ export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions =
// Seed a sample command directly on the hub so the UI
// shows something even without any plugged-in devframes.
context.commands.register({
- id: 'minimal-vite-devtools-hub:ping',
+ id: 'minimal-vite-devframe-hub:ping',
title: 'Vite Hub · Ping',
icon: 'ph:bell-duotone',
category: 'kit',
@@ -132,7 +132,7 @@ export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions =
})
await context.messages.add({
level: 'success',
- message: 'Minimal Vite DevTools Hub started',
+ message: 'Minimal Vite Devframe Hub started',
description: `Side-car WS on port ${port}. ${options.devframes?.length ?? 0} devframe(s) registered.`,
})
@@ -148,7 +148,7 @@ export function minimalViteDevToolsHub(options: MinimalViteDevToolsHubOptions =
// Tell the browser where to find the WS endpoint. `connectDevframe`
// resolves this URL relative to its `baseURL` option.
- const metaPath = `${base}${DEVTOOLS_CONNECTION_META_FILENAME}`
+ const metaPath = `${base}${DEVFRAME_CONNECTION_META_FILENAME}`
server.middlewares.use(metaPath, (_req, res) => {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ backend: 'websocket', websocket: port }))
diff --git a/examples/minimal-vite-devtools-hub/tsconfig.json b/examples/minimal-vite-devframe-hub/tsconfig.json
similarity index 100%
rename from examples/minimal-vite-devtools-hub/tsconfig.json
rename to examples/minimal-vite-devframe-hub/tsconfig.json
diff --git a/examples/minimal-vite-devtools-hub/vite.config.ts b/examples/minimal-vite-devframe-hub/vite.config.ts
similarity index 69%
rename from examples/minimal-vite-devtools-hub/vite.config.ts
rename to examples/minimal-vite-devframe-hub/vite.config.ts
index 5ab3361..925e42b 100644
--- a/examples/minimal-vite-devtools-hub/vite.config.ts
+++ b/examples/minimal-vite-devframe-hub/vite.config.ts
@@ -1,12 +1,12 @@
import { defineConfig } from 'vite'
import { alias } from '../../alias'
import demoDevframe from './src/devframe'
-import { minimalViteDevToolsHub } from './src/minimal-vite-devtools-hub'
+import { minimalViteDevframeHub } from './src/minimal-vite-devframe-hub'
export default defineConfig({
resolve: { alias },
plugins: [
- minimalViteDevToolsHub({
+ minimalViteDevframeHub({
devframes: [demoDevframe],
}),
],
diff --git a/examples/next-runtime-snapshot/src/client/app/components/connect.tsx b/examples/next-runtime-snapshot/src/client/app/components/connect.tsx
index e1c6599..87a33af 100644
--- a/examples/next-runtime-snapshot/src/client/app/components/connect.tsx
+++ b/examples/next-runtime-snapshot/src/client/app/components/connect.tsx
@@ -1,12 +1,12 @@
'use client'
-import type { DevToolsRpcClient } from 'devframe/client'
+import type { DevframeRpcClient } from 'devframe/client'
import type { ReactNode } from 'react'
import { connectDevframe } from 'devframe/client'
import { createContext, useContext, useEffect, useState } from 'react'
interface ConnectionState {
- rpc: DevToolsRpcClient | null
+ rpc: DevframeRpcClient | null
error: string | null
}
diff --git a/examples/next-runtime-snapshot/tests/_utils.ts b/examples/next-runtime-snapshot/tests/_utils.ts
index adcac27..4dcf3d5 100644
--- a/examples/next-runtime-snapshot/tests/_utils.ts
+++ b/examples/next-runtime-snapshot/tests/_utils.ts
@@ -1,9 +1,9 @@
import type { StartedServer } from 'devframe/node'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
-import { DEVTOOLS_CONNECTION_META_FILENAME } from 'devframe/constants'
+import { DEVFRAME_CONNECTION_META_FILENAME } from 'devframe/constants'
import {
- createH3DevToolsHost,
+ createH3DevframeHost,
createHostContext,
startHttpAndWs,
} from 'devframe/node'
@@ -36,7 +36,7 @@ export async function startSnapshotServer(): Promise {
const app = new H3()
const origin = `http://${host}:${port}`
- const h3Host = createH3DevToolsHost({
+ const h3Host = createH3DevframeHost({
origin,
appName: devframe.id,
mount: (base, dir) => mountStaticHandler(app, base, dir),
@@ -45,7 +45,7 @@ export async function startSnapshotServer(): Promise {
const ctx = await createHostContext({ cwd: process.cwd(), mode: 'dev', host: h3Host })
await devframe.setup(ctx)
- const metaPath = `${basePath}${DEVTOOLS_CONNECTION_META_FILENAME}`
+ const metaPath = `${basePath}${DEVFRAME_CONNECTION_META_FILENAME}`
app.use(metaPath, () => ({ backend: 'websocket', websocket: port }))
// Mount the static handler unconditionally — it only stat()s on
// request, so a missing dist just produces 404s for HTML routes.
diff --git a/examples/streaming-chat/README.md b/examples/streaming-chat/README.md
index f0d2cd1..c3f9214 100644
--- a/examples/streaming-chat/README.md
+++ b/examples/streaming-chat/README.md
@@ -2,7 +2,7 @@
End-to-end demo of devframe's streaming-channel API combined with shared
state for persistent chat history. Mirrors the AI-deltas use case from
-[vitejs/devtools#306](https://github.com/vitejs/devtools/issues/306):
+[vitejs/devframe#306](https://github.com/vitejs/devframe/issues/306):
the server emits synthesized "tokens" one at a time over a streaming
channel, while the conversation log lives in a devframe `sharedState` so
it survives reloads, syncs across panels, and replays cleanly when a
diff --git a/examples/streaming-chat/src/client/app.tsx b/examples/streaming-chat/src/client/app.tsx
index 327aae5..3fec8e1 100644
--- a/examples/streaming-chat/src/client/app.tsx
+++ b/examples/streaming-chat/src/client/app.tsx
@@ -1,4 +1,4 @@
-import type { DevToolsRpcClient } from 'devframe/client'
+import type { DevframeRpcClient } from 'devframe/client'
import type { StreamReader } from 'devframe/utils/streaming-channel'
import type { ChatHistory, ChatMessage } from '../devframe'
import { connectDevframe } from 'devframe/client'
@@ -8,7 +8,7 @@ const CHANNEL_NAME = 'devframe-streaming-chat:tokens'
const HISTORY_KEY = 'devframe-streaming-chat:history'
export function App() {
- const [rpc, setRpc] = useState(null)
+ const [rpc, setRpc] = useState(null)
const [demoPrompts, setDemoPrompts] = useState([])
const [messages, setMessages] = useState([])
const [liveTokens, setLiveTokens] = useState>({})
diff --git a/examples/streaming-chat/src/devframe.ts b/examples/streaming-chat/src/devframe.ts
index 09d245a..5e53cf6 100644
--- a/examples/streaming-chat/src/devframe.ts
+++ b/examples/streaming-chat/src/devframe.ts
@@ -33,7 +33,7 @@ export interface ChatHistory {
}
declare module 'devframe/types' {
- interface DevToolsRpcSharedStates {
+ interface DevframeRpcSharedStates {
[HISTORY_KEY]: ChatHistory
}
}
diff --git a/examples/streaming-chat/tests/_utils.ts b/examples/streaming-chat/tests/_utils.ts
index 54d48d9..755de93 100644
--- a/examples/streaming-chat/tests/_utils.ts
+++ b/examples/streaming-chat/tests/_utils.ts
@@ -1,13 +1,13 @@
-import type { DevToolsNodeContext, StartedServer } from 'devframe/node'
+import type { DevframeNodeContext, StartedServer } from 'devframe/node'
import { existsSync } from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import {
- DEVTOOLS_CONNECTION_META_FILENAME,
+ DEVFRAME_CONNECTION_META_FILENAME,
} from 'devframe/constants'
import {
- createH3DevToolsHost,
+ createH3DevframeHost,
createHostContext,
startHttpAndWs,
} from 'devframe/node'
@@ -29,7 +29,7 @@ export const CLIENT_DIST = resolve(HERE, '../dist/client')
*/
export async function startStreamingChatServer(): Promise {
// Build the client only if a test exercises the served HTML — RPC-only
// tests don't need the dist (we don't call assertClientBuilt unless the
@@ -41,7 +41,7 @@ export async function startStreamingChatServer(): Promise mountStaticHandler(app, base, dir),
@@ -50,7 +50,7 @@ export async function startStreamingChatServer(): Promise ({ backend: 'websocket', websocket: port }))
if (existsSync(path.join(resolve(distDir), 'index.html'))) {
mountStaticHandler(app, basePath, resolve(distDir))
diff --git a/examples/streaming-chat/tests/streaming-chat.test.ts b/examples/streaming-chat/tests/streaming-chat.test.ts
index bc1aba4..a53ed8e 100644
--- a/examples/streaming-chat/tests/streaming-chat.test.ts
+++ b/examples/streaming-chat/tests/streaming-chat.test.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext, StartedServer } from 'devframe/node'
+import type { DevframeNodeContext, StartedServer } from 'devframe/node'
import type { ChatHistory } from '../src/devframe'
import { createRpcStreamingClientHost } from 'devframe/client'
import { createRpcClient } from 'devframe/rpc/client'
@@ -78,13 +78,13 @@ async function readAll(reader: AsyncIterable): Promise {
return out
}
-async function getHistory(ctx: DevToolsNodeContext): Promise {
+async function getHistory(ctx: DevframeNodeContext): Promise {
const state = await ctx.rpc.sharedState.get(HISTORY_KEY)
return state.value() as ChatHistory
}
describe('devframe-streaming-chat (example)', () => {
- let server: StartedServer & { basePath: string, ctx: DevToolsNodeContext }
+ let server: StartedServer & { basePath: string, ctx: DevframeNodeContext }
beforeEach(async () => {
server = await startStreamingChatServer()
diff --git a/package.json b/package.json
index b28803d..39f5205 100644
--- a/package.json
+++ b/package.json
@@ -29,22 +29,22 @@
"postinstall": "npx simple-git-hooks && skills-npm"
},
"devDependencies": {
- "@antfu/eslint-config": "catalog:devtools",
+ "@antfu/eslint-config": "catalog:tooling",
"@antfu/ni": "catalog:build",
"@antfu/utils": "catalog:inlined",
"@playwright/test": "catalog:testing",
"@types/node": "catalog:types",
"@types/ws": "catalog:types",
- "bumpp": "catalog:devtools",
- "eslint": "catalog:devtools",
- "nano-staged": "catalog:devtools",
- "simple-git-hooks": "catalog:devtools",
- "skills-npm": "catalog:devtools",
+ "bumpp": "catalog:tooling",
+ "eslint": "catalog:tooling",
+ "nano-staged": "catalog:tooling",
+ "simple-git-hooks": "catalog:tooling",
+ "skills-npm": "catalog:tooling",
"tsdown": "catalog:build",
"tsnapi": "catalog:testing",
"tsx": "catalog:build",
"turbo": "catalog:build",
- "typescript": "catalog:devtools",
+ "typescript": "catalog:tooling",
"vite": "catalog:build",
"vitest": "catalog:testing"
},
diff --git a/packages/devframe/README.md b/packages/devframe/README.md
index 6665550..6243ce3 100644
--- a/packages/devframe/README.md
+++ b/packages/devframe/README.md
@@ -6,7 +6,7 @@
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
-Framework-neutral foundation for building generic DevTools.
+Framework-neutral foundation for building generic devframes.
Documentation: [https://devfra.me/](https://devfra.me/).
diff --git a/packages/devframe/package.json b/packages/devframe/package.json
index ae80384..f0ddbb3 100644
--- a/packages/devframe/package.json
+++ b/packages/devframe/package.json
@@ -2,7 +2,7 @@
"name": "devframe",
"type": "module",
"version": "0.4.1",
- "description": "Framework for building generic DevTools",
+ "description": "Framework for building generic devframes",
"author": "Anthony Fu ",
"license": "MIT",
"homepage": "https://github.com/devframes/devframe#readme",
diff --git a/packages/devframe/src/adapters/build.ts b/packages/devframe/src/adapters/build.ts
index a82d104..3ea701e 100644
--- a/packages/devframe/src/adapters/build.ts
+++ b/packages/devframe/src/adapters/build.ts
@@ -7,12 +7,12 @@ import { colors as c } from 'devframe/utils/colors'
import { structuredCloneStringify } from 'devframe/utils/structured-clone'
import { dirname, resolve } from 'pathe'
import {
- DEVTOOLS_CONNECTION_META_FILENAME,
- DEVTOOLS_RPC_DUMP_DIRNAME,
- DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME,
+ DEVFRAME_CONNECTION_META_FILENAME,
+ DEVFRAME_RPC_DUMP_DIRNAME,
+ DEVFRAME_RPC_DUMP_MANIFEST_FILENAME,
} from '../constants'
import { createHostContext } from '../node/context'
-import { createH3DevToolsHost } from '../node/host-h3'
+import { createH3DevframeHost } from '../node/host-h3'
import { collectStaticRpcDump } from '../rpc/dump/static'
import { strictJsonStringify } from '../rpc/serialization'
import { resolveBasePath } from './_shared'
@@ -47,7 +47,7 @@ export interface CreateBuildOptions {
* - When `def.spa` is configured, also write `/spa-loader.json`
* describing the SPA's data-loader mode (`'query'` / `'upload'` /
* `'none'`). The output is mount-path agnostic — the same bundle
- * works at `/`, `/devtools/`, or any base, no rewriting required.
+ * works at `/`, `/devframe/`, or any base, no rewriting required.
*/
export async function createBuild(d: DevframeDefinition, options: CreateBuildOptions = {}): Promise {
const outDir = resolve(options.outDir ?? 'dist-static')
@@ -66,11 +66,11 @@ export async function createBuild(d: DevframeDefinition, options: CreateBuildOpt
const ctx = await createHostContext({
cwd: process.cwd(),
mode: 'build',
- host: createH3DevToolsHost({ origin: 'http://localhost', appName: d.id }),
+ host: createH3DevframeHost({ origin: 'http://localhost', appName: d.id }),
})
await d.setup(ctx)
- await fs.mkdir(resolve(outDir, DEVTOOLS_RPC_DUMP_DIRNAME), { recursive: true })
+ await fs.mkdir(resolve(outDir, DEVFRAME_RPC_DUMP_DIRNAME), { recursive: true })
const jsonSerializableMethods: string[] = []
for (const def of ctx.rpc.definitions.values()) {
@@ -78,12 +78,12 @@ export async function createBuild(d: DevframeDefinition, options: CreateBuildOpt
jsonSerializableMethods.push(def.name)
}
await fs.writeFile(
- resolve(outDir, DEVTOOLS_CONNECTION_META_FILENAME),
+ resolve(outDir, DEVFRAME_CONNECTION_META_FILENAME),
JSON.stringify({ backend: 'static', jsonSerializableMethods }, null, 2),
'utf-8',
)
- console.log(c.cyan`[devframe] writing RPC dump to ${resolve(outDir, DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME)}`)
+ console.log(c.cyan`[devframe] writing RPC dump to ${resolve(outDir, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME)}`)
const dump = await collectStaticRpcDump(ctx.rpc.definitions.values(), ctx)
const indent = options.pretty ? 2 : undefined
for (const [filepath, file] of Object.entries(dump.files)) {
@@ -102,7 +102,7 @@ export async function createBuild(d: DevframeDefinition, options: CreateBuildOpt
)
}
await fs.writeFile(
- resolve(outDir, DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME),
+ resolve(outDir, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME),
JSON.stringify(dump.manifest, null, 2),
'utf-8',
)
diff --git a/packages/devframe/src/adapters/dev.ts b/packages/devframe/src/adapters/dev.ts
index fc417d5..1c0ec53 100644
--- a/packages/devframe/src/adapters/dev.ts
+++ b/packages/devframe/src/adapters/dev.ts
@@ -6,9 +6,9 @@ import { mountStaticHandler } from 'devframe/utils/serve-static'
import { getPort } from 'get-port-please'
import { H3 } from 'h3'
import { resolve } from 'pathe'
-import { DEVTOOLS_CONNECTION_META_FILENAME } from '../constants'
+import { DEVFRAME_CONNECTION_META_FILENAME } from '../constants'
import { createHostContext } from '../node/context'
-import { createH3DevToolsHost } from '../node/host-h3'
+import { createH3DevframeHost } from '../node/host-h3'
import { startHttpAndWs } from '../node/server'
import { normalizeBasePath, resolveBasePath } from './_shared'
@@ -125,7 +125,7 @@ export async function createDevServer(
const app = options.app ?? new H3()
const origin = `http://${host}:${port}`
- const h3Host = createH3DevToolsHost({
+ const h3Host = createH3DevframeHost({
origin,
appName: def.id,
mount: (base, dir) => {
@@ -146,7 +146,7 @@ export async function createDevServer(
// to know it's a websocket backend bound to that same port. The path
// sits at the SPA root (next to index.html) so the deployed SPA can
// discover it via a relative `./__connection.json` fetch.
- const connectionMetaPath = `${basePath}${DEVTOOLS_CONNECTION_META_FILENAME}`
+ const connectionMetaPath = `${basePath}${DEVFRAME_CONNECTION_META_FILENAME}`
app.use(connectionMetaPath, () => ({ backend: 'websocket', websocket: port }))
if (distDir)
diff --git a/packages/devframe/src/adapters/embedded.ts b/packages/devframe/src/adapters/embedded.ts
index 17d86b6..ae36dc8 100644
--- a/packages/devframe/src/adapters/embedded.ts
+++ b/packages/devframe/src/adapters/embedded.ts
@@ -1,9 +1,9 @@
-import type { DevToolsNodeContext } from '../types/context'
+import type { DevframeNodeContext } from '../types/context'
import type { DevframeDefinition } from '../types/devframe'
export interface CreateEmbeddedOptions {
/** Target context the devframe is registered into. Required. */
- ctx: DevToolsNodeContext
+ ctx: DevframeNodeContext
}
/**
diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts b/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts
index f443e4d..ecf705a 100644
--- a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts
+++ b/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts
@@ -1,11 +1,11 @@
-import type { DevToolsHost } from '../../../types/host'
+import type { DevframeHost } from '../../../types/host'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { createHostContext } from 'devframe/node'
import { describe, expect, it } from 'vitest'
import { buildMcpServerFromContext } from '../build-server'
-function nullHost(): DevToolsHost {
+function nullHost(): DevframeHost {
return {
mountStatic: () => { /* no-op */ },
resolveOrigin: () => 'mcp://test',
diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/devframe/src/adapters/mcp/build-server.ts
index 365de52..e969d50 100644
--- a/packages/devframe/src/adapters/mcp/build-server.ts
+++ b/packages/devframe/src/adapters/mcp/build-server.ts
@@ -1,5 +1,5 @@
import type { RpcFunctionDefinitionAnyWithContext } from 'devframe/rpc'
-import type { AgentTool, DevframeDefinition, DevToolsHost, DevToolsNodeContext } from 'devframe/types'
+import type { AgentTool, DevframeDefinition, DevframeHost, DevframeNodeContext } from 'devframe/types'
import type { GenericSchema } from 'valibot'
import { homedir } from 'node:os'
import process from 'node:process'
@@ -50,7 +50,7 @@ export interface McpServerHandle {
* @internal
*/
export function buildMcpServerFromContext(
- ctx: DevToolsNodeContext,
+ ctx: DevframeNodeContext,
options: { serverName: string, serverVersion: string, exposeSharedState: boolean | ((k: string) => boolean) },
): { server: Server, dispose: () => void } {
const server = new Server(
@@ -104,12 +104,12 @@ export async function createMcpServer(
if (transport !== 'stdio')
throw diagnostics.DF0017({ transport, reason: 'Only stdio transport is supported in this release.' })
- const host: DevToolsHost = {
+ const host: DevframeHost = {
mountStatic: () => { /* MCP has no static surface */ },
resolveOrigin: () => 'mcp://devframe',
getStorageDir: scope => scope === 'workspace'
- ? join(process.cwd(), `node_modules/.${definition.id}/devtools`)
- : join(homedir(), `.${definition.id}/devtools`),
+ ? join(process.cwd(), `node_modules/.${definition.id}/devframe`)
+ : join(homedir(), `.${definition.id}/devframe`),
}
const ctx = await createHostContext({
@@ -145,7 +145,7 @@ export async function createMcpServer(
}
}
-function registerToolHandlers(server: Server, ctx: DevToolsNodeContext): void {
+function registerToolHandlers(server: Server, ctx: DevframeNodeContext): void {
server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools = ctx.agent.list().tools.map(tool => projectTool(tool, ctx))
return { tools }
@@ -180,7 +180,7 @@ function registerToolHandlers(server: Server, ctx: DevToolsNodeContext): void {
function registerResourceHandlers(
server: Server,
- ctx: DevToolsNodeContext,
+ ctx: DevframeNodeContext,
exposeSharedState: boolean | ((key: string) => boolean),
): void {
server.setRequestHandler(ListResourcesRequestSchema, async () => {
@@ -242,7 +242,7 @@ function registerResourceHandlers(
})
}
-function projectTool(tool: AgentTool, ctx: DevToolsNodeContext): Record {
+function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Record {
const inputSchema = tool.inputSchema ?? computeInputSchema(tool, ctx)
const outputSchema = tool.outputSchema ?? computeOutputSchema(tool, ctx)
return {
@@ -259,20 +259,20 @@ function projectTool(tool: AgentTool, ctx: DevToolsNodeContext): Record | undefined
+ const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined
if (!def)
return { type: 'object', properties: {} }
const args = def.args as readonly GenericSchema[] | undefined
return valibotArgsToJsonSchema(args).schema
}
-function computeOutputSchema(tool: AgentTool, ctx: DevToolsNodeContext): unknown {
+function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown {
if (tool.kind !== 'rpc' || !tool.rpcName)
return undefined
- const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined
+ const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined
if (!def)
return undefined
return valibotReturnToJsonSchema(def.returns as GenericSchema | undefined)
diff --git a/packages/devframe/src/client/index.ts b/packages/devframe/src/client/index.ts
index 3531de0..d94b4f8 100644
--- a/packages/devframe/src/client/index.ts
+++ b/packages/devframe/src/client/index.ts
@@ -1,6 +1,6 @@
-import { getDevToolsRpcClient } from './rpc'
+import { getDevframeRpcClient } from './rpc'
export * from './rpc'
export * from './rpc-streaming'
-export const connectDevframe = getDevToolsRpcClient
+export const connectDevframe = getDevframeRpcClient
diff --git a/packages/devframe/src/client/rpc-shared-state.ts b/packages/devframe/src/client/rpc-shared-state.ts
index 12e9574..09bb4af 100644
--- a/packages/devframe/src/client/rpc-shared-state.ts
+++ b/packages/devframe/src/client/rpc-shared-state.ts
@@ -1,9 +1,9 @@
import type { RpcSharedStateGetOptions, RpcSharedStateHost } from 'devframe/types'
import type { SharedState, SharedStatePatch } from 'devframe/utils/shared-state'
-import type { DevToolsRpcClient } from './rpc'
+import type { DevframeRpcClient } from './rpc'
import { createSharedState } from 'devframe/utils/shared-state'
-export function createRpcSharedStateClientHost(rpc: DevToolsRpcClient): RpcSharedStateHost {
+export function createRpcSharedStateClientHost(rpc: DevframeRpcClient): RpcSharedStateHost {
const sharedState = new Map>()
const initialValues = new Map()
const keyAddedListeners = new Set<(key: string) => void>()
diff --git a/packages/devframe/src/client/rpc-static.ts b/packages/devframe/src/client/rpc-static.ts
index 9242c63..ecdff34 100644
--- a/packages/devframe/src/client/rpc-static.ts
+++ b/packages/devframe/src/client/rpc-static.ts
@@ -1,5 +1,5 @@
-import type { DevToolsRpcClientMode } from './rpc'
-import { DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME } from 'devframe/constants'
+import type { DevframeRpcClientMode } from './rpc'
+import { DEVFRAME_RPC_DUMP_MANIFEST_FILENAME } from 'devframe/constants'
import { createStaticRpcCaller } from './static-rpc'
export interface CreateStaticRpcClientModeOptions {
@@ -8,8 +8,8 @@ export interface CreateStaticRpcClientModeOptions {
export async function createStaticRpcClientMode(
options: CreateStaticRpcClientModeOptions,
-): Promise {
- const manifest = await options.fetchJsonFromBases(DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME)
+): Promise {
+ const manifest = await options.fetchJsonFromBases(DEVFRAME_RPC_DUMP_MANIFEST_FILENAME)
const staticCaller = createStaticRpcCaller(manifest, options.fetchJsonFromBases)
return {
diff --git a/packages/devframe/src/client/rpc-streaming.ts b/packages/devframe/src/client/rpc-streaming.ts
index ebb6c55..7264658 100644
--- a/packages/devframe/src/client/rpc-streaming.ts
+++ b/packages/devframe/src/client/rpc-streaming.ts
@@ -1,5 +1,5 @@
import type { StreamErrorPayload, StreamReader, StreamSink } from 'devframe/utils/streaming-channel'
-import type { DevToolsRpcClient } from './rpc'
+import type { DevframeRpcClient } from './rpc'
import { createStreamReader, createStreamSink } from 'devframe/utils/streaming-channel'
const STREAM_KEY_SEPARATOR = '\x1F'
@@ -41,7 +41,7 @@ export interface RpcStreamingClientHost {
* registers the two `:chunk` / `:end` event handlers once, then per-stream
* state lives in a `Map`.
*/
-export function createRpcStreamingClientHost(rpc: DevToolsRpcClient): RpcStreamingClientHost {
+export function createRpcStreamingClientHost(rpc: DevframeRpcClient): RpcStreamingClientHost {
const readers = new Map>()
const uploads = new Map>()
diff --git a/packages/devframe/src/client/rpc-ws.ts b/packages/devframe/src/client/rpc-ws.ts
index b0b99d2..54d8ccb 100644
--- a/packages/devframe/src/client/rpc-ws.ts
+++ b/packages/devframe/src/client/rpc-ws.ts
@@ -1,5 +1,5 @@
-import type { ConnectionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, EventEmitter } from 'devframe/types'
-import type { DevToolsClientRpcHost, DevToolsRpcClientMode, DevToolsRpcClientOptions, RpcClientEvents } from './rpc'
+import type { ConnectionMeta, DevframeRpcClientFunctions, DevframeRpcServerFunctions, EventEmitter } from 'devframe/types'
+import type { DevframeClientRpcHost, DevframeRpcClientMode, DevframeRpcClientOptions, RpcClientEvents } from './rpc'
import { createRpcClient } from 'devframe/rpc/client'
import { createWsRpcChannel } from 'devframe/rpc/transports/ws-client'
import { promiseWithResolver } from 'devframe/utils/promise'
@@ -9,9 +9,9 @@ export interface CreateWsRpcClientModeOptions {
authToken: string
connectionMeta: ConnectionMeta
events: EventEmitter
- clientRpc: DevToolsClientRpcHost
- rpcOptions?: DevToolsRpcClientOptions['rpcOptions']
- wsOptions?: DevToolsRpcClientOptions['wsOptions']
+ clientRpc: DevframeClientRpcHost
+ rpcOptions?: DevframeRpcClientOptions['rpcOptions']
+ wsOptions?: DevframeRpcClientOptions['wsOptions']
}
function isNumeric(str: string | number | undefined) {
@@ -22,7 +22,7 @@ function isNumeric(str: string | number | undefined) {
export function createWsRpcClientMode(
options: CreateWsRpcClientModeOptions,
-): DevToolsRpcClientMode {
+): DevframeRpcClientMode {
const {
authToken,
connectionMeta,
@@ -46,7 +46,7 @@ export function createWsRpcClientMode(
for (const name of connectionMeta.jsonSerializableMethods ?? [])
definitions.set(name, { jsonSerializable: true })
- const serverRpc = createRpcClient(
+ const serverRpc = createRpcClient(
clientRpc.functions,
{
channel: createWsRpcChannel({
@@ -84,7 +84,7 @@ export function createWsRpcClientMode(
info.device.type,
].filter(i => i).join(' ')
- const result = await serverRpc.$call('vite:anonymous:auth', {
+ const result = await serverRpc.$call('devframe:anonymous:auth', {
authToken: token,
ua,
origin: location.origin,
diff --git a/packages/devframe/src/client/rpc.ts b/packages/devframe/src/client/rpc.ts
index d090986..d5c3bf5 100644
--- a/packages/devframe/src/client/rpc.ts
+++ b/packages/devframe/src/client/rpc.ts
@@ -1,10 +1,10 @@
import type { BirpcOptions, BirpcReturn } from 'birpc'
import type { RpcCacheOptions, RpcFunctionsCollector } from 'devframe/rpc'
import type { WsRpcChannelOptions } from 'devframe/rpc/transports/ws-client'
-import type { ConnectionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, EventEmitter, RpcSharedStateHost } from 'devframe/types'
+import type { ConnectionMeta, DevframeRpcClientFunctions, DevframeRpcServerFunctions, EventEmitter, RpcSharedStateHost } from 'devframe/types'
import type { RpcStreamingClientHost } from './rpc-streaming'
import {
- DEVTOOLS_CONNECTION_META_FILENAME,
+ DEVFRAME_CONNECTION_META_FILENAME,
} from 'devframe/constants'
import { RpcCacheManager, RpcFunctionsCollectorBase } from 'devframe/rpc'
import { createEventEmitter } from 'devframe/utils/events'
@@ -14,23 +14,23 @@ import { createStaticRpcClientMode } from './rpc-static'
import { createRpcStreamingClientHost } from './rpc-streaming'
import { createWsRpcClientMode } from './rpc-ws'
-export interface DevToolsRpcContext {
+export interface DevframeRpcContext {
/**
* The RPC client to interact with the server
*/
- readonly rpc: DevToolsRpcClient
+ readonly rpc: DevframeRpcClient
}
-export type DevToolsClientRpcHost = RpcFunctionsCollector
+export type DevframeClientRpcHost = RpcFunctionsCollector
export interface RpcClientEvents {
'rpc:is-trusted:updated': (isTrusted: boolean) => void
}
-const CONNECTION_META_KEY = '__VITE_DEVTOOLS_CONNECTION_META__'
-const CONNECTION_AUTH_TOKEN_KEY = '__VITE_DEVTOOLS_CONNECTION_AUTH_TOKEN__'
+const CONNECTION_META_KEY = '__DEVFRAME_CONNECTION_META__'
+const CONNECTION_AUTH_TOKEN_KEY = '__DEVFRAME_CONNECTION_AUTH_TOKEN__'
-export interface DevToolsRpcClientOptions {
+export interface DevframeRpcClientOptions {
connectionMeta?: ConnectionMeta
baseURL?: string | string[]
/**
@@ -38,15 +38,15 @@ export interface DevToolsRpcClientOptions {
*/
authToken?: string
wsOptions?: Partial
- rpcOptions?: Partial>
+ rpcOptions?: Partial>
cacheOptions?: boolean | Partial
}
-export type DevToolsRpcClientCall = BirpcReturn['$call']
-export type DevToolsRpcClientCallEvent = BirpcReturn['$callEvent']
-export type DevToolsRpcClientCallOptional = BirpcReturn['$callOptional']
+export type DevframeRpcClientCall = BirpcReturn['$call']
+export type DevframeRpcClientCallEvent = BirpcReturn['$callEvent']
+export type DevframeRpcClientCallOptional = BirpcReturn['$callOptional']
-export interface DevToolsRpcClient {
+export interface DevframeRpcClient {
/**
* The events of the client
*/
@@ -83,19 +83,19 @@ export interface DevToolsRpcClient {
/**
* Call a RPC function on the server
*/
- call: DevToolsRpcClientCall
+ call: DevframeRpcClientCall
/**
* Call a RPC event on the server, and does not expect a response
*/
- callEvent: DevToolsRpcClientCallEvent
+ callEvent: DevframeRpcClientCallEvent
/**
* Call a RPC optional function on the server
*/
- callOptional: DevToolsRpcClientCallOptional
+ callOptional: DevframeRpcClientCallOptional
/**
* The client RPC host
*/
- client: DevToolsClientRpcHost
+ client: DevframeClientRpcHost
/**
* The shared state host
@@ -113,14 +113,14 @@ export interface DevToolsRpcClient {
cacheManager: RpcCacheManager
}
-export interface DevToolsRpcClientMode {
+export interface DevframeRpcClientMode {
readonly isTrusted: boolean
- ensureTrusted: DevToolsRpcClient['ensureTrusted']
- requestTrust: DevToolsRpcClient['requestTrust']
- requestTrustWithToken: DevToolsRpcClient['requestTrustWithToken']
- call: DevToolsRpcClient['call']
- callEvent: DevToolsRpcClient['callEvent']
- callOptional: DevToolsRpcClient['callOptional']
+ ensureTrusted: DevframeRpcClient['ensureTrusted']
+ requestTrust: DevframeRpcClient['requestTrust']
+ requestTrustWithToken: DevframeRpcClient['requestTrustWithToken']
+ call: DevframeRpcClient['call']
+ callEvent: DevframeRpcClient['callEvent']
+ callOptional: DevframeRpcClient['callOptional']
}
function getConnectionAuthTokenFromWindows(userAuthToken?: string): string {
@@ -168,9 +168,9 @@ function findConnectionMetaFromWindows(): ConnectionMeta | undefined {
}
}
-export async function getDevToolsRpcClient(
- options: DevToolsRpcClientOptions = {},
-): Promise {
+export async function getDevframeRpcClient(
+ options: DevframeRpcClientOptions = {},
+): Promise {
// Default to a relative base — the SPA owns its mount path at runtime,
// so the connection meta and dump shards live alongside `index.html`.
// Embedded surfaces that run inside a host page (e.g. a webcomponent
@@ -202,7 +202,7 @@ export async function getDevToolsRpcClient(
const errors: Error[] = []
for (const base of bases) {
try {
- connectionMeta = await fetch(resolveBasePath(base, DEVTOOLS_CONNECTION_META_FILENAME))
+ connectionMeta = await fetch(resolveBasePath(base, DEVFRAME_CONNECTION_META_FILENAME))
.then(r => r.json()) as ConnectionMeta
resolvedBaseURL = base
;(globalThis as any)[CONNECTION_META_KEY] = connectionMeta
@@ -220,11 +220,11 @@ export async function getDevToolsRpcClient(
}
const cacheManager = new RpcCacheManager({ functions: [], ...(typeof options.cacheOptions === 'object' ? options.cacheOptions : {}) })
- const context: DevToolsRpcContext = {
+ const context: DevframeRpcContext = {
rpc: undefined!,
}
const authToken = getConnectionAuthTokenFromWindows(options.authToken)
- const clientRpc: DevToolsClientRpcHost = new RpcFunctionsCollectorBase(context)
+ const clientRpc: DevframeClientRpcHost = new RpcFunctionsCollectorBase(context)
async function fetchJsonFromBases(path: string): Promise {
const candidates = [
@@ -283,7 +283,7 @@ export async function getDevToolsRpcClient(
wsOptions: options.wsOptions,
})
- const rpc: DevToolsRpcClient = {
+ const rpc: DevframeRpcClient = {
events,
get isTrusted() {
return mode.isTrusted
@@ -316,7 +316,7 @@ export async function getDevToolsRpcClient(
// Listen for auth updates from other tabs (e.g., auth URL page).
// Channel name kept for cross-tab interop with the Vite DevTools auth page.
try {
- const bc = new BroadcastChannel('vite-devtools-auth')
+ const bc = new BroadcastChannel('devframe-auth')
bc.onmessage = (event) => {
if (event.data?.type === 'auth-update' && event.data.authToken) {
rpc.requestTrustWithToken(event.data.authToken)
diff --git a/packages/devframe/src/client/static-rpc.test.ts b/packages/devframe/src/client/static-rpc.test.ts
index 7008268..e91fe3a 100644
--- a/packages/devframe/src/client/static-rpc.test.ts
+++ b/packages/devframe/src/client/static-rpc.test.ts
@@ -1,11 +1,11 @@
-import { DEVTOOLS_RPC_DUMP_DIRNAME } from 'devframe/constants'
+import { DEVFRAME_RPC_DUMP_DIRNAME } from 'devframe/constants'
import { hash } from 'devframe/utils/hash'
import { structuredCloneStringify } from 'devframe/utils/structured-clone'
import { describe, expect, it } from 'vitest'
import { createStaticRpcCaller } from './static-rpc'
-const DEMO_STATIC_VERSION_PATH = `${DEVTOOLS_RPC_DUMP_DIRNAME}/demo~version.static.json`
-const DEMO_QUERY_BASE_PATH = `${DEVTOOLS_RPC_DUMP_DIRNAME}/demo~get-item`
+const DEMO_STATIC_VERSION_PATH = `${DEVFRAME_RPC_DUMP_DIRNAME}/demo~version.static.json`
+const DEMO_QUERY_BASE_PATH = `${DEVFRAME_RPC_DUMP_DIRNAME}/demo~get-item`
const DEMO_QUERY_RECORDS_PATH = `${DEMO_QUERY_BASE_PATH}.record`
const DEMO_QUERY_FALLBACK_PATH = `${DEMO_QUERY_BASE_PATH}.fallback.json`
@@ -80,7 +80,7 @@ describe('createStaticRpcCaller', () => {
await expect(caller.callOptional('demo:missing', [])).resolves.toBeUndefined()
await expect(caller.callEvent('demo:missing', [])).resolves.toBeUndefined()
- await expect(caller.call('demo:missing', [])).rejects.toThrow('[devtools-rpc] Function "demo:missing" not found in dump store')
+ await expect(caller.call('demo:missing', [])).rejects.toThrow('[devframe-rpc] Function "demo:missing" not found in dump store')
})
it('treats callEvent as no-op in static mode even for known methods', async () => {
@@ -117,7 +117,7 @@ describe('createStaticRpcCaller', () => {
{
'demo:graph': {
type: 'static',
- path: `${DEVTOOLS_RPC_DUMP_DIRNAME}/demo~graph.static.json`,
+ path: `${DEVFRAME_RPC_DUMP_DIRNAME}/demo~graph.static.json`,
serialization: 'structured-clone',
},
},
@@ -161,7 +161,7 @@ describe('createStaticRpcCaller', () => {
{
'demo:legacy-static': {
type: 'static',
- path: `${DEVTOOLS_RPC_DUMP_DIRNAME}/demo~legacy.static.json`,
+ path: `${DEVFRAME_RPC_DUMP_DIRNAME}/demo~legacy.static.json`,
// no `serialization` field — must default to JSON parsing
},
},
diff --git a/packages/devframe/src/client/static-rpc.ts b/packages/devframe/src/client/static-rpc.ts
index 796d366..06ba74d 100644
--- a/packages/devframe/src/client/static-rpc.ts
+++ b/packages/devframe/src/client/static-rpc.ts
@@ -102,14 +102,14 @@ export function createStaticRpcCaller(
async function call(functionName: string, args: any[]) {
if (!(functionName in manifest)) {
- throw new Error(`[devtools-rpc] Function "${functionName}" not found in dump store`)
+ throw new Error(`[devframe-rpc] Function "${functionName}" not found in dump store`)
}
const entry = manifest[functionName]
if (isStaticEntry(entry)) {
if (args.length > 0) {
throw new Error(
- `[devtools-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
+ `[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
)
}
return await loadStatic(entry)
@@ -130,7 +130,7 @@ export function createStaticRpcCaller(
}
throw new Error(
- `[devtools-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
+ `[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
)
}
@@ -139,7 +139,7 @@ export function createStaticRpcCaller(
}
throw new Error(
- `[devtools-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
+ `[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`,
)
}
diff --git a/packages/devframe/src/constants.ts b/packages/devframe/src/constants.ts
index c04f0b3..106fd8e 100644
--- a/packages/devframe/src/constants.ts
+++ b/packages/devframe/src/constants.ts
@@ -1,17 +1,17 @@
-// DevTools runtime routes and static output conventions.
-export const DEVTOOLS_MOUNT_PATH = '/__devtools/'
-export const DEVTOOLS_MOUNT_PATH_NO_TRAILING_SLASH = '/__devtools'
-export const DEVTOOLS_DIRNAME = '__devtools'
+// Devframe runtime routes and static output conventions.
+export const DEVFRAME_MOUNT_PATH = '/__devframe/'
+export const DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH = '/__devframe'
+export const DEVFRAME_DIRNAME = '__devframe'
-export const DEVTOOLS_CONNECTION_META_FILENAME = '__connection.json'
-export const DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME = '__rpc-dump/index.json'
-export const DEVTOOLS_DOCK_IMPORTS_FILENAME = '__client-imports.js'
-export const DEVTOOLS_DOCK_IMPORTS_VIRTUAL_ID = '/__devtools-client-imports.js'
-export const DEVTOOLS_RPC_DUMP_DIRNAME = '__rpc-dump'
+export const DEVFRAME_CONNECTION_META_FILENAME = '__connection.json'
+export const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = '__rpc-dump/index.json'
+export const DEVFRAME_DOCK_IMPORTS_FILENAME = '__client-imports.js'
+export const DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID = '/__devframe-client-imports.js'
+export const DEVFRAME_RPC_DUMP_DIRNAME = '__rpc-dump'
/**
* URL fragment / query parameter name carrying the remote dock
* connection descriptor (defined as `RemoteConnectionInfo` in
* `@vitejs/devtools-kit`) injected into remote-UI iframe dock URLs.
*/
-export const REMOTE_CONNECTION_KEY = 'vite-devtools-kit-connection'
+export const REMOTE_CONNECTION_KEY = 'devframe-remote-connection'
diff --git a/packages/devframe/src/define.ts b/packages/devframe/src/define.ts
index 64af131..3de788f 100644
--- a/packages/devframe/src/define.ts
+++ b/packages/devframe/src/define.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext } from 'devframe/types'
+import type { DevframeNodeContext } from 'devframe/types'
import { createDefineWrapperWithContext } from 'devframe/rpc'
-export const defineRpcFunction = createDefineWrapperWithContext()
+export const defineRpcFunction = createDefineWrapperWithContext()
diff --git a/packages/devframe/src/helpers/vite.ts b/packages/devframe/src/helpers/vite.ts
index 45d1fd2..647213c 100644
--- a/packages/devframe/src/helpers/vite.ts
+++ b/packages/devframe/src/helpers/vite.ts
@@ -3,7 +3,7 @@ import { serveStaticNodeMiddleware } from 'devframe/utils/serve-static'
import { resolve } from 'pathe'
import { resolveBasePath } from '../adapters/_shared'
import { createDevServer, resolveDevServerPort } from '../adapters/dev'
-import { DEVTOOLS_CONNECTION_META_FILENAME } from '../constants'
+import { DEVFRAME_CONNECTION_META_FILENAME } from '../constants'
import { diagnostics } from '../node/diagnostics'
export interface ViteDevBridgeOptions {
@@ -110,7 +110,7 @@ export function viteDevBridge(d: DevframeDefinition, options: ViteDevBridgeOptio
return
}
- const metaPath = `${base}${DEVTOOLS_CONNECTION_META_FILENAME}`
+ const metaPath = `${base}${DEVFRAME_CONNECTION_META_FILENAME}`
server.middlewares.use(metaPath, (_req: unknown, res: any) => {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ backend: 'websocket', websocket: port }))
diff --git a/packages/devframe/src/node/__tests__/host-agent.test.ts b/packages/devframe/src/node/__tests__/host-agent.test.ts
index d0dac66..ab8e152 100644
--- a/packages/devframe/src/node/__tests__/host-agent.test.ts
+++ b/packages/devframe/src/node/__tests__/host-agent.test.ts
@@ -1,17 +1,17 @@
import type { RpcFunctionDefinitionAnyWithContext } from '../../rpc/types'
-import type { DevToolsNodeContext } from '../../types/context'
+import type { DevframeNodeContext } from '../../types/context'
import { describe, expect, it, vi } from 'vitest'
-import { DevToolsAgentHost } from '../host-agent'
+import { DevframeAgentHost } from '../host-agent'
import { RpcFunctionsHost } from '../host-functions'
-function createContext(): DevToolsNodeContext {
- const ctx = {} as DevToolsNodeContext
+function createContext(): DevframeNodeContext {
+ const ctx = {} as DevframeNodeContext
ctx.rpc = new RpcFunctionsHost(ctx)
- ctx.agent = new DevToolsAgentHost(ctx)
+ ctx.agent = new DevframeAgentHost(ctx)
return ctx
}
-function rpcDef(def: RpcFunctionDefinitionAnyWithContext): RpcFunctionDefinitionAnyWithContext {
+function rpcDef(def: RpcFunctionDefinitionAnyWithContext): RpcFunctionDefinitionAnyWithContext {
return def
}
diff --git a/packages/devframe/src/node/__tests__/host-functions.test.ts b/packages/devframe/src/node/__tests__/host-functions.test.ts
index 028aad9..e21f145 100644
--- a/packages/devframe/src/node/__tests__/host-functions.test.ts
+++ b/packages/devframe/src/node/__tests__/host-functions.test.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext } from 'devframe/types'
+import type { DevframeNodeContext } from 'devframe/types'
import { defineRpcFunction } from 'devframe'
import { describe, expect, it } from 'vitest'
import { RpcFunctionsHost } from '../host-functions'
@@ -11,7 +11,7 @@ const returnV2 = async () => 'v2'
const setupWith = (handler: () => Promise) => async () => ({ handler })
describe('rpcFunctionsHost', () => {
- const mockContext = {} as DevToolsNodeContext
+ const mockContext = {} as DevframeNodeContext
describe('register() collision detection', () => {
it('should register a new RPC function successfully', () => {
@@ -131,7 +131,7 @@ describe('rpcFunctionsHost', () => {
describe('broadcast() without rpc group', () => {
it('should not throw in build mode', async () => {
- const host = new RpcFunctionsHost({ mode: 'build' } as DevToolsNodeContext)
+ const host = new RpcFunctionsHost({ mode: 'build' } as DevframeNodeContext)
await expect(host.broadcast({
method: 'devframe:terminals:updated',
args: [],
@@ -139,7 +139,7 @@ describe('rpcFunctionsHost', () => {
})
it('should not throw in dev mode when rpc group is not yet set', async () => {
- const host = new RpcFunctionsHost({ mode: 'dev' } as DevToolsNodeContext)
+ const host = new RpcFunctionsHost({ mode: 'dev' } as DevframeNodeContext)
await expect(host.broadcast({
method: 'devframe:terminals:updated',
args: [],
diff --git a/packages/devframe/src/node/__tests__/rpc-agent-introspection.test.ts b/packages/devframe/src/node/__tests__/rpc-agent-introspection.test.ts
index d125765..d698213 100644
--- a/packages/devframe/src/node/__tests__/rpc-agent-introspection.test.ts
+++ b/packages/devframe/src/node/__tests__/rpc-agent-introspection.test.ts
@@ -1,8 +1,8 @@
-import type { DevToolsHost } from '../../types/host'
+import type { DevframeHost } from '../../types/host'
import { describe, expect, it } from 'vitest'
import { createHostContext } from '../context'
-function nullHost(): DevToolsHost {
+function nullHost(): DevframeHost {
return {
mountStatic: () => { /* no-op */ },
resolveOrigin: () => 'http://localhost:0',
diff --git a/packages/devframe/src/node/__tests__/rpc-streaming.test.ts b/packages/devframe/src/node/__tests__/rpc-streaming.test.ts
index e5e95db..48f7727 100644
--- a/packages/devframe/src/node/__tests__/rpc-streaming.test.ts
+++ b/packages/devframe/src/node/__tests__/rpc-streaming.test.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions } from 'devframe/types'
+import type { DevframeNodeContext, DevframeRpcClientFunctions, DevframeRpcServerFunctions } from 'devframe/types'
import { AsyncLocalStorage } from 'node:async_hooks'
import { createRpcStreamingClientHost } from 'devframe/client'
import { createRpcClient } from 'devframe/rpc/client'
@@ -24,12 +24,12 @@ interface Harness {
async function bootHost(): Promise {
const port = allocatePort()
- const mockContext = {} as DevToolsNodeContext
+ const mockContext = {} as DevframeNodeContext
const rpcHost = new RpcFunctionsHost(mockContext)
const asyncStorage = new AsyncLocalStorage()
- const rpcGroup = createRpcServer(
+ const rpcGroup = createRpcServer(
rpcHost.functions,
{
rpcOptions: {
@@ -70,13 +70,13 @@ async function bootHost(): Promise {
}
interface FakeClient {
- rpc: ReturnType>
+ rpc: ReturnType>
streaming: ReturnType
close: () => void
}
function bootClient(port: number): FakeClient {
- // Mimic the minimal `DevToolsRpcClient` surface that
+ // Mimic the minimal `DevframeRpcClient` surface that
// `createRpcStreamingClientHost` uses (events, isTrusted, callEvent,
// client.register).
const listeners = new Set<(trusted: boolean) => void>()
@@ -97,7 +97,7 @@ function bootClient(port: number): FakeClient {
},
}
- const rpc = createRpcClient(
+ const rpc = createRpcClient(
clientFns,
{
channel: createWsRpcChannel({ url: `ws://127.0.0.1:${port}` }),
diff --git a/packages/devframe/src/node/__tests__/storage.test.ts b/packages/devframe/src/node/__tests__/storage.test.ts
index 8fbf93f..49f511b 100644
--- a/packages/devframe/src/node/__tests__/storage.test.ts
+++ b/packages/devframe/src/node/__tests__/storage.test.ts
@@ -10,7 +10,7 @@ function wait(ms: number) {
describe('createStorage', () => {
it('falls back to initial value when persisted JSON is invalid', async () => {
- const dir = fs.mkdtempSync(join(os.tmpdir(), 'vite-devtools-storage-'))
+ const dir = fs.mkdtempSync(join(os.tmpdir(), 'devframe-storage-'))
const filepath = join(dir, 'state.json')
fs.writeFileSync(filepath, '{invalid json', 'utf-8')
diff --git a/packages/devframe/src/node/auth/revoke.ts b/packages/devframe/src/node/auth/revoke.ts
index 23208b1..bd4b309 100644
--- a/packages/devframe/src/node/auth/revoke.ts
+++ b/packages/devframe/src/node/auth/revoke.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext } from 'devframe/types'
+import type { DevframeNodeContext } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import type { RpcFunctionsHost } from '../host-functions'
import type { InternalAnonymousAuthStorage } from '../internal/context'
@@ -10,7 +10,7 @@ import type { InternalAnonymousAuthStorage } from '../internal/context'
* Shared between persisted-auth revocation and remote-dock token revocation.
*/
export async function revokeActiveConnectionsForToken(
- context: DevToolsNodeContext,
+ context: DevframeNodeContext,
token: string,
): Promise {
const rpcHost = context.rpc as unknown as RpcFunctionsHost | undefined
@@ -41,7 +41,7 @@ export async function revokeActiveConnectionsForToken(
* using this token that they are no longer trusted.
*/
export async function revokeAuthToken(
- context: DevToolsNodeContext,
+ context: DevframeNodeContext,
storage: SharedState,
token: string,
): Promise {
diff --git a/packages/devframe/src/node/auth/state.ts b/packages/devframe/src/node/auth/state.ts
index f6cd43c..39015ec 100644
--- a/packages/devframe/src/node/auth/state.ts
+++ b/packages/devframe/src/node/auth/state.ts
@@ -1,11 +1,11 @@
-import type { DevToolsNodeRpcSession } from 'devframe/types'
+import type { DevframeNodeRpcSession } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import type { InternalAnonymousAuthStorage } from '../internal/context'
import { humanId } from 'devframe/utils/human-id'
export interface PendingAuthRequest {
clientAuthToken: string
- session: DevToolsNodeRpcSession
+ session: DevframeNodeRpcSession
ua: string
origin: string
resolve: (result: { isTrusted: boolean }) => void
diff --git a/packages/devframe/src/node/context.ts b/packages/devframe/src/node/context.ts
index e780f92..00dca4e 100644
--- a/packages/devframe/src/node/context.ts
+++ b/packages/devframe/src/node/context.ts
@@ -1,18 +1,18 @@
import type { RpcFunctionDefinitionAny } from 'devframe/rpc'
-import type { DevToolsHost, DevToolsNodeContext } from 'devframe/types'
+import type { DevframeHost, DevframeNodeContext } from 'devframe/types'
import { diagnostics as rpcDiagnostics } from '../rpc/diagnostics'
import { diagnostics as devframeDiagnostics } from './diagnostics'
-import { DevToolsAgentHost } from './host-agent'
-import { DevToolsDiagnosticsHost } from './host-diagnostics'
+import { DevframeAgentHost } from './host-agent'
+import { DevframeDiagnosticsHost } from './host-diagnostics'
import { RpcFunctionsHost } from './host-functions'
-import { DevToolsViewHost } from './host-views'
+import { DevframeViewHost } from './host-views'
import { BUILTIN_AGENT_RPC } from './rpc'
export interface CreateHostContextOptions {
cwd: string
workspaceRoot?: string
mode: 'dev' | 'build'
- host: DevToolsHost
+ host: DevframeHost
/**
* Built-in RPC declarations to register on the host. Framework
* adapters (vite, rolldown, cli) can pass the ones they need; the
@@ -22,17 +22,17 @@ export interface CreateHostContextOptions {
}
/**
- * Framework- and build-tool-agnostic core of the DevTools node context.
+ * Framework- and build-tool-agnostic core of the Devframe node context.
* Wires the RPC host, view (HTTP file-serving) host, diagnostics, and
* agent subsystems. Host adapters can wrap this to augment `ctx` with
* extra surfaces — for example, `@vitejs/devtools-kit`'s
* `createKitContext` attaches `docks`, `terminals`, `messages`,
* `commands`, and `createJsonRenderer` when mounted into Vite DevTools.
*/
-export async function createHostContext(options: CreateHostContextOptions): Promise {
+export async function createHostContext(options: CreateHostContextOptions): Promise {
const { cwd, workspaceRoot = cwd, mode, host, builtinRpcDeclarations = [] } = options
- const context: DevToolsNodeContext = {
+ const context: DevframeNodeContext = {
cwd,
workspaceRoot,
mode,
@@ -41,11 +41,11 @@ export async function createHostContext(options: CreateHostContextOptions): Prom
views: undefined!,
diagnostics: undefined!,
agent: undefined!,
- } as unknown as DevToolsNodeContext
+ } as unknown as DevframeNodeContext
const rpcHost = new RpcFunctionsHost(context)
- const viewsHost = new DevToolsViewHost(context)
- const diagnosticsHost = new DevToolsDiagnosticsHost(context, [devframeDiagnostics, rpcDiagnostics])
+ const viewsHost = new DevframeViewHost(context)
+ const diagnosticsHost = new DevframeDiagnosticsHost(context, [devframeDiagnostics, rpcDiagnostics])
context.rpc = rpcHost
context.views = viewsHost
context.diagnostics = diagnosticsHost
@@ -53,7 +53,7 @@ export async function createHostContext(options: CreateHostContextOptions): Prom
// Agent host must be constructed after `rpcHost` so it can subscribe
// to `onChanged` — it auto-discovers RPC functions flagged with
// the `agent` field.
- const agentHost = new DevToolsAgentHost(context)
+ const agentHost = new DevframeAgentHost(context)
context.agent = agentHost
// Auto-register devframe's own agent introspection RPCs. These power
diff --git a/packages/devframe/src/node/diagnostics.ts b/packages/devframe/src/node/diagnostics.ts
index 0feadf2..ac1cbce 100644
--- a/packages/devframe/src/node/diagnostics.ts
+++ b/packages/devframe/src/node/diagnostics.ts
@@ -9,7 +9,7 @@ export const diagnostics = defineDiagnostics({
why: (p: { name: string }) => `RPC function "${p.name}" is not registered`,
},
DF0007: {
- why: 'AsyncLocalStorage is not set, it likely to be an internal bug of the DevTools foundation',
+ why: 'AsyncLocalStorage is not set, it likely to be an internal bug of the Devframe foundation',
},
DF0008: {
why: (p: { distDir: string }) => `distDir ${p.distDir} does not exist`,
diff --git a/packages/devframe/src/node/host-agent.ts b/packages/devframe/src/node/host-agent.ts
index da7d074..5ccb644 100644
--- a/packages/devframe/src/node/host-agent.ts
+++ b/packages/devframe/src/node/host-agent.ts
@@ -7,9 +7,9 @@ import type {
AgentResourceInput,
AgentTool,
AgentToolInput,
- DevToolsAgentHostEvents,
- DevToolsAgentHost as DevToolsAgentHostType,
- DevToolsNodeContext,
+ DevframeAgentHostEvents,
+ DevframeAgentHost as DevframeAgentHostType,
+ DevframeNodeContext,
EventEmitter,
RpcFunctionAgentOptions,
} from 'devframe/types'
@@ -34,15 +34,15 @@ interface RegisteredResource {
*
* @experimental
*/
-export class DevToolsAgentHost implements DevToolsAgentHostType {
- public readonly events: EventEmitter = createEventEmitter()
+export class DevframeAgentHost implements DevframeAgentHostType {
+ public readonly events: EventEmitter = createEventEmitter()
private readonly tools = new Map()
private readonly resources = new Map()
private _rpcUnsubscribe: (() => void) | undefined
constructor(
- public readonly context: DevToolsNodeContext,
+ public readonly context: DevframeNodeContext,
) {
// Watch the RPC host for new `agent`-flagged definitions.
this._rpcUnsubscribe = context.rpc.onChanged(() => {
@@ -205,7 +205,7 @@ export class DevToolsAgentHost implements DevToolsAgentHostType {
return out
}
- private _findRpcDefinition(id: string): RpcFunctionDefinitionAnyWithContext | undefined {
+ private _findRpcDefinition(id: string): RpcFunctionDefinitionAnyWithContext | undefined {
const def = this.context.rpc.definitions.get(id)
if (def?.agent)
return def
@@ -214,7 +214,7 @@ export class DevToolsAgentHost implements DevToolsAgentHostType {
private _coercePositionalArgs(
args: unknown,
- def: RpcFunctionDefinitionAnyWithContext,
+ def: RpcFunctionDefinitionAnyWithContext,
): unknown[] {
if (Array.isArray(args))
return args
diff --git a/packages/devframe/src/node/host-diagnostics.ts b/packages/devframe/src/node/host-diagnostics.ts
index 4485317..7b5c69a 100644
--- a/packages/devframe/src/node/host-diagnostics.ts
+++ b/packages/devframe/src/node/host-diagnostics.ts
@@ -1,24 +1,24 @@
-import type { DevToolsDiagnosticsHost as DevToolsDiagnosticsHostType, DevToolsDiagnosticsLogger, DevToolsNodeContext } from 'devframe/types'
+import type { DevframeDiagnosticsHost as DevframeDiagnosticsHostType, DevframeDiagnosticsLogger, DevframeNodeContext } from 'devframe/types'
import { defineDiagnostics } from 'nostics'
import { devframeReporter } from '../utils/diagnostics-reporter'
-export class DevToolsDiagnosticsHost implements DevToolsDiagnosticsHostType {
+export class DevframeDiagnosticsHost implements DevframeDiagnosticsHostType {
private _registry: Record = {}
- readonly logger: DevToolsDiagnosticsLogger = new Proxy({} as DevToolsDiagnosticsLogger, {
+ readonly logger: DevframeDiagnosticsLogger = new Proxy({} as DevframeDiagnosticsLogger, {
get: (_, code: string) => this._registry[code],
})
- readonly defineDiagnostics: DevToolsDiagnosticsHostType['defineDiagnostics'] = (opts) => {
+ readonly defineDiagnostics: DevframeDiagnosticsHostType['defineDiagnostics'] = (opts) => {
const merged = {
...opts,
reporters: [devframeReporter, ...(opts.reporters ?? [])],
} as Parameters[0]
- return defineDiagnostics(merged) as ReturnType
+ return defineDiagnostics(merged) as ReturnType
}
constructor(
- public readonly context: DevToolsNodeContext,
+ public readonly context: DevframeNodeContext,
initialDefinitions: Array> = [],
) {
for (const d of initialDefinitions)
diff --git a/packages/devframe/src/node/host-functions.ts b/packages/devframe/src/node/host-functions.ts
index e0a3ee6..6c5b8e3 100644
--- a/packages/devframe/src/node/host-functions.ts
+++ b/packages/devframe/src/node/host-functions.ts
@@ -1,5 +1,5 @@
import type { BirpcGroup } from 'birpc'
-import type { DevToolsNodeContext, DevToolsNodeRpcSession, DevToolsNodeRpcSessionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, RpcBroadcastOptions, RpcFunctionsHost as RpcFunctionsHostType, RpcSharedStateHost, RpcStreamingHost } from 'devframe/types'
+import type { DevframeNodeContext, DevframeNodeRpcSession, DevframeNodeRpcSessionMeta, DevframeRpcClientFunctions, DevframeRpcServerFunctions, RpcBroadcastOptions, RpcFunctionsHost as RpcFunctionsHostType, RpcSharedStateHost, RpcStreamingHost } from 'devframe/types'
import type { AsyncLocalStorage } from 'node:async_hooks'
import { RpcFunctionsCollectorBase } from 'devframe/rpc'
import { createDebug } from 'obug'
@@ -7,16 +7,16 @@ import { diagnostics } from './diagnostics'
import { createRpcSharedStateServerHost } from './rpc-shared-state'
import { createRpcStreamingServerHost } from './rpc-streaming'
-const debugBroadcast = createDebug('vite:devtools:rpc:broadcast')
+const debugBroadcast = createDebug('devframe:rpc:broadcast')
-export class RpcFunctionsHost extends RpcFunctionsCollectorBase implements RpcFunctionsHostType {
+export class RpcFunctionsHost extends RpcFunctionsCollectorBase implements RpcFunctionsHostType {
/**
* @internal
*/
- _rpcGroup: BirpcGroup = undefined!
- _asyncStorage: AsyncLocalStorage = undefined!
+ _rpcGroup: BirpcGroup = undefined!
+ _asyncStorage: AsyncLocalStorage = undefined!
- constructor(context: DevToolsNodeContext) {
+ constructor(context: DevframeNodeContext) {
super(context)
this.sharedState = createRpcSharedStateServerHost(this)
@@ -33,30 +33,30 @@ export class RpcFunctionsHost extends RpcFunctionsCollectorBase,
+ T extends keyof DevframeRpcServerFunctions,
+ Args extends Parameters,
>(
method: T,
...args: Args
- ): Promise>> {
+ ): Promise>> {
if (!this.definitions.has(method as string)) {
throw diagnostics.DF0006({ name: String(method) })
}
const handler = await this.getHandler(method)
return await Promise.resolve(
- (handler as (...args: Args) => ReturnType)(...args),
- ) as Awaited>
+ (handler as (...args: Args) => ReturnType)(...args),
+ ) as Awaited>
}
async broadcast<
- T extends keyof DevToolsRpcClientFunctions,
- Args extends Parameters,
+ T extends keyof DevframeRpcClientFunctions,
+ Args extends Parameters,
>(
options: RpcBroadcastOptions,
): Promise {
@@ -78,7 +78,7 @@ export class RpcFunctionsHost extends RpcFunctionsCollectorBase void | Promise
/**
* Namespace for storage paths returned by `getStorageDir`. Workspace
- * state lives under `${workspaceRoot}/node_modules/./devtools/`
- * and global state under `${homedir()}/./devtools/`. Pick the
+ * state lives under `${workspaceRoot}/node_modules/./devframe/`
+ * and global state under `${homedir()}/./devframe/`. Pick the
* devtool's id (or another stable, filesystem-safe identifier) so the
* standalone host doesn't collide with other tools' storage.
*/
@@ -33,9 +33,9 @@ export interface CreateH3DevToolsHostOptions {
}
/**
- * h3-backed {@link DevToolsHost} — used by the standalone CLI adapter.
+ * h3-backed {@link DevframeHost} — used by the standalone CLI adapter.
*/
-export function createH3DevToolsHost(options: CreateH3DevToolsHostOptions): DevToolsHost {
+export function createH3DevframeHost(options: CreateH3DevframeHostOptions): DevframeHost {
const workspaceRoot = options.workspaceRoot ?? process.cwd()
return {
mountStatic(base, distDir) {
@@ -45,7 +45,7 @@ export function createH3DevToolsHost(options: CreateH3DevToolsHostOptions): DevT
return options.origin
},
getStorageDir(scope) {
- const namespace = `.${options.appName}/devtools`
+ const namespace = `.${options.appName}/devframe`
return scope === 'workspace'
? join(workspaceRoot, 'node_modules', namespace)
: join(homedir(), namespace)
diff --git a/packages/devframe/src/node/host-views.ts b/packages/devframe/src/node/host-views.ts
index 7226b62..dcf704e 100644
--- a/packages/devframe/src/node/host-views.ts
+++ b/packages/devframe/src/node/host-views.ts
@@ -1,15 +1,15 @@
-import type { DevToolsNodeContext, DevToolsViewHost as DevToolsViewHostType } from 'devframe/types'
+import type { DevframeNodeContext, DevframeViewHost as DevframeViewHostType } from 'devframe/types'
import { existsSync } from 'node:fs'
import { diagnostics } from './diagnostics'
-export class DevToolsViewHost implements DevToolsViewHostType {
+export class DevframeViewHost implements DevframeViewHostType {
/**
* @internal
*/
public buildStaticDirs: { baseUrl: string, distDir: string }[] = []
constructor(
- public readonly context: DevToolsNodeContext,
+ public readonly context: DevframeNodeContext,
) {
}
diff --git a/packages/devframe/src/node/internal/context.ts b/packages/devframe/src/node/internal/context.ts
index 20e0081..43506b8 100644
--- a/packages/devframe/src/node/internal/context.ts
+++ b/packages/devframe/src/node/internal/context.ts
@@ -1,4 +1,4 @@
-import type { DevToolsNodeContext } from 'devframe/types'
+import type { DevframeNodeContext } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import { humanId } from 'devframe/utils/human-id'
import { join } from 'pathe'
@@ -21,7 +21,7 @@ export interface RemoteTokenRecord {
originLock: boolean
}
-export interface DevToolsInternalContext {
+export interface DevframeInternalContext {
storage: {
auth: SharedState
}
@@ -55,9 +55,9 @@ export interface DevToolsInternalContext {
}
}
-export const internalContextMap = new WeakMap()
+export const internalContextMap = new WeakMap()
-export function getInternalContext(context: DevToolsNodeContext): DevToolsInternalContext {
+export function getInternalContext(context: DevframeNodeContext): DevframeInternalContext {
if (!internalContextMap.has(context)) {
const storage = createStorage({
filepath: join(context.host.getStorageDir('global'), 'auth.json'),
@@ -73,7 +73,7 @@ export function getInternalContext(context: DevToolsNodeContext): DevToolsIntern
void revokeActiveConnectionsForToken(context, token)
}
- const internalContext: DevToolsInternalContext = {
+ const internalContext: DevframeInternalContext = {
storage: {
auth: storage,
},
diff --git a/packages/devframe/src/node/internal/index.ts b/packages/devframe/src/node/internal/index.ts
index e3c0234..3b119ec 100644
--- a/packages/devframe/src/node/internal/index.ts
+++ b/packages/devframe/src/node/internal/index.ts
@@ -20,7 +20,7 @@ export {
} from './context'
export type {
- DevToolsInternalContext,
+ DevframeInternalContext,
InternalAnonymousAuthStorage,
RemoteTokenRecord,
} from './context'
diff --git a/packages/devframe/src/node/rpc-shared-state.ts b/packages/devframe/src/node/rpc-shared-state.ts
index 329862b..ef4b2da 100644
--- a/packages/devframe/src/node/rpc-shared-state.ts
+++ b/packages/devframe/src/node/rpc-shared-state.ts
@@ -1,11 +1,11 @@
-import type { DevToolsRpcSharedStates, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost } from 'devframe/types'
+import type { DevframeRpcSharedStates, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost } from 'devframe/types'
import type { SharedState, SharedStatePatch } from 'devframe/utils/shared-state'
import { createSharedState } from 'devframe/utils/shared-state'
import { createDebug } from 'obug'
import { diagnostics } from './diagnostics'
-const debug = createDebug('vite:devtools:rpc:state:changed')
-const debugSubscribe = createDebug('vite:devtools:rpc:state:subscribe')
+const debug = createDebug('devframe:rpc:state:changed')
+const debugSubscribe = createDebug('devframe:rpc:state:subscribe')
export function createRpcSharedStateServerHost(
rpc: RpcFunctionsHost,
@@ -97,7 +97,7 @@ export function createRpcSharedStateServerHost(
handler: async (key: string) => {
if (!sharedState.has(key))
return undefined
- const state = await host.get(key as keyof DevToolsRpcSharedStates)
+ const state = await host.get(key as keyof DevframeRpcSharedStates)
return state.value()
},
// Pre-compute snapshots for the build-mode static dump so the SPA
@@ -111,7 +111,7 @@ export function createRpcSharedStateServerHost(
name: 'devframe:rpc:server-state:set',
type: 'query',
handler: async (key: string, value: any, syncId: string) => {
- const state = await host.get(key as keyof DevToolsRpcSharedStates, {
+ const state = await host.get(key as keyof DevframeRpcSharedStates, {
initialValue: value,
})
state.mutate(() => value, syncId)
@@ -124,7 +124,7 @@ export function createRpcSharedStateServerHost(
handler: async (key: string, patches: SharedStatePatch[], syncId: string) => {
if (!sharedState.has(key))
return
- const state = await host.get(key as keyof DevToolsRpcSharedStates)
+ const state = await host.get(key as keyof DevframeRpcSharedStates)
state.patch(patches, syncId)
},
})
diff --git a/packages/devframe/src/node/rpc-streaming.ts b/packages/devframe/src/node/rpc-streaming.ts
index 023775e..3e1d2d8 100644
--- a/packages/devframe/src/node/rpc-streaming.ts
+++ b/packages/devframe/src/node/rpc-streaming.ts
@@ -1,5 +1,5 @@
import type {
- DevToolsNodeRpcSessionMeta,
+ DevframeNodeRpcSessionMeta,
RpcFunctionsHost,
RpcStreamingChannel,
RpcStreamingChannelOptions,
@@ -10,7 +10,7 @@ import { createStreamReader, createStreamSink } from 'devframe/utils/streaming-c
import { createDebug } from 'obug'
import { diagnostics } from './diagnostics'
-const debug = createDebug('vite:devtools:rpc:streaming')
+const debug = createDebug('devframe:rpc:streaming')
const STREAM_KEY_SEPARATOR = '\x1F'
@@ -20,7 +20,7 @@ function streamKey(channel: string, id: string): string {
interface ServerStreamRecord {
sink: StreamSink
- subscribers: Set
+ subscribers: Set
unbinders: (() => void)[]
/** Timer scheduled when stream closes with no subscribers; cleared on resubscribe. */
retentionTimer?: ReturnType
@@ -29,7 +29,7 @@ interface ServerStreamRecord {
interface ServerInboundRecord {
reader: StreamReader
/** First session that wrote to this inbound — locks ownership for cleanup. */
- uploaderMeta?: DevToolsNodeRpcSessionMeta
+ uploaderMeta?: DevframeNodeRpcSessionMeta
}
interface ChannelState {
@@ -254,7 +254,7 @@ export function createRpcStreamingServerHost(rpc: RpcFunctionsHost): RpcStreamin
args: [name, sink.id, seq, chunk],
event: true,
optional: true,
- filter: client => record.subscribers.has(client.$meta as DevToolsNodeRpcSessionMeta),
+ filter: client => record.subscribers.has(client.$meta as DevframeNodeRpcSessionMeta),
})
}),
)
@@ -265,7 +265,7 @@ export function createRpcStreamingServerHost(rpc: RpcFunctionsHost): RpcStreamin
args: [name, sink.id, error],
event: true,
optional: true,
- filter: client => record.subscribers.has(client.$meta as DevToolsNodeRpcSessionMeta),
+ filter: client => record.subscribers.has(client.$meta as DevframeNodeRpcSessionMeta),
})
maybeFreeStream(state, sink.id)
}),
@@ -332,7 +332,7 @@ export function createRpcStreamingServerHost(rpc: RpcFunctionsHost): RpcStreamin
return {
create: createChannel,
- _onSessionDisconnected(meta: DevToolsNodeRpcSessionMeta) {
+ _onSessionDisconnected(meta: DevframeNodeRpcSessionMeta) {
// Outbound: drop subscriber, abort if last one drops.
if (meta.subscribedStreams) {
for (const key of meta.subscribedStreams) {
diff --git a/packages/devframe/src/node/rpc/index.ts b/packages/devframe/src/node/rpc/index.ts
index 7fd3f00..413ed6c 100644
--- a/packages/devframe/src/node/rpc/index.ts
+++ b/packages/devframe/src/node/rpc/index.ts
@@ -20,7 +20,7 @@ export const BUILTIN_AGENT_RPC = [
] as const
declare module 'devframe/types' {
- interface DevToolsRpcServerFunctions {
+ interface DevframeRpcServerFunctions {
'devframe:agent:list-tools': () => Promise
'devframe:agent:invoke-tool': (id: string, args: unknown) => Promise
'devframe:agent:list-resources': () => Promise
diff --git a/packages/devframe/src/node/server.ts b/packages/devframe/src/node/server.ts
index 4c81177..d4410cb 100644
--- a/packages/devframe/src/node/server.ts
+++ b/packages/devframe/src/node/server.ts
@@ -1,5 +1,5 @@
import type { BirpcGroup } from 'birpc'
-import type { DevToolsNodeContext, DevToolsNodeRpcSession, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions } from 'devframe/types'
+import type { DevframeNodeContext, DevframeNodeRpcSession, DevframeRpcClientFunctions, DevframeRpcServerFunctions } from 'devframe/types'
import type { WebSocketServer } from 'ws'
import type { RpcFunctionsHost } from './host-functions'
import { AsyncLocalStorage } from 'node:async_hooks'
@@ -11,7 +11,7 @@ import { WebSocketServer as WSServer } from 'ws'
import { getInternalContext } from './internal/context'
export interface StartHttpAndWsOptions {
- context: DevToolsNodeContext
+ context: DevframeNodeContext
host?: string
port: number
/**
@@ -45,7 +45,7 @@ export interface StartedServer {
port: number
app: H3
wss: WebSocketServer
- rpcGroup: BirpcGroup
+ rpcGroup: BirpcGroup
close: () => Promise
}
@@ -62,9 +62,9 @@ export async function startHttpAndWs(options: StartHttpAndWsOptions): Promise()
+ const asyncStorage = new AsyncLocalStorage()
- const rpcGroup = createRpcServer(
+ const rpcGroup = createRpcServer(
rpcHost.functions,
{
rpcOptions: {
@@ -103,15 +103,15 @@ export async function startHttpAndWs(options: StartHttpAndWsOptions): Promise {
const session = rpcHost.getCurrentRpcSession()
diff --git a/packages/devframe/src/rpc/dump/__tests__/static.test.ts b/packages/devframe/src/rpc/dump/__tests__/static.test.ts
index 1b2369a..1b1194a 100644
--- a/packages/devframe/src/rpc/dump/__tests__/static.test.ts
+++ b/packages/devframe/src/rpc/dump/__tests__/static.test.ts
@@ -1,5 +1,5 @@
import { defineRpcFunction } from 'devframe'
-import { DEVTOOLS_RPC_DUMP_DIRNAME } from 'devframe/constants'
+import { DEVFRAME_RPC_DUMP_DIRNAME } from 'devframe/constants'
import { strictJsonStringify } from 'devframe/rpc'
import { structuredCloneDeserialize, structuredCloneStringify } from 'devframe/utils/structured-clone'
import { describe, expect, it } from 'vitest'
@@ -15,7 +15,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getVersion], {})
- const expectedPath = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~json-version.static.json`
+ const expectedPath = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~json-version.static.json`
expect(result.manifest['test:json-version']).toEqual({
type: 'static',
@@ -33,7 +33,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getVersion], {})
- const expectedPath = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~get-version.static.json`
+ const expectedPath = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~get-version.static.json`
expect(result.manifest['test:get-version']).toEqual({
type: 'static',
@@ -63,7 +63,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getItem], {})
- const basePath = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~get-item`
+ const basePath = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~get-item`
const manifest = result.manifest['test:get-item'] as {
type: 'query'
records: Record
@@ -114,7 +114,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getGraph], {})
- const path = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~graph.static.json`
+ const path = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~graph.static.json`
const file = result.files[path]!
expect(file.serialization).toBe('structured-clone')
@@ -136,7 +136,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getMap], {})
- const path = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~roundtrip-map.static.json`
+ const path = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~roundtrip-map.static.json`
const file = result.files[path]!
// Server side: write to disk as sc-encoded text.
@@ -160,7 +160,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getEntries], {})
- const fallbackPath = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~entries.fallback.json`
+ const fallbackPath = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~entries.fallback.json`
const fallback = result.files[fallbackPath]!
expect(fallback.serialization).toBe('structured-clone')
@@ -188,7 +188,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getList], {})
- const path = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~json-list.static.json`
+ const path = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~json-list.static.json`
const file = result.files[path]!
expect(file.serialization).toBe('json')
@@ -207,7 +207,7 @@ describe('collectStaticRpcDump', () => {
})
const result = await collectStaticRpcDump([getMapJson], {})
- const path = `${DEVTOOLS_RPC_DUMP_DIRNAME}/test~bad-json.static.json`
+ const path = `${DEVFRAME_RPC_DUMP_DIRNAME}/test~bad-json.static.json`
const file = result.files[path]!
// collectStaticRpcDump records the value as-is; the strict
diff --git a/packages/devframe/src/rpc/dump/static.ts b/packages/devframe/src/rpc/dump/static.ts
index 2120d05..01b4723 100644
--- a/packages/devframe/src/rpc/dump/static.ts
+++ b/packages/devframe/src/rpc/dump/static.ts
@@ -1,6 +1,6 @@
import type { RpcDumpRecord, RpcFunctionDefinitionAny } from '../types'
import {
- DEVTOOLS_RPC_DUMP_DIRNAME,
+ DEVFRAME_RPC_DUMP_DIRNAME,
} from 'devframe/constants'
import { getRpcHandler } from '../handler'
import { dumpFunctions } from './collect'
@@ -48,15 +48,15 @@ function makeDumpKey(name: string): string {
}
function makeStaticPath(name: string): string {
- return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.static.json`
+ return `${DEVFRAME_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.static.json`
}
function makeQueryRecordPath(name: string, hash: string): string {
- return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.record.${hash}.json`
+ return `${DEVFRAME_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.record.${hash}.json`
}
function makeQueryFallbackPath(name: string): string {
- return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.fallback.json`
+ return `${DEVFRAME_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.fallback.json`
}
async function resolveRecord(record: RpcDumpRecord | (() => Promise)): Promise {
diff --git a/packages/devframe/src/rpc/transports/ws-client.ts b/packages/devframe/src/rpc/transports/ws-client.ts
index e246309..230ff94 100644
--- a/packages/devframe/src/rpc/transports/ws-client.ts
+++ b/packages/devframe/src/rpc/transports/ws-client.ts
@@ -30,7 +30,7 @@ const EMPTY_DEFS: ReadonlyMap>
- onConnected?: (ws: WebSocket, req: IncomingMessage, meta: DevToolsNodeRpcSessionMeta) => void
- onDisconnected?: (ws: WebSocket, meta: DevToolsNodeRpcSessionMeta) => void
+ onConnected?: (ws: WebSocket, req: IncomingMessage, meta: DevframeNodeRpcSessionMeta) => void
+ onDisconnected?: (ws: WebSocket, meta: DevframeNodeRpcSessionMeta) => void
/** Override the default per-call serializer. Most callers should leave this unset. */
serialize?: ChannelOptions['serialize']
/** Override the default per-call deserializer. Most callers should leave this unset. */
@@ -98,7 +98,7 @@ export function attachWsRpcTransport<
}
wss.on('connection', (ws, req) => {
- const meta: DevToolsNodeRpcSessionMeta = {
+ const meta: DevframeNodeRpcSessionMeta = {
id: sessionId++,
ws,
subscribedStates: new Set(),
diff --git a/packages/devframe/src/rpc/transports/ws.test.ts b/packages/devframe/src/rpc/transports/ws.test.ts
index 0d8370c..d84c52e 100644
--- a/packages/devframe/src/rpc/transports/ws.test.ts
+++ b/packages/devframe/src/rpc/transports/ws.test.ts
@@ -8,7 +8,7 @@ import { attachWsRpcTransport } from './ws-server'
vi.stubGlobal('WebSocket', WebSocket)
-describe('devtools rpc', () => {
+describe('devframe rpc', () => {
it('should work w/ ws transport', async () => {
const PORT = 3333
// Use 127.0.0.1 on both client and server so they agree on the
diff --git a/packages/devframe/src/types/agent.ts b/packages/devframe/src/types/agent.ts
index a966a3a..bb559bb 100644
--- a/packages/devframe/src/types/agent.ts
+++ b/packages/devframe/src/types/agent.ts
@@ -33,7 +33,7 @@ export interface AgentTool {
}
/**
- * Input accepted by `DevToolsAgentHost.registerTool()`. Handler is
+ * Input accepted by `DevframeAgentHost.registerTool()`. Handler is
* stripped from the serializable `AgentTool` projection.
*
* @experimental
@@ -53,7 +53,7 @@ export interface AgentToolInput {
/**
* Serializable description of an agent-readable resource. Resources
- * surface structured or textual snapshots of devtools state.
+ * surface structured or textual snapshots of devframe state.
*
* @experimental
*/
@@ -68,7 +68,7 @@ export interface AgentResource {
}
/**
- * Input accepted by `DevToolsAgentHost.registerResource()`.
+ * Input accepted by `DevframeAgentHost.registerResource()`.
*
* @experimental
*/
@@ -115,11 +115,11 @@ export interface AgentHandle {
}
/**
- * Events emitted by `DevToolsAgentHost`.
+ * Events emitted by `DevframeAgentHost`.
*
* @experimental
*/
-export interface DevToolsAgentHostEvents {
+export interface DevframeAgentHostEvents {
'agent:tool:registered': (tool: AgentTool) => void
'agent:tool:unregistered': (id: string) => void
'agent:resource:registered': (resource: AgentResource) => void
@@ -140,8 +140,8 @@ export interface DevToolsAgentHostEvents {
* @experimental The agent-native surface is experimental and may change
* without a major version bump until it stabilizes.
*/
-export interface DevToolsAgentHost {
- readonly events: EventEmitter
+export interface DevframeAgentHost {
+ readonly events: EventEmitter
/**
* Register a tool not backed by an RPC function. Use this when you
diff --git a/packages/devframe/src/types/context.ts b/packages/devframe/src/types/context.ts
index a5c4b71..2fa2761 100644
--- a/packages/devframe/src/types/context.ts
+++ b/packages/devframe/src/types/context.ts
@@ -1,9 +1,9 @@
-import type { DevToolsAgentHost } from './agent'
-import type { DevToolsDiagnosticsHost } from './diagnostics'
-import type { DevToolsHost } from './host'
-import type { DevToolsViewHost } from './views'
+import type { DevframeAgentHost } from './agent'
+import type { DevframeDiagnosticsHost } from './diagnostics'
+import type { DevframeHost } from './host'
+import type { DevframeViewHost } from './views'
-export interface DevToolsCapabilities {
+export interface DevframeCapabilities {
rpc?: boolean
views?: boolean
}
@@ -15,7 +15,7 @@ export interface DevToolsCapabilities {
* `createKitContext` adds `docks`, `terminals`, `messages`, and
* `commands` when mounted into Vite DevTools.
*/
-export interface DevToolsNodeContext {
+export interface DevframeNodeContext {
readonly workspaceRoot: string
readonly cwd: string
/**
@@ -37,20 +37,20 @@ export interface DevToolsNodeContext {
* Host runtime abstraction — exposes `mountStatic` / `resolveOrigin` /
* `getStorageDir`.
*/
- host: DevToolsHost
+ host: DevframeHost
rpc: import('./rpc').RpcFunctionsHost
- views: DevToolsViewHost
+ views: DevframeViewHost
/**
* Structured diagnostics host — wraps `nostics` and lets integrations
* register their own coded errors/warnings into the shared lookup.
*/
- diagnostics: DevToolsDiagnosticsHost
+ diagnostics: DevframeDiagnosticsHost
/**
* Agent host — aggregates the agent-exposed surface of this devtool.
*
* @experimental
*/
- agent: DevToolsAgentHost
+ agent: DevframeAgentHost
}
export interface ConnectionMeta {
diff --git a/packages/devframe/src/types/devframe.ts b/packages/devframe/src/types/devframe.ts
index a1e8a28..a95906d 100644
--- a/packages/devframe/src/types/devframe.ts
+++ b/packages/devframe/src/types/devframe.ts
@@ -1,6 +1,6 @@
import type { CAC } from 'cac'
import type { CliFlagsSchema } from '../adapters/flags'
-import type { DevToolsNodeContext } from './context'
+import type { DevframeNodeContext } from './context'
export type DevframeRuntime = 'cli' | 'build' | 'spa' | 'vite' | 'embedded'
@@ -120,7 +120,7 @@ export interface DevframeDefinition {
spa?: boolean | Record
}
/** Server-side setup — the primary entrypoint. Runs in every runtime. */
- setup: (ctx: DevToolsNodeContext, info?: DevframeSetupInfo) => void | Promise
+ setup: (ctx: DevframeNodeContext, info?: DevframeSetupInfo) => void | Promise
/** Browser-only setup for the SPA adapter (bundled into the client). */
setupBrowser?: (ctx: DevframeBrowserContext) => void | Promise
cli?: DevframeCliOptions
diff --git a/packages/devframe/src/types/diagnostics.ts b/packages/devframe/src/types/diagnostics.ts
index 7ad491b..03d4815 100644
--- a/packages/devframe/src/types/diagnostics.ts
+++ b/packages/devframe/src/types/diagnostics.ts
@@ -7,7 +7,7 @@ import type { defineDiagnostics, Diagnostic, DiagnosticDefinition } from 'nostic
* types don't allow assigning a narrow-keyed result to a generically-keyed
* one. The host stores them in a heterogeneous registry.
*/
-export type DevToolsDiagnosticsDefinition = ReturnType>
+export type DevframeDiagnosticsDefinition = ReturnType>
/**
* The shared diagnostics lookup exposed by the host. A `Proxy` that resolves
@@ -16,14 +16,14 @@ export type DevToolsDiagnosticsDefinition = ReturnType
+export type DevframeDiagnosticsLogger = Record
/**
* Options accepted by the host's `defineDiagnostics()` factory — mirrors
* `nostics`'s shape but the host pre-wires its ANSI console reporter, so
* plugins typically omit `reporters`.
*/
-export interface DevToolsDefineDiagnosticsOptions> {
+export interface DevframeDefineDiagnosticsOptions> {
docsBase?: string | ((code: keyof Codes) => string | undefined)
codes: Codes
reporters?: ReadonlyArray<(d: Diagnostic, o?: any) => void>
@@ -52,7 +52,7 @@ export interface DevToolsDefineDiagnosticsOptions>(
- options: DevToolsDefineDiagnosticsOptions,
+ options: DevframeDefineDiagnosticsOptions,
) => ReturnType>
}
diff --git a/packages/devframe/src/types/host.ts b/packages/devframe/src/types/host.ts
index 70e5cd8..686f311 100644
--- a/packages/devframe/src/types/host.ts
+++ b/packages/devframe/src/types/host.ts
@@ -1,4 +1,4 @@
-// DevToolsHost — abstraction over the runtime that serves the DevTools
+// DevframeHost — abstraction over the runtime that serves the Devframe
// UI and RPC endpoints (Vite dev server, standalone h3 CLI server, static
// snapshot, embedded, etc.).
//
@@ -8,10 +8,10 @@
// - packages/devframe/src/node/host-h3.ts — h3 CLI server
// - (build/spa/embedded) — added as the respective adapters land
-export interface DevToolsHost {
+export interface DevframeHost {
/**
* Serve a static directory at the given URL base. Called by
- * `DevToolsViewHost.hostStatic`. Implementations map this to whatever
+ * `DevframeViewHost.hostStatic`. Implementations map this to whatever
* the underlying runtime expects (Vite middleware, h3 handler, no-op
* for build snapshots).
*/
@@ -27,16 +27,16 @@ export interface DevToolsHost {
resolveOrigin: () => string
/**
- * Resolve a directory the host owns for persisted devtools state.
+ * Resolve a directory the host owns for persisted devframe state.
* Each host picks its own app-name namespace so storage doesn't
- * collide between, say, the Vite host (`.vite/devtools`) and a
- * standalone CLI host (`./devtools`).
+ * collide between, say, the Vite host (`.vite/devframe`) and a
+ * standalone CLI host (`./devframe`).
*
* - `workspace` — per-project state (settings, caches). Typically
- * under `${workspaceRoot}/node_modules/./devtools/`.
+ * under `${workspaceRoot}/node_modules/./devframe/`.
* - `global` — per-user state (auth tokens, machine-wide
* preferences). Typically under
- * `${homedir()}/./devtools/`.
+ * `${homedir()}/./devframe/`.
*
* Implementations should ensure the directory exists or be safe to
* pass to a downstream `createStorage(...)` call that creates it
diff --git a/packages/devframe/src/types/rpc-augments.ts b/packages/devframe/src/types/rpc-augments.ts
index 8d4d5e5..9403d08 100644
--- a/packages/devframe/src/types/rpc-augments.ts
+++ b/packages/devframe/src/types/rpc-augments.ts
@@ -1,7 +1,7 @@
/**
* To be extended
*/
-export interface DevToolsRpcClientFunctions {
+export interface DevframeRpcClientFunctions {
/**
* Streaming chunk pushed from server to subscribed clients. Wired by
* `RpcStreamingHost`; do not register manually.
@@ -28,7 +28,7 @@ export interface DevToolsRpcClientFunctions {
/**
* To be extended
*/
-export interface DevToolsRpcServerFunctions {
+export interface DevframeRpcServerFunctions {
/**
* Subscribe a client to a shared-state key. Wired by
* `RpcSharedStateHost`; do not register manually.
@@ -97,4 +97,4 @@ export interface DevToolsRpcServerFunctions {
/**
* To be extended
*/
-export interface DevToolsRpcSharedStates {}
+export interface DevframeRpcSharedStates {}
diff --git a/packages/devframe/src/types/rpc.ts b/packages/devframe/src/types/rpc.ts
index caed6a1..643eb9d 100644
--- a/packages/devframe/src/types/rpc.ts
+++ b/packages/devframe/src/types/rpc.ts
@@ -1,16 +1,16 @@
import type { BirpcReturn } from 'birpc'
import type { RpcFunctionsCollectorBase } from 'devframe/rpc'
-import type { DevToolsNodeRpcSessionMeta } from 'devframe/rpc/transports/ws-server'
+import type { DevframeNodeRpcSessionMeta } from 'devframe/rpc/transports/ws-server'
import type { SharedState } from 'devframe/utils/shared-state'
import type { StreamReader, StreamSink } from 'devframe/utils/streaming-channel'
-import type { DevToolsNodeContext } from './context'
-import type { DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, DevToolsRpcSharedStates } from './rpc-augments'
+import type { DevframeNodeContext } from './context'
+import type { DevframeRpcClientFunctions, DevframeRpcServerFunctions, DevframeRpcSharedStates } from './rpc-augments'
-export type { DevToolsNodeRpcSessionMeta }
+export type { DevframeNodeRpcSessionMeta }
-export interface DevToolsNodeRpcSession {
- meta: DevToolsNodeRpcSessionMeta
- rpc: BirpcReturn
+export interface DevframeNodeRpcSession {
+ meta: DevframeNodeRpcSessionMeta
+ rpc: BirpcReturn
}
export interface RpcBroadcastOptions {
@@ -18,29 +18,29 @@ export interface RpcBroadcastOptions {
args: Args
optional?: boolean
event?: boolean
- filter?: (client: BirpcReturn) => boolean | void
+ filter?: (client: BirpcReturn) => boolean | void
}
-export type RpcFunctionsHost = RpcFunctionsCollectorBase & {
+export type RpcFunctionsHost = RpcFunctionsCollectorBase & {
/**
* Invoke a locally registered server RPC function directly.
*
* This bypasses transport and is useful for server-side cross-function calls.
*/
invokeLocal: <
- T extends keyof DevToolsRpcServerFunctions,
- Args extends Parameters,
+ T extends keyof DevframeRpcServerFunctions,
+ Args extends Parameters,
>(
method: T,
...args: Args
- ) => Promise>>
+ ) => Promise>>
/**
* Broadcast a message to all connected clients
*/
broadcast: <
- T extends keyof DevToolsRpcClientFunctions,
- Args extends Parameters,
+ T extends keyof DevframeRpcClientFunctions,
+ Args extends Parameters,
>(
options: RpcBroadcastOptions,
) => Promise
@@ -50,7 +50,7 @@ export type RpcFunctionsHost = RpcFunctionsCollectorBase DevToolsNodeRpcSession | undefined
+ getCurrentRpcSession: () => DevframeNodeRpcSession | undefined
/**
* The shared state host
@@ -72,7 +72,7 @@ export interface RpcSharedStateGetOptions {
}
export interface RpcSharedStateHost {
- get: (key: T, options?: RpcSharedStateGetOptions) => Promise>
+ get: (key: T, options?: RpcSharedStateGetOptions) => Promise>
keys: () => string[]
/**
* Subscribe to new shared-state keys becoming available. Fires when
@@ -180,5 +180,5 @@ export interface RpcStreamingHost {
*
* @internal
*/
- _onSessionDisconnected: (meta: DevToolsNodeRpcSessionMeta) => void
+ _onSessionDisconnected: (meta: DevframeNodeRpcSessionMeta) => void
}
diff --git a/packages/devframe/src/types/views.ts b/packages/devframe/src/types/views.ts
index 42ae967..111a90f 100644
--- a/packages/devframe/src/types/views.ts
+++ b/packages/devframe/src/types/views.ts
@@ -1,4 +1,4 @@
-export interface DevToolsViewHost {
+export interface DevframeViewHost {
/**
* @internal
*/
diff --git a/packages/devframe/test/dts-dedupe.test.ts b/packages/devframe/test/dts-dedupe.test.ts
index c96cbbc..0df727e 100644
--- a/packages/devframe/test/dts-dedupe.test.ts
+++ b/packages/devframe/test/dts-dedupe.test.ts
@@ -6,9 +6,9 @@ import { describe, expect, it } from 'vitest'
const distRoot = fileURLToPath(new URL('../dist/', import.meta.url))
const AUGMENTABLE_INTERFACES = [
- 'DevToolsRpcClientFunctions',
- 'DevToolsRpcServerFunctions',
- 'DevToolsRpcSharedStates',
+ 'DevframeRpcClientFunctions',
+ 'DevframeRpcServerFunctions',
+ 'DevframeRpcSharedStates',
] as const
describe('rpc-augments dedupe', () => {
diff --git a/packages/hub/src/client/client-script.ts b/packages/hub/src/client/client-script.ts
index 6f90555..86d2f8f 100644
--- a/packages/hub/src/client/client-script.ts
+++ b/packages/hub/src/client/client-script.ts
@@ -1,4 +1,4 @@
-import type { DevToolsMessagesClient } from '../types/messages'
+import type { DevframeMessagesClient } from '../types/messages'
import type { DockEntryState, DocksContext } from './docks'
/**
@@ -12,5 +12,5 @@ export interface DockClientScriptContext extends DocksContext {
/**
* Messages client scoped to this dock entry's source
*/
- messages: DevToolsMessagesClient
+ messages: DevframeMessagesClient
}
diff --git a/packages/hub/src/client/context.ts b/packages/hub/src/client/context.ts
index 88701e0..df79766 100644
--- a/packages/hub/src/client/context.ts
+++ b/packages/hub/src/client/context.ts
@@ -1,11 +1,11 @@
-import type { DevToolsClientContext } from './docks'
+import type { DevframeClientContext } from './docks'
const CLIENT_CONTEXT_KEY = '__DEVFRAME_HUB_CLIENT_CONTEXT__'
/**
- * Get the global DevTools client context, or `undefined` if not yet initialized.
+ * Get the global Devframe client context, or `undefined` if not yet initialized.
*/
-export function getDevToolsClientContext(): DevToolsClientContext | undefined {
+export function getDevframeClientContext(): DevframeClientContext | undefined {
return (globalThis as any)[CLIENT_CONTEXT_KEY]
}
diff --git a/packages/hub/src/client/docks.ts b/packages/hub/src/client/docks.ts
index 9ae4391..a5d217c 100644
--- a/packages/hub/src/client/docks.ts
+++ b/packages/hub/src/client/docks.ts
@@ -1,12 +1,12 @@
-import type { DevToolsRpcContext } from 'devframe/client'
+import type { DevframeRpcContext } from 'devframe/client'
import type { EventEmitter } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import type { WhenContext } from 'devframe/utils/when'
-import type { DevToolsClientCommand, DevToolsCommandEntry, DevToolsCommandKeybinding } from '../types/commands'
-import type { DevToolsDockEntriesGrouped, DevToolsDockEntry, DevToolsDockUserEntry } from '../types/docks'
-import type { DevToolsDocksUserSettings } from '../types/settings'
+import type { DevframeClientCommand, DevframeCommandEntry, DevframeCommandKeybinding } from '../types/commands'
+import type { DevframeDockEntriesGrouped, DevframeDockEntry, DevframeDockUserEntry } from '../types/docks'
+import type { DevframeDocksUserSettings } from '../types/settings'
-export type { DevToolsClientRpcHost, RpcClientEvents } from 'devframe/client'
+export type { DevframeClientRpcHost, RpcClientEvents } from 'devframe/client'
export interface DockPanelStorage {
mode: 'float' | 'edge'
@@ -21,7 +21,7 @@ export interface DockPanelStorage {
export type DockClientType = 'embedded' | 'standalone'
-export interface DocksContext extends DevToolsRpcContext {
+export interface DocksContext extends DevframeRpcContext {
/**
* Type of the client environment
*
@@ -55,7 +55,7 @@ export interface WhenClauseContext {
readonly context: WhenContext
}
-export type DevToolsClientContext = DocksContext
+export type DevframeClientContext = DocksContext
export interface DocksPanelContext {
store: DockPanelStorage
@@ -66,11 +66,11 @@ export interface DocksPanelContext {
export interface DocksEntriesContext {
selectedId: string | null
- readonly selected: DevToolsDockEntry | null
- entries: DevToolsDockEntry[]
+ readonly selected: DevframeDockEntry | null
+ entries: DevframeDockEntry[]
entryToStateMap: Map
- groupedEntries: DevToolsDockEntriesGrouped
- settings: SharedState
+ groupedEntries: DevframeDockEntriesGrouped
+ settings: SharedState
/**
* Get the state of a dock entry by its ID
*/
@@ -90,7 +90,7 @@ export interface DocksEntriesContext {
}
export interface DockEntryState {
- entryMeta: DevToolsDockEntry
+ entryMeta: DevframeDockEntry
readonly isActive: boolean
domElements: {
iframe?: HTMLIFrameElement | null
@@ -102,7 +102,7 @@ export interface DockEntryState {
export interface DockEntryStateEvents {
'entry:activated': () => void
'entry:deactivated': () => void
- 'entry:updated': (newMeta: DevToolsDockUserEntry) => void
+ 'entry:updated': (newMeta: DevframeDockUserEntry) => void
'dom:panel:mounted': (panel: HTMLDivElement) => void
'dom:iframe:mounted': (iframe: HTMLIFrameElement) => void
}
@@ -111,15 +111,15 @@ export interface CommandsContext {
/**
* All commands (server + client)
*/
- readonly commands: DevToolsCommandEntry[]
+ readonly commands: DevframeCommandEntry[]
/**
* Palette-visible commands only (filtered by `showInPalette !== false`)
*/
- readonly paletteCommands: DevToolsCommandEntry[]
+ readonly paletteCommands: DevframeCommandEntry[]
/**
* Register client-side command(s). Returns cleanup function.
*/
- register: (cmd: DevToolsClientCommand | DevToolsClientCommand[]) => () => void
+ register: (cmd: DevframeClientCommand | DevframeClientCommand[]) => () => void
/**
* Execute a command by ID. Delegates to RPC for server commands.
*/
@@ -127,11 +127,11 @@ export interface CommandsContext {
/**
* Get effective keybindings for a command (defaults merged with overrides)
*/
- getKeybindings: (id: string) => DevToolsCommandKeybinding[]
+ getKeybindings: (id: string) => DevframeCommandKeybinding[]
/**
* User settings store (persisted, includes command shortcuts)
*/
- settings: SharedState
+ settings: SharedState
/**
* Whether the command palette is open
*/
diff --git a/packages/hub/src/client/remote.ts b/packages/hub/src/client/remote.ts
index 5d3d1f2..6897104 100644
--- a/packages/hub/src/client/remote.ts
+++ b/packages/hub/src/client/remote.ts
@@ -1,9 +1,9 @@
-import type { DevToolsRpcClient, DevToolsRpcClientOptions } from 'devframe/client'
+import type { DevframeRpcClient, DevframeRpcClientOptions } from 'devframe/client'
import type { RemoteConnectionInfo } from '../types'
-import { getDevToolsRpcClient } from 'devframe/client'
+import { getDevframeRpcClient } from 'devframe/client'
import { REMOTE_CONNECTION_KEY } from 'devframe/constants'
-export type ConnectRemoteDevToolsOptions = Omit
+export type ConnectRemoteDevframeOptions = Omit
function base64UrlDecode(value: string): string {
const padLen = (4 - value.length % 4) % 4
@@ -104,17 +104,17 @@ export function parseRemoteConnection(input?: string): RemoteConnectionInfo | nu
}
/**
- * One-liner for a hosted DevTools page: reads the connection descriptor from
- * the current URL and returns a connected {@link DevToolsRpcClient}.
+ * One-liner for a hosted Devframe page: reads the connection descriptor from
+ * the current URL and returns a connected {@link DevframeRpcClient}.
*
- * Pairs with `remote: true` on a `DevToolsViewIframe` registered on the node
+ * Pairs with `remote: true` on a `DevframeViewIframe` registered on the node
* side — the hub injects the descriptor into the iframe URL.
*
* @throws if no descriptor is present in the URL.
*/
-export async function connectRemoteDevTools(
- options: ConnectRemoteDevToolsOptions = {},
-): Promise {
+export async function connectRemoteDevframe(
+ options: ConnectRemoteDevframeOptions = {},
+): Promise {
const info = parseRemoteConnection()
if (!info) {
throw new Error(
@@ -122,7 +122,7 @@ export async function connectRemoteDevTools(
+ `Open this page through a hub-registered dock with \`remote: true\`.`,
)
}
- return getDevToolsRpcClient({
+ return getDevframeRpcClient({
...options,
connectionMeta: info,
authToken: info.authToken,
diff --git a/packages/hub/src/constants.ts b/packages/hub/src/constants.ts
index 71f9acc..f0712da 100644
--- a/packages/hub/src/constants.ts
+++ b/packages/hub/src/constants.ts
@@ -1,5 +1,5 @@
-import type { DevToolsDockEntryCategory } from './types/docks'
-import type { DevToolsDocksUserSettings } from './types/settings'
+import type { DevframeDockEntryCategory } from './types/docks'
+import type { DevframeDocksUserSettings } from './types/settings'
export * from 'devframe/constants'
@@ -11,9 +11,9 @@ export const DEFAULT_CATEGORIES_ORDER: Record = {
'web': 300,
'advanced': 400,
'~builtin': 1000,
-} satisfies Record
+} satisfies Record
-export const DEFAULT_STATE_USER_SETTINGS: () => DevToolsDocksUserSettings = () => ({
+export const DEFAULT_STATE_USER_SETTINGS: () => DevframeDocksUserSettings = () => ({
docksHidden: [],
docksCategoriesHidden: [],
docksPinned: [],
diff --git a/packages/hub/src/define.ts b/packages/hub/src/define.ts
index 479fce3..bc09f47 100644
--- a/packages/hub/src/define.ts
+++ b/packages/hub/src/define.ts
@@ -1,20 +1,20 @@
import type { WhenContext, WhenExpression } from 'devframe/utils/when'
import type { HubNodeContext } from './node/context'
-import type { DevToolsServerCommandInput } from './types/commands'
-import type { DevToolsDockUserEntry } from './types/docks'
+import type { DevframeServerCommandInput } from './types/commands'
+import type { DevframeDockUserEntry } from './types/docks'
import type { JsonRenderSpec } from './types/json-render'
import { createDefineWrapperWithContext } from 'devframe/rpc'
export const defineRpcFunction = createDefineWrapperWithContext()
export function defineCommand(
- command: Omit & { when?: WhenExpression },
-): DevToolsServerCommandInput {
- return command as DevToolsServerCommandInput
+ command: Omit & { when?: WhenExpression },
+): DevframeServerCommandInput {
+ return command as DevframeServerCommandInput
}
export function defineDockEntry<
- const T extends DevToolsDockUserEntry,
+ const T extends DevframeDockUserEntry,
const W extends string = '',
>(
entry: Omit & { when?: WhenExpression },
diff --git a/packages/hub/src/node/__tests__/host-commands.test.ts b/packages/hub/src/node/__tests__/host-commands.test.ts
index ccc4e56..795e346 100644
--- a/packages/hub/src/node/__tests__/host-commands.test.ts
+++ b/packages/hub/src/node/__tests__/host-commands.test.ts
@@ -1,10 +1,10 @@
import type { HubNodeContext } from '../context'
import { describe, expect, it } from 'vitest'
-import { DevToolsCommandsHost } from '../host-commands'
+import { DevframeCommandsHost } from '../host-commands'
describe('devToolsCommandsHost command id validation', () => {
it('rejects duplicate ids inside one command tree', () => {
- const host = new DevToolsCommandsHost({} as HubNodeContext)
+ const host = new DevframeCommandsHost({} as HubNodeContext)
expect(() => host.register({
id: 'tool:parent',
@@ -17,7 +17,7 @@ describe('devToolsCommandsHost command id validation', () => {
})
it('rejects child ids that collide with existing command trees', () => {
- const host = new DevToolsCommandsHost({} as HubNodeContext)
+ const host = new DevframeCommandsHost({} as HubNodeContext)
host.register({
id: 'tool:parent',
title: 'Parent',
@@ -41,7 +41,7 @@ describe('devToolsCommandsHost command id validation', () => {
})
it('validates updated children against other command trees', () => {
- const host = new DevToolsCommandsHost({} as HubNodeContext)
+ const host = new DevframeCommandsHost({} as HubNodeContext)
host.register({
id: 'other:parent',
title: 'Other parent',
diff --git a/packages/hub/src/node/__tests__/host-docks.test.ts b/packages/hub/src/node/__tests__/host-docks.test.ts
index c230ac4..23cb164 100644
--- a/packages/hub/src/node/__tests__/host-docks.test.ts
+++ b/packages/hub/src/node/__tests__/host-docks.test.ts
@@ -6,7 +6,7 @@ import { REMOTE_CONNECTION_KEY } from 'devframe/constants'
import { getInternalContext } from 'devframe/node/internal'
import { describe, expect, it } from 'vitest'
import { parseRemoteConnection } from '../../client/remote'
-import { DevToolsDockHost } from '../host-docks'
+import { DevframeDockHost } from '../host-docks'
function createContext(): HubNodeContext {
const storageDir = mkdtempSync(join(tmpdir(), 'devframe-hub-docks-'))
@@ -23,7 +23,7 @@ describe('devToolsDockHost remote URL enrichment', () => {
it('preserves hash routes and replaces existing remote descriptors', () => {
const context = createContext()
getInternalContext(context).wsEndpoint = { url: 'ws://localhost:4173' }
- const host = new DevToolsDockHost(context)
+ const host = new DevframeDockHost(context)
host.register({
type: 'iframe',
@@ -60,7 +60,7 @@ describe('devToolsDockHost remote URL enrichment', () => {
it('preserves non-route fragments with the ampersand descriptor form', () => {
const context = createContext()
getInternalContext(context).wsEndpoint = { url: 'ws://localhost:4173' }
- const host = new DevToolsDockHost(context)
+ const host = new DevframeDockHost(context)
host.register({
type: 'iframe',
diff --git a/packages/hub/src/node/__tests__/host-messages.test.ts b/packages/hub/src/node/__tests__/host-messages.test.ts
index f4857a1..34e791e 100644
--- a/packages/hub/src/node/__tests__/host-messages.test.ts
+++ b/packages/hub/src/node/__tests__/host-messages.test.ts
@@ -1,10 +1,10 @@
import type { HubNodeContext } from '../context'
import { describe, expect, it } from 'vitest'
-import { DevToolsMessagesHost } from '../host-messages'
+import { DevframeMessagesHost } from '../host-messages'
describe('devToolsMessagesHost', () => {
it('caps removal history', async () => {
- const host = new DevToolsMessagesHost({} as HubNodeContext)
+ const host = new DevframeMessagesHost({} as HubNodeContext)
for (let i = 0; i < 1005; i++) {
const id = `message:${i}`
diff --git a/packages/hub/src/node/__tests__/host-terminals.test.ts b/packages/hub/src/node/__tests__/host-terminals.test.ts
index fc70198..7f5057d 100644
--- a/packages/hub/src/node/__tests__/host-terminals.test.ts
+++ b/packages/hub/src/node/__tests__/host-terminals.test.ts
@@ -1,8 +1,8 @@
-import type { DevToolsTerminalSession } from '../../types/terminals'
+import type { DevframeTerminalSession } from '../../types/terminals'
import type { HubNodeContext } from '../context'
import process from 'node:process'
import { describe, expect, it, vi } from 'vitest'
-import { DevToolsTerminalHost } from '../host-terminals'
+import { DevframeTerminalHost } from '../host-terminals'
interface FakeSink {
write: ReturnType
@@ -40,7 +40,7 @@ function createTerminalHost() {
} as unknown as HubNodeContext
return {
- host: new DevToolsTerminalHost(context),
+ host: new DevframeTerminalHost(context),
sinks,
}
}
@@ -74,7 +74,7 @@ describe('devToolsTerminalHost stream lifecycle', () => {
cancelled = true
},
})
- const session: DevToolsTerminalSession = {
+ const session: DevframeTerminalSession = {
id: 'terminal',
title: 'Terminal',
status: 'running',
diff --git a/packages/hub/src/node/context.ts b/packages/hub/src/node/context.ts
index cd08d05..d7bbe8a 100644
--- a/packages/hub/src/node/context.ts
+++ b/packages/hub/src/node/context.ts
@@ -1,16 +1,16 @@
import type { CreateHostContextOptions } from 'devframe/node'
-import type { DevToolsHost, DevToolsNodeContext } from 'devframe/types'
-import type { DevToolsCommandsHost } from '../types/commands'
-import type { DevToolsDockHost } from '../types/docks'
+import type { DevframeHost, DevframeNodeContext } from 'devframe/types'
+import type { DevframeCommandsHost } from '../types/commands'
+import type { DevframeDockHost } from '../types/docks'
import type { JsonRenderer, JsonRenderSpec } from '../types/json-render'
-import type { DevToolsMessagesHost } from '../types/messages'
-import type { DevToolsTerminalHost } from '../types/terminals'
+import type { DevframeMessagesHost } from '../types/messages'
+import type { DevframeTerminalHost } from '../types/terminals'
import { createHostContext } from 'devframe/node'
import { debounce } from 'perfect-debounce'
-import { DevToolsCommandsHost as CommandsHostImpl } from './host-commands'
-import { DevToolsDockHost as DocksHostImpl } from './host-docks'
-import { DevToolsMessagesHost as MessagesHostImpl } from './host-messages'
-import { DevToolsTerminalHost as TerminalsHostImpl } from './host-terminals'
+import { DevframeCommandsHost as CommandsHostImpl } from './host-commands'
+import { DevframeDockHost as DocksHostImpl } from './host-docks'
+import { DevframeMessagesHost as MessagesHostImpl } from './host-messages'
+import { DevframeTerminalHost as TerminalsHostImpl } from './host-terminals'
import { registerHubBuiltins } from './hub-builtins'
import { builtinHubRpcDeclarations } from './rpc-builtins'
@@ -39,19 +39,19 @@ export interface HubHostCapabilities {
/**
* Hub-augmented node context — extends devframe's framework-neutral
- * `DevToolsNodeContext` with the hub-level subsystems (`docks`,
+ * `DevframeNodeContext` with the hub-level subsystems (`docks`,
* `terminals`, `messages`, `commands`) and the `createJsonRenderer`
* factory.
*
* Framework kits further extend this with their own slots (e.g.
* `viteConfig`, `viteServer`).
*/
-export interface HubNodeContext extends DevToolsNodeContext {
- readonly host: DevToolsHost & HubHostCapabilities
- docks: DevToolsDockHost
- terminals: DevToolsTerminalHost
- messages: DevToolsMessagesHost
- commands: DevToolsCommandsHost
+export interface HubNodeContext extends DevframeNodeContext {
+ readonly host: DevframeHost & HubHostCapabilities
+ docks: DevframeDockHost
+ terminals: DevframeTerminalHost
+ messages: DevframeMessagesHost
+ commands: DevframeCommandsHost
/**
* Create a JsonRenderer handle for building json-render powered UIs.
*/
diff --git a/packages/hub/src/node/diagnostics.ts b/packages/hub/src/node/diagnostics.ts
index 94a5fdd..e10eb37 100644
--- a/packages/hub/src/node/diagnostics.ts
+++ b/packages/hub/src/node/diagnostics.ts
@@ -46,7 +46,7 @@ export const diagnostics = defineDiagnostics({
},
DF8500: {
why: (p: { id: string }) => `Built-in command "${p.id}" requires a host capability that this host does not implement.`,
- fix: 'Implement the matching capability on the `DevToolsHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`.',
+ fix: 'Implement the matching capability on the `DevframeHost` returned to `createHubContext`. For `hub:open-path`, implement `host.openPath(filepath, line?, column?)`.',
},
},
})
diff --git a/packages/hub/src/node/host-commands.ts b/packages/hub/src/node/host-commands.ts
index fb48dd7..0fc7a1f 100644
--- a/packages/hub/src/node/host-commands.ts
+++ b/packages/hub/src/node/host-commands.ts
@@ -1,14 +1,14 @@
import type {
- DevToolsCommandHandle,
- DevToolsCommandsHost as DevToolsCommandsHostType,
- DevToolsServerCommandEntry,
- DevToolsServerCommandInput,
+ DevframeCommandHandle,
+ DevframeCommandsHost as DevframeCommandsHostType,
+ DevframeServerCommandEntry,
+ DevframeServerCommandInput,
} from '../types/commands'
import type { HubNodeContext } from './context'
import { createEventEmitter } from 'devframe/utils/events'
import { diagnostics } from './diagnostics'
-function findChildCommand(command: DevToolsServerCommandInput, id: string): DevToolsServerCommandInput | undefined {
+function findChildCommand(command: DevframeServerCommandInput, id: string): DevframeServerCommandInput | undefined {
for (const child of command.children ?? []) {
if (child.id === id)
return child
@@ -19,7 +19,7 @@ function findChildCommand(command: DevToolsServerCommandInput, id: string): DevT
return undefined
}
-function collectCommandIds(command: DevToolsServerCommandInput, ids: string[] = []): string[] {
+function collectCommandIds(command: DevframeServerCommandInput, ids: string[] = []): string[] {
ids.push(command.id)
for (const child of command.children ?? [])
collectCommandIds(child, ids)
@@ -27,8 +27,8 @@ function collectCommandIds(command: DevToolsServerCommandInput, ids: string[] =
}
function validateCommandIds(
- commands: Map,
- command: DevToolsServerCommandInput,
+ commands: Map,
+ command: DevframeServerCommandInput,
ignoreTopLevelId?: string,
): void {
const ids = collectCommandIds(command)
@@ -50,15 +50,15 @@ function validateCommandIds(
}
}
-export class DevToolsCommandsHost implements DevToolsCommandsHostType {
- public readonly commands: DevToolsCommandsHostType['commands'] = new Map()
- public readonly events: DevToolsCommandsHostType['events'] = createEventEmitter()
+export class DevframeCommandsHost implements DevframeCommandsHostType {
+ public readonly commands: DevframeCommandsHostType['commands'] = new Map()
+ public readonly events: DevframeCommandsHostType['events'] = createEventEmitter()
constructor(
public readonly context: HubNodeContext,
) {}
- register(command: DevToolsServerCommandInput): DevToolsCommandHandle {
+ register(command: DevframeServerCommandInput): DevframeCommandHandle {
if (this.commands.has(command.id)) {
throw diagnostics.DF8400({ id: command.id })
}
@@ -68,7 +68,7 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
return {
id: command.id,
- update: (patch: Partial>) => {
+ update: (patch: Partial>) => {
if ('id' in patch) {
throw diagnostics.DF8401()
}
@@ -108,11 +108,11 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
return found.handler(...args)
}
- list(): DevToolsServerCommandEntry[] {
+ list(): DevframeServerCommandEntry[] {
return Array.from(this.commands.values()).map(cmd => this.toSerializable(cmd))
}
- private findCommand(id: string): DevToolsServerCommandInput | undefined {
+ private findCommand(id: string): DevframeServerCommandInput | undefined {
// Check top-level
const topLevel = this.commands.get(id)
if (topLevel)
@@ -128,13 +128,13 @@ export class DevToolsCommandsHost implements DevToolsCommandsHostType {
return undefined
}
- private toSerializable(cmd: DevToolsServerCommandInput): DevToolsServerCommandEntry {
+ private toSerializable(cmd: DevframeServerCommandInput): DevframeServerCommandEntry {
const { handler: _, children, ...rest } = cmd
return {
...rest,
source: 'server',
...(children
- ? { children: children.map((c: DevToolsServerCommandInput) => this.toSerializable(c)) }
+ ? { children: children.map((c: DevframeServerCommandInput) => this.toSerializable(c)) }
: {}
),
}
diff --git a/packages/hub/src/node/host-docks.ts b/packages/hub/src/node/host-docks.ts
index 584c341..9be2abf 100644
--- a/packages/hub/src/node/host-docks.ts
+++ b/packages/hub/src/node/host-docks.ts
@@ -1,15 +1,15 @@
-import type { DevToolsNodeContext } from 'devframe/types'
+import type { DevframeNodeContext } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import type {
- DevToolsDockEntry,
- DevToolsDockHost as DevToolsDockHostType,
- DevToolsDockUserEntry,
- DevToolsViewBuiltin,
- DevToolsViewIframe,
+ DevframeDockEntry,
+ DevframeDockHost as DevframeDockHostType,
+ DevframeDockUserEntry,
+ DevframeViewBuiltin,
+ DevframeViewIframe,
RemoteConnectionInfo,
RemoteDockOptions,
} from '../types/docks'
-import type { DevToolsDocksUserSettings } from '../types/settings'
+import type { DevframeDocksUserSettings } from '../types/settings'
import type { HubNodeContext } from './context'
import { REMOTE_CONNECTION_KEY } from 'devframe/constants'
import { createStorage } from 'devframe/node'
@@ -82,10 +82,10 @@ function buildRemoteUrl(baseUrl: string, payload: RemoteConnectionInfo, transpor
return `${beforeHash}${sep}${param}${hash}`
}
-export class DevToolsDockHost implements DevToolsDockHostType {
- public readonly views: DevToolsDockHostType['views'] = new Map()
- public readonly events: DevToolsDockHostType['events'] = createEventEmitter()
- public userSettings: SharedState = undefined!
+export class DevframeDockHost implements DevframeDockHostType {
+ public readonly views: DevframeDockHostType['views'] = new Map()
+ public readonly events: DevframeDockHostType['events'] = createEventEmitter()
+ public userSettings: SharedState = undefined!
/** Dock-id → allocated remote token + resolved options. */
private readonly remoteDocks = new Map