Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Any change to one lands in the other in the same PR: adding a dock, wiring a new

## Structured Diagnostics (Error Codes)

All node-side warnings and errors use structured diagnostics via [`nostics`](https://www.npmjs.com/package/nostics). Never use raw `console.warn`, `console.error`, or `throw new Error` with ad-hoc messages in node-side code - always define a coded diagnostic.
All node-side warnings and errors use structured diagnostics via [`nostics`](https://www.npmjs.com/package/nostics). Never use raw `console.warn`, `console.error`, or `throw new Error` with ad-hoc messages in node-side code - always define a coded diagnostic. Import `defineDiagnostics` (and `Diagnostic` for `instanceof` checks) from `devframe/utils/nostics` rather than from `nostics` directly - it pre-wires devframe's ANSI console reporter, so a plugin's `diagnostics.ts` never builds its own reporter (`colors`, `ansiFormatter`) or depends on `nostics` itself.

Prefix: **`DF`**. Codes are sequential 4-digit numbers (e.g. `DF0033`). Check the existing diagnostics file to find the next available number.

Expand Down
1 change: 1 addition & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const alias = {
'devframe/utils/hash': r('devframe/src/utils/hash.ts'),
'devframe/utils/launch-editor': r('devframe/src/utils/launch-editor.ts'),
'devframe/utils/nanoid': r('devframe/src/utils/nanoid.ts'),
'devframe/utils/nostics': r('devframe/src/utils/nostics.ts'),
'devframe/utils/open': r('devframe/src/utils/open.ts'),
'devframe/utils/remote-assets': r('devframe/src/utils/remote-assets.ts'),
'devframe/utils/simple-schema': r('devframe/src/utils/simple-schema.ts'),
Expand Down
1 change: 1 addition & 0 deletions packages/devframe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"./utils/hash": "./dist/utils/hash.mjs",
"./utils/launch-editor": "./dist/utils/launch-editor.mjs",
"./utils/nanoid": "./dist/utils/nanoid.mjs",
"./utils/nostics": "./dist/utils/nostics.mjs",
"./utils/open": "./dist/utils/open.mjs",
"./utils/remote-assets": "./dist/utils/remote-assets.mjs",
"./utils/simple-schema": "./dist/utils/simple-schema.mjs",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Diagnostic } from 'nostics'
import { Diagnostic } from 'devframe/utils/nostics'
import { describe, expect, it } from 'vitest'
import { formatMcpError, stringifyForMcp } from '../stringify'

Expand Down
2 changes: 1 addition & 1 deletion packages/devframe/src/adapters/mcp/stringify.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Diagnostic } from 'nostics'
import { Diagnostic } from 'devframe/utils/nostics'

/**
* JSON-coercing serializer for MCP text payloads.
Expand Down
2 changes: 1 addition & 1 deletion packages/devframe/src/cli/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Tool } from '@modelcontextprotocol/server'
import type { DevframeInstanceRecord } from '../node/instance-registry'
import process from 'node:process'
import { toAgentToolName } from 'devframe/utils/agent-tool-name'
import { Diagnostic } from 'nostics'
import { Diagnostic } from 'devframe/utils/nostics'
import { joinURL } from 'ufo'
import { diagnostics } from '../node/diagnostics'
import { listLiveDevframeInstances, probeDevframeOrigin } from '../node/instance-registry'
Expand Down
4 changes: 1 addition & 3 deletions packages/devframe/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { defineDiagnostics } from 'nostics'
import { devframeReporter } from '../utils/diagnostics-reporter'
import { defineDiagnostics } from 'devframe/utils/nostics'

// DF00xx codes are allocated across packages (e.g. @devframes/json-render
// owns DF0037–DF0041), so this file alone doesn't show the next free
// number — check `docs/errors/` for the full allocation before adding one.
export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [devframeReporter],
codes: {
DF0006: {
why: (p: { name: string }) => `RPC function "${p.name}" is not registered`,
Expand Down
16 changes: 4 additions & 12 deletions packages/devframe/src/node/host-diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { DevframeDiagnosticsHost as DevframeDiagnosticsHostType, DevframeDiagnosticsLogger, DevframeNodeContext } from 'devframe/types'
import { defineDiagnostics } from 'nostics'
import { devframeReporter } from '../utils/diagnostics-reporter'
import { defineDiagnostics } from 'devframe/utils/nostics'

export class DevframeDiagnosticsHost implements DevframeDiagnosticsHostType {
private _registry: Record<string, unknown> = {}
Expand All @@ -9,16 +8,9 @@ export class DevframeDiagnosticsHost implements DevframeDiagnosticsHostType {
get: (_, code: string) => this._registry[code],
})

readonly defineDiagnostics: DevframeDiagnosticsHostType['defineDiagnostics'] = (opts) => {
const merged = {
...opts,
reporters: [devframeReporter, ...(opts.reporters ?? [])],
} as Parameters<typeof defineDiagnostics>[0]
// Runtime passthrough: the per-call `Codes` generic can't be threaded
// through this assigned arrow, so the narrow return type is restored by
// the property's declared signature at every call site.
return defineDiagnostics(merged) as any
}
// Already pre-wires devframe's ANSI console reporter — no extra merging
// needed here, the host's `defineDiagnostics` just is the shared one.
readonly defineDiagnostics: DevframeDiagnosticsHostType['defineDiagnostics'] = defineDiagnostics

constructor(
public readonly context: DevframeNodeContext,
Expand Down
4 changes: 1 addition & 3 deletions packages/devframe/src/rpc/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { defineDiagnostics } from 'nostics'
import { devframeReporter } from '../utils/diagnostics-reporter'
import { defineDiagnostics } from 'devframe/utils/nostics'

export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [devframeReporter],
codes: {
DF0019: {
why: (p: { name: string }) =>
Expand Down
26 changes: 12 additions & 14 deletions packages/devframe/src/types/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { defineDiagnostics, Diagnostic, DiagnosticDefinition } from 'nostics'
import type { defineDiagnostics } from 'devframe/utils/nostics'

/**
* The shared diagnostics lookup exposed by the host. A `Proxy` that resolves
Expand All @@ -10,15 +10,14 @@ import type { defineDiagnostics, Diagnostic, DiagnosticDefinition } from 'nostic
export type DevframeDiagnosticsLogger = Record<string, any>

/**
* Options accepted by the host's `defineDiagnostics()` factory — mirrors
* `nostics`'s shape but the host pre-wires its ANSI console reporter, so
* plugins typically omit `reporters`.
* Options accepted by the host's `defineDiagnostics()` factory. Re-exported
* from `devframe/utils/nostics` — the same shape every module-level
* `diagnostics.ts` (devframe core, `@devframes/hub`, the built-in plugins)
* accepts, since `host.defineDiagnostics()` and the top-level
* `defineDiagnostics` from `devframe/utils/nostics` pre-wire the identical
* ANSI console reporter.
*/
export interface DevframeDefineDiagnosticsOptions<Codes extends Record<string, DiagnosticDefinition>> {
docsBase?: string | ((code: keyof Codes) => string | undefined)
codes: Codes
reporters?: ReadonlyArray<(d: Diagnostic, o?: any) => void>
}
export type { DevframeDefineDiagnosticsOptions } from 'devframe/utils/nostics'

/**
* Host for structured diagnostics — a thin layer over `nostics` that lets
Expand Down Expand Up @@ -64,10 +63,9 @@ export interface DevframeDiagnosticsHost {

/**
* Build a typed diagnostics object with the host's ANSI console reporter
* pre-wired. Mirrors `nostics`'s `defineDiagnostics` so integrations don't
* need to take a direct dependency on `nostics`.
* pre-wired. The same `devframe/utils/nostics` `defineDiagnostics` every
* built-in plugin's module-level `diagnostics.ts` uses, so integrations
* don't need to take a direct dependency on `nostics`.
*/
defineDiagnostics: <const Codes extends Record<string, DiagnosticDefinition>>(
options: DevframeDefineDiagnosticsOptions<Codes>,
) => ReturnType<typeof defineDiagnostics<Codes, any>>
defineDiagnostics: typeof defineDiagnostics
}
12 changes: 0 additions & 12 deletions packages/devframe/src/utils/diagnostics-reporter.ts

