Utils
Diagnostics

Getting Started

1. Install

npm i @dev-bench/utils

The 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");
});

On this page