feat(hub)!: introduce @devframes/hub framework-neutral hub layer#24
Conversation
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.
✅ Deploy Preview for devfra ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Introduces a new framework-neutral @devframes/hub package that extracts and standardizes the multi-tool “hub” layer (docks/terminals/messages/commands + shared-state/RPC protocol) so different framework kits can build on the same infrastructure. This PR also adds a minimal Vite example that exercises the hub protocol end-to-end and documents the new surface + diagnostics.
Changes:
- Added
@devframes/hubpackage with node/client entrypoints, shared types/constants, built-in RPC/commands, and diagnostics in the DF8xxx range. - Wired workspace tooling (Turbo, TS path aliases, pnpm catalogs/lockfile) to build/consume the new package.
- Added docs (hub guide + error reference pages) and a minimal Vite example as a protocol witness.
Reviewed changes
Copilot reviewed 61 out of 67 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| turbo.json | Adds Turbo build pipelines for @devframes/hub and the minimal Vite example. |
| tsconfig.base.json | Adds TS path aliases for @devframes/hub/* entrypoints. |
| pnpm-workspace.yaml | Adds tinyexec to the dependency catalog. |
| pnpm-lock.yaml | Locks new hub/example dependencies and workspace importers. |
| alias.ts | Adds monorepo alias mappings for @devframes/hub/*. |
| AGENTS.md | Documents hub package purpose and reserves DF8xxx diagnostic ranges. |
| packages/hub/package.json | Defines the new published package exports and dependencies. |
| packages/hub/LICENSE.md | Adds package-level MIT license. |
| packages/hub/tsconfig.json | Hub package TS config (composite + DOM lib). |
| packages/hub/tsdown.config.ts | Build configuration for hub multi-entry outputs. |
| packages/hub/src/index.ts | Exposes hub’s public API (define helpers + types). |
| packages/hub/src/define.ts | Adds defineCommand, defineDockEntry, defineJsonRenderSpec, and hub-context defineRpcFunction. |
| packages/hub/src/constants.ts | Adds hub defaults and re-exports devframe constants. |
| packages/hub/src/utils/diagnostics-reporter.ts | Adds ANSI diagnostics reporter wiring for hub diagnostics. |
| packages/hub/src/types/index.ts | Hub types barrel + selective devframe type re-exports. |
| packages/hub/src/types/commands.ts | Command palette types (server/client commands, keybindings, host). |
| packages/hub/src/types/docks.ts | Dock entry types including remote-UI options and connection descriptor shape. |
| packages/hub/src/types/json-render.ts | Json-render spec/renderer types. |
| packages/hub/src/types/messages.ts | Message/toast queue types and host/client surfaces. |
| packages/hub/src/types/settings.ts | Persisted hub user settings types. |
| packages/hub/src/types/terminals.ts | Terminal session and child-process terminal types. |
| packages/hub/src/node/index.ts | Hub node entrypoint barrel. |
| packages/hub/src/node/context.ts | Implements createHubContext() wiring hosts, shared-state sync, and built-ins. |
| packages/hub/src/node/diagnostics.ts | Defines DF8xxx diagnostics for hub subsystems. |
| packages/hub/src/node/host-commands.ts | Implements commands registry + execution/list serialization. |
| packages/hub/src/node/host-docks.ts | Implements dock registry, built-in dock entries, and remote-UI URL enrichment. |
| packages/hub/src/node/host-messages.ts | Implements message queue + events + auto-delete + capacity eviction. |
| packages/hub/src/node/host-terminals.ts | Implements terminal sessions + streaming channel integration + child-process terminals. |
| packages/hub/src/node/hub-builtins.ts | Registers hub built-in command(s) like hub:open-path. |
| packages/hub/src/node/rpc-builtins.ts | Adds hub built-in RPC(s) like hub:commands:execute. |
| packages/hub/src/node/mount-devframe.ts | Adds framework-neutral mountDevframe() primitive. |
| packages/hub/src/node/utils.ts | Adds createSimpleClientScript() helper for quick client-script entries. |
| packages/hub/src/client/index.ts | Hub client entrypoint barrel + devframe client re-exports. |
| packages/hub/src/client/context.ts | Adds global client-context getter and key export. |
| packages/hub/src/client/docks.ts | Defines client-side dock/commands context types. |
| packages/hub/src/client/client-script.ts | Defines dock client-script context shape. |
| packages/hub/src/client/remote.ts | Adds remote-UI descriptor parsing and connectRemoteDevTools(). |
| examples/minimal-vite-devtools-kit/package.json | Adds minimal Vite example package and scripts. |
| examples/minimal-vite-devtools-kit/vite.config.ts | Example Vite config wiring the minimal hub kit plugin. |
| examples/minimal-vite-devtools-kit/tsconfig.json | Example TS config for Vite + DOM UI. |
| examples/minimal-vite-devtools-kit/index.html | Minimal DOM UI shell. |
| examples/minimal-vite-devtools-kit/README.md | Run instructions + protocol-witness explanation. |
| examples/minimal-vite-devtools-kit/src/minimal-hub-kit.ts | Minimal Vite plugin that creates hub context and sidecar WS server. |
| examples/minimal-vite-devtools-kit/src/devframe.ts | Demo devframe definition that registers commands/messages. |
| examples/minimal-vite-devtools-kit/src/client/main.ts | Browser-side UI reading shared state and calling hub built-ins. |
| examples/minimal-vite-devtools-kit/src/client/style.css | Styling for the minimal DOM UI. |
| docs/.vitepress/config.ts | Adds hub guide to docs nav. |
| docs/guide/hub.md | Documents hub concepts, protocol, host capabilities, and example. |
| docs/errors/DF8100.md | Error reference for dock already registered. |
| docs/errors/DF8101.md | Error reference for cannot change dock id. |
| docs/errors/DF8102.md | Error reference for dock not registered. |
| docs/errors/DF8200.md | Error reference for terminal session already registered. |
| docs/errors/DF8201.md | Error reference for terminal session not registered. |
| docs/errors/DF8400.md | Error reference for command already registered. |
| docs/errors/DF8401.md | Error reference for cannot change command id. |
| docs/errors/DF8402.md | Error reference for command not registered. |
| docs/errors/DF8500.md | Error reference for missing host capability for built-in command. |
| tests/snapshots/tsnapi/@devframes/hub/index.snapshot.js | Adds tsnapi public API snapshot for @devframes/hub. |
| tests/snapshots/tsnapi/@devframes/hub/index.snapshot.d.ts | Adds tsnapi type snapshot for @devframes/hub. |
| tests/snapshots/tsnapi/@devframes/hub/node.snapshot.js | Adds tsnapi public API snapshot for @devframes/hub/node. |
| tests/snapshots/tsnapi/@devframes/hub/node.snapshot.d.ts | Adds tsnapi type snapshot for @devframes/hub/node. |
| tests/snapshots/tsnapi/@devframes/hub/client.snapshot.js | Adds tsnapi public API snapshot for @devframes/hub/client. |
| tests/snapshots/tsnapi/@devframes/hub/client.snapshot.d.ts | Adds tsnapi type snapshot for @devframes/hub/client. |
| tests/snapshots/tsnapi/@devframes/hub/constants.snapshot.js | Adds tsnapi public API snapshot for @devframes/hub/constants. |
| tests/snapshots/tsnapi/@devframes/hub/constants.snapshot.d.ts | Adds tsnapi type snapshot for @devframes/hub/constants. |
| tests/snapshots/tsnapi/@devframes/hub/types.snapshot.js | Adds tsnapi public API snapshot for @devframes/hub/types. |
| tests/snapshots/tsnapi/@devframes/hub/types.snapshot.d.ts | Adds tsnapi type snapshot for @devframes/hub/types. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function buildRemoteUrl(baseUrl: string, payload: RemoteConnectionInfo, transport: 'fragment' | 'query'): string { | ||
| const encoded = base64UrlEncode(JSON.stringify(payload)) | ||
| const param = `${REMOTE_CONNECTION_KEY}=${encoded}` | ||
| if (transport === 'fragment') { | ||
| // Replace any existing fragment 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 record = this.remoteDocks.get(view.id) | ||
| const endpoint = getInternalContext(this.context as DevToolsNodeContext).wsEndpoint | ||
| if (!record || !endpoint) | ||
| return view | ||
|
|
| session.stream.pipeTo(writer).catch(() => { | ||
| // pipeTo rejection surfaces via writer.abort -> sink.error already. | ||
| }) | ||
| this._boundStreams.set(session.id, { | ||
| dispose: () => { | ||
| if (sink && !sink.closed) | ||
| sink.close() | ||
| }, | ||
| stream: session.stream, | ||
| }) |
| let controller: ReadableStreamDefaultController<string> | undefined | ||
| const stream = new ReadableStream<string>({ | ||
| start(_controller) { | ||
| controller = _controller | ||
| }, | ||
| }) | ||
|
|
||
| function createChildProcess() { | ||
| const cp = exec( | ||
| executeOptions.command, | ||
| executeOptions.args || [], | ||
| { | ||
| nodeOptions: { | ||
| env: { | ||
| COLORS: 'true', | ||
| FORCE_COLOR: 'true', | ||
| ...(executeOptions.env || {}), | ||
| }, | ||
| cwd: executeOptions.cwd ?? process.cwd(), | ||
| stdio: 'pipe', | ||
| }, | ||
| }, | ||
| ) | ||
|
|
||
| ;(async () => { | ||
| for await (const chunk of cp) { | ||
| controller?.enqueue(chunk) | ||
| } | ||
| })() |
| const docksSharedState = await context.rpc.sharedState.get('devframe:docks', { initialValue: [] }) | ||
| const refreshDocks = debounce(() => { | ||
| docksSharedState.mutate(() => docks.values()) | ||
| }, debounceMs) | ||
| docks.events.on('dock:entry:updated', refreshDocks) | ||
|
|
# Conflicts: # pnpm-lock.yaml
| import { defineConfig } from 'vitest/config' | ||
| import { alias } from './alias' | ||
|
|
||
| export default defineConfig({ | ||
| resolve: { | ||
| alias, | ||
| }, |
| import { defineConfig } from 'vite' | ||
| import { alias } from '../../alias' | ||
| import demoDevframe from './src/devframe' | ||
| import { minimalViteDevframeHub } from './src/minimal-vite-devframe-hub' | ||
|
|
||
| export default defineConfig({ | ||
| resolve: { alias }, |
| import { defineConfig } from 'vitest/config' | ||
| import { alias } from '../../alias' | ||
|
|
||
| export default defineConfig({ | ||
| resolve: { | ||
| alias, | ||
| }, |
| import type { EventEmitter } from 'devframe/types' | ||
| import type { ChildProcess } from 'node:child_process' | ||
| import type { DevframeDockEntryIcon } from './docks' | ||
|
|
||
| export interface DevframeTerminalHost { |
| throw lastError | ||
| } | ||
|
|
||
| describe('devToolsTerminalHost stream lifecycle', () => { |
| import { describe, expect, it } from 'vitest' | ||
| import { DevframeMessagesHost } from '../host-messages' | ||
|
|
||
| describe('devToolsMessagesHost', () => { |
| } as unknown as HubNodeContext | ||
| } | ||
|
|
||
| describe('devToolsDockHost remote URL enrichment', () => { |
| import { describe, expect, it } from 'vitest' | ||
| import { DevframeCommandsHost } from '../host-commands' | ||
|
|
||
| describe('devToolsCommandsHost command id validation', () => { |
| import { createHostContext, startHttpAndWs } from '../../../../devframe/src/node' | ||
| import { getInternalContext } from '../../../../devframe/src/node/internal' | ||
| import { createHubContext } from '../context' |
Host-specific behavior (editor open, finder reveal, ...) belongs in kit-registered RPC functions, not in the framework-neutral hub surface. Drop HubHostCapabilities, hub:open-path, hub-builtins.ts, and the DF8500 diagnostic; trim the docs and examples to match.
Address downstream feedback from @vitejs/devtools-kit:
- Drop the Vite-specific `~viteplus` category from the hub's
framework-neutral surface; widen `DevframeDockEntryCategory` to
`(string & {})` so kits register their own categories ad-hoc.
- Rename `DevframeDockHost` → `DevframeDocksHost` and
`DevframeTerminalHost` → `DevframeTerminalsHost` so host class names
match their `ctx.docks` / `ctx.terminals` property names.
- Rename `HubNodeContext` → `DevframeHubContext` to follow the
`Devframe*` prefix pattern the rest of the codebase uses.
- Rename hub's `defineRpcFunction` → `defineHubRpcFunction` so the
hub-context-typed factory can't be confused with devframe's or a
kit's identically-named export.
This subpath is the bridge `@devframes/hub` (and any future first-party hub adapter) uses to reach into devframe's remote-dock token machinery and basePath resolver. Calling it "internal" while a published package depends on it understates the contract: rename to `hub-internals` and document it as the stable surface for first-party hub adapters.
# Conflicts: # examples/streaming-chat/src/devframe.ts
The error-reference Source links and one example README pointed at github.com/vitejs/devframe, which does not exist. Repoint to the canonical devframes/devframe repo, and fix the streaming-chat README to cite vitejs/devtools#306 (the original AI-deltas issue thread).
|
abrahem79
left a comment
There was a problem hiding this comment.
`# Get all witnesses
curl -X POST https://api.trongrid.io/wallet/listwitnesses
Get paginated list
curl "https://api.trongrid.io/wallet/getpaginatednowwitnesslist?offset=0&limit=50"`
Introduces
@devframes/hub— the framework-neutral hub layer that lifts docks/terminals/messages/commands out of@vitejs/devtools-kitso any framework can build a hub kit on the same protocol. Hub-kit authors ship UI on top of the RPC + shared-state surface defined here; framework hosts only implement an adapter that callsmountDevframe().mountDevframe()is the framework-neutral mount primitive. The built-inhub:commands:executeRPC dispatches any registered server command — host-specific capabilities (editor open, finder reveal, …) ship as kit-registered RPCs rather than as part of the hub surface.Two minimal example apps act as protocol witnesses:
examples/minimal-vite-devframe-hub/— Vite plugin + tiny DOM UI.examples/minimal-next-devframe-hub/— Next.js host wiring the same protocol through App Router.The
DF8xxxdiagnostic range is reserved for hub-side codes with per-subsystem sub-ranges; nine error reference pages anddocs/guide/hub.mddocument the surface.The
devframepackage's internal identifiers (DEVTOOLS_MOUNT_PATH, theREMOTE_CONNECTION_KEYvalue, etc.) are renamed fromdevtoolstodevframeso the surface lines up with the package name. The previously-privatedevframe/node/internalsubpath becomesdevframe/node/hub-internalsand is declared the stable public surface for first-party hub adapters.Coordinated thinning of
@vitejs/devtools-kitto re-export from@devframes/hubis a follow-up PR in thevitejs/devtoolsrepo.This PR was created with the help of an agent.