This file was deleted.

70 changes: 70 additions & 0 deletions packages/devframe/src/utils/nostics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { AnyDiagnosticReporter, Diagnostic, DiagnosticDefinition, Diagnostics } from 'nostics'
import { colors } from 'devframe/utils/colors'
import { defineDiagnostics as defineNosticsDiagnostics } from 'nostics'
import { ansiFormatter } from 'nostics/formatters/ansi'

const formatAnsi = ansiFormatter(colors)

/**
* The reporter every {@link defineDiagnostics} call below wires in ahead of
* any caller-supplied ones: prints the diagnostic through devframe's own
* ANSI colors via `console[method]` (default `'warn'`).
*/
function devframeReporter(d: Diagnostic, { method = 'warn' }: { method?: 'log' | 'warn' | 'error' } = {}): void {
// eslint-disable-next-line no-console
console[method](formatAnsi(d))
}

/**
* Options accepted by {@link defineDiagnostics} — identical to `nostics`'s
* own `DefineDiagnosticsOptions`, minus the reporter devframe already
* prepends.
*/
export type DevframeDefineDiagnosticsOptions<
Codes extends Record<string, DiagnosticDefinition>,
Reporters extends readonly AnyDiagnosticReporter[] = [],
> = Parameters<typeof defineDiagnostics<Codes, Reporters>>[0]

