JSDoc
Functions
setDiagnosticHook
functionsetDiagnosticHook(fn: typeof diagnosticHook): void
addDiagnosticListener
functionSubscribe an additional listener (e.g. a dev console store). Returns a dispose function — call it to unsubscribe. Additive to `setDiagnosticHook` — both the singleton hook and every registered listener receive every event, regardless of `kind`. Listeners are invoked in insertion order, after the singleton hook. Removing a listener during emission is safe: `Set.forEach` will not revisit items removed while iterating.
addDiagnosticListener(fn: (e: DiagnosticEvent) => void): () => void
clearDiagnosticListeners
functionTest-only: drop every registered listener.
clearDiagnosticListeners(): void
resetDiagnostics
functionTest-only: clear the dedup set between tests.
resetDiagnostics(): void
warnDegraded
functionwarnDegraded(context: string, detail: string): void
warnNotImplemented
functionwarnNotImplemented(category: string, detail: string): void
warnUnexpected
functionwarnUnexpected(context: string, detail: string, data?: Record<string, unknown>): void
logInfo
functionEmit an informational lifecycle message. Never dedup'd — boot and ops messages typically fire at most a handful of times per process and operators want to see every one. Prefer a stable `context` (e.g. `"server.boot"`, `"relay.subscribe"`) so the UI debug panel and greps downstream can slice by call site.
logInfo(context: string, detail: string, data?: Record<string, unknown>): void
logError
functionReport a caught error (or any thrown value). Never dedup'd — every error is potentially its own story. If `err` is an `Error`, its `name`, `message`, and `stack` are captured as structured data on the diagnostic event. Otherwise the raw value is passed through under `data.err`. `detail` is optional — when omitted, the error's `.message` (or its string coercion) is used, which is the right default for `.catch(logError)`-style call sites.
logError(context: string, err: unknown, detail?: string): void
tryCatchWarn
functionRun `fn` and capture any throw into a discriminated union. Routes failures through `logError(context, err)` so the diagnostic sink sees the error regardless of how the caller handles it. Always returns a `Promise` — `fn` may be sync or async, both work. This keeps the type signature monomorphic (no overload explosion) and forces every call site to `await`, which is almost always what callers want when the wrapper exists specifically to capture errors. Example: const r = await tryCatchWarn("git.diff", () => runGitDiff(ref)) return match(r) .with({ ok: true }, ({ value }) => parseDiff(value)) .with({ ok: false }, () => null) .exhaustive()
<T>(context: string, fn: () => T | Promise<T>): Promise<TryCatchResult<T>>
Types
DiagnosticKind
typeRuntime diagnostics — deferred-work tracking, unexpected-state reporting, and the general structured-log sink for production code. Five functions, grouped by the kind they emit: - `warnNotImplemented` — feature exists upstream, port has nothing. Dedup'd. - `warnDegraded` — fallback running; usable but not faithful. Dedup'd. - `warnUnexpected` — value that should be impossible under the type system (Zod `safeParse` failure, non-exhaustive `match` arm, type-assertion guard). Never dedup'd. - `logInfo` — lifecycle / boot / ops messages that callers want visible but carry no "something is wrong" signal. Never dedup'd. - `logError` — something threw or errored. Serialises Error instances (name/message/stack). Never dedup'd. And a thin wrapper that turns throws into a discriminated-union result: - `tryCatchWarn<T>(context, fn)` — always async; returns `{ ok: true, value: T } | { ok: false, error: unknown }`. Routes throws through `logError`. Callers MUST `.exhaustive()` on `{ok}`. All five kinds flow through an optional `diagnosticHook` so the TUI / web debug panel can surface them. Tests call `resetDiagnostics()` in `beforeEach` to clear the dedup set. Usage: warnDegraded('polling.getPaneCurrentPath', 'Falling back to "/" — tmux display-message failed') warnNotImplemented('client.reconnect', 'Exponential backoff not yet tuned') warnUnexpected('connection.handleSseBlock', 'SSE message failed schema validation', { issues }) logInfo('server.boot', 'listening on port 7800', { port: 7800 }) logError('polling.tick', err) const result = await tryCatchWarn('some.op', () => doWork()) match(result) .with({ ok: true }, ({ value }) => ...) .with({ ok: false }, ({ error }) => ...) .exhaustive()
type DiagnosticKind = | "not-implemented" | "degraded" | "unexpected" | "info" | "error"
TryCatchResult
typeDiscriminated result returned by `tryCatchWarn`. Callers MUST branch with `match(result).with({ ok: true }, ...).with({ ok: false }, ...).exhaustive()` — the whole point is that "succeeded" and "failed" are distinct types.
type TryCatchResult = TryCatchResult<T>
Interfaces
DiagnosticEvent
interfaceinterface DiagnosticEvent
kind
propertykind: DiagnosticKind
category
propertycategory: string
detail
propertydetail: string
stack
propertystack: string
data
propertydata: Record<string, unknown>
timestamp
propertytimestamp: number