Effects layer for x-trace-history: state atom, hook installation, activation gating, JS API at window.BareDOM.traceHistory.*, a synchronous cause-id chain via a wrapper around EventTarget.prototype.dispatchEvent, and a small subscriber API consumed by the dock UI.
See docs/x-trace-history-roadmap.md for the broader plan.
Effects layer for x-trace-history: state atom, hook installation, activation gating, JS API at window.BareDOM.traceHistory.*, a synchronous cause-id chain via a wrapper around EventTarget.prototype.dispatchEvent, and a small subscriber API consumed by the dock UI. See docs/x-trace-history-roadmap.md for the broader plan.
(active-session-id)Return the id of the currently-recording session, or nil when no session is active. Useful for UI state.
Return the id of the currently-recording session, or nil when no session is active. Useful for UI state.
(clear!)Empty the ring buffer and reset the record-id counter to 0. Drops all sessions too — sessions reference t-ranges in the buffer, so an emptied buffer leaves them with no records anyway. Document the joint behavior so callers don't expect sessions to survive Clear.
Intentionally preserves :paused? state, the :components index, and the component-id counter — component identity is monotonic for the page lifetime since live elements still carry their stored cids; resetting would cause new components to collide with already-stored ids.
Empty the ring buffer and reset the record-id counter to 0. Drops all sessions too — sessions reference t-ranges in the buffer, so an emptied buffer leaves them with no records anyway. Document the joint behavior so callers don't expect sessions to survive Clear. Intentionally preserves :paused? state, the :components index, and the component-id counter — component identity is monotonic for the page lifetime since live elements still carry their stored cids; resetting would cause new components to collide with already-stored ids.
(components)Returns a JS object mapping componentId (as string key) to {tag, firstSeen}. The index is monotonic for the lifetime of the page — not cleared by clear!. Useful for resolving componentId values found in records back to their tag and first-observed timestamp.
Returns a JS object mapping componentId (as string key) to
{tag, firstSeen}. The index is monotonic for the lifetime of the page —
not cleared by clear!. Useful for resolving componentId values found in
records back to their tag and first-observed timestamp.(download-trace!)Trigger a browser download of the current trace as
baredom-trace-YYYYMMDD-HHmmss.trace.json. The same timestamp drives
both the envelope's exportedAt and the filename slug, so a file's
name and contents agree on when it was captured.
Returns nil. Safe to call from anywhere — pure side effect on the document.
Trigger a browser download of the current trace as `baredom-trace-YYYYMMDD-HHmmss.trace.json`. The same timestamp drives both the envelope's `exportedAt` and the filename slug, so a file's name and contents agree on when it was captured. Returns nil. Safe to call from anywhere — pure side effect on the document.
(export!)Return the current recorder state as a schema-stable JS envelope
ready for JSON.stringify. Captures records, the components index,
and any sessions metadata, plus wall-clock context (exportedAt,
origin, userAgent, forensic). Does NOT mutate state.
The returned envelope's records field is reference-equal to the
recorder's memoised JS array — callers MUST NOT mutate it. Treat
the envelope as read-only or call JSON.parse(JSON.stringify(env))
for an independent copy.
Safe to call when paused, mid-session, or with an empty buffer. See docs/x-trace-history-schema.md for the contract.
Return the current recorder state as a schema-stable JS envelope ready for `JSON.stringify`. Captures records, the components index, and any sessions metadata, plus wall-clock context (`exportedAt`, `origin`, `userAgent`, `forensic`). Does NOT mutate state. The returned envelope's `records` field is reference-equal to the recorder's memoised JS array — callers MUST NOT mutate it. Treat the envelope as read-only or call `JSON.parse(JSON.stringify(env))` for an independent copy. Safe to call when paused, mid-session, or with an empty buffer. See docs/x-trace-history-schema.md for the contract.
(forensic-mode?)True when the recorder was installed in forensic mode (?baredom-trace-history=raw or window.BAREDOM_TRACE_HISTORY="raw"). Forensic mode disables the sample-rate cap and is the dock's signal to default the :state category checkbox to ON. Captured at install time so per-record reads don't have to touch window state.
Disambiguated from model/forensic? (which reads live window state
at the moment of the call) by name — the recorder's flag is frozen
at install.
True when the recorder was installed in forensic mode (?baredom-trace-history=raw or window.BAREDOM_TRACE_HISTORY="raw"). Forensic mode disables the sample-rate cap and is the dock's signal to default the :state category checkbox to ON. Captured at install time so per-record reads don't have to touch window state. Disambiguated from `model/forensic?` (which reads live window state at the moment of the call) by name — the recorder's flag is frozen at install.
(import-records target-id)Return the JS array of records inside the import with id target-id,
in chronological order. Returns an empty array when the id is
unknown so the dock can degrade gracefully on a stale chip click.
Return the JS array of records inside the import with id `target-id`, in chronological order. Returns an empty array when the id is unknown so the dock can degrade gracefully on a stale chip click.
(import-trace! input)(import-trace! input label)Load an exported trace into the recorder. Accepts either a JSON string or an already-parsed JS object. On success returns the new import id (number); on validation failure returns nil and logs the reason via console.warn so callers can react.
label is the human-readable string shown on the chip — usually
the source filename minus extension; falls back to Import N when
blank/missing. Imports do NOT replace the live buffer — they are
stored alongside it. clear! does NOT drop imports; use
remove-import! to remove a single one.
Load an exported trace into the recorder. Accepts either a JSON string or an already-parsed JS object. On success returns the new import id (number); on validation failure returns nil and logs the reason via console.warn so callers can react. `label` is the human-readable string shown on the chip — usually the source filename minus extension; falls back to `Import N` when blank/missing. Imports do NOT replace the live buffer — they are stored alongside it. `clear!` does NOT drop imports; use `remove-import!` to remove a single one.
(imports)Return a JS array of import metadata: {id, label, importedAt,
recordCount}. recordCount is computed from the stored envelope at
call time (cheap — it's a .-length read on the envelope's records
array, no allocation).
Return a JS array of import metadata: {id, label, importedAt,
recordCount}. recordCount is computed from the stored envelope at
call time (cheap — it's a `.-length` read on the envelope's records
array, no allocation).(install!)Activate recording. Idempotent and re-entrant: safe to call multiple
times. Re-runs every time so hot-reloads of the recorder ns refresh the
hook references to the newly-loaded record! symbol.
Resets suppression-depth and cause-stack so a hot-reload that
interrupts a with-suppressed-recording! body or a throwing handler
inside the dispatch wrapper cannot leave either stuck (a stuck
suppression-depth silently disables recording; a stuck cause-stack
stamps every subsequent record with a stale causeId).
Activate recording. Idempotent and re-entrant: safe to call multiple times. Re-runs every time so hot-reloads of the recorder ns refresh the hook references to the newly-loaded `record!` symbol. Resets `suppression-depth` and `cause-stack` so a hot-reload that interrupts a `with-suppressed-recording!` body or a throwing handler inside the dispatch wrapper cannot leave either stuck (a stuck suppression-depth silently disables recording; a stuck cause-stack stamps every subsequent record with a stale causeId).
(mark-internal! el)Stamp el so events originating inside its shadow tree are bypassed
by the recorder. Public — used by the dock to mark its host element
on mount.
Stamp `el` so events originating inside its shadow tree are bypassed by the recorder. Public — used by the dock to mark its host element on mount.
(observed-tags)Returns a sorted, distinct CLJS vector of tag-name strings for every component the recorder has seen — both live components on this page and components carried in any imported traces. The dock uses this to populate the tag-filter dropdown without taking a static dependency on x-debug-registry, which would otherwise pull every component into the x-trace-history ESM bundle and defeat per-module distribution.
Returns a sorted, distinct CLJS vector of tag-name strings for every component the recorder has seen — both live components on this page and components carried in any imported traces. The dock uses this to populate the tag-filter dropdown without taking a static dependency on x-debug-registry, which would otherwise pull every component into the x-trace-history ESM bundle and defeat per-module distribution.
(pause!)Stop recording new events. Existing records are preserved.
Stop recording new events. Existing records are preserved.
(paused?)Returns true iff recording is currently paused.
Returns true iff recording is currently paused.
(records)Returns a JS Array of all recorded events, oldest first. The returned array is memoised against the ring-buffer identity — callers MUST NOT mutate it.
Returns a JS Array of all recorded events, oldest first. The returned array is memoised against the ring-buffer identity — callers MUST NOT mutate it.
(register!)Called once at app start. If activated, install recording and log a console banner. Mirrors x-debug's opt-in pattern.
Called once at app start. If activated, install recording and log a console banner. Mirrors x-debug's opt-in pattern.
(remove-import! target-id)Drop the import with id target-id. No-op when id is unknown so
double-removes from concurrent UI clicks don't error. Returns nil.
Drop the import with id `target-id`. No-op when id is unknown so double-removes from concurrent UI clicks don't error. Returns nil.
(resume!)Resume recording after pause!.
Resume recording after pause!.
(session-records target-id)Return a JS array of records inside the session with the given id, in chronological order. Empty array if id is unknown or the session has no records in the current buffer.
Return a JS array of records inside the session with the given id, in chronological order. Empty array if id is unknown or the session has no records in the current buffer.
(sessions)Return a JS array of session metadata, one entry per session. Each entry is a JS object: {id, label, startT, endT, recordCount}. endT is null while the session is active. recordCount is computed from the live ring buffer at call time — sessions do not own storage.
The JS-array conversion of the buffer is hoisted out of the session loop so it pays one O(N) materialisation instead of O(sessions × N).
Return a JS array of session metadata, one entry per session.
Each entry is a JS object: {id, label, startT, endT, recordCount}.
endT is null while the session is active. recordCount is computed
from the live ring buffer at call time — sessions do not own
storage.
The JS-array conversion of the buffer is hoisted out of the session
loop so it pays one O(N) materialisation instead of O(sessions × N).(start-session!)Begin a new bounded recording slice. Returns the new session id. If a session is already active, it is auto-stopped first (with a console warning) so the user doesn't end up with a never-ending open session because they forgot to click Stop.
Sessions are bounded by record-id range, not timestamp. Performance
timers have clamped resolution (Spectre mitigations) and adjacent
records can share a t, so a t-range filter is unreliable at the
boundaries. Ids are atomic and strictly monotonic.
Begin a new bounded recording slice. Returns the new session id. If a session is already active, it is auto-stopped first (with a console warning) so the user doesn't end up with a never-ending open session because they forgot to click Stop. Sessions are bounded by record-id range, not timestamp. Performance timers have clamped resolution (Spectre mitigations) and adjacent records can share a `t`, so a t-range filter is unreliable at the boundaries. Ids are atomic and strictly monotonic.
(stop-session!)Close the currently-active session by stamping its :end-t and :end-id. No-op when nothing is active. After stop, sessions reports the session with both timestamps and id bounds set.
Close the currently-active session by stamping its :end-t and :end-id. No-op when nothing is active. After stop, sessions reports the session with both timestamps and id bounds set.
(subscribe! f)Register f as a no-arg callback fired on the next animation frame after any record is pushed (or after pause! / resume! / clear!). Returns an opaque token to pass to unsubscribe!. Safe to call before install!.
Register f as a no-arg callback fired on the next animation frame after any record is pushed (or after pause! / resume! / clear!). Returns an opaque token to pass to unsubscribe!. Safe to call before install!.
(uninstall!)Deactivate recording. Used by tests; not part of the consumer API. Leaves the dispatchEvent wrapper installed (it falls through when inactive) and the JS API in place so any console references remain valid. Clears the cause stack so a leftover frame from an interrupted dispatch can't taint subsequent records.
Deactivate recording. Used by tests; not part of the consumer API. Leaves the dispatchEvent wrapper installed (it falls through when inactive) and the JS API in place so any console references remain valid. Clears the cause stack so a leftover frame from an interrupted dispatch can't taint subsequent records.
(unsubscribe! tok)Remove the subscriber identified by tok. Idempotent: calling with
an unknown token is a no-op.
Remove the subscriber identified by `tok`. Idempotent: calling with an unknown token is a no-op.
(with-suppressed-recording! f)Call thunk f with all recording suppressed for its synchronous extent. Use this around dock setup so custom-element constructors that fire during innerHTML parsing — and call du/set-attr! / du/setv! / dispatch on freshly-created, not-yet-appended internal elements — do not leak records into the trace. The internal-host marker only catches events whose target is already attached somewhere in the marked shadow tree; constructor-time mutations on detached internals predate that attachment, so they need this broader gate. Counter-based, so nested calls compose.
Call thunk f with all recording suppressed for its synchronous extent. Use this around dock setup so custom-element constructors that fire during innerHTML parsing — and call du/set-attr! / du/setv! / dispatch on freshly-created, not-yet-appended internal elements — do not leak records into the trace. The internal-host marker only catches events whose target is already attached somewhere in the marked shadow tree; constructor-time mutations on detached internals predate that attachment, so they need this broader gate. Counter-based, so nested calls compose.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |