Evidence
What happened before
bugbottle/breadcrumbs records the last few things the reporter did, so the
report says what was happening when it broke — not only what broke. It is a
separate entry point: an application that does not import it does not carry it.
import { initBreadcrumbs } from "bugbottle/breadcrumbs";
initBreadcrumbs();Four things are recorded and nothing else: clicks (a short selector and the
element's visible text), navigation (path and query, including pushState and
replaceState, which fire no event of their own), form submits (the selector
only) and visibility changes. The last 30 are kept.
[
{ "ts": "2026-09-07T08:12:29.100Z", "kind": "click", "target": "button#save-order", "text": "Save order" },
{ "ts": "2026-09-07T08:12:30.400Z", "kind": "navigation", "from": "/orders/1", "to": "/orders/2?tab=notes" },
{ "ts": "2026-09-07T08:12:31.000Z", "kind": "submit", "target": "form#checkout" },
{ "ts": "2026-09-07T08:12:34.700Z", "kind": "visibility", "to": "hidden" }
]buildReport attaches them on its own while the buffer is recording, as
breadcrumbs; pass includeBreadcrumbs: false to leave them out of one
report. toMarkdown renders them as a "What happened before" list.
Deliberately absent: input values, keystrokes, and anything read out of a field. A breadcrumb says where someone clicked, never what they typed. On top of that, three controls are yours:
initBreadcrumbs({
maxEntries: 30,
beforeBreadcrumb: (crumb) =>
crumb.to?.startsWith("/admin") ? null : { ...crumb, to: crumb.to?.replace(/\/\d+/, "/:id") },
});beforeBreadcrumbsees every breadcrumb before it is stored. Return null to drop it, or a changed one to redact it.- Anything inside
[data-bugbottle]is skipped entirely — that is the library's own furniture, including the panel frombugbottle/ui. - Anything inside
[data-bugbottle-mask]records its selector and no text, so a name, an address or an amount on a card never travels with the click.
getBreadcrumbs() returns a copy of the timeline, and resetBreadcrumbs()
empties it, removes the listeners and puts history back as it found it.
initBreadcrumbs returns that same function as its stop(), so a caller that
started the recorder can undo it without importing a second name.