Skip to content

Commit 695f656

Browse files
antfuclaude
andcommitted
feat: add ctx.diagnostics host; rename ctx.logs to ctx.messages
Introduce `ctx.diagnostics` — a `DevToolsDiagnosticsHost` that wraps logs-sdk and lets integrations register their own coded errors / warnings into a shared logger via `register()` / `defineDiagnostics` / `createLogger`, without taking a direct logs-sdk dependency. Rename the user-facing message subsystem from `logs` → `messages` end-to-end: the `ctx.logs` field, host class, types, RPC wire names (`devtoolskit:internal:logs:*` → `:messages:*`), event names (`log:added/...` → `message:added/...`), file names, and the dock panel ("Logs & Notifications" → "Messages & Notifications", id `~logs` → `~messages`). Deprecated `DevToolsLogs*` type aliases and a `ctx.logs` getter that emits DF0018 on first access keep the old surface working for one cycle. Wire-protocol break: clients on `:logs:*` will not connect to a server on `:messages:*`. The Vue components and webcomponents UI ship updated together; external consumers must update RPC method strings if they call the internals directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 91d3f5c commit 695f656

60 files changed

Lines changed: 1342 additions & 542 deletions

Some content is hidden

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

devframe/docs/.vitepress/sidebar.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export default function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[]
1313
{ text: 'Dock System', link: `${prefix}/guide/dock-system` },
1414
{ text: 'Commands', link: `${prefix}/guide/commands` },
1515
{ text: 'When Clauses', link: `${prefix}/guide/when-clauses` },
16-
{ text: 'Logs & Notifications', link: `${prefix}/guide/logs` },
16+
{ text: 'Messages & Notifications', link: `${prefix}/guide/messages` },
17+
{ text: 'Structured Diagnostics', link: `${prefix}/guide/diagnostics` },
1718
{ text: 'Terminals', link: `${prefix}/guide/terminals` },
1819
{ text: 'Client', link: `${prefix}/guide/client` },
1920
{ text: 'Standalone CLI', link: `${prefix}/guide/standalone-cli` },
@@ -25,7 +26,7 @@ export default function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[]
2526
text: 'Error Reference',
2627
link: `${prefix}/errors/`,
2728
collapsed: true,
28-
items: Array.from({ length: 17 }, (_, i) => {
29+
items: Array.from({ length: 18 }, (_, i) => {
2930
const code = `DF${String(i + 1).padStart(4, '0')}`
3031
return { text: code, link: `${prefix}/errors/${code}` }
3132
}),

devframe/docs/errors/DF0018.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0018: `ctx.logs` Deprecated
6+
7+
> Package: `devframe`
8+
9+
## Message
10+
11+
> `` `ctx.logs` is deprecated and will be removed in a future release. Use `ctx.messages` instead. ``
12+
13+
## Cause
14+
15+
The user-facing message subsystem has been renamed from `logs` to `messages` to disambiguate it from the structured diagnostics surface (`ctx.diagnostics`, powered by [`logs-sdk`](https://github.com/vercel-labs/logs-sdk)).
16+
17+
`ctx.logs` continues to work as an alias of `ctx.messages` for one release cycle, but emits this warning the first time it is accessed in a given process.
18+
19+
## Example
20+
21+
Code that triggers it:
22+
23+
```ts
24+
ctx.logs.add({ message: 'something happened', level: 'info' })
25+
```
26+
27+
## Fix
28+
29+
Replace `ctx.logs` with `ctx.messages`:
30+
31+
```ts
32+
ctx.messages.add({ message: 'something happened', level: 'info' })
33+
```
34+
35+
The runtime behavior is identical — the same host instance backs both fields.
36+
37+
The associated type names have been renamed too (with deprecated aliases kept for one release):
38+
39+
| Old | New |
40+
|-----|-----|
41+
| `DevToolsLogsHost` | `DevToolsMessagesHost` |
42+
| `DevToolsLogsClient` | `DevToolsMessagesClient` |
43+
| `DevToolsLogEntry` | `DevToolsMessageEntry` |
44+
| `DevToolsLogEntryInput` | `DevToolsMessageEntryInput` |
45+
| `DevToolsLogHandle` | `DevToolsMessageHandle` |
46+
| `DevToolsLogLevel` | `DevToolsMessageLevel` |
47+
48+
The event names emitted by the host have changed from `log:added` / `log:updated` / `log:removed` / `log:cleared` to `message:added` / `message:updated` / `message:removed` / `message:cleared`.
49+
50+
## Source
51+
52+
`packages/devframe/src/node/context.ts`

devframe/docs/errors/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ Emitted by `devframe` — framework-neutral host / shared-state / auth surface.
3535
| [DF0015](./DF0015) | error | Agent Tool Already Registered ||
3636
| [DF0016](./DF0016) | error | Agent Resource Already Registered ||
3737
| [DF0017](./DF0017) | error | MCP Server Start Failure ||
38+
| [DF0018](./DF0018) | warn | `ctx.logs` Deprecated ||

devframe/docs/guide/devtool-definition.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ interface DevToolsNodeContext {
7878
docks: DevToolsDockHost // dock entries
7979
views: DevToolsViewHost // static file hosting
8080
terminals: DevToolsTerminalHost
81-
logs: DevToolsLogsHost
81+
messages: DevToolsMessagesHost
82+
diagnostics: DevToolsDiagnosticsHost
8283
commands: DevToolsCommandsHost
8384
agent: DevToolsAgentHost // experimental
8485

@@ -92,7 +93,8 @@ Each host has a dedicated page:
9293
- [Shared State](./shared-state)`ctx.rpc.sharedState`
9394
- [Dock System](./dock-system)`ctx.docks`, `ctx.views`
9495
- [Commands](./commands)`ctx.commands`
95-
- [Logs](./logs)`ctx.logs`
96+
- [Messages](./messages)`ctx.messages`
97+
- [Diagnostics](./diagnostics)`ctx.diagnostics`
9698
- [Terminals](./terminals)`ctx.terminals`
9799
- [Agent-Native](./agent-native)`ctx.agent`
98100

devframe/docs/guide/diagnostics.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Structured Diagnostics
6+
7+
`ctx.diagnostics` is a thin layer over [`logs-sdk`](https://github.com/vercel-labs/logs-sdk) that lets integrations register their own coded errors and warnings into a shared logger — without taking a direct dependency on `logs-sdk`.
8+
9+
Use it for *author-defined coded diagnostics* (errors, warnings, deprecations) that have a stable code, a documentation URL, and a structured payload. For free-form runtime output that should appear in the DevTools UI, use [`ctx.messages`](./messages) instead.
10+
11+
| Surface | Purpose | Example |
12+
|---------|---------|---------|
13+
| `ctx.diagnostics` | Coded errors and warnings emitted from node-side plugin code | `MYP0001: Plugin foo not configured` |
14+
| [`ctx.messages`](./messages) | Free-form, user-facing notifications shown in the Messages panel | `'Audit complete — 3 issues found'` |
15+
16+
## Shape
17+
18+
```ts
19+
interface DevToolsDiagnosticsHost {
20+
/** Combined logs-sdk Logger across all registered diagnostics. */
21+
readonly logger: Logger
22+
23+
/** Register additional diagnostic definitions. */
24+
register: (definitions: DiagnosticsResult) => void
25+
26+
/** Re-export of logs-sdk's `defineDiagnostics`. */
27+
defineDiagnostics: typeof defineDiagnostics
28+
29+
/** Re-export of logs-sdk's `createLogger`. */
30+
createLogger: typeof createLogger
31+
}
32+
```
33+
34+
The host ships pre-seeded with devframe's own `DF*` codes, plus the host package's codes (`DTK*` for `@vitejs/devtools`, etc.). Call `register()` to add your own.
35+
36+
## Register Your Own Codes
37+
38+
```ts
39+
export function MyPlugin(): PluginWithDevTools {
40+
return {
41+
name: 'my-plugin',
42+
devtools: {
43+
setup(ctx) {
44+
const myDiagnostics = ctx.diagnostics.defineDiagnostics({
45+
docsBase: 'https://example.com/errors',
46+
codes: {
47+
MYP0001: {
48+
message: (p: { name: string }) => `Plugin "${p.name}" is not configured`,
49+
hint: 'Add the plugin to your `vite.config.ts` and pass an options object.',
50+
},
51+
MYP0002: {
52+
message: 'Cache directory missing — running cold.',
53+
level: 'warn',
54+
},
55+
},
56+
})
57+
58+
ctx.diagnostics.register(myDiagnostics)
59+
60+
// Now you can emit codes through the shared logger:
61+
ctx.diagnostics.logger.MYP0002().log()
62+
},
63+
},
64+
}
65+
}
66+
```
67+
68+
## Code Conventions
69+
70+
Use a **4-letter prefix + 4-digit number** for your codes (e.g. `MYP0001`). Pick a prefix that's specific to your plugin or tool — short enough to type, distinctive enough not to collide with other integrations.
71+
72+
Prefixes already in use in this monorepo:
73+
74+
| Prefix | Owner |
75+
|--------|-------|
76+
| `DF` | `devframe` |
77+
| `DTK` | `@vitejs/devtools` (Vite-specific) |
78+
| `RDDT` | `@vitejs/devtools-rolldown` |
79+
| `VDT` | `@vitejs/devtools-vite` (reserved) |
80+
81+
Each definition supports a `message` (string or function), an optional `hint`, an optional `level` (`'error'` / `'warn'` / `'suggestion'` / `'deprecation'` — defaults to `'error'`), and a `docsBase` for generating documentation URLs. See [`logs-sdk`](https://github.com/vercel-labs/logs-sdk) for the full schema.
82+
83+
## Emit a Diagnostic
84+
85+
Each registered code becomes a callable factory on `ctx.diagnostics.logger`. The factory returns an object with `.throw()`, `.warn()`, `.error()`, `.log()`, and `.format()`.
86+
87+
```ts
88+
// Throw — control flow stops here
89+
throw ctx.diagnostics.logger.MYP0001({ name: 'foo' }).throw()
90+
91+
// Log without throwing
92+
ctx.diagnostics.logger.MYP0002().log()
93+
94+
// Override level per call
95+
ctx.diagnostics.logger.MYP0002().warn()
96+
97+
// Attach a `cause`
98+
ctx.diagnostics.logger.MYP0001({ name: 'foo' }, { cause: error }).log()
99+
```
100+
101+
`.throw()` is also typed `never` — TypeScript will treat the line after it as unreachable, so prefix it with `throw` for control-flow narrowing:
102+
103+
```ts
104+
throw ctx.diagnostics.logger.MYP0001({ name }).throw()
105+
```
106+
107+
## Typed Logger Reference
108+
109+
`ctx.diagnostics.logger` is loosely typed (it covers an unbounded set of registered codes). If you want full type narrowing — e.g. autocompletion for your plugin's specific codes — keep your own typed reference returned from `createLogger`:
110+
111+
```ts
112+
const myDiagnostics = ctx.diagnostics.defineDiagnostics({
113+
docsBase: 'https://example.com/errors',
114+
codes: {
115+
MYP0001: { message: (p: { name: string }) => `…${p.name}` },
116+
},
117+
})
118+
119+
// Register so the shared logger can also see it
120+
ctx.diagnostics.register(myDiagnostics)
121+
122+
// Keep a typed reference for your own emit sites
123+
const logger = ctx.diagnostics.createLogger({ diagnostics: [myDiagnostics] })
124+
logger.MYP0001({ name: 'foo' }).warn()
125+
```
126+
127+
Both loggers share the formatter and reporter defaults set by the host (ANSI console output).
128+
129+
## Updating the Combined Logger
130+
131+
`ctx.diagnostics.logger` is a *getter* — it always returns the freshest combined logger, rebuilt each time `register()` is called. Don't cache it:
132+
133+
```ts
134+
// ❌ Stale after a later register() call
135+
const log = ctx.diagnostics.logger
136+
log.MYP0001({ name: 'foo' }).log()
137+
138+
// ✅ Always fresh
139+
ctx.diagnostics.logger.MYP0001({ name: 'foo' }).log()
140+
```
141+
142+
If you want a stable reference, use `ctx.diagnostics.createLogger({ diagnostics: [myDiagnostics] })` — that one stays bound to *your* definitions.
143+
144+
## Document Your Codes
145+
146+
Pair each code with a documentation page. devframe and the published Vite DevTools packages follow this layout:
147+
148+
```
149+
docs/errors/
150+
index.md # Table of all codes
151+
MYP0001.md # One page per code
152+
MYP0002.md
153+
```
154+
155+
Each page covers the message, cause, example, and fix — see any [DF code page](https://devtools.vite.dev/devframe/errors/) for the canonical template. Set `docsBase` on `defineDiagnostics({...})` so the URL is auto-attached to every emitted diagnostic.
156+
157+
## When to Use What
158+
159+
- **`ctx.diagnostics`** — Coded conditions you want users (or other tools) to be able to look up: misconfiguration, deprecations, validation failures, internal invariants. Always have a docs page. Often `.throw()`.
160+
- **`ctx.messages`** — User-facing activity surfaces in the DevTools UI: progress indicators, audit results, "URL copied" toasts. No code, no docs URL — just a message and a level.
161+
162+
The two systems are intentionally separate: diagnostics are for tool authors and CI; messages are for the human in front of the DevTools panel.
Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
outline: deep
33
---
44

5-
# Logs & Notifications
5+
# Messages & Notifications
66

7-
`ctx.logs` is a structured log store with live updates, toasts, and positional hints that link a log entry back to a DOM element or source file. Use it to surface a11y findings, lint errors, runtime failures, or short-lived notifications like "URL copied".
7+
`ctx.messages` is a structured message store with live updates, toasts, and positional hints that link a message entry back to a DOM element or source file. Use it to surface a11y findings, lint errors, runtime failures, or short-lived notifications like "URL copied".
88

99
The same API works from the server and the browser: each call is a Promise, but most callers fire-and-forget.
1010

11+
> **Note:** Previously named `ctx.logs`. The old field still works as a deprecated alias for one release cycle — see [DF0018](/errors/DF0018) for migration details.
12+
1113
## Entry Fields
1214

1315
| Field | Type | Required | Description |
@@ -31,7 +33,7 @@ The same API works from the server and the browser: each call is a Promise, but
3133
## Fire-and-Forget
3234

3335
```ts
34-
ctx.logs.add({
36+
ctx.messages.add({
3537
message: 'Plugin initialized',
3638
level: 'info',
3739
})
@@ -42,7 +44,7 @@ ctx.logs.add({
4244
`await` the call to get a handle for live updates:
4345

4446
```ts
45-
const handle = await ctx.logs.add({
47+
const handle = await ctx.messages.add({
4648
id: 'my-devtool:build',
4749
message: 'Building…',
4850
level: 'info',
@@ -63,27 +65,27 @@ Re-adding with the same `id` updates the existing entry — use this to replace
6365

6466
## Toasts
6567

66-
Set `notify: true` to also render the log as a toast:
68+
Set `notify: true` to also render the message as a toast:
6769

6870
```ts
69-
ctx.logs.add({
71+
ctx.messages.add({
7072
message: 'URL copied to clipboard',
7173
level: 'success',
7274
notify: true,
7375
autoDismiss: 3000,
7476
})
7577
```
7678

77-
`autoDismiss` controls how long the toast stays on screen; the log entry persists in the panel until explicitly removed or `autoDelete` fires.
79+
`autoDismiss` controls how long the toast stays on screen; the message entry persists in the panel until explicitly removed or `autoDelete` fires.
7880

7981
## Positional Hints
8082

8183
### File Position
8284

83-
Linking a log to a source file makes it clickable — clicking opens the file in the user's editor:
85+
Linking a message to a source file makes it clickable — clicking opens the file in the user's editor:
8486

8587
```ts
86-
ctx.logs.add({
88+
ctx.messages.add({
8789
message: 'Unused import',
8890
level: 'warn',
8991
category: 'lint',
@@ -93,11 +95,11 @@ ctx.logs.add({
9395

9496
### Element Position
9597

96-
DOM anchors are rendered as a highlight overlay when the user hovers the log entry:
98+
DOM anchors are rendered as a highlight overlay when the user hovers the message entry:
9799

98100
```ts
99101
// Typically from a browser-side audit:
100-
ctx.logs.add({
102+
ctx.messages.add({
101103
message: 'Button missing accessible name',
102104
level: 'warn',
103105
category: 'a11y',
@@ -115,7 +117,7 @@ ctx.logs.add({
115117

116118
```ts
117119
async function rebuild(ctx) {
118-
const handle = await ctx.logs.add({
120+
const handle = await ctx.messages.add({
119121
id: 'my-devtool:rebuild',
120122
message: 'Rebuilding…',
121123
level: 'info',
@@ -141,7 +143,7 @@ async function rebuild(ctx) {
141143
### Category filter
142144

143145
```ts
144-
ctx.logs.events.on('log:added', (entry) => {
146+
ctx.messages.events.on('message:added', (entry) => {
145147
if (entry.category === 'a11y') {
146148
console.log('a11y finding:', entry.message)
147149
}
@@ -151,26 +153,26 @@ ctx.logs.events.on('log:added', (entry) => {
151153
## Removing Entries
152154

153155
```ts
154-
await ctx.logs.remove('my-devtool:build')
155-
await ctx.logs.clear() // all entries
156+
await ctx.messages.remove('my-devtool:build')
157+
await ctx.messages.clear() // all entries
156158
```
157159

158160
## Events
159161

160-
The host emits events for anyone who wants to observe the log stream:
162+
The host emits events for anyone who wants to observe the message stream:
161163

162164
```ts
163-
ctx.logs.events.on('log:added', (entry) => { /**/ })
164-
ctx.logs.events.on('log:updated', (entry) => { /**/ })
165-
ctx.logs.events.on('log:removed', (id) => { /**/ })
166-
ctx.logs.events.on('log:cleared', () => { /**/ })
165+
ctx.messages.events.on('message:added', (entry) => { /**/ })
166+
ctx.messages.events.on('message:updated', (entry) => { /**/ })
167+
ctx.messages.events.on('message:removed', (id) => { /**/ })
168+
ctx.messages.events.on('message:cleared', () => { /**/ })
167169
```
168170

169-
Use this to bridge logs into external tools — e.g. mirror them into a structured log file or forward certain categories to your own reporter.
171+
Use this to bridge messages into external tools — e.g. mirror them into a structured log file or forward certain categories to your own reporter.
170172

171173
## Server vs Browser
172174

173-
Both sides share the same API. Browser-side calls go through the RPC client (`rpc.logs` or — more idiomatically — the exported `DevToolsLogsClient` interface). Entries carry a `from` field so the UI can distinguish server-originated logs from browser-originated ones.
175+
Both sides share the same API. Browser-side calls go through the RPC client (— more idiomatically — the exported `DevToolsMessagesClient` interface). Entries carry a `from` field so the UI can distinguish server-originated messages from browser-originated ones.
174176

175177
> [!NOTE]
176-
> The separate, Node-side [structured diagnostics system](https://github.com/vercel-labs/logs-sdk) used for DevFrame's own warnings / errors (`DF`-prefixed codes) is distinct from `ctx.logs`. Diagnostics are author-defined coded errors with documentation URLs; `ctx.logs` is free-form plugin output shown in the Logs panel.
178+
> The separate, Node-side structured diagnostics system used for DevFrame's own warnings / errors (`DF`-prefixed codes) is distinct from `ctx.messages`. See the [Diagnostics guide](./diagnostics) for `ctx.diagnostics`, the host-level wrapper around [`logs-sdk`](https://github.com/vercel-labs/logs-sdk).

0 commit comments

Comments
 (0)