Getting Started
1. Install
npm i @dev-bench/utilsThe package is ESM-only, Node ≥ 20, with no runtime deps.
2. Replace // TODO and ad-hoc console.warn
Pick the function that matches the signal:
import { warnNotImplemented, warnDegraded } from "@dev-bench/utils";
// Was: // TODO: implement reconnect backoff
warnNotImplemented("client.reconnect", "Exponential backoff not yet tuned");
// Was: console.warn("falling back to /")
warnDegraded("polling.getPaneCurrentPath", "tmux display-message failed");Both kinds are dedup'd by kind:category, so a hot loop emits exactly once.
3. Promote should-be-impossible branches
warnUnexpected is the sink for type-system invariants you can't prove at
compile time — Zod safeParse failures, match(...).otherwise() arms,
if (!('foo' in x)) fall-throughs. Never dedup'd; every occurrence is a
distinct event.
import { warnUnexpected } from "@dev-bench/utils";
const r = SseEventSchema.safeParse(payload);
if (!r.success) {
warnUnexpected("relay.parseSse", "schema validation failed", {
issues: r.error.issues,
});
return;
}4. Wrap throws with tryCatchWarn
Always async, returns a discriminated union. Pair with ts-pattern so the
caller is forced to handle both arms:
import { tryCatchWarn } from "@dev-bench/utils";
import { match } from "ts-pattern";
const r = await tryCatchWarn("git.diff", () => runGitDiff(ref));
return match(r)
.with({ ok: true }, ({ value }) => parseDiff(value))
.with({ ok: false }, () => null)
.exhaustive();The throw is routed through logError(category, err) automatically — you do
not need a separate catch block to surface it.
5. Surface events in your UI / TUI
setDiagnosticHook wires every emission into your renderer. Pass null to
unsubscribe.
import { setDiagnosticHook, type DiagnosticEvent } from "@dev-bench/utils";
const events: DiagnosticEvent[] = [];
setDiagnosticHook((e) => {
events.push(e);
redrawDebugPanel(events);
});
// later, e.g. on shutdown:
setDiagnosticHook(null);The hook fires before the underlying console.{warn,error,log} call, so a
custom hook can suppress, route, or annotate without losing the original
signal.
6. In tests
resetDiagnostics() clears the dedup set so each test sees a fresh
warnNotImplemented/warnDegraded for the same category:
import { beforeEach, expect, test, vi } from "vitest";
import {
resetDiagnostics,
setDiagnosticHook,
warnDegraded,
type DiagnosticEvent,
} from "@dev-bench/utils";
beforeEach(() => {
resetDiagnostics();
setDiagnosticHook(null);
});
test("warnDegraded is dedup'd by category", () => {
const events: DiagnosticEvent[] = [];
setDiagnosticHook((e) => events.push(e));
warnDegraded("polling.tick", "first");
warnDegraded("polling.tick", "second");
expect(events).toHaveLength(1);
expect(events[0]?.kind).toBe("degraded");
});