diff --git a/AGENTS.md b/AGENTS.md index c579c0721..0e16672bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ **`devframe`** is the framework-neutral container for one devtool integration, portable across hub UI providers. Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/embedded outputs) without caring how it'll be displayed. A devframe 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-devframe orchestration (docks, terminals, messages, commands). It does not ship UI - hub UI providers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. It does ship a **headless client runtime** (`createDevframeClientRuntime()` from `@devframes/hub/client`): booted in the host page, it assembles the shared `DevframeClientContext` (panel, docks, commands, when) and imports each dock entry's client script (`action` / `custom-render` / iframe `clientScript`) into that page - how a built-in devframe like the a11y inspector runs its page script inside the user app's page. See `examples/hub-vite/` 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-devframe orchestration (docks, terminals, messages, commands). It does not ship UI - hub UI providers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. It does ship a **headless client runtime** (`createDevframeClientRuntime()` from `@devframes/hub/client`): booted in the host page, it assembles the shared `DevframeClientContext` (panel, docks, commands, when) and imports each dock entry's client script (`action` / `custom-render` / iframe `clientScript`) into that page - how a built-in devframe like the a11y inspector runs its page script inside the user app's page. See `examples/custom-hub-vite/` for a working ~120-line Vite host demonstrating the protocol end to end. ## Terminology @@ -73,7 +73,7 @@ The framework kits - `@devframes/vite`, `@devframes/nuxt`, `@devframes/next` - e - **`.../hub`** - **mount a whole `@devframes/hub` (many devframes) inside that tool** (the "I'm standing up devtools" scope). Wraps `initHub`, defaults the UI slot to `@devframes/hub-ui`'s `createUi()` (overridable via `ui`, or `ui: false` for headless), and ships a browser client helper at `.../hub/client` (a thin, lifecycle-managing wrapper over `@devframes/hub/client`'s `createDevframeClientRuntime`). `@devframes/hub` and `@devframes/hub-ui` are **optional peers** of these packages; `hub-ui` is loaded lazily (a bundler-ignored dynamic `import()` in the Next hub) so it stays optional and its `import.meta.url` asset lookups resolve at request time. - **The bare root (`.`) throws** a helpful error pointing at the two subpaths - never put real code on it. - **Vite and Nuxt already have native hub UI providers** (`@vitejs/devtools-kit`, `@nuxt/devtools`), so `@devframes/vite/hub` and `@devframes/nuxt/hub` still work but emit a one-time `console.warn` recommending those (silence with `{ quiet: true }`). `@devframes/next/hub` has no native counterpart, so it warns nothing. -- The **full hub examples** (`examples/hub-vite`, `examples/hub-next`) consume `.../hub` on the node side but keep hand-rolling their own hub UI provider against `@devframes/hub/client` with `ui: false` - that hand-rolled hub UI provider is the whole point of those reference hosts. The **minimal** ones (`examples/hub-*-minimal`) consume `.../hub` with the default `@devframes/hub-ui` and inject its `embedded.js`, needing no browser-side code. +- The **full hub examples** (`examples/custom-hub-vite`, `examples/custom-hub-next`) consume `.../hub` on the node side but keep hand-rolling their own hub UI provider against `@devframes/hub/client` with `ui: false` - that hand-rolled hub UI provider is the whole point of those reference hosts. The **minimal** ones (`examples/hub-vite`, `examples/hub-next`, `examples/hub-deno`, `examples/hub-fastify`, `examples/hub-hono`, `examples/hub-nitro`, `examples/hub-rsbuild`, `examples/hub-sveltekit`) consume `.../hub` with the default `@devframes/hub-ui` and inject its `embedded.js`, needing no browser-side code. ### Design system @@ -105,7 +105,7 @@ These reinforce devframe's positioning as "the container for one devtool integra ### Hub example parity -`examples/hub-vite/` (Vite plugin + vanilla client) and `examples/hub-next/` (Next.js App Router + React client) are the two reference hosts, and they stay at **feature parity**. They mount the same set of plugins and demo devframes, expose the same dock rail / iframe stage / subsystem drawer, and speak the same hub protocol - the only differences should be the host framework's own plumbing (how static assets are mounted, how the side-car server starts, how the client is rendered). +`examples/custom-hub-vite/` (Vite plugin + vanilla client) and `examples/custom-hub-next/` (Next.js App Router + React client) are the two reference hosts, and they stay at **feature parity**. They mount the same set of plugins and demo devframes, expose the same dock rail / iframe stage / subsystem drawer, and speak the same hub protocol - the only differences should be the host framework's own plumbing (how static assets are mounted, how the side-car server starts, how the client is rendered). Any change to one lands in the other in the same PR: adding a dock, wiring a new hub subsystem, changing the drawer layout, adopting a new client-runtime API. Their READMEs mirror each other too. If a capability genuinely can't exist on one host, say so explicitly in both READMEs rather than letting the examples silently drift. diff --git a/docs/app/components/global/DevframeEcosystemMap.vue b/docs/app/components/global/DevframeEcosystemMap.vue index 770c29460..6af55b843 100644 --- a/docs/app/components/global/DevframeEcosystemMap.vue +++ b/docs/app/components/global/DevframeEcosystemMap.vue @@ -185,7 +185,7 @@ const nodes: EcosystemNode[] = [ linksFrom: 'handler', label: 'Nitro', icon: 'i-unjs-nitro', - href: 'https://github.com/devframes/devframe/tree/main/examples/hub-nitro-minimal', + href: 'https://github.com/devframes/devframe/tree/main/examples/hub-nitro', color: 'neutral', shape: 'pill', style: 'dashed', @@ -198,7 +198,7 @@ const nodes: EcosystemNode[] = [ linksFrom: { source: 'handler', direction: 'vertical' }, label: 'Hono', icon: 'i-logos-hono', - href: 'https://github.com/devframes/devframe/tree/main/examples/hub-hono-minimal', + href: 'https://github.com/devframes/devframe/tree/main/examples/hub-hono', color: 'neutral', shape: 'pill', style: 'dashed', @@ -211,7 +211,7 @@ const nodes: EcosystemNode[] = [ linksFrom: { source: 'handler', dashed: true, direction: 'vertical' }, label: 'Next.js', icon: 'i-logos-nextjs-icon', - href: 'https://github.com/devframes/devframe/tree/main/examples/hub-next-minimal', + href: 'https://github.com/devframes/devframe/tree/main/examples/hub-next', color: 'neutral', shape: 'pill', style: 'dashed', diff --git a/docs/content/1.guide/16.hub.md b/docs/content/1.guide/16.hub.md index 2e47c31cd..784237ac5 100644 --- a/docs/content/1.guide/16.hub.md +++ b/docs/content/1.guide/16.hub.md @@ -9,7 +9,7 @@ description: '@devframes/hub orchestrates many devtools sharing a UI: a dock reg ![Hub screenshot](/screenshots/hub-1.png) -_Orchestrating multiple devtools (from [A Playground](https://github.com/devframes/devframe/tree/main/examples/hub-vite))_ +_Orchestrating multiple devtools (from [A Playground](https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite))_ ## What the hub adds @@ -248,8 +248,8 @@ The hub ships a headless client runtime, `createDevframeClientRuntime()` (`@devf Two minimal hubs mount every built-in devframe behind an icon dock, plus a "Tabbed Tool" demonstrating [shared-iframe soft navigation](/guide/client-context#shared-iframe-soft-navigation): -- [`examples/hub-vite/`](https://github.com/devframes/devframe/tree/main/examples/hub-vite): a ~120-line Vite host with a vanilla DOM UI. -- [`examples/hub-next/`](https://github.com/devframes/devframe/tree/main/examples/hub-next): the same, from a Next.js App Router app. +- [`examples/custom-hub-vite/`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite): a ~120-line Vite host with a vanilla DOM UI. +- [`examples/custom-hub-next/`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next): the same, from a Next.js App Router app. ## Diagnostics diff --git a/docs/content/1.guide/17.client-context.md b/docs/content/1.guide/17.client-context.md index 5538fbfe1..8554feaac 100644 --- a/docs/content/1.guide/17.client-context.md +++ b/docs/content/1.guide/17.client-context.md @@ -108,4 +108,4 @@ It materializes a [client-only dock](#client-only-docks) per tab (id `: ### The hub UI provider's part -A hub UI provider keeps one iframe alive per `frameId` (shown/hidden); on mount, it sets the element on the anchor's `docks.getStateById(anchorId)` state (`domElements.iframe`) and emits `dom:iframe:mounted`. See the "Tabbed Tool" in [`examples/hub-vite`](https://github.com/devframes/devframe/tree/main/examples/hub-vite) / [`hub-next`](https://github.com/devframes/devframe/tree/main/examples/hub-next). +A hub UI provider keeps one iframe alive per `frameId` (shown/hidden); on mount, it sets the element on the anchor's `docks.getStateById(anchorId)` state (`domElements.iframe`) and emits `dom:iframe:mounted`. See the "Tabbed Tool" in [`examples/custom-hub-vite`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite) / [`custom-hub-next`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next). diff --git a/docs/content/1.guide/21.build-your-own-json-render-frontend.md b/docs/content/1.guide/21.build-your-own-json-render-frontend.md index 6eb9dd852..cdeb92059 100644 --- a/docs/content/1.guide/21.build-your-own-json-render-frontend.md +++ b/docs/content/1.guide/21.build-your-own-json-render-frontend.md @@ -6,7 +6,7 @@ description: '@devframes/json-render-ui is the reference frontend, not the proto --- `@devframes/json-render-ui` is the reference frontend, not the protocol; any -implementation of the renderer contract replaces it. The [Next hub witness](https://github.com/devframes/devframe/tree/main/examples/hub-next) ships a React one at +implementation of the renderer contract replaces it. The [Next hub witness](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next) ships a React one at `src/client/json-render/`. ## The contract diff --git a/docs/content/1.guide/22.build-your-own-hub-ui.md b/docs/content/1.guide/22.build-your-own-hub-ui.md index 25618ff88..2340fb89a 100644 --- a/docs/content/1.guide/22.build-your-own-hub-ui.md +++ b/docs/content/1.guide/22.build-your-own-hub-ui.md @@ -89,6 +89,6 @@ Renderer modules self-style (sometimes via a shadow root). Keep a live ## Reference points - `packages/hub-ui`: the full reference hub UI provider (Vue, `@antfu/design`). -- [`examples/hub-vite`](https://github.com/devframes/devframe/tree/main/examples/hub-vite) and - [`examples/hub-next`](https://github.com/devframes/devframe/tree/main/examples/hub-next): hand-rolled +- [`examples/custom-hub-vite`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite) and + [`examples/custom-hub-next`](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next): hand-rolled hub UI providers in vanilla DOM and React. diff --git a/docs/content/1.guide/23.built-with.md b/docs/content/1.guide/23.built-with.md index f3d08e837..baa2d43ae 100644 --- a/docs/content/1.guide/23.built-with.md +++ b/docs/content/1.guide/23.built-with.md @@ -41,18 +41,18 @@ pnpm --filter dev | [json-render](https://github.com/devframes/devframe/tree/main/examples/json-render) | Vue | Server-authored view via `@devframes/json-render-ui`; live state + action bridge. | | [streaming-chat](https://github.com/devframes/devframe/tree/main/examples/streaming-chat) | Preact | Streams tokens; history in shared state. | | [next-runtime-snapshot](https://github.com/devframes/devframe/tree/main/examples/next-runtime-snapshot) | React (Next.js) | App Router SPA surfacing the Node runtime. | -| [hub-vite](https://github.com/devframes/devframe/tree/main/examples/hub-vite) | Vanilla TS (Vite) | ~120-line Vite host wiring `@devframes/hub`; hand-built hub UI provider. | -| [hub-next](https://github.com/devframes/devframe/tree/main/examples/hub-next) | React (Next.js) | Same protocol, Next.js route. | +| [custom-hub-vite](https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite) | Vanilla TS (Vite) | ~120-line Vite host wiring `@devframes/hub`; hand-built hub UI provider. | +| [custom-hub-next](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next) | React (Next.js) | Same protocol, Next.js route. | The **minimal** family mounts `initHub({ ui: createUi() })` with `@devframes/hub-ui`: | Example | Host framework | Shows | |---------|------|-------| -| [hub-vite-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-vite-minimal) | Vite | Dev middleware. | -| [hub-next-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-next-minimal) | Next.js | App Router route. | -| [hub-nitro-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-nitro-minimal) | Nitro | Catch-all route. | -| [hub-hono-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-hono-minimal) | Hono | Node and Bun. | -| [hub-fastify-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-fastify-minimal) | Fastify | `nodeMiddleware`. | -| [hub-sveltekit-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-sveltekit-minimal) | SvelteKit | Catch-all endpoint. | -| [hub-deno-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-deno-minimal) | Deno | `Deno.serve` + upgrade socket. | -| [hub-rsbuild-minimal](https://github.com/devframes/devframe/tree/main/examples/hub-rsbuild-minimal) | Rsbuild | Dev middleware. | +| [hub-vite](https://github.com/devframes/devframe/tree/main/examples/hub-vite) | Vite | Dev middleware. | +| [hub-next](https://github.com/devframes/devframe/tree/main/examples/hub-next) | Next.js | App Router route. | +| [hub-nitro](https://github.com/devframes/devframe/tree/main/examples/hub-nitro) | Nitro | Catch-all route. | +| [hub-hono](https://github.com/devframes/devframe/tree/main/examples/hub-hono) | Hono | Node and Bun. | +| [hub-fastify](https://github.com/devframes/devframe/tree/main/examples/hub-fastify) | Fastify | `nodeMiddleware`. | +| [hub-sveltekit](https://github.com/devframes/devframe/tree/main/examples/hub-sveltekit) | SvelteKit | Catch-all endpoint. | +| [hub-deno](https://github.com/devframes/devframe/tree/main/examples/hub-deno) | Deno | `Deno.serve` + upgrade socket. | +| [hub-rsbuild](https://github.com/devframes/devframe/tree/main/examples/hub-rsbuild) | Rsbuild | Dev middleware. | diff --git a/docs/content/1.guide/8.json-render.md b/docs/content/1.guide/8.json-render.md index 05e798226..7eace6675 100644 --- a/docs/content/1.guide/8.json-render.md +++ b/docs/content/1.guide/8.json-render.md @@ -105,7 +105,7 @@ view renders full-bleed, multiple get a `title`-labeled switcher. A custom frontend renders from shared state: connect with `connectDevframe()`, read the view's state (keyed `devframe:json-render::`), subscribe to `updated` events, and render with your registry; the [Next -hub example](https://github.com/devframes/devframe/tree/main/examples/hub-next) has a React renderer. In a +hub example](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next) has a React renderer. In a **static** build spec + state are read-only: actions unavailable, local state and bindings still work. diff --git a/docs/content/3.frameworks/3.next.md b/docs/content/3.frameworks/3.next.md index ba1ec5dc6..fe64b9f4e 100644 --- a/docs/content/3.frameworks/3.next.md +++ b/docs/content/3.frameworks/3.next.md @@ -53,7 +53,7 @@ export const GET = handler.fetch ## Hosting a hub -[`@devframes/hub`](/guide/hub)'s `initHub` mounts every devframe under `/` behind one `handler` (memoize on `globalThis`; see `examples/hub-next`): +[`@devframes/hub`](/guide/hub)'s `initHub` mounts every devframe under `/` behind one `handler` (memoize on `globalThis`; see `examples/custom-hub-next`): ```ts [devframe/host.ts] import { DEVFRAMES_HUB_BASE, initHub } from '@devframes/hub/initiate' @@ -133,4 +133,4 @@ No native hub UI provider here, so this scope stays quiet; `createDevframeNextHo - [Vite](/frameworks/vite) - [Hub](/guide/hub) -- [hub-next](https://github.com/devframes/devframe/tree/main/examples/hub-next) +- [custom-hub-next](https://github.com/devframes/devframe/tree/main/examples/custom-hub-next) diff --git a/docs/content/6.errors/DF0076.md b/docs/content/6.errors/DF0076.md index d5796c119..c5d9bfa20 100644 --- a/docs/content/6.errors/DF0076.md +++ b/docs/content/6.errors/DF0076.md @@ -23,7 +23,7 @@ hub.attach(myNodeHttpServer) // ✗ throws DF0076 on Bun/Deno ## Fix -On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` and complete the upgrade with `attachBunWsTransport` / `attachDenoWsTransport` (see the `hub-deno-minimal` example), or connect over the SSE endpoint instead; it rides the instance's ordinary HTTP surface and needs no upgrade wiring. A side-car (`ws: { sidecar: true }`) also binds the native WebSocket adapter for you on its own port. +On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` and complete the upgrade with `attachBunWsTransport` / `attachDenoWsTransport` (see the `hub-deno` example), or connect over the SSE endpoint instead; it rides the instance's ordinary HTTP surface and needs no upgrade wiring. A side-car (`ws: { sidecar: true }`) also binds the native WebSocket adapter for you on its own port. ## Source diff --git a/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md b/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md index 7f5f56060..a85a3d7aa 100644 --- a/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md +++ b/docs/content/9.posts/1.pluggable-extensible-playful-devtools.md @@ -278,7 +278,7 @@ hub.nodeMiddleware Mounted devframes share one RPC registry, state store, connection, authentication gate, and optional aggregate MCP endpoint. The hub remains headless: [`@devframes/hub-ui`](/guide/hub-initiate#the-ui-slot) provides the reference hub UI provider, while a product can supply another hub UI provider independently of the underlying tools. -Like a single devframe, a hub can mount into almost any host framework through the standard handler. The repository includes working reference projects for [Vite](https://github.com/devframes/devframe/tree/main/examples/hub-vite-minimal), [Next.js](https://github.com/devframes/devframe/tree/main/examples/hub-next-minimal), [Hono](https://github.com/devframes/devframe/tree/main/examples/hub-hono-minimal), [Nitro](https://github.com/devframes/devframe/tree/main/examples/hub-nitro-minimal), and [Rsbuild](https://github.com/devframes/devframe/tree/main/examples/hub-rsbuild-minimal). Each example connects the same handler and UI entry to its host framework's native server API. A complete DevTools host can build its own design system and interaction model on top of the hub's foundation. +Like a single devframe, a hub can mount into almost any host framework through the standard handler. The repository includes working reference projects for [Vite](https://github.com/devframes/devframe/tree/main/examples/hub-vite), [Next.js](https://github.com/devframes/devframe/tree/main/examples/hub-next), [Hono](https://github.com/devframes/devframe/tree/main/examples/hub-hono), [Nitro](https://github.com/devframes/devframe/tree/main/examples/hub-nitro), and [Rsbuild](https://github.com/devframes/devframe/tree/main/examples/hub-rsbuild). Each example connects the same handler and UI entry to its host framework's native server API. A complete DevTools host can build its own design system and interaction model on top of the hub's foundation. ### Vite DevTools diff --git a/examples/a11y-messages-playground/src/a11y-messages-playground.ts b/examples/a11y-messages-playground/src/a11y-messages-playground.ts index 8a0e3492c..fb2b4b80c 100644 --- a/examples/a11y-messages-playground/src/a11y-messages-playground.ts +++ b/examples/a11y-messages-playground/src/a11y-messages-playground.ts @@ -18,7 +18,7 @@ export interface A11yMessagesPlaygroundOptions { /** * A tiny Vite plugin that runs `@devframes/hub` inside the Vite dev server - - * the same shape as `examples/hub-vite`, trimmed to the two plugins this + * the same shape as `examples/custom-hub-vite`, trimmed to the two plugins this * playground pairs (a11y + messages). One `initHub()` call assembles the whole * hub: it mounts each devframe as a dock, shares the WebSocket with Vite's own * server, serves the discovery endpoints, and registers the playground in the diff --git a/examples/hub-next/.gitignore b/examples/custom-hub-next/.gitignore similarity index 100% rename from examples/hub-next/.gitignore rename to examples/custom-hub-next/.gitignore diff --git a/examples/hub-next/README.md b/examples/custom-hub-next/README.md similarity index 92% rename from examples/hub-next/README.md rename to examples/custom-hub-next/README.md index af4b67497..c8d5f725b 100644 --- a/examples/hub-next/README.md +++ b/examples/custom-hub-next/README.md @@ -8,7 +8,7 @@ A tiny, copyable **vite-devtools-style hub on Next.js**. [vite-devtools](https:/ ```sh pnpm install -pnpm --filter hub-next dev +pnpm --filter custom-hub-next dev ``` On first load the hub asks you to authorize. `initHub()` gates every connection by default (devframe's interactive OTP), so it prints a 6-digit code and a magic link in the terminal, and the page shows an authorization view that exchanges the code for a bearer token stored in the browser. The hub UI provider opts out of devframe's native `prompt()` (`simpleAuth: false`) to render that view; open the magic link instead to authorize without typing. Each embedded SPA then inherits the token the host page stored. @@ -22,7 +22,7 @@ Selecting a tool loads its SPA in the stage. The bottom drawer mirrors the hub's The A11y Inspector shows a live axe-core report of this hub's own page: the devframe declares its own page-script module as the a11y dock's `clientScript`, so the hub serves it same-origin with no host wiring; the hub client runtime - `createDevframeClientRuntime()` booted in `app/page.tsx` - imports it into the page, so the docked panel and the page script share the origin and tab their in-page channel handshakes across. -The **RPC & State Inspector** carries an **Instances** tab that lists every devframe dev server running on your machine. The hub registers itself in the shared registry (`~/.devframe/instances/`) on startup via `registerDevframeInstance()`, so it shows up as "this instance"; start another example (e.g. `pnpm --filter hub-vite dev`, or any `node bin.mjs` CLI example) in a second terminal and it appears there too, each linking to its own SPA. +The **RPC & State Inspector** carries an **Instances** tab that lists every devframe dev server running on your machine. The hub registers itself in the shared registry (`~/.devframe/instances/`) on startup via `registerDevframeInstance()`, so it shows up as "this instance"; start another example (e.g. `pnpm --filter custom-hub-vite dev`, or any `node bin.mjs` CLI example) in a second terminal and it appears there too, each linking to its own SPA. ## One namespace, one route @@ -45,9 +45,9 @@ The instance is memoized on `globalThis`, so Next's dev-time module re-evaluatio - One authorization covers the whole hub: `initHub()` gates the shared transport by default, so a single OTP handshake trusts every mounted devframe, the discovery endpoints, and the built-ins. The hub UI provider drives its own authorization view (`simpleAuth: false`) and each embedded SPA inherits the stored token - The browser reads `devframe:docks` / `devframe:commands` shared state and dispatches commands over RPC - byte-for-byte the same protocol the Vite host speaks - `createDevframeClientRuntime()` boots the hub's framework-level client runtime in the host page: it publishes the shared client context and imports each dock's `clientScript` (here, the a11y page script) so devframes run page scripts in the user app's page -- The **JSON Render** dock renders through a **local React renderer** (`src/client/json-render/react-renderer.tsx` - a compact React port of the base catalog) registered at `createDevframeClientRuntime({ renderers })`. The hub *also* publishes the reference Vue frontend through its renderer manifest (`renderers: [jsonRenderUiRenderer()]` on `initHub`), but local registration takes precedence - witnessing that any frontend implementing the `JsonRenderDockRenderer` contract can replace the reference one. Delete the local `renderers` option and the same dock renders via the manifest-served module instead. (The sibling `hub-vite` witness ships no local renderer and consumes the manifest directly - the other side of the swap seam.) +- The **JSON Render** dock renders through a **local React renderer** (`src/client/json-render/react-renderer.tsx` - a compact React port of the base catalog) registered at `createDevframeClientRuntime({ renderers })`. The hub *also* publishes the reference Vue frontend through its renderer manifest (`renderers: [jsonRenderUiRenderer()]` on `initHub`), but local registration takes precedence - witnessing that any frontend implementing the `JsonRenderDockRenderer` contract can replace the reference one. Delete the local `renderers` option and the same dock renders via the manifest-served module instead. (The sibling `custom-hub-vite` witness ships no local renderer and consumes the manifest directly - the other side of the swap seam.) - The **No Renderer** dock witnesses the missing-renderer path: its type is covered by nothing, so `renderers.mount()` resolves `{ status: 'missing-renderer' }` and the hub UI provider shows *No renderer for "demo-unrendered" in the current environment* instead of a dead panel -- The **Client Script Demo** dock witnesses the **URL shape of client scripts**: the Next host declares no `clientModuleResolution` (Next's bundler exposes no browser-reachable on-demand module URL, so bare-specifier client scripts are unsupported here), so it mounts `demo-dock-client`'s prebuilt self-contained bundle statically and passes the served URL as `action.importFrom`. The sibling `hub-vite` host consumes the **same package** as a bare npm specifier through its `/@id/{specifier}` template - the two shapes of `importFrom` side by side +- The **Client Script Demo** dock witnesses the **URL shape of client scripts**: the Next host declares no `clientModuleResolution` (Next's bundler exposes no browser-reachable on-demand module URL, so bare-specifier client scripts are unsupported here), so it mounts `demo-dock-client`'s prebuilt self-contained bundle statically and passes the served URL as `action.importFrom`. The sibling `custom-hub-vite` host consumes the **same package** as a bare npm specifier through its `/@id/{specifier}` template - the two shapes of `importFrom` side by side ## Hosting built-in devframes in a bundler diff --git a/examples/hub-next-minimal/package.json b/examples/custom-hub-next/package.json similarity index 54% rename from examples/hub-next-minimal/package.json rename to examples/custom-hub-next/package.json index 498fbf381..84dac59aa 100644 --- a/examples/hub-next-minimal/package.json +++ b/examples/custom-hub-next/package.json @@ -1,17 +1,18 @@ { - "name": "hub-next-minimal", + "name": "custom-hub-next", "type": "module", "version": "0.9.9", "private": true, - "description": "Minimal Next.js host that mounts the devframe hub on a single App Router route.", - "homepage": "https://github.com/devframes/devframe/tree/main/examples/hub-next-minimal", + "description": "Next.js reference hub that exercises every devframe hub subsystem end to end.", + "homepage": "https://github.com/devframes/devframe/tree/main/examples/custom-hub-next", "scripts": { - "dev": "next dev src/client", - "build": "next build src/client" + "dev": "pnpm -C ../.. run build && next dev src/client", + "build": "next build src/client", + "test": "vitest run --config vitest.config.ts" }, "dependencies": { + "@antfu/design": "catalog:frontend", "@devframes/hub": "workspace:*", - "@devframes/hub-ui": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", "@devframes/next": "workspace:*", @@ -24,13 +25,25 @@ "@devframes/plugin-messages": "workspace:*", "@devframes/plugin-og": "workspace:*", "@devframes/plugin-terminals": "workspace:*", + "@json-render/react": "catalog:frontend", + "colorjs.io": "catalog:frontend", + "demo-dock-client": "workspace:*", "devframe": "workspace:*", + "dompurify": "catalog:frontend", + "json-render": "workspace:*", "next": "catalog:frontend", "react": "catalog:frontend", "react-dom": "catalog:frontend" }, "devDependencies": { + "@iconify-json/ph": "catalog:frontend", "@types/react": "catalog:types", - "@types/react-dom": "catalog:types" + "@types/react-dom": "catalog:types", + "@unocss/postcss": "catalog:frontend", + "get-port-please": "catalog:deps", + "pathe": "catalog:deps", + "unocss": "catalog:frontend", + "vitest": "catalog:testing", + "ws": "catalog:deps" } } diff --git a/examples/hub-next/spa/next-demo-tool/index.html b/examples/custom-hub-next/spa/next-demo-tool/index.html similarity index 100% rename from examples/hub-next/spa/next-demo-tool/index.html rename to examples/custom-hub-next/spa/next-demo-tool/index.html diff --git a/examples/hub-next/spa/next-tabbed-tool/index.html b/examples/custom-hub-next/spa/next-tabbed-tool/index.html similarity index 100% rename from examples/hub-next/spa/next-tabbed-tool/index.html rename to examples/custom-hub-next/spa/next-tabbed-tool/index.html diff --git a/examples/custom-hub-next/src/client/app/%5F_devframes/[[...path]]/route.ts b/examples/custom-hub-next/src/client/app/%5F_devframes/[[...path]]/route.ts new file mode 100644 index 000000000..a881c8d82 --- /dev/null +++ b/examples/custom-hub-next/src/client/app/%5F_devframes/[[...path]]/route.ts @@ -0,0 +1,25 @@ +import { ensureNextDevframeHub } from '../../../devframe/next-devframe-hub' + +export const runtime = 'nodejs' +export const dynamic = 'force-dynamic' + +/** + * The whole hub behind one route: `initHub`'s web-standard `handler` serves + * every mounted devframe SPA (`/__devframes/git/…`, `/__devframes/terminals/…`, + * the a11y agent module, …), each frame's `__connection.json`, the hub-level + * discovery endpoints (`/__devframes/__connection.json`, `__index.json`, + * `__client-imports.js`), and the aggregate MCP endpoint + * (`/__devframes/__mcp`). + * + * Next.js reserves `_`-prefixed segment folders, so `__devframes` is + * URL-encoded as `%5F_devframes` in the app directory name. + * + * MCP speaks Streamable-HTTP: `POST` (requests), `GET` (the SSE stream), and + * `DELETE` (session teardown) all route to the same handler. + */ +async function handler(request: Request): Promise { + const hub = await ensureNextDevframeHub() + return hub.handler(request) +} + +export { handler as DELETE, handler as GET, handler as POST } diff --git a/examples/hub-next/src/client/app/globals.css b/examples/custom-hub-next/src/client/app/globals.css similarity index 100% rename from examples/hub-next/src/client/app/globals.css rename to examples/custom-hub-next/src/client/app/globals.css diff --git a/examples/hub-next/src/client/app/icons.ts b/examples/custom-hub-next/src/client/app/icons.ts similarity index 100% rename from examples/hub-next/src/client/app/icons.ts rename to examples/custom-hub-next/src/client/app/icons.ts diff --git a/examples/custom-hub-next/src/client/app/layout.tsx b/examples/custom-hub-next/src/client/app/layout.tsx new file mode 100644 index 000000000..d72946369 --- /dev/null +++ b/examples/custom-hub-next/src/client/app/layout.tsx @@ -0,0 +1,23 @@ +import type { Metadata } from 'next' +import type { ReactNode } from 'react' +import './globals.css' +import '@antfu/design/styles.css' + +export const metadata: Metadata = { + title: 'Next Devframe Hub', + description: 'A Next.js host for the @devframes/hub protocol.', +} + +// Follow the OS theme before paint (@antfu/design dark: is class-based). +const themeScript = `(function(){try{if(window.matchMedia('(prefers-color-scheme: dark)').matches)document.documentElement.classList.add('dark')}catch(e){}})();` + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + + + +
+
+