/**
* Drop-in replacement for `nostics`'s `defineDiagnostics()` with devframe's
* ANSI console reporter pre-wired ahead of any `reporters` passed in. Every
* `diagnostics.ts` in devframe core, `@devframes/hub`, `@devframes/json-render`,
* and the built-in plugins defines its codes through this instead of
* `nostics`'s own `defineDiagnostics` — the reporter registration lives
* here, once, so none of them need to build their own reporter (`colors`,
* `ansiFormatter`) or take a direct dependency on `nostics` themselves.
*/
export function defineDiagnostics<
const Codes extends Record<string, DiagnosticDefinition>,
const Reporters extends readonly AnyDiagnosticReporter[] = [],
>(options: {
docsBase?: string | ((code: keyof Codes) => string | undefined)
codes: Codes
reporters?: Reporters
}): Diagnostics<Codes, readonly [typeof devframeReporter, ...Reporters]> {
return defineNosticsDiagnostics({
...options,
reporters: [devframeReporter, ...(options.reporters ?? [])],
}) as Diagnostics<Codes, readonly [typeof devframeReporter, ...Reporters]>
}

export {
createConsoleReporter,
defineProdDiagnostics,
Diagnostic,
formatDiagnostic,
} from 'nostics'

export type {
AnyDiagnosticReporter,
ConsoleMethod,
ConsoleReporterOptions,
DiagnosticCallParams,
DiagnosticDefinition,
DiagnosticHandle,
DiagnosticInit,
DiagnosticReporter,
Diagnostics,
} from 'nostics'

export { ansiFormatter } from 'nostics/formatters/ansi'
1 change: 1 addition & 0 deletions packages/devframe/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const serverEntries = {
'node/hub-internals': 'src/node/hub-internals/index.ts',
'internal/index': 'src/internal/index.ts',
'utils/launch-editor': 'src/utils/launch-editor.ts',
'utils/nostics': 'src/utils/nostics.ts',
'utils/open': 'src/utils/open.ts',
'utils/remote-assets': 'src/utils/remote-assets.ts',
'utils/serve-static': 'src/utils/serve-static.ts',
Expand Down
1 change: 0 additions & 1 deletion packages/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
"@standard-schema/spec": "catalog:deps",
"destr": "catalog:deps",
"h3": "catalog:deps",
"nostics": "catalog:deps",
"pathe": "catalog:deps",
"perfect-debounce": "catalog:deps",
"tinyexec": "catalog:deps",
Expand Down
4 changes: 1 addition & 3 deletions packages/hub/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defineDiagnostics } from 'nostics'
import { hubReporter } from '../utils/diagnostics-reporter'
import { defineDiagnostics } from 'devframe/utils/nostics'

