Skip to content

Commit dfeffc2

Browse files
antfuclaude
andauthored
feat(devframe)!: switch DevTools mount-path convention from /. to /__ (#315)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 20e68f5 commit dfeffc2

50 files changed

Lines changed: 133 additions & 133 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ Monorepo (`pnpm` workspaces + `turbo`). ESM TypeScript; bundled with `tsdown`. P
1212
| `packages/core` | `@vitejs/devtools` | Vite plugin, CLI, standalone/webcomponents client. Wraps devframe's createHostContext with the Vite plugin scan |
1313
| `packages/kit` | `@vitejs/devtools-kit` | Vite-specific superset of devframe — adds PluginWithDevTools, ViteDevToolsNodeContext, and re-exports devframe's public types |
1414
| `packages/ui` | `@vitejs/devtools-ui` | Shared UI components, composables, and UnoCSS preset (`presetDevToolsUI`). Private, not published |
15-
| `packages/rolldown` | `@vitejs/devtools-rolldown` | Nuxt UI for Rolldown build data. Serves at `/.devtools-rolldown/` |
16-
| `packages/vite` | `@vitejs/devtools-vite` | Nuxt UI for Vite DevTools (WIP). Serves at `/.devtools-vite/` |
17-
| `packages/self-inspect` | `@vitejs/devtools-self-inspect` | Meta-introspection — DevTools for the DevTools. Serves at `/.devtools-self-inspect/` |
15+
| `packages/rolldown` | `@vitejs/devtools-rolldown` | Nuxt UI for Rolldown build data. Serves at `/__devtools-rolldown/` |
16+
| `packages/vite` | `@vitejs/devtools-vite` | Nuxt UI for Vite DevTools (WIP). Serves at `/__devtools-vite/` |
17+
| `packages/self-inspect` | `@vitejs/devtools-self-inspect` | Meta-introspection — DevTools for the DevTools. Serves at `/__devtools-self-inspect/` |
1818
| `packages/webext` || Browser extension scaffolding (ancillary) |
1919

2020
Other top-level directories:
@@ -63,7 +63,7 @@ pnpm -C docs run docs # docs dev server
6363
- Use workspace aliases from `alias.ts`.
6464
- RPC functions must use `defineRpcFunction` from kit; always namespace IDs (`my-plugin:fn-name`).
6565
- Shared state via `devframe/utils/shared-state`; keep values serializable.
66-
- Nuxt UI base paths: `/.devtools-rolldown/`, `/.devtools-vite/`, `/.devtools-self-inspect/`.
66+
- Nuxt UI base paths: `/__devtools-rolldown/`, `/__devtools-vite/`, `/__devtools-self-inspect/`.
6767
- Shared UI components/preset in `packages/ui`; use `presetDevToolsUI` from `@vitejs/devtools-ui/unocss`.
6868
- Currently focused on Rolldown build-mode analysis; dev-mode support is deferred.
6969

@@ -73,8 +73,8 @@ These apply to everything inside `packages/devframe` and to how host packages (`
7373

7474
- **Headless by default.** No default startup banners, no opinionated logging to stdout, no default styling. Provide hooks (`onReady`, `cli.configure`, etc.); let the application print its own branding. Structured diagnostics via `logs-sdk` are fine — ad-hoc `console.log`s baked into adapters are not.
7575
- **File watching is the app's job, not devframe's.** Don't add a generic watcher primitive. Authors wire chokidar / fs.watch / watchman themselves and signal change via `ctx.rpc.sharedState.set(...)` or event-type RPCs. devframe stays out of the filesystem-observation business.
76-
- **Mount path depends on adapter context.** Given `id: 'foo'`, the default mount path is `/.foo/` for *hosted* adapters (`vite`, `kit`, `embedded`) and `/` for *standalone* adapters (`cli`, `spa`, `build`). Authors override via `DevtoolDefinition.basePath`. Don't hardcode `DEVTOOLS_MOUNT_PATH` in adapter code paths that may run standalone.
77-
- **SPAs own their basePath at runtime.** Build SPAs with relative asset paths (`vite.base: './'`); discover the effective base in the browser from the executing script's location / `document.baseURI`. `createBuild` / `createSpa` copy SPA output verbatim — no HTML rewriting, no build-time `--base` injection. The client (`connectDevtool`) resolves `.connection.json` relative to the runtime base automatically.
76+
- **Mount path depends on adapter context.** Given `id: 'foo'`, the default mount path is `/__foo/` for *hosted* adapters (`vite`, `kit`, `embedded`) and `/` for *standalone* adapters (`cli`, `spa`, `build`). Authors override via `DevtoolDefinition.basePath`. Don't hardcode `DEVTOOLS_MOUNT_PATH` in adapter code paths that may run standalone.
77+
- **SPAs own their basePath at runtime.** Build SPAs with relative asset paths (`vite.base: './'`); discover the effective base in the browser from the executing script's location / `document.baseURI`. `createBuild` / `createSpa` copy SPA output verbatim — no HTML rewriting, no build-time `--base` injection. The client (`connectDevtool`) resolves `__connection.json` relative to the runtime base automatically.
7878
- **CLI flags compose from both sides.** The `cac` instance backing `createCli` is exposed both to the `DevtoolDefinition` (`cli.configure(cli)`) — for capabilities contributed by the tool itself — and to the `createCli` caller — for flags added at the final assembly stage. Parsed flag values are forwarded to `setup(ctx, { flags })`. Never hardcode domain-specific flags into `createCli`.
7979

8080
## Structured Diagnostics (Error Codes)

devframe/docs/guide/adapters.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ my-devtool build --out-dir dist-static --base /devtools/
4848
my-devtool mcp # stdio MCP server (experimental)
4949
```
5050

51-
> Standalone CLI serves the SPA at `/` by default — no `/.devtools/` prefix. That prefix is reserved for *hosted* adapters where devframe mounts alongside an existing app. See [Mount paths](#mount-paths) below.
51+
> Standalone CLI serves the SPA at `/` by default — no `/__devtools/` prefix. That prefix is reserved for *hosted* adapters where devframe mounts alongside an existing app. See [Mount paths](#mount-paths) below.
5252
5353
### Options
5454

@@ -180,7 +180,7 @@ The basePath where a devtool's SPA is mounted depends on the adapter it's runnin
180180
| Adapter kind | Default basePath | Reason |
181181
|--------------|------------------|--------|
182182
| `cli`, `spa`, `build` (standalone) | `/` | The devtool is the only thing on the origin. |
183-
| `vite`, `kit`, `embedded` (hosted) | `/.<id>/` | The devtool shares the origin with a host app and must namespace itself. |
183+
| `vite`, `kit`, `embedded` (hosted) | `/__<id>/` | The devtool shares the origin with a host app and must namespace itself. |
184184

185185
Override either side explicitly with `DevtoolDefinition.basePath`:
186186

@@ -196,7 +196,7 @@ SPA authors should **build with relative asset paths** (`vite.base: './'`) rathe
196196

197197
## Vite
198198

199-
A thin Vite plugin that mounts a devtool's SPA into an existing Vite dev server as a *hosted* adapter — the mount path defaults to `/.<id>/` to avoid colliding with the app. It **does not** start an RPC WebSocket server — use `kit` or `cli` when you need RPC.
199+
A thin Vite plugin that mounts a devtool's SPA into an existing Vite dev server as a *hosted* adapter — the mount path defaults to `/__<id>/` to avoid colliding with the app. It **does not** start an RPC WebSocket server — use `kit` or `cli` when you need RPC.
200200

201201
```ts
202202
import { createVitePlugin } from 'devframe/adapters/vite'
@@ -210,7 +210,7 @@ export default defineConfig({
210210

211211
| Option | Default | Description |
212212
|--------|---------|-------------|
213-
| `base` | `def.basePath ?? '/.<id>/'` | Mount path inside the Vite dev server. |
213+
| `base` | `def.basePath ?? '/__<id>/'` | Mount path inside the Vite dev server. |
214214

215215
Use this adapter when a devtool's UI is purely static (no server calls) and you want to surface it during Vite `serve` without shipping a separate dev server. Set `DevtoolDefinition.basePath` on the definition if you want a custom path that stays consistent across adapters.
216216

@@ -221,7 +221,7 @@ Produces a self-contained static deploy of a devtool:
221221
1. Copies the author's SPA dist (`cli.distDir` or `options.distDir`) into `<outDir>`.
222222
2. Runs `setup(ctx)` with `mode: 'build'`.
223223
3. Collects RPC dumps for every `'static'` function and any `'query'` function with `dump.inputs` / `snapshot: true`.
224-
4. Writes `<outDir>/.connection.json` (`{ backend: 'static' }`) and sharded dump files under `<outDir>/.rpc-dump/` — both at the SPA root so the deployed client discovers them via relative paths from `document.baseURI`.
224+
4. Writes `<outDir>/__connection.json` (`{ backend: 'static' }`) and sharded dump files under `<outDir>/__rpc-dump/` — both at the SPA root so the deployed client discovers them via relative paths from `document.baseURI`.
225225
5. When `def.spa` is set, also writes `<outDir>/spa-loader.json` describing how the SPA hydrates its data.
226226

227227
```ts
@@ -240,7 +240,7 @@ await createBuild(devtool, {
240240
| `base` | `/` | Absolute URL base the output is served from. |
241241
| `distDir` | `def.cli?.distDir` | Override the SPA dist directory. |
242242

243-
The resulting directory can be hosted by any static web server (`serve`, nginx, GitHub Pages, …). The client auto-detects `static` mode via `./.connection.json` resolved against `document.baseURI` and runs in read-only form.
243+
The resulting directory can be hosted by any static web server (`serve`, nginx, GitHub Pages, …). The client auto-detects `static` mode via `./__connection.json` resolved against `document.baseURI` and runs in read-only form.
244244

245245
> [!TIP]
246246
> `createBuild` copies the SPA verbatim. To deploy under a custom URL base, build your SPA with relative asset paths (`vite.base: './'`) — the client discovers the effective base at runtime. No HTML rewriting is performed at build time.
@@ -276,7 +276,7 @@ The returned object has the shape `{ name, devtools: { setup, capabilities } }`.
276276

277277
## Embedded
278278

279-
Register a devtool into an already-running context at runtime. Mirrors the internal plugin-scan that Kit runs at startup, but exposes it for callers that need dynamic, post-startup registration. The host decides the mount path; `embedded` is treated as a hosted adapter and inherits the `/.<id>/` default when one is needed.
279+
Register a devtool into an already-running context at runtime. Mirrors the internal plugin-scan that Kit runs at startup, but exposes it for callers that need dynamic, post-startup registration. The host decides the mount path; `embedded` is treated as a hosted adapter and inherits the `/__<id>/` default when one is needed.
280280

281281
```ts
282282
import { createEmbedded } from 'devframe/adapters/embedded'

devframe/docs/guide/client.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ const rpc = await connectDevtool()
1818
const modules = await rpc.call('my-devtool:get-modules', { limit: 10 })
1919
```
2020

21-
`connectDevtool` auto-detects the backend via `.devtools/.connection.json` and falls back through a sequence of base URLs. No arguments are needed when the client is hosted from the default mount path.
21+
`connectDevtool` auto-detects the backend via `__devtools/__connection.json` and falls back through a sequence of base URLs. No arguments are needed when the client is hosted from the default mount path.
2222

2323
### Runtime basePath discovery
2424

25-
SPAs built for devframe are designed to be **base-agnostic**: the same artifact can be served at `/`, at `/.<id>/`, or at any custom subpath, without rebuilding. `connectDevtool` resolves `.connection.json` relative to the page at runtime by reading `document.baseURI` and the executing script's URL.
25+
SPAs built for devframe are designed to be **base-agnostic**: the same artifact can be served at `/`, at `/__<id>/`, or at any custom subpath, without rebuilding. `connectDevtool` resolves `__connection.json` relative to the page at runtime by reading `document.baseURI` and the executing script's URL.
2626

2727
The practical consequence for SPA authors:
2828

@@ -46,16 +46,16 @@ await connectDevtool({
4646

4747
| Option | Description |
4848
|--------|-------------|
49-
| `baseURL` | Mount path to probe for `.connection.json`. Accepts an array for fallback. Default: `'./'` — resolved relative to `document.baseURI` so the SPA finds its meta wherever it was deployed. Pass an explicit absolute path (e.g. `'/.devtools/'`) when calling from outside the SPA — for instance, an embedded webcomponent injected into a host app. |
49+
| `baseURL` | Mount path to probe for `__connection.json`. Accepts an array for fallback. Default: `'./'` — resolved relative to `document.baseURI` so the SPA finds its meta wherever it was deployed. Pass an explicit absolute path (e.g. `'/__devtools/'`) when calling from outside the SPA — for instance, an embedded webcomponent injected into a host app. |
5050
| `authToken` | Override the auth token. Defaults to a locally-persisted human-readable id. |
5151
| `cacheOptions` | `true` to enable caching with defaults, or an options object. |
5252
| `wsOptions` | Forwarded to the WebSocket transport (reconnect, heartbeat, etc.). |
5353
| `rpcOptions` | Forwarded to `birpc`. |
54-
| `connectionMeta` | Skip the `.connection.json` fetch with a pre-known descriptor. |
54+
| `connectionMeta` | Skip the `__connection.json` fetch with a pre-known descriptor. |
5555

5656
## Modes
5757

58-
The client runs in one of two modes depending on what the server advertises in `.devtools/.connection.json`:
58+
The client runs in one of two modes depending on what the server advertises in `__devtools/__connection.json`:
5959

6060
| Backend | When | Capabilities |
6161
|---------|------|--------------|
@@ -156,9 +156,9 @@ const rpc = await connectDevtool({ cacheOptions: true })
156156

157157
With caching on, `query` / `static` function responses are memoized per argument hash. Server-side broadcasts like `rpc:cache:invalidate` clear entries automatically — plugins that mutate state should broadcast that message after the change.
158158

159-
## Discovery (`.connection.json`)
159+
## Discovery (`__connection.json`)
160160

161-
DevFrame writes a small JSON descriptor at `<base>/.connection.json` so the client knows where to connect:
161+
DevFrame writes a small JSON descriptor at `<base>/__connection.json` so the client knows where to connect:
162162

163163
```json
164164
{

devframe/docs/guide/devtool-definition.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default defineDevtool({
2121
title: 'My Devtool',
2222
icon: 'ph:gauge-duotone',
2323
type: 'iframe',
24-
url: '/.devtools/',
24+
url: '/__devtools/',
2525
})
2626
},
2727
})
@@ -35,7 +35,7 @@ export default defineDevtool({
3535
| `name` | `string` | **Required.** Display name shown in the dock and agent manifests. |
3636
| `icon` | `string \| { light, dark }` | Optional Iconify name or URL; supports light/dark pairs. |
3737
| `version` | `string` | Optional version string surfaced to clients. |
38-
| `basePath` | `string` | Optional mount path override. Defaults depend on the adapter: `/` for standalone (`cli` / `spa` / `build`), `/.<id>/` for hosted (`vite` / `kit` / `embedded`). |
38+
| `basePath` | `string` | Optional mount path override. Defaults depend on the adapter: `/` for standalone (`cli` / `spa` / `build`), `/__<id>/` for hosted (`vite` / `kit` / `embedded`). |
3939
| `capabilities` | `{ dev?, build?, spa? }` | Per-runtime feature flags. A `boolean` applies to the runtime as a whole; an object enables individual features. |
4040
| `setup` | `(ctx, info?) => void \| Promise<void>` | **Required.** Server-side entry point. Runs in every runtime. The optional second argument carries runtime metadata — most notably the parsed CLI `flags` when running under `createCli`. |
4141
| `setupBrowser` | `(ctx) => void \| Promise<void>` | Browser-only entry used by the SPA adapter. |

devframe/docs/guide/dock-system.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ export default defineDevtool({
3434
id: 'my-devtool',
3535
name: 'My Devtool',
3636
setup(ctx) {
37-
ctx.views.hostStatic('/.my-devtool/', clientDist)
37+
ctx.views.hostStatic('/__my-devtool/', clientDist)
3838

3939
ctx.docks.register({
4040
id: 'my-devtool:main',
4141
title: 'My Devtool',
4242
icon: 'ph:gauge-duotone',
4343
type: 'iframe',
44-
url: '/.my-devtool/',
44+
url: '/__my-devtool/',
4545
})
4646
},
4747
})
@@ -225,7 +225,7 @@ The handle only supports `update`. Docks are not individually unregisterable tod
225225
`ctx.views` is a thin helper used for hosting static assets. You'll typically only call `hostStatic`:
226226

227227
```ts
228-
ctx.views.hostStatic('/.my-devtool/', clientDist)
228+
ctx.views.hostStatic('/__my-devtool/', clientDist)
229229
```
230230

231231
Internal state (`buildStaticDirs`) is used by the build adapter — treat it as an implementation detail.

devframe/docs/guide/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ DevFrame keeps its surface small and pushes UX decisions to the application cons
1717

1818
- **Headless.** No default startup banners, logging, or styling. Hook into `onReady`, `cli.configure`, and friends to print your own output.
1919
- **App-owned file watching.** Wire your own watcher (chokidar, fs.watch, …) and signal change via `ctx.rpc.sharedState.set(...)` or event-type RPCs. DevFrame does not ship a watcher primitive.
20-
- **Context-aware mount paths.** Standalone adapters (`cli`, `spa`, `build`) serve at `/` by default; hosted adapters (`vite`, `kit`, `embedded`) serve at `/.<id>/`. Override via `DevtoolDefinition.basePath`.
20+
- **Context-aware mount paths.** Standalone adapters (`cli`, `spa`, `build`) serve at `/` by default; hosted adapters (`vite`, `kit`, `embedded`) serve at `/__<id>/`. Override via `DevtoolDefinition.basePath`.
2121
- **SPAs own their base at runtime.** Build with relative asset paths (`vite.base: './'`); `connectDevtool` discovers the effective base from the executing script's location. No HTML rewrites at build time.
2222
- **CLI flags compose.** The `cac` instance is exposed to both the devtool (`cli.configure`) and the caller of `createCli`, so capability flags and app flags merge cleanly.
2323

@@ -97,7 +97,7 @@ const devtool = defineDevtool({
9797
title: 'My Devtool',
9898
icon: 'ph:gauge-duotone',
9999
type: 'iframe',
100-
url: '/.devtools/',
100+
url: '/__devtools/',
101101
})
102102
},
103103
})
@@ -113,7 +113,7 @@ node ./my-devtool.js build # self-contained static deploy in dist-static/
113113
node ./my-devtool.js mcp # stdio MCP server (experimental)
114114
```
115115

116-
The CLI adapter serves the SPA at `/` by default. When the same devtool is embedded inside a host (`vite`, `kit`, `embedded`), the default becomes `/.my-devtool/`. Override either side via `defineDevtool({ basePath })`.
116+
The CLI adapter serves the SPA at `/` by default. When the same devtool is embedded inside a host (`vite`, `kit`, `embedded`), the default becomes `/__my-devtool/`. Override either side via `defineDevtool({ basePath })`.
117117

118118
## Adapters at a Glance
119119

devframe/docs/guide/nuxt.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ At build time the module:
6868
return { provide: { rpc } }
6969
```
7070

71-
At runtime the built SPA fetches `./.connection.json` (resolved against `document.baseURI`) and branches on the `backend` field — `websocket` in dev, `static` from a `createBuild` snapshot.
71+
At runtime the built SPA fetches `./__connection.json` (resolved against `document.baseURI`) and branches on the `backend` field — `websocket` in dev, `static` from a `createBuild` snapshot.
7272

7373
## Relationship to `createCli`
7474

devframe/examples/devframe-files-inspector/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ A simplified [node-modules-inspector](https://github.com/antfu/node-modules-insp
88

99
The Preact client showcases two patterns relevant to devframe authors:
1010

11-
1. **Runtime base discovery.** The client is built with `vite.base: './'` and reads `document.baseURI` at runtime to resolve its mount path. The same `dist/client` works under any base path (`/.devframe-files-inspector/`, `/`, `/custom/`, …) without rebuilding.
11+
1. **Runtime base discovery.** The client is built with `vite.base: './'` and reads `document.baseURI` at runtime to resolve its mount path. The same `dist/client` works under any base path (`/__devframe-files-inspector/`, `/`, `/custom/`, …) without rebuilding.
1212
2. **Two RPC types.** `:list-files` is a `query` with `dump.inputs: [[]]` (live in dev, baked in static). `:get-cwd` is a `static` RPC.
1313

1414
## Run
1515

1616
```sh
1717
pnpm install
1818
pnpm -C examples/devframe-files-inspector run build # build the Preact client
19-
pnpm -C examples/devframe-files-inspector run dev # http://127.0.0.1:9876/.devframe-files-inspector/
19+
pnpm -C examples/devframe-files-inspector run dev # http://127.0.0.1:9876/__devframe-files-inspector/
2020
pnpm -C examples/devframe-files-inspector run cli:build # static deploy in ./dist/static
2121
serve examples/devframe-files-inspector/dist/static # any static host works (relative paths)
2222
pnpm -C examples/devframe-files-inspector run test # E2E tests

devframe/examples/devframe-files-inspector/src/devtool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineRpcFunction } from 'devframe'
33
import { defineDevtool } from 'devframe/types'
44
import { glob } from 'tinyglobby'
55

6-
const BASE_PATH = '/.devframe-files-inspector/'
6+
const BASE_PATH = '/__devframe-files-inspector/'
77
const distDir = fileURLToPath(new URL('../dist/client', import.meta.url))
88

99
export default defineDevtool({

0 commit comments

Comments
 (0)