honest framework

honest-errors

Two raw payloads, one normalized report. What happens to it is a pure function of the environment, not the catch block.

Sentry's beforeSend and your error boundaries each decide handling separately. honest-errors decides once — a pure function of environment and severity.

A request fails three ways in three places. The browser throws a TypeError with a stack trace and a window.location. The server catches an exception with a traceback and a request id. A background job times out with neither. Each site decides for itself what to log, whether to retry, whether to page someone, so the same underlying failure is loud in one place, silent in another, and retried into a storm in a third. There is no single answer to "what do we do when something breaks," because the decision is scattered across every catch block you ever wrote.

honest-errors makes the decision once. Two raw payloads — one shaped like a browser error, one like a server error — are normalized to a single report. What happens to that report is a pure function of the environment: the same report routes to console, to the log, to an alert, by a table, not by whichever catch block happened to catch it.

Normalize, then decide

The two normalizers are the only place that knows what a raw browser error or a raw server error looks like. After them, the rest of the module sees one shape. The behaviour is a dict-dispatch on environment and severity: development prints, production logs and alerts, a test swallows. No if env == "prod" sprinkled through the code: one table, read the same way every time.

Repeats are handled by a pure, state-threaded throttle: the same error a hundred times in a second becomes one report plus a count, and the suppression state is passed through as a value, not stashed in a module global. There is no I/O inside honest-errors at all. It decides; the boundary acts.

Why one report is a poka-yoke

Scattered error handling is not a style problem, it is a coverage problem: you cannot enumerate what your program does on failure when the answer lives in fifty independent catch blocks. Collapse them to one normalized report and one behaviour table, and the failure behaviour becomes a finite, readable thing, testable by feeding reports through the table, not by triggering fifty exceptions and watching what happens.

honest-errors is a leaf: it depends on nothing above it. honest-observe composes its normalizers to record failures; honest-alerts composes its behaviour table and rate-limiter to raise them. The policy is written once and reused, never re-decided.