// Hub-side diagnostics for docks, terminals, messages, and commands.
// Shares the `DF` prefix with devframe core; the hub reserves the
Expand All @@ -12,7 +11,6 @@ import { hubReporter } from '../utils/diagnostics-reporter'
// DF8400-DF8499 — commands
export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [hubReporter],
codes: {
DF8000: {
why: (p: { id: string }) => `Devframe id "${p.id}" collides with a reserved hub path — it cannot be mounted directly under the hub base.`,
Expand Down
12 changes: 0 additions & 12 deletions packages/hub/src/utils/diagnostics-reporter.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/json-render/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
},
"dependencies": {
"@json-render/core": "catalog:deps",
"nostics": "catalog:deps",
"zod": "catalog:deps"
},
"devDependencies": {
Expand Down
15 changes: 1 addition & 14 deletions packages/json-render/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import type { Diagnostic } from 'nostics'
import { colors as c } from 'devframe/utils/colors'
import { defineDiagnostics } from 'nostics'
import { ansiFormatter } from 'nostics/formatters/ansi'

const formatAnsi = ansiFormatter(c)

interface ReporterOptions { method?: 'log' | 'warn' | 'error' }

function jsonRenderReporter(d: Diagnostic, { method = 'warn' }: ReporterOptions = {}): void {
// eslint-disable-next-line no-console
console[method](formatAnsi(d))
}
import { defineDiagnostics } from 'devframe/utils/nostics'

// `@devframes/json-render` protocol/runtime diagnostics. These share the
// `DF` prefix and live in the devframe core range (next free after the
// current highest `DF00xx`, DF0037). Browser-only render failures keep
// `console.*` in the UI package.
export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [jsonRenderReporter],
codes: {
DF0038: {
why: (p: { id: string, key: string, issues: string }) =>
Expand Down
1 change: 0 additions & 1 deletion plugins/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"cac": "catalog:deps",
"chokidar": "catalog:deps",
"image-meta": "catalog:deps",
"nostics": "catalog:deps",
"pathe": "catalog:deps",
"perfect-debounce": "catalog:deps",
"tinyglobby": "catalog:deps",
Expand Down
2 changes: 1 addition & 1 deletion plugins/assets/src/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineDiagnostics } from 'nostics'
import { defineDiagnostics } from 'devframe/utils/nostics'

// Uses the plugin's own `DP_ASSETS_` prefix per the built-in plugin
// convention, keeping it collision-free with devframe core (`DF`) and the
Expand Down
3 changes: 1 addition & 2 deletions plugins/code-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@
"dependencies": {
"@devframes/vite": "workspace:*",
"cac": "catalog:deps",
"get-port-please": "catalog:deps",
"nostics": "catalog:deps"
"get-port-please": "catalog:deps"
},
"devDependencies": {
"@antfu/design": "catalog:frontend",
Expand Down
15 changes: 1 addition & 14 deletions plugins/code-server/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
import type { Diagnostic } from 'nostics'
import { colors as c } from 'devframe/utils/colors'
import { defineDiagnostics } from 'nostics'
import { ansiFormatter } from 'nostics/formatters/ansi'

const formatAnsi = ansiFormatter(c)

interface ReporterOptions { method?: 'log' | 'warn' | 'error' }

function reporter(d: Diagnostic, { method = 'warn' }: ReporterOptions = {}): void {
// eslint-disable-next-line no-console
console[method](formatAnsi(d))
}
import { defineDiagnostics } from 'devframe/utils/nostics'

/**
* Structured diagnostics for the code-server plugin. Uses the plugin's own
Expand All @@ -19,7 +7,6 @@ function reporter(d: Diagnostic, { method = 'warn' }: ReporterOptions = {}): voi
*/
export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [reporter],
codes: {
DP_CODE_SERVER_0001: {
why: (p: { bin: string }) =>
Expand Down
3 changes: 1 addition & 2 deletions plugins/data-inspector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@
"dependencies": {
"cac": "catalog:deps",
"get-port-please": "catalog:deps",
"jora": "catalog:deps",
"nostics": "catalog:deps"
"jora": "catalog:deps"
},
"devDependencies": {
"@antfu/design": "catalog:frontend",
Expand Down
15 changes: 1 addition & 14 deletions plugins/data-inspector/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
import type { Diagnostic } from 'nostics'
import { colors as c } from 'devframe/utils/colors'
import { defineDiagnostics } from 'nostics'
import { ansiFormatter } from 'nostics/formatters/ansi'

const formatAnsi = ansiFormatter(c)

interface ReporterOptions { method?: 'log' | 'warn' | 'error' }

function reporter(d: Diagnostic, { method = 'warn' }: ReporterOptions = {}): void {
// eslint-disable-next-line no-console
console[method](formatAnsi(d))
}
import { defineDiagnostics } from 'devframe/utils/nostics'

/**
* Structured diagnostics for `@devframes/plugin-data-inspector`. Node-side
Expand All @@ -19,7 +7,6 @@ function reporter(d: Diagnostic, { method = 'warn' }: ReporterOptions = {}): voi
*/
export const diagnostics = defineDiagnostics({
docsBase: 'https://devfra.me/errors',
reporters: [reporter],
codes: {
DP_DATA_INSPECTOR_0001: {
why: (p: { id: string }) => `No data source is registered under "${p.id}".`,
Expand Down
Loading
Loading