feat(hub)!: introduce @devframes/hub framework-neutral hub layer#24
Draft
antfu wants to merge 5 commits into
Draft
feat(hub)!: introduce @devframes/hub framework-neutral hub layer#24antfu wants to merge 5 commits into
antfu wants to merge 5 commits into
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.
Comment on lines
+45
to
+55
| 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}` | ||
| } |
Comment on lines
+134
to
+138
| const record = this.remoteDocks.get(view.id) | ||
| const endpoint = getInternalContext(this.context as DevToolsNodeContext).wsEndpoint | ||
| if (!record || !endpoint) | ||
| return view | ||
|
|
Comment on lines
+121
to
+130
| 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, | ||
| }) |
Comment on lines
+142
to
+170
| 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) | ||
| } | ||
| })() |
Comment on lines
+115
to
+120
| 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
Comment on lines
1
to
+7
| import { defineConfig } from 'vitest/config' | ||
| import { alias } from './alias' | ||
|
|
||
| export default defineConfig({ | ||
| resolve: { | ||
| alias, | ||
| }, |
Comment on lines
+1
to
+7
| 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 }, |
Comment on lines
+1
to
+7
| import { defineConfig } from 'vitest/config' | ||
| import { alias } from '../../alias' | ||
|
|
||
| export default defineConfig({ | ||
| resolve: { | ||
| alias, | ||
| }, |
Comment on lines
+1
to
+5
| 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', () => { |
Comment on lines
+5
to
+7
| import { createHostContext, startHttpAndWs } from '../../../../devframe/src/node' | ||
| import { getInternalContext } from '../../../../devframe/src/node/internal' | ||
| import { createHubContext } from '../context' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, with built-in RPCshub:open-path/hub:commands:executeexposing the host capabilities the UI relies on.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; ten error reference pages anddocs/guide/hub.mddocument the surface.Internal
devtoolsidentifiers in thedevframepackage (mount paths, constants, types likeDEVTOOLS_MOUNT_PATH, theREMOTE_CONNECTION_KEYvalue) are renamed todevframeso the surface lines up with the package name.Coordinated thinning of
@vitejs/devtools-kitto re-export from@devframes/hubis a follow-up PR in thevitejs/devtoolsrepo.