+ + Vite Devframe Hub +

+

Connecting…

+
+ +
+ + +
+ +
+ + +
+
+ +
+
+

Transport

+

Connecting…

+ +
+
+ +
+

Commands

+
  • Waiting for snapshot…
+
+ +
+
+ +
+

Messages

+
  • No messages yet.
+
+ +
+

Terminals

+
  • No terminal sessions.
+
+
+
+ + + diff --git a/examples/hub-vite-minimal/package.json b/examples/custom-hub-vite/package.json similarity index 64% rename from examples/hub-vite-minimal/package.json rename to examples/custom-hub-vite/package.json index 0f377ef4a..b2e1f1fd2 100644 --- a/examples/hub-vite-minimal/package.json +++ b/examples/custom-hub-vite/package.json @@ -1,19 +1,18 @@ { - "name": "hub-vite-minimal", + "name": "custom-hub-vite", "type": "module", "version": "0.9.9", "private": true, - "description": "Minimal Vite host that mounts the devframe hub as dev middleware.", - "homepage": "https://github.com/devframes/devframe/tree/main/examples/hub-vite-minimal", + "description": "Vite reference hub that exercises every devframe hub subsystem end to end.", + "homepage": "https://github.com/devframes/devframe/tree/main/examples/custom-hub-vite", "scripts": { - "dev": "vite", + "dev": "pnpm -C ../.. run build && vite", "build": "vite build", - "preview": "vite preview --host", "typecheck": "tsc --noEmit" }, "dependencies": { + "@antfu/design": "catalog:frontend", "@devframes/hub": "workspace:*", - "@devframes/hub-ui": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", "@devframes/plugin-a11y": "workspace:*", @@ -26,10 +25,15 @@ "@devframes/plugin-og": "workspace:*", "@devframes/plugin-terminals": "workspace:*", "@devframes/vite": "workspace:*", - "devframe": "workspace:*" + "colorjs.io": "catalog:frontend", + "demo-dock-client": "workspace:*", + "devframe": "workspace:*", + "dompurify": "catalog:frontend", + "json-render": "workspace:*" }, "devDependencies": { - "@types/node": "catalog:types", + "@iconify-json/ph": "catalog:frontend", + "unocss": "catalog:frontend", "vite": "catalog:build" } } diff --git a/examples/hub-vite/public/data/notes.md b/examples/custom-hub-vite/public/data/notes.md similarity index 100% rename from examples/hub-vite/public/data/notes.md rename to examples/custom-hub-vite/public/data/notes.md diff --git a/examples/hub-vite/public/data/site.json b/examples/custom-hub-vite/public/data/site.json similarity index 100% rename from examples/hub-vite/public/data/site.json rename to examples/custom-hub-vite/public/data/site.json diff --git a/examples/hub-vite/public/favicon.svg b/examples/custom-hub-vite/public/favicon.svg similarity index 100% rename from examples/hub-vite/public/favicon.svg rename to examples/custom-hub-vite/public/favicon.svg diff --git a/examples/hub-vite/public/images/banner.svg b/examples/custom-hub-vite/public/images/banner.svg similarity index 100% rename from examples/hub-vite/public/images/banner.svg rename to examples/custom-hub-vite/public/images/banner.svg diff --git a/examples/hub-vite/public/images/tile-green.png b/examples/custom-hub-vite/public/images/tile-green.png similarity index 100% rename from examples/hub-vite/public/images/tile-green.png rename to examples/custom-hub-vite/public/images/tile-green.png diff --git a/examples/hub-vite/public/images/tile-sage.png b/examples/custom-hub-vite/public/images/tile-sage.png similarity index 100% rename from examples/hub-vite/public/images/tile-sage.png rename to examples/custom-hub-vite/public/images/tile-sage.png diff --git a/examples/hub-vite/public/logo.svg b/examples/custom-hub-vite/public/logo.svg similarity index 100% rename from examples/hub-vite/public/logo.svg rename to examples/custom-hub-vite/public/logo.svg diff --git a/examples/hub-vite/public/robots.txt b/examples/custom-hub-vite/public/robots.txt similarity index 100% rename from examples/hub-vite/public/robots.txt rename to examples/custom-hub-vite/public/robots.txt diff --git a/examples/hub-vite/public/styles/theme.css b/examples/custom-hub-vite/public/styles/theme.css similarity index 100% rename from examples/hub-vite/public/styles/theme.css rename to examples/custom-hub-vite/public/styles/theme.css diff --git a/examples/hub-vite/spa/demo-tool/index.html b/examples/custom-hub-vite/spa/demo-tool/index.html similarity index 100% rename from examples/hub-vite/spa/demo-tool/index.html rename to examples/custom-hub-vite/spa/demo-tool/index.html diff --git a/examples/hub-vite/spa/tabbed-tool/index.html b/examples/custom-hub-vite/spa/tabbed-tool/index.html similarity index 100% rename from examples/hub-vite/spa/tabbed-tool/index.html rename to examples/custom-hub-vite/spa/tabbed-tool/index.html diff --git a/examples/hub-vite/src/client/env.d.ts b/examples/custom-hub-vite/src/client/env.d.ts similarity index 100% rename from examples/hub-vite/src/client/env.d.ts rename to examples/custom-hub-vite/src/client/env.d.ts diff --git a/examples/hub-vite/src/client/icons.ts b/examples/custom-hub-vite/src/client/icons.ts similarity index 100% rename from examples/hub-vite/src/client/icons.ts rename to examples/custom-hub-vite/src/client/icons.ts diff --git a/examples/hub-vite/src/client/main.ts b/examples/custom-hub-vite/src/client/main.ts similarity index 100% rename from examples/hub-vite/src/client/main.ts rename to examples/custom-hub-vite/src/client/main.ts diff --git a/examples/hub-vite/src/devframe.ts b/examples/custom-hub-vite/src/devframe.ts similarity index 100% rename from examples/hub-vite/src/devframe.ts rename to examples/custom-hub-vite/src/devframe.ts diff --git a/examples/hub-vite/src/tabbed-tool.ts b/examples/custom-hub-vite/src/tabbed-tool.ts similarity index 100% rename from examples/hub-vite/src/tabbed-tool.ts rename to examples/custom-hub-vite/src/tabbed-tool.ts diff --git a/examples/hub-vite/src/unrendered-dock.ts b/examples/custom-hub-vite/src/unrendered-dock.ts similarity index 100% rename from examples/hub-vite/src/unrendered-dock.ts rename to examples/custom-hub-vite/src/unrendered-dock.ts diff --git a/examples/custom-hub-vite/tsconfig.json b/examples/custom-hub-vite/tsconfig.json new file mode 100644 index 000000000..46dbffd99 --- /dev/null +++ b/examples/custom-hub-vite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "esModuleInterop": true, + "isolatedDeclarations": false + }, + "include": ["src", "vite.config.ts"] +} diff --git a/examples/hub-vite/uno.config.ts b/examples/custom-hub-vite/uno.config.ts similarity index 100% rename from examples/hub-vite/uno.config.ts rename to examples/custom-hub-vite/uno.config.ts diff --git a/examples/custom-hub-vite/vite.config.ts b/examples/custom-hub-vite/vite.config.ts new file mode 100644 index 000000000..638eb5fe3 --- /dev/null +++ b/examples/custom-hub-vite/vite.config.ts @@ -0,0 +1,202 @@ +import type { DevframeHubContext } from '@devframes/hub/node' +import { defineHubRpcFunction } from '@devframes/hub' +import { jsonRenderUiRenderer } from '@devframes/json-render-ui/hub' +import { toJsonRenderDockEntry } from '@devframes/json-render/hub' +import createA11yDevframe from '@devframes/plugin-a11y' +import createAssetsDevframe from '@devframes/plugin-assets' +import createCodeServerDevframe from '@devframes/plugin-code-server' +import { createDataInspectorDevframe } from '@devframes/plugin-data-inspector' +import { registerDataSource } from '@devframes/plugin-data-inspector/registry' +import createGitDevframe from '@devframes/plugin-git' +import createInspectDevframe from '@devframes/plugin-inspect' +import createMessagesDevframe from '@devframes/plugin-messages' +import createOgDevframe from '@devframes/plugin-og' +import createTerminalsDevframe from '@devframes/plugin-terminals' +import { viteDevframeHub } from '@devframes/vite/hub' +import { createDashboardView } from 'json-render/dashboard' +import UnoCSS from 'unocss/vite' +import { defineConfig } from 'vite' +import { alias } from '../../alias' +import demoDevframe from './src/devframe' +import tabbedToolDevframe from './src/tabbed-tool' +import { unrenderedDockEntry } from './src/unrendered-dock' + +const a11yDevframe = createA11yDevframe() +const assetsDevframe = createAssetsDevframe() +const codeServerDevframe = createCodeServerDevframe() +const gitDevframe = createGitDevframe() +const inspectDevframe = createInspectDevframe() +const messagesDevframe = createMessagesDevframe() +const ogDevframe = createOgDevframe() +const terminalsDevframe = createTerminalsDevframe() +// Colon-free id override: the hub instance derives each frame's mount path +// (`/__devframes//`) from its id, and `:` - which the plugin's default id +// (`devframes:plugin:data-inspector`) carries - is a route-param marker to +// the router underneath. +const dataInspectorDevframe = createDataInspectorDevframe({ id: 'devframes_plugin_data-inspector' }) + +// Minimal hub-local RPCs the vanilla client reads for its message / terminal +// lists. A more ambitious host would standardise these (alongside the +// built-in `hub:commands:execute`); the reference keeps them example-local and +// hands them to `viteDevframeHub` via `rpcDeclarations`. +const messagesList = defineHubRpcFunction({ + name: 'example:vite-devframe-hub:messages:list', + type: 'static', + jsonSerializable: true, + setup: (ctx: DevframeHubContext) => ({ + async handler() { + return Array.from(ctx.messages.entries.values()) + }, + }), +}) + +const terminalsList = defineHubRpcFunction({ + name: 'example:vite-devframe-hub:terminals:list', + type: 'static', + jsonSerializable: true, + setup: (ctx: DevframeHubContext) => ({ + async handler() { + return Array.from(ctx.terminals.sessions.values()).map(s => ({ + id: s.id, + title: s.title, + description: s.description, + status: s.status, + })) + }, + }), +}) + +export default defineConfig({ + resolve: { alias }, + /** + * Dev tooling reached from arbitrary hostnames (LAN IPs, tunnels, tailnets): + * accept any Host header and fall back to the next free port when busy. + */ + server: { allowedHosts: true, strictPort: false }, + plugins: [ + UnoCSS(), + { + /** + * The host registers its own live objects as data-inspector sources - + * the registry is process-global, so this works from any plugin hook. + */ + name: 'vite-devframe-hub:data-sources', + configureServer(server) { + registerDataSource({ + id: 'vite:server', + title: 'Vite Dev Server', + description: 'The live ViteDevServer instance serving this hub.', + icon: 'i-ph:lightning-duotone', + data: () => server, + queries: [ + { title: 'Plugin names', query: 'config.plugins.name' }, + { + title: 'Module graph', + description: 'Client-environment modules with their importers', + query: 'environments.client.moduleGraph.idToModuleMap.mapEntries().value.({ url, type, importers: importers.fromSet().url })', + }, + { + title: 'Resolved config (clean)', + query: 'config', + excludeFunctions: true, + excludeUnderscoreProps: true, + }, + ], + }) + }, + }, + // The whole Vite host: `@devframes/vite/hub` wraps `initHub` and mounts it + // as connect middleware. This host renders its own vanilla client + // (src/client/main.ts) against `@devframes/hub/client`, so it opts out of + // the default `@devframes/hub-ui` slot with `ui: false`. `quiet` silences + // the Vite-DevTools recommendation for this reference example. + viteDevframeHub({ + ui: false, + quiet: true, + register: { + id: 'example:vite-devframe-hub', + name: 'Vite Devframe Hub', + }, + rpcDeclarations: [messagesList, terminalsList], + devframes: [ + demoDevframe, + // Every built-in plugin, dogfooded end-to-end through the hub mount + // path - the same set a full hub UI provider like vite-devtools would surface. + gitDevframe, + terminalsDevframe, + codeServerDevframe, + inspectDevframe, + dataInspectorDevframe, + a11yDevframe, + messagesDevframe, + ogDevframe, + assetsDevframe, + // Shared-iframe soft-navigation demo. The `dock` override marks this + // iframe a `subTabs` anchor, so the client host attaches the frame-nav + // adapter and materializes one client-only dock per tab the SPA + // reports - all sharing this one iframe. + { + devframe: tabbedToolDevframe, + dock: { + category: 'app', + frameId: 'tabbed-tool', + subTabs: { protocol: 'postmessage' }, + }, + }, + ], + /** + * Serve the reference json-render frontend as a prebuilt renderer + * module: the hub publishes it in the renderer manifest and the client + * (src/client/main.ts) imports it lazily the first time a + * `json-render` dock mounts, with no Vue and no renderer code compiled + * into this host's own bundle. + */ + renderers: [jsonRenderUiRenderer()], + configure: async (context) => { + // Seed a sample command directly on the hub so the UI shows something + // even without any plugged-in devframes. + context.commands.register({ + id: 'example:vite-devframe-hub:ping', + title: 'Vite Hub · Ping', + icon: 'ph:bell-duotone', + category: 'kit', + handler: () => 'pong', + }) + + // Dogfood the opt-in JSON-render hub integration: author a view on the + // hub context and project it onto a `json-render` dock, rendered by the + // manifest module above. + const view = createDashboardView(context) + context.docks.register(toJsonRenderDockEntry(view, { + id: 'example:json-render', + title: 'JSON Render', + icon: 'ph:layout-duotone', + category: 'app', + })) + // Bare-specifier client script demo: `importFrom` names the npm + // package itself, imported through Vite's own module graph via the + // host's `clientModuleResolution` (`'/@id/{specifier}'`). The Next + // reference host consumes the same package as a prebuilt + // self-contained bundle instead (see examples/demo-dock-client). + context.docks.register({ + type: 'action', + id: 'example:demo-client-script', + title: 'Client Script Demo', + icon: 'ph:plugs-connected-duotone', + category: 'app', + action: { importFrom: 'demo-dock-client' }, + }) + + // Witness the missing-renderer path: a dock type nothing covers, so + // the client shows its fallback view instead of a dead panel. + context.docks.register(unrenderedDockEntry) + + await context.messages.add({ + level: 'success', + message: 'Vite Devframe Hub started', + description: 'Mounted under /__devframes/ with the built-in plugins.', + }) + }, + }), + ], +}) diff --git a/examples/demo-dock-client/README.md b/examples/demo-dock-client/README.md index 89534a3e2..96b07ab52 100644 --- a/examples/demo-dock-client/README.md +++ b/examples/demo-dock-client/README.md @@ -2,8 +2,8 @@ The shared dock client script the two reference hubs consume in their two supported shapes: one package, both `importFrom` forms: -- **`hub-vite`** registers it by **bare specifier** (`action: { importFrom: 'demo-dock-client' }`). The Vite host advertises `clientModuleResolution: '/@id/{specifier}'` (the `@devframes/vite/hub` default), so the client runtime imports `src/index.ts` through Vite's own module graph, where Vite transforms the linked source directly (no build needed on this path) and resolves its bare `nanoevents` import there too. -- **`hub-next`** mounts the prebuilt **self-contained bundle** (`dist/bundle.mjs`, nanoevents inlined) statically and passes the served URL. Next declares no `clientModuleResolution`, so the URL shape is the supported one there. +- **`custom-hub-vite`** registers it by **bare specifier** (`action: { importFrom: 'demo-dock-client' }`). The Vite host advertises `clientModuleResolution: '/@id/{specifier}'` (the `@devframes/vite/hub` default), so the client runtime imports `src/index.ts` through Vite's own module graph, where Vite transforms the linked source directly (no build needed on this path) and resolves its bare `nanoevents` import there too. +- **`custom-hub-next`** mounts the prebuilt **self-contained bundle** (`dist/bundle.mjs`, nanoevents inlined) statically and passes the served URL. Next declares no `clientModuleResolution`, so the URL shape is the supported one there. The script itself demonstrates the state pattern bare-specifier client scripts should follow: shared state anchored on `globalThis` (`__devframes_demo_dock_client__`), the same design as `vite-plugin-vue-tracer`'s `__vue_tracer__` store, where realm identity is the contract and module identity is best-effort. On each dock activation it bumps the shared counter and reports into the hub's messages feed, naming the URL it was loaded from. diff --git a/examples/demo-dock-client/tsdown.config.ts b/examples/demo-dock-client/tsdown.config.ts index 3e664a022..6c4b5c8a0 100644 --- a/examples/demo-dock-client/tsdown.config.ts +++ b/examples/demo-dock-client/tsdown.config.ts @@ -5,11 +5,11 @@ const tsconfig = '../../tsconfig.base.json' /** * The bare-specifier consumption path needs no build at all: the package's * `.` export points straight at `src/index.ts`, which a Vite host transforms - * like any linked workspace source (hub-vite imports `'demo-dock-client'` + * like any linked workspace source (custom-hub-vite imports `'demo-dock-client'` * via the `/@id/{specifier}` template). What gets built here is only the * **URL-shape** consumption path: * 1. `dist/bundle.mjs`, self-contained (nanoevents inlined), for hosts - * without bare-specifier resolution (hub-next mounts it statically and + * without bare-specifier resolution (custom-hub-next mounts it statically and * passes the served URL as `importFrom`); * 2. `dist/node.mjs`, the node-side path helper the Next host uses to * locate the bundle. diff --git a/examples/hub-deno-minimal/README.md b/examples/hub-deno/README.md similarity index 96% rename from examples/hub-deno-minimal/README.md rename to examples/hub-deno/README.md index 85058f3e2..d8a1ad97a 100644 --- a/examples/hub-deno-minimal/README.md +++ b/examples/hub-deno/README.md @@ -1,9 +1,9 @@ -# hub-deno-minimal +# hub-deno The minimal [Deno](https://deno.com) host for `@devframes/hub`: one `initHub()` call served through `Deno.serve`, with the RPC socket riding a same-origin fetch upgrade. ```sh -pnpm --filter hub-deno-minimal dev +pnpm --filter hub-deno dev ``` Open (the host page carries the floating dock via one script tag) or for the standalone hub UI. diff --git a/examples/hub-deno-minimal/package.json b/examples/hub-deno/package.json similarity index 96% rename from examples/hub-deno-minimal/package.json rename to examples/hub-deno/package.json index 185c1321e..1469b698c 100644 --- a/examples/hub-deno-minimal/package.json +++ b/examples/hub-deno/package.json @@ -1,5 +1,5 @@ { - "name": "hub-deno-minimal", + "name": "hub-deno", "type": "module", "version": "0.9.9", "private": true, diff --git a/examples/hub-deno-minimal/src/hub.ts b/examples/hub-deno/src/hub.ts similarity index 97% rename from examples/hub-deno-minimal/src/hub.ts rename to examples/hub-deno/src/hub.ts index dda668d82..0bd51cc01 100644 --- a/examples/hub-deno-minimal/src/hub.ts +++ b/examples/hub-deno/src/hub.ts @@ -50,14 +50,14 @@ export const hub: HubInstance = globalRef.__hubDenoMinimal ??= initHub({ */ configure(ctx) { ctx.commands.register({ - id: 'example:hub-deno-minimal:ping', + id: 'example:hub-deno:ping', title: 'Deno Hub · Ping', icon: 'ph:bell-duotone', category: 'kit', handler: () => 'pong', }) ctx.rpc.register({ - name: 'example:hub-deno-minimal:probe', + name: 'example:hub-deno:probe', type: 'query', jsonSerializable: true, handler: () => 'pong', diff --git a/examples/hub-deno-minimal/src/server.ts b/examples/hub-deno/src/server.ts similarity index 100% rename from examples/hub-deno-minimal/src/server.ts rename to examples/hub-deno/src/server.ts diff --git a/examples/hub-deno-minimal/tsconfig.json b/examples/hub-deno/tsconfig.json similarity index 100% rename from examples/hub-deno-minimal/tsconfig.json rename to examples/hub-deno/tsconfig.json diff --git a/examples/hub-fastify-minimal/README.md b/examples/hub-fastify/README.md similarity index 95% rename from examples/hub-fastify-minimal/README.md rename to examples/hub-fastify/README.md index e7c7b1dfa..0ce1bffc8 100644 --- a/examples/hub-fastify-minimal/README.md +++ b/examples/hub-fastify/README.md @@ -1,9 +1,9 @@ -# hub-fastify-minimal +# hub-fastify The minimal [Fastify](https://fastify.dev) host for `@devframes/hub`: one `initHub()` call mounted through Fastify's connect-middleware layer, with the RPC socket riding Fastify's own HTTP server. ```sh -pnpm --filter hub-fastify-minimal dev +pnpm --filter hub-fastify dev ``` Open (the host page carries the floating dock via one script tag) or for the standalone hub UI. diff --git a/examples/hub-fastify-minimal/package.json b/examples/hub-fastify/package.json similarity index 96% rename from examples/hub-fastify-minimal/package.json rename to examples/hub-fastify/package.json index fe15c3992..19876e09f 100644 --- a/examples/hub-fastify-minimal/package.json +++ b/examples/hub-fastify/package.json @@ -1,5 +1,5 @@ { - "name": "hub-fastify-minimal", + "name": "hub-fastify", "type": "module", "version": "0.9.9", "private": true, diff --git a/examples/hub-fastify-minimal/src/hub.ts b/examples/hub-fastify/src/hub.ts similarity index 98% rename from examples/hub-fastify-minimal/src/hub.ts rename to examples/hub-fastify/src/hub.ts index 9728fefca..eb01e5166 100644 --- a/examples/hub-fastify-minimal/src/hub.ts +++ b/examples/hub-fastify/src/hub.ts @@ -50,7 +50,7 @@ export const hub: HubInstance = globalRef.__hubFastifyMinimal ??= initHub({ */ configure(ctx) { ctx.commands.register({ - id: 'example:hub-fastify-minimal:ping', + id: 'example:hub-fastify:ping', title: 'Fastify Hub · Ping', icon: 'ph:bell-duotone', category: 'kit', diff --git a/examples/hub-fastify-minimal/src/server.ts b/examples/hub-fastify/src/server.ts similarity index 100% rename from examples/hub-fastify-minimal/src/server.ts rename to examples/hub-fastify/src/server.ts diff --git a/examples/hub-fastify-minimal/tsconfig.json b/examples/hub-fastify/tsconfig.json similarity index 100% rename from examples/hub-fastify-minimal/tsconfig.json rename to examples/hub-fastify/tsconfig.json diff --git a/examples/hub-hono-minimal/README.md b/examples/hub-hono/README.md similarity index 92% rename from examples/hub-hono-minimal/README.md rename to examples/hub-hono/README.md index 56391e3be..520d449ee 100644 --- a/examples/hub-hono-minimal/README.md +++ b/examples/hub-hono/README.md @@ -1,10 +1,10 @@ -# hub-hono-minimal +# hub-hono The minimal [Hono](https://hono.dev) host for `@devframes/hub`: one `initHub()` call, one catch-all route, and the same app file runs on Node and Bun. ```sh -pnpm --filter hub-hono-minimal dev # Node (tsx) -pnpm --filter hub-hono-minimal dev:bun # Bun +pnpm --filter hub-hono dev # Node (tsx) +pnpm --filter hub-hono dev:bun # Bun ``` Open (the host page carries the floating dock via one script tag) or for the standalone hub UI. diff --git a/examples/hub-hono-minimal/package.json b/examples/hub-hono/package.json similarity index 97% rename from examples/hub-hono-minimal/package.json rename to examples/hub-hono/package.json index f586a3095..f673afb5c 100644 --- a/examples/hub-hono-minimal/package.json +++ b/examples/hub-hono/package.json @@ -1,5 +1,5 @@ { - "name": "hub-hono-minimal", + "name": "hub-hono", "type": "module", "version": "0.9.9", "private": true, diff --git a/examples/hub-hono-minimal/src/app.ts b/examples/hub-hono/src/app.ts similarity index 97% rename from examples/hub-hono-minimal/src/app.ts rename to examples/hub-hono/src/app.ts index 36a7a9c29..125ac97c5 100644 --- a/examples/hub-hono-minimal/src/app.ts +++ b/examples/hub-hono/src/app.ts @@ -53,14 +53,14 @@ export const hub: HubInstance = globalRef.__hubHonoMinimal ??= initHub({ */ configure(ctx) { ctx.commands.register({ - id: 'example:hub-hono-minimal:ping', + id: 'example:hub-hono:ping', title: 'Hono Hub · Ping', icon: 'ph:bell-duotone', category: 'kit', handler: () => 'pong', }) ctx.rpc.register({ - name: 'example:hub-hono-minimal:probe', + name: 'example:hub-hono:probe', type: 'query', jsonSerializable: true, handler: () => 'pong', diff --git a/examples/hub-hono-minimal/src/bun.ts b/examples/hub-hono/src/bun.ts similarity index 100% rename from examples/hub-hono-minimal/src/bun.ts rename to examples/hub-hono/src/bun.ts diff --git a/examples/hub-hono-minimal/src/server.ts b/examples/hub-hono/src/server.ts similarity index 100% rename from examples/hub-hono-minimal/src/server.ts rename to examples/hub-hono/src/server.ts diff --git a/examples/hub-hono-minimal/tsconfig.json b/examples/hub-hono/tsconfig.json similarity index 100% rename from examples/hub-hono-minimal/tsconfig.json rename to examples/hub-hono/tsconfig.json diff --git a/examples/hub-next-minimal/src/client/app/%5F_devframes/[[...path]]/route.ts b/examples/hub-next-minimal/src/client/app/%5F_devframes/[[...path]]/route.ts deleted file mode 100644 index 37a27cbc0..000000000 --- a/examples/hub-next-minimal/src/client/app/%5F_devframes/[[...path]]/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ensureHub } from '../../../hub' - -export const runtime = 'nodejs' -export const dynamic = 'force-dynamic' - -// The whole hub namespace behind one catch-all route: `initHub`'s -// web-standard `handler` serves every frame SPA, the discovery endpoints, -// and the embedded/standalone UI. Next reserves `_`-prefixed segment folders, -// so `__devframes` is URL-encoded as `%5F_devframes` in the app directory. -// MCP would speak Streamable-HTTP over GET/POST/DELETE - the same handler. -async function handler(request: Request): Promise { - const hub = await ensureHub() - return hub.handler(request) -} - -export { handler as DELETE, handler as GET, handler as POST } diff --git a/examples/hub-next-minimal/src/client/app/layout.tsx b/examples/hub-next-minimal/src/client/app/layout.tsx deleted file mode 100644 index cb3a99511..000000000 --- a/examples/hub-next-minimal/src/client/app/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { ReactNode } from 'react' - -export const metadata = { - title: 'Hub Next (minimal)', -} - -export default function RootLayout({ children }: { children: ReactNode }) { - return ( - - - {children} - {/* The floating-dock bootstrap - one dev-only module script, the - whole embedded integration. */} - + Hub Vite (minimal) - -
-
-

- - Vite Devframe Hub -

-

Connecting…

-
- -
- - -
- -
- - -
-
- -
-
-

Transport

-

Connecting…

- -
-
- -
-

Commands

-
  • Waiting for snapshot…
-
- -
-
- -
-

Messages

-
  • No messages yet.
-
- -
-

Terminals

-
  • No terminal sessions.
-
-
-
- + +

Hub Vite (minimal)

+

This page is the host app. The devtools ride along:

+ diff --git a/examples/hub-vite/package.json b/examples/hub-vite/package.json index 9963a12cf..72fa1c233 100644 --- a/examples/hub-vite/package.json +++ b/examples/hub-vite/package.json @@ -3,16 +3,17 @@ "type": "module", "version": "0.9.9", "private": true, - "description": "Vite reference hub that exercises every devframe hub subsystem end to end.", + "description": "Minimal Vite host that mounts the devframe hub as dev middleware.", "homepage": "https://github.com/devframes/devframe/tree/main/examples/hub-vite", "scripts": { - "dev": "pnpm -C ../.. run build && vite", + "dev": "vite", "build": "vite build", + "preview": "vite preview --host", "typecheck": "tsc --noEmit" }, "dependencies": { - "@antfu/design": "catalog:frontend", "@devframes/hub": "workspace:*", + "@devframes/hub-ui": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", "@devframes/plugin-a11y": "workspace:*", @@ -25,15 +26,10 @@ "@devframes/plugin-og": "workspace:*", "@devframes/plugin-terminals": "workspace:*", "@devframes/vite": "workspace:*", - "colorjs.io": "catalog:frontend", - "demo-dock-client": "workspace:*", - "devframe": "workspace:*", - "dompurify": "catalog:frontend", - "json-render": "workspace:*" + "devframe": "workspace:*" }, "devDependencies": { - "@iconify-json/ph": "catalog:frontend", - "unocss": "catalog:frontend", + "@types/node": "catalog:types", "vite": "catalog:build" } } diff --git a/examples/hub-vite/tsconfig.json b/examples/hub-vite/tsconfig.json index 46dbffd99..906a7ddc8 100644 --- a/examples/hub-vite/tsconfig.json +++ b/examples/hub-vite/tsconfig.json @@ -1,12 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "lib": ["ESNext", "DOM"], - "module": "ESNext", - "moduleResolution": "Bundler", - "noEmit": true, - "esModuleInterop": true, - "isolatedDeclarations": false + "lib": ["esnext", "dom"], + "types": ["node"] }, - "include": ["src", "vite.config.ts"] + "include": ["vite.config.ts"], + "exclude": ["dist", "node_modules"] } diff --git a/examples/hub-vite/vite.config.ts b/examples/hub-vite/vite.config.ts index 638eb5fe3..0e524826c 100644 --- a/examples/hub-vite/vite.config.ts +++ b/examples/hub-vite/vite.config.ts @@ -1,202 +1,106 @@ -import type { DevframeHubContext } from '@devframes/hub/node' -import { defineHubRpcFunction } from '@devframes/hub' +import type { DevframeJsonRenderSpec } from '@devframes/json-render' +import type { DevframeJsonRenderDockEntry } from '@devframes/json-render/hub' +import { createUi } from '@devframes/hub-ui' import { jsonRenderUiRenderer } from '@devframes/json-render-ui/hub' -import { toJsonRenderDockEntry } from '@devframes/json-render/hub' -import createA11yDevframe from '@devframes/plugin-a11y' -import createAssetsDevframe from '@devframes/plugin-assets' -import createCodeServerDevframe from '@devframes/plugin-code-server' +import { createA11yDevframe } from '@devframes/plugin-a11y' +import { createAssetsDevframe } from '@devframes/plugin-assets' +import { createCodeServerDevframe } from '@devframes/plugin-code-server' import { createDataInspectorDevframe } from '@devframes/plugin-data-inspector' -import { registerDataSource } from '@devframes/plugin-data-inspector/registry' -import createGitDevframe from '@devframes/plugin-git' -import createInspectDevframe from '@devframes/plugin-inspect' -import createMessagesDevframe from '@devframes/plugin-messages' -import createOgDevframe from '@devframes/plugin-og' -import createTerminalsDevframe from '@devframes/plugin-terminals' +import { createGitDevframe } from '@devframes/plugin-git' +import { createInspectDevframe } from '@devframes/plugin-inspect' +import { createMessagesDevframe } from '@devframes/plugin-messages' +import { createOgDevframe } from '@devframes/plugin-og' +import { createTerminalsDevframe } from '@devframes/plugin-terminals' import { viteDevframeHub } from '@devframes/vite/hub' -import { createDashboardView } from 'json-render/dashboard' -import UnoCSS from 'unocss/vite' import { defineConfig } from 'vite' -import { alias } from '../../alias' -import demoDevframe from './src/devframe' -import tabbedToolDevframe from './src/tabbed-tool' -import { unrenderedDockEntry } from './src/unrendered-dock' -const a11yDevframe = createA11yDevframe() -const assetsDevframe = createAssetsDevframe() -const codeServerDevframe = createCodeServerDevframe() -const gitDevframe = createGitDevframe() -const inspectDevframe = createInspectDevframe() -const messagesDevframe = createMessagesDevframe() -const ogDevframe = createOgDevframe() -const terminalsDevframe = createTerminalsDevframe() -// Colon-free id override: the hub instance derives each frame's mount path -// (`/__devframes//`) from its id, and `:` - which the plugin's default id -// (`devframes:plugin:data-inspector`) carries - is a route-param marker to -// the router underneath. -const dataInspectorDevframe = createDataInspectorDevframe({ id: 'devframes_plugin_data-inspector' }) +// Every built-in plugin, dogfooded end to end through the hub mount path. +// `data-inspector`'s default id carries `:` (a route-param marker), so it +// gets a colon-free id override to be a valid `/` segment; the +// assets watcher is off since this host demonstrates mounting, not authoring. +const builtinDevframes = [ + createGitDevframe(), + createTerminalsDevframe(), + createCodeServerDevframe(), + createInspectDevframe(), + createDataInspectorDevframe({ id: 'devframes_plugin_data-inspector' }), + createA11yDevframe(), + createMessagesDevframe(), + // `defaultUrl` doubles as the snapshot the static build bakes for the OG + // panel (its dump fetches the page at build time). + createOgDevframe({ defaultUrl: 'https://vite.dev' }), + createAssetsDevframe({ watch: false }), +] -// Minimal hub-local RPCs the vanilla client reads for its message / terminal -// lists. A more ambitious host would standardise these (alongside the -// built-in `hub:commands:execute`); the reference keeps them example-local and -// hands them to `viteDevframeHub` via `rpcDeclarations`. -const messagesList = defineHubRpcFunction({ - name: 'example:vite-devframe-hub:messages:list', - type: 'static', - jsonSerializable: true, - setup: (ctx: DevframeHubContext) => ({ - async handler() { - return Array.from(ctx.messages.entries.values()) - }, - }), -}) +// A server-authored JSON-render dock: the whole view is this serializable +// spec, with no client build. It renders through whatever `'json-render'` +// renderer the hub composes (below, the reference `@devframes/json-render-ui` +// module); without one, the hub UI provider shows its missing-renderer fallback. +const jsonRenderSpec: DevframeJsonRenderSpec = { + root: 'root', + elements: { + root: { type: 'Card', props: { title: 'JSON Render' }, children: ['body'] }, + body: { type: 'Stack', props: { gap: 8 }, children: ['text', 'badge'] }, + text: { type: 'Text', props: { text: 'This dock is a JSON spec authored on the server and rendered by the module composed via initHub({ renderers }).' }, children: [] }, + badge: { type: 'Badge', props: { text: 'renderer manifest', variant: 'info' }, children: [] }, + }, +} -const terminalsList = defineHubRpcFunction({ - name: 'example:vite-devframe-hub:terminals:list', - type: 'static', - jsonSerializable: true, - setup: (ctx: DevframeHubContext) => ({ - async handler() { - return Array.from(ctx.terminals.sessions.values()).map(s => ({ - id: s.id, - title: s.title, - description: s.description, - status: s.status, - })) - }, - }), -}) +const jsonRenderDock: DevframeJsonRenderDockEntry = { + type: 'json-render', + id: 'json-render-demo', + title: 'JSON Render', + icon: 'ph:layout-duotone', + view: { spec: jsonRenderSpec }, +} +/** + * The minimal Vite host: one `viteDevframeHub()` plugin from + * `@devframes/vite/hub`. It wraps `initHub` (mounted as connect middleware on + * Vite's dev server, sharing its HTTP server for the WS upgrade at + * `/__devframes/__ws`) and injects the UI's `embedded.js` bootstrap into the + * host page, the whole embedded integration in one call. `quiet` silences the + * Vite-DevTools recommendation for this reference example. + */ export default defineConfig({ - resolve: { alias }, /** - * Dev tooling reached from arbitrary hostnames (LAN IPs, tunnels, tailnets): - * accept any Host header and fall back to the next free port when busy. + * Dev tooling reached from arbitrary hostnames (LAN IPs, tunnels): accept + * any Host header and fall back to the next free port when busy. */ server: { allowedHosts: true, strictPort: false }, plugins: [ - UnoCSS(), - { - /** - * The host registers its own live objects as data-inspector sources - - * the registry is process-global, so this works from any plugin hook. - */ - name: 'vite-devframe-hub:data-sources', - configureServer(server) { - registerDataSource({ - id: 'vite:server', - title: 'Vite Dev Server', - description: 'The live ViteDevServer instance serving this hub.', - icon: 'i-ph:lightning-duotone', - data: () => server, - queries: [ - { title: 'Plugin names', query: 'config.plugins.name' }, - { - title: 'Module graph', - description: 'Client-environment modules with their importers', - query: 'environments.client.moduleGraph.idToModuleMap.mapEntries().value.({ url, type, importers: importers.fromSet().url })', - }, - { - title: 'Resolved config (clean)', - query: 'config', - excludeFunctions: true, - excludeUnderscoreProps: true, - }, - ], - }) - }, - }, - // The whole Vite host: `@devframes/vite/hub` wraps `initHub` and mounts it - // as connect middleware. This host renders its own vanilla client - // (src/client/main.ts) against `@devframes/hub/client`, so it opts out of - // the default `@devframes/hub-ui` slot with `ui: false`. `quiet` silences - // the Vite-DevTools recommendation for this reference example. viteDevframeHub({ - ui: false, quiet: true, - register: { - id: 'example:vite-devframe-hub', - name: 'Vite Devframe Hub', - }, - rpcDeclarations: [messagesList, terminalsList], - devframes: [ - demoDevframe, - // Every built-in plugin, dogfooded end-to-end through the hub mount - // path - the same set a full hub UI provider like vite-devtools would surface. - gitDevframe, - terminalsDevframe, - codeServerDevframe, - inspectDevframe, - dataInspectorDevframe, - a11yDevframe, - messagesDevframe, - ogDevframe, - assetsDevframe, - // Shared-iframe soft-navigation demo. The `dock` override marks this - // iframe a `subTabs` anchor, so the client host attaches the frame-nav - // adapter and materializes one client-only dock per tab the SPA - // reports - all sharing this one iframe. - { - devframe: tabbedToolDevframe, - dock: { - category: 'app', - frameId: 'tabbed-tool', - subTabs: { protocol: 'postmessage' }, - }, - }, - ], + /** + * Bake the hub into `vite build` output too (`dist/__devframes/`), so + * the production bundle ships the same docks against a `static` + * backend: baked reads (each panel's snapshot RPCs), no live server. + * Preview with `pnpm build && pnpm preview`. + */ + build: true, + devframes: builtinDevframes, + /** + * Rebrand the reference UI to Vite's own purple in one field, no CSS: + * `createUi`'s `branding` option publishes `ConnectionMeta.configs.ui.branding`, + * which the dock reads at connect time and feeds into `--devframe-primary` + * (see `@devframes/hub-ui`'s `primary-ramp.css`). Passing `ui` overrides + * the default `createUi()` the plugin would otherwise use. + */ + ui: createUi({ branding: { primaryColor: '#646cff', productName: 'Devframes on Vite' } }), /** * Serve the reference json-render frontend as a prebuilt renderer - * module: the hub publishes it in the renderer manifest and the client - * (src/client/main.ts) imports it lazily the first time a - * `json-render` dock mounts, with no Vue and no renderer code compiled - * into this host's own bundle. + * module, the one-liner that makes `'json-render'` docks render in + * the prebuilt hub UI provider. Swap it for any community implementation of + * the same contract. */ renderers: [jsonRenderUiRenderer()], - configure: async (context) => { - // Seed a sample command directly on the hub so the UI shows something - // even without any plugged-in devframes. - context.commands.register({ - id: 'example:vite-devframe-hub:ping', - title: 'Vite Hub · Ping', - icon: 'ph:bell-duotone', - category: 'kit', - handler: () => 'pong', - }) - - // Dogfood the opt-in JSON-render hub integration: author a view on the - // hub context and project it onto a `json-render` dock, rendered by the - // manifest module above. - const view = createDashboardView(context) - context.docks.register(toJsonRenderDockEntry(view, { - id: 'example:json-render', - title: 'JSON Render', - icon: 'ph:layout-duotone', - category: 'app', - })) - // Bare-specifier client script demo: `importFrom` names the npm - // package itself, imported through Vite's own module graph via the - // host's `clientModuleResolution` (`'/@id/{specifier}'`). The Next - // reference host consumes the same package as a prebuilt - // self-contained bundle instead (see examples/demo-dock-client). - context.docks.register({ - type: 'action', - id: 'example:demo-client-script', - title: 'Client Script Demo', - icon: 'ph:plugs-connected-duotone', - category: 'app', - action: { importFrom: 'demo-dock-client' }, - }) - - // Witness the missing-renderer path: a dock type nothing covers, so - // the client shows its fallback view instead of a dead panel. - context.docks.register(unrenderedDockEntry) - - await context.messages.add({ - level: 'success', - message: 'Vite Devframe Hub started', - description: 'Mounted under /__devframes/ with the built-in plugins.', - }) + configure(ctx) { + ctx.docks.register(jsonRenderDock) }, + // Gate with devframe's interactive OTP (the default): the hub prints a + // 6-digit code + magic link on startup, and the reference UI's + // authorization view exchanges it for a bearer token. See + // docs/content/1.guide/13.security.md. }), ], }) diff --git a/knip.jsonc b/knip.jsonc index e7939d4e1..9ccebfac9 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -59,7 +59,7 @@ // to the package, so it mis-reports the (genuinely-listed) dep as unlisted. "ignoreDependencies": ["ai", "@ai-sdk/vue", "@comark/nuxt.*", "@nuxt/ui.*", "@shikijs/magic-move"] }, - "examples/hub-next": { + "examples/custom-hub-next": { // The React client build's root sits at `src/client` (`next dev // src/client`), not the workspace root, so knip's default PostCSS // config glob (workspace-root `postcss.config.*`) misses it. @@ -81,7 +81,7 @@ "@devframes/plugin-terminals" ] }, - "examples/hub-next-minimal": { + "examples/hub-next": { // `BUILTIN_PLUGIN_PACKAGES` (`src/client/hub.ts`) lists these as string // literals loaded through a runtime `import()` carrying // `webpackIgnore`/`turbopackIgnore`, deliberately invisible to bundlers @@ -98,7 +98,7 @@ "@devframes/plugin-terminals" ] }, - "examples/hub-vite": { + "examples/custom-hub-vite": { // `demo-dock-client` is consumed only as a runtime string: the demo // dock's `action.importFrom` bare specifier, resolved by Vite through // the hub's `clientModuleResolution` template (`/@id/{specifier}`), @@ -122,7 +122,7 @@ // The Deno entry runs via `deno run src/server.ts`, a command string knip // doesn't parse (unlike `tsx`/`bun`), so name the entry explicitly; it // pulls in `src/hub.ts` and every plugin dependency from there. - "examples/hub-deno-minimal": { + "examples/hub-deno": { "entry": ["src/server.ts"] }, "packages/devframe": { diff --git a/packages/devframe/src/node/diagnostics.ts b/packages/devframe/src/node/diagnostics.ts index 0ca5bec8f..a075e3d47 100644 --- a/packages/devframe/src/node/diagnostics.ts +++ b/packages/devframe/src/node/diagnostics.ts @@ -209,7 +209,7 @@ export const diagnostics = defineDiagnostics({ DF0076: { why: (p: { runtime: string }) => `\`attach\` / \`handleUpgrade\` drive a raw \`node:http\` upgrade into crossws's Node adapter, which refuses to run on ${p.runtime}.`, - fix: 'On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno-minimal example), or connect over the SSE endpoint instead.', + fix: 'On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno example), or connect over the SSE endpoint instead.', }, }, }) diff --git a/packages/hub-ui/playground/hub-plugin.ts b/packages/hub-ui/playground/hub-plugin.ts index efa1aaa41..97d194af8 100644 --- a/packages/hub-ui/playground/hub-plugin.ts +++ b/packages/hub-ui/playground/hub-plugin.ts @@ -13,7 +13,7 @@ import { seedPlayground } from './seed' * Git dashboard, below) so the dock bar has real content to switch between, * not just the client-only entries `seed.ts` registers. No `ui` slot, no * renderer manifest: this playground is developing hub-ui itself, not - * exercising the wider hub protocol (`examples/hub-vite` already does that). + * exercising the wider hub protocol (`examples/custom-hub-vite` already does that). * * A hand-rolled slice of `@devframes/vite/hub` rather than that package * itself, because pulling it in here would make `@devframes/hub-ui` and diff --git a/packages/hub-ui/playground/seed.ts b/packages/hub-ui/playground/seed.ts index 5e316f12a..a93adaece 100644 --- a/packages/hub-ui/playground/seed.ts +++ b/packages/hub-ui/playground/seed.ts @@ -7,7 +7,7 @@ import { PLAYGROUND_GROUP_ID } from './constants' * A dock type no renderer covers; registering it exercises the viewer's * fallback view (`No renderer for "playground-unrendered" in the current * environment`) instead of leaving the dock bar empty. Mirrors - * `examples/hub-vite/src/unrendered-dock.ts`. + * `examples/custom-hub-vite/src/unrendered-dock.ts`. */ interface PlaygroundUnrenderedDockEntry extends DevframeDockEntryBase { type: 'playground-unrendered' diff --git a/playwright.config.ts b/playwright.config.ts index 5ff649fcb..8c3f05f86 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -93,7 +93,7 @@ export default defineConfig({ }, { command: 'pnpm exec next dev src/client -p 9878', - cwd: 'examples/hub-next', + cwd: 'examples/custom-hub-next', env: { PORT: '9878', DEVFRAME_INSTANCES_DIR: nextHubRegistry }, url: 'http://localhost:9878/', timeout: 120_000, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1cb78833..a9b7653ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -617,6 +617,176 @@ importers: specifier: catalog:build version: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0) + examples/custom-hub-next: + dependencies: + '@antfu/design': + specifier: catalog:frontend + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.8.1)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/hub': + specifier: workspace:* + version: link:../../packages/hub + '@devframes/json-render': + specifier: workspace:* + version: link:../../packages/json-render + '@devframes/json-render-ui': + specifier: workspace:* + version: link:../../packages/json-render-ui + '@devframes/next': + specifier: workspace:* + version: link:../../packages/next + '@devframes/plugin-a11y': + specifier: workspace:* + version: link:../../plugins/a11y + '@devframes/plugin-assets': + specifier: workspace:* + version: link:../../plugins/assets + '@devframes/plugin-code-server': + specifier: workspace:* + version: link:../../plugins/code-server + '@devframes/plugin-data-inspector': + specifier: workspace:* + version: link:../../plugins/data-inspector + '@devframes/plugin-git': + specifier: workspace:* + version: link:../../plugins/git + '@devframes/plugin-inspect': + specifier: workspace:* + version: link:../../plugins/inspect + '@devframes/plugin-messages': + specifier: workspace:* + version: link:../../plugins/messages + '@devframes/plugin-og': + specifier: workspace:* + version: link:../../plugins/og + '@devframes/plugin-terminals': + specifier: workspace:* + version: link:../../plugins/terminals + '@json-render/react': + specifier: catalog:frontend + version: 0.20.0(react@19.2.8)(zod@4.5.4) + colorjs.io: + specifier: catalog:frontend + version: 0.7.1 + demo-dock-client: + specifier: workspace:* + version: link:../demo-dock-client + devframe: + specifier: workspace:* + version: link:../../packages/devframe + dompurify: + specifier: catalog:frontend + version: 3.4.14 + json-render: + specifier: workspace:* + version: link:../json-render + next: + specifier: catalog:frontend + version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.62.1)(@types/node@26.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: + specifier: catalog:frontend + version: 19.2.8 + react-dom: + specifier: catalog:frontend + version: 19.2.8(react@19.2.8) + devDependencies: + '@iconify-json/ph': + specifier: catalog:frontend + version: 1.2.2 + '@types/react': + specifier: catalog:types + version: 19.2.18 + '@types/react-dom': + specifier: catalog:types + version: 19.2.5(@types/react@19.2.18) + '@unocss/postcss': + specifier: catalog:frontend + version: 66.8.1(postcss@8.5.26) + get-port-please: + specifier: catalog:deps + version: 3.2.0 + pathe: + specifier: catalog:deps + version: 2.0.3 + unocss: + specifier: catalog:frontend + version: 66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) + vitest: + specifier: catalog:testing + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) + ws: + specifier: catalog:deps + version: 8.21.3 + + examples/custom-hub-vite: + dependencies: + '@antfu/design': + specifier: catalog:frontend + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.8.1)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/hub': + specifier: workspace:* + version: link:../../packages/hub + '@devframes/json-render': + specifier: workspace:* + version: link:../../packages/json-render + '@devframes/json-render-ui': + specifier: workspace:* + version: link:../../packages/json-render-ui + '@devframes/plugin-a11y': + specifier: workspace:* + version: link:../../plugins/a11y + '@devframes/plugin-assets': + specifier: workspace:* + version: link:../../plugins/assets + '@devframes/plugin-code-server': + specifier: workspace:* + version: link:../../plugins/code-server + '@devframes/plugin-data-inspector': + specifier: workspace:* + version: link:../../plugins/data-inspector + '@devframes/plugin-git': + specifier: workspace:* + version: link:../../plugins/git + '@devframes/plugin-inspect': + specifier: workspace:* + version: link:../../plugins/inspect + '@devframes/plugin-messages': + specifier: workspace:* + version: link:../../plugins/messages + '@devframes/plugin-og': + specifier: workspace:* + version: link:../../plugins/og + '@devframes/plugin-terminals': + specifier: workspace:* + version: link:../../plugins/terminals + '@devframes/vite': + specifier: workspace:* + version: link:../../packages/vite + colorjs.io: + specifier: catalog:frontend + version: 0.7.1 + demo-dock-client: + specifier: workspace:* + version: link:../demo-dock-client + devframe: + specifier: workspace:* + version: link:../../packages/devframe + dompurify: + specifier: catalog:frontend + version: 3.4.14 + json-render: + specifier: workspace:* + version: link:../json-render + devDependencies: + '@iconify-json/ph': + specifier: catalog:frontend + version: 1.2.2 + unocss: + specifier: catalog:frontend + version: 66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) + vite: + specifier: catalog:build + version: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0) + examples/demo-dock-client: dependencies: nanoevents: @@ -676,7 +846,7 @@ importers: specifier: catalog:deps version: 8.21.3 - examples/hub-deno-minimal: + examples/hub-deno: dependencies: '@devframes/hub': specifier: workspace:* @@ -719,7 +889,7 @@ importers: specifier: catalog:types version: 26.4.0 - examples/hub-fastify-minimal: + examples/hub-fastify: dependencies: '@devframes/hub': specifier: workspace:* @@ -771,7 +941,7 @@ importers: specifier: catalog:build version: 4.23.13 - examples/hub-hono-minimal: + examples/hub-hono: dependencies: '@devframes/hub': specifier: workspace:* @@ -824,106 +994,6 @@ importers: version: 4.23.13 examples/hub-next: - dependencies: - '@antfu/design': - specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.8.1)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) - '@devframes/hub': - specifier: workspace:* - version: link:../../packages/hub - '@devframes/json-render': - specifier: workspace:* - version: link:../../packages/json-render - '@devframes/json-render-ui': - specifier: workspace:* - version: link:../../packages/json-render-ui - '@devframes/next': - specifier: workspace:* - version: link:../../packages/next - '@devframes/plugin-a11y': - specifier: workspace:* - version: link:../../plugins/a11y - '@devframes/plugin-assets': - specifier: workspace:* - version: link:../../plugins/assets - '@devframes/plugin-code-server': - specifier: workspace:* - version: link:../../plugins/code-server - '@devframes/plugin-data-inspector': - specifier: workspace:* - version: link:../../plugins/data-inspector - '@devframes/plugin-git': - specifier: workspace:* - version: link:../../plugins/git - '@devframes/plugin-inspect': - specifier: workspace:* - version: link:../../plugins/inspect - '@devframes/plugin-messages': - specifier: workspace:* - version: link:../../plugins/messages - '@devframes/plugin-og': - specifier: workspace:* - version: link:../../plugins/og - '@devframes/plugin-terminals': - specifier: workspace:* - version: link:../../plugins/terminals - '@json-render/react': - specifier: catalog:frontend - version: 0.20.0(react@19.2.8)(zod@4.5.4) - colorjs.io: - specifier: catalog:frontend - version: 0.7.1 - demo-dock-client: - specifier: workspace:* - version: link:../demo-dock-client - devframe: - specifier: workspace:* - version: link:../../packages/devframe - dompurify: - specifier: catalog:frontend - version: 3.4.14 - json-render: - specifier: workspace:* - version: link:../json-render - next: - specifier: catalog:frontend - version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.62.1)(@types/node@26.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: - specifier: catalog:frontend - version: 19.2.8 - react-dom: - specifier: catalog:frontend - version: 19.2.8(react@19.2.8) - devDependencies: - '@iconify-json/ph': - specifier: catalog:frontend - version: 1.2.2 - '@types/react': - specifier: catalog:types - version: 19.2.18 - '@types/react-dom': - specifier: catalog:types - version: 19.2.5(@types/react@19.2.18) - '@unocss/postcss': - specifier: catalog:frontend - version: 66.8.1(postcss@8.5.26) - get-port-please: - specifier: catalog:deps - version: 3.2.0 - pathe: - specifier: catalog:deps - version: 2.0.3 - unocss: - specifier: catalog:frontend - version: 66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) - vitest: - specifier: catalog:testing - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) - ws: - specifier: catalog:deps - version: 8.21.3 - - examples/hub-next-minimal: dependencies: '@devframes/hub': specifier: workspace:* @@ -987,7 +1057,7 @@ importers: specifier: catalog:types version: 19.2.5(@types/react@19.2.18) - examples/hub-nitro-minimal: + examples/hub-nitro: dependencies: '@devframes/hub': specifier: workspace:* @@ -1033,7 +1103,7 @@ importers: specifier: catalog:types version: 26.4.0 - examples/hub-rsbuild-minimal: + examples/hub-rsbuild: dependencies: '@devframes/hub': specifier: workspace:* @@ -1085,7 +1155,7 @@ importers: specifier: catalog:types version: 26.4.0 - examples/hub-sveltekit-minimal: + examples/hub-sveltekit: dependencies: '@devframes/hub': specifier: workspace:* @@ -1144,76 +1214,6 @@ importers: version: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0) examples/hub-vite: - dependencies: - '@antfu/design': - specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.8.1)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) - '@devframes/hub': - specifier: workspace:* - version: link:../../packages/hub - '@devframes/json-render': - specifier: workspace:* - version: link:../../packages/json-render - '@devframes/json-render-ui': - specifier: workspace:* - version: link:../../packages/json-render-ui - '@devframes/plugin-a11y': - specifier: workspace:* - version: link:../../plugins/a11y - '@devframes/plugin-assets': - specifier: workspace:* - version: link:../../plugins/assets - '@devframes/plugin-code-server': - specifier: workspace:* - version: link:../../plugins/code-server - '@devframes/plugin-data-inspector': - specifier: workspace:* - version: link:../../plugins/data-inspector - '@devframes/plugin-git': - specifier: workspace:* - version: link:../../plugins/git - '@devframes/plugin-inspect': - specifier: workspace:* - version: link:../../plugins/inspect - '@devframes/plugin-messages': - specifier: workspace:* - version: link:../../plugins/messages - '@devframes/plugin-og': - specifier: workspace:* - version: link:../../plugins/og - '@devframes/plugin-terminals': - specifier: workspace:* - version: link:../../plugins/terminals - '@devframes/vite': - specifier: workspace:* - version: link:../../packages/vite - colorjs.io: - specifier: catalog:frontend - version: 0.7.1 - demo-dock-client: - specifier: workspace:* - version: link:../demo-dock-client - devframe: - specifier: workspace:* - version: link:../../packages/devframe - dompurify: - specifier: catalog:frontend - version: 3.4.14 - json-render: - specifier: workspace:* - version: link:../json-render - devDependencies: - '@iconify-json/ph': - specifier: catalog:frontend - version: 1.2.2 - unocss: - specifier: catalog:frontend - version: 66.8.1(@unocss/postcss@66.8.1(postcss@8.5.26))(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) - vite: - specifier: catalog:build - version: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0) - - examples/hub-vite-minimal: dependencies: '@devframes/hub': specifier: workspace:* @@ -7675,11 +7675,6 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.43: - resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} - engines: {node: '>=6.0.0'} - hasBin: true - baseline-browser-mapping@2.11.20: resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} engines: {node: '>=6.0.0'} @@ -7782,9 +7777,6 @@ packages: caniuse-api@4.0.0: resolution: {integrity: sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==} - caniuse-lite@1.0.30001806: - resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} - caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} @@ -17902,7 +17894,7 @@ snapshots: autoprefixer@10.5.4(postcss@8.5.26): dependencies: browserslist: 4.28.6 - caniuse-lite: 1.0.30001806 + caniuse-lite: 1.0.30001810 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.26 @@ -17983,8 +17975,6 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.43: {} - baseline-browser-mapping@2.11.20: {} beautiful-mermaid@1.1.3: @@ -18030,8 +18020,8 @@ snapshots: browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.43 - caniuse-lite: 1.0.30001806 + baseline-browser-mapping: 2.11.20 + caniuse-lite: 1.0.30001810 electron-to-chromium: 1.5.393 node-releases: 2.0.51 update-browserslist-db: 1.2.3(browserslist@4.28.6) @@ -18109,9 +18099,7 @@ snapshots: caniuse-api@4.0.0: dependencies: browserslist: 4.28.6 - caniuse-lite: 1.0.30001806 - - caniuse-lite@1.0.30001806: {} + caniuse-lite: 1.0.30001810 caniuse-lite@1.0.30001810: {} diff --git a/scripts/smoke-bun.ts b/scripts/smoke-bun.ts index 89ac05035..eb1f58be8 100644 --- a/scripts/smoke-bun.ts +++ b/scripts/smoke-bun.ts @@ -3,7 +3,7 @@ * * bun scripts/smoke-bun.ts * - * Boots `examples/hub-hono-minimal/src/bun.ts` (Bun's fetch-upgrade wiring + * Boots `examples/hub-hono/src/bun.ts` (Bun's fetch-upgrade wiring * over the hub's context) and exercises four surfaces end to end: HTTP through * the catch-all handler, the discovery documents, the embedded bootstrap, and * an RPC round-trip over a same-origin WebSocket upgrade, with no side-car port @@ -12,7 +12,7 @@ * Prerequisites: `pnpm install && pnpm build` (the hub serves built dists). */ import process from 'node:process' -import { startBunServer } from '../examples/hub-hono-minimal/src/bun' +import { startBunServer } from '../examples/hub-hono/src/bun' function fail(step: string, detail: unknown): never { console.error(`✗ ${step}:`, detail) @@ -59,7 +59,7 @@ const rpc = createRpcClient({}, { const handshake = await rpc.$call('anonymous:devframe:auth', { authToken: '', ua: 'smoke', origin }) as { isTrusted: boolean } if (!handshake.isTrusted) fail('handshake', handshake) -const pong = await rpc.$call('example:hub-hono-minimal:probe') +const pong = await rpc.$call('example:hub-hono:probe') if (pong !== 'pong') fail('rpc probe', pong) console.log('✓ WS RPC round-trip over the fetch-upgrade tier: probe →', pong) diff --git a/scripts/verify-typecheck-coverage.ts b/scripts/verify-typecheck-coverage.ts index 73e4b5bd9..9e02c7b22 100644 --- a/scripts/verify-typecheck-coverage.ts +++ b/scripts/verify-typecheck-coverage.ts @@ -32,8 +32,8 @@ const WORKSPACE_PATTERNS = ['packages/*', 'plugins/*', 'examples/*', 'starter', * already has one). */ const EXCEPTIONS: Record = { - 'examples/hub-next': 'packages/hub/src/node/host-terminals.ts types a child-process env as NodeJS.ProcessEnv, and Next.js\'s ambient types require a literal NODE_ENV on that interface once this app pulls hub into its program. See plans/README.md "Execution notes" for plan 001.', - 'examples/hub-next-minimal': 'Same Next.js + hub NODE_ENV ambient conflict as examples/hub-next; the minimal Next host pulls hub into its program too.', + 'examples/custom-hub-next': 'packages/hub/src/node/host-terminals.ts types a child-process env as NodeJS.ProcessEnv, and Next.js\'s ambient types require a literal NODE_ENV on that interface once this app pulls hub into its program. See plans/README.md "Execution notes" for plan 001.', + 'examples/hub-next': 'Same Next.js + hub NODE_ENV ambient conflict as examples/custom-hub-next; the minimal Next host pulls hub into its program too.', 'docs': 'Nuxt app extending the comark-docs layer: tsconfig.json only holds project references into generated `.nuxt/tsconfig.*.json`, which exist only after `nuxt prepare` resolves the layer, so type-checking it would drag a full Nuxt prepare into the Turbo graph.', } diff --git a/skills/devframe/SKILL.md b/skills/devframe/SKILL.md index 46edb2435..c637aaae2 100644 --- a/skills/devframe/SKILL.md +++ b/skills/devframe/SKILL.md @@ -510,7 +510,7 @@ Plus broadcasts (`devframe:docks:activate`, `devframe:terminals:updated`, `devfr The hub is headless; `DevframeHubUi` is pure data (`viewer` / `embedded` / `assets` / `setup`). `@devframes/hub-ui`'s `createUi()` is the reference hub UI provider - a standalone devtools page plus a floating dock injected via one `