re-frame2 — API¶
Type: Reference Reference for the CLJS implementation's API: signatures, status, cross-references. No rationale — per-Spec docs own the why. Pattern-level contracts live in 000-Vision §The pattern and the per-Spec docs.
:fx-overridesasymmetry: id-valued at the pattern level; CLJS reference also accepts fn values — see 002 §:fx-overrides.
Conventions¶
- Status — exactly one base value, optionally combined with a single qualifier:
- Base values:
v1(ships in v1),v1 (preserved)(exists in current re-frame; preserved unchanged),v1 (preserved + extended)(exists today; v1 adds new arity or behaviour),post-v1 lib(design spec in v1 Specs but ships in a post-v1 library). - Qualifier:
dev-only(elided in production builds — the macro emit site or runtime body, depending on the API). - Examples:
v1,v1 (preserved),v1 (dev-only),v1 (preserved, dev-only),post-v1 lib. - The
re-frame.alphanamespace is dissolved — no APIs in this reference live outsidere-frame.core(with the documented per-namespace exceptions:re-frame.test-supportandre-frame.test-helpers). - Macro/Fn: marked
M(macro) orFn. - Spec column — names exactly the canonical owning Spec (the per-Spec doc whose contract this API implements). Migration rules and other cross-references are NOT in the Spec column; they appear in the Notes column when relevant.
- Configure keys — runtime configuration is uniformly via
(rf/configure! <key> <opts>). Every<key>is enumerated in §Configure keys below; per-area tables call out which keys their APIs read but do not redefine the key's vocabulary. - Per-artefact public namespaces. The core surfaces live in
re-frame.core. Per-feature artefacts ship their own public namespace; consumers:requirethe namespace directly (with the documented exception of the epoch surface, which late-binds re-exports throughre-frame.core):
| Namespace | Artefact | Surfaces |
|---|---|---|
re-frame.core |
core | the registration / dispatch / subscribe / interceptor / lifecycle / configure surfaces; late-binds re-exports for two artefacts: (a) the re-frame.epoch surface (epoch-history, restore-epoch, reset-frame-db!, register-epoch-listener!, unregister-epoch-listener!, projected-record, projected-history); (b) the re-frame.ssr query surface (render-to-string, render-tree-hash, project-error, render-head, active-head, head-model->html, head-snapshot). The streaming-render-shell / streaming-render-continuation / streaming-build-final-payload triple is not re-exported — the streaming surface is host-adapter territory and the SSR-aware host (re-frame.ssr.ring / equivalents) requires [re-frame.ssr :as ssr] directly. Re-exports activate when the named artefact is on the classpath; absent artefacts surface :rf.error/<feature>-artefact-missing errors. Per rf2-bc7dw (re-export drift reconciliation). |
re-frame.test-support |
core | dispatch-sequence, assert-path-equals / assert-db-equals, poll-until, fixture machinery (per §Testing). Runtime-state axis — registrar, frames, app-db, drain. The assert-*-equals fn-family mirrors the :rf.assert/* Story event-family. View-tree assertions live in the sibling re-frame.test-helpers. |
re-frame.test-helpers |
core | View-assertion helpers — hiccup-walk (find-by-testid / find-by-attr family, text-content, extract-handler, invoke-handler), the testid authoring helper, and the expand-tree walker (per §Testing — View-assertion helpers). View-tree axis — hiccup data, testids, attached handlers. Runtime-state assertions live in the sibling re-frame.test-support. |
re-frame.ssr |
day8/re-frame2-ssr |
render-to-string, render-tree-hash, streaming-render-*, render-head, active-head, head-model->html, head-snapshot, project-error (per §SSR). |
re-frame.ssr.ring |
day8/re-frame2-ssr-ring |
the Ring host-adapter (default-html-shell, streaming-prefix/suffix, trusted-shell hooks per Spec 011). |
re-frame.schemas |
day8/re-frame2-schemas |
app-schemas, app-schema-at, app-schema-meta-at, app-schemas-digest, set-schema-validator!/-explainer!/-printer!/set-schema-fns!, validate-at-boundary-interceptor (per §Schemas). |
re-frame.http |
day8/re-frame2-http |
the verb helpers get / post / put / delete / patch / head / options (per §HTTP requests). |
re-frame.machines |
day8/re-frame2-machines (post-v1 scaffolding) |
reg-machine, make-machine-handler, machine-transition, sub-machine, machines, machine-meta, the :rf.machine/spawn / :rf.machine/destroy fx (per §Machines). |
re-frame.epoch |
day8/re-frame2-epoch |
epoch-history, restore-epoch, reset-frame-db!, register-epoch-listener!, unregister-epoch-listener!, (rf/configure! :epoch-history ...). Re-exported through re-frame.core via late-bind hooks — (:require [re-frame.epoch]) at boot before consuming the surfaces through re-frame.core (per Tool-Pair §Time-travel — Artefact home). |
re-frame.adapter.uix / re-frame.adapter.helix |
day8/re-frame2-uix / day8/re-frame2-helix |
UIx- and Helix-specific surfaces (per §UIx adapter / §Helix adapter). |
- Projection-maintenance rule. This doc is a non-canonical projection — the canonical contract lives in the per-Spec docs cited in each row's Spec column. The projection MUST stay in sync with shipped artefacts. Every row carries: owner (Spec column) — the canonical spec doc; artefact / namespace — where the public-var lives (table above); public-var status —
v1/v1 (preserved)/post-v1 lib/post-v1 (planned, rf2-<id>)for surfaces specced normatively but not yet shipped (the spec contract holds; the impl is tracked by the named bead); verification pointer — the conformance fixture, the per-artefact test, or the AI-Audit row that asserts the row holds. Rows that document a surface neither shipped nor on a tracking bead MUST be cut from this projection — the design's normative claim then lives only in the owner spec.
Tier taxonomy¶
Every public-surface row in this document is supported — a documented API that downstream apps and tools may rely on, in the status the row's Status column gives. Supported is not the same as front-porch: a system that ships register-epoch-listener!, projected-history, adapter hooks, and reset-frame-db! has a large public surface, but only a small slice of it is what a new app developer should ever reach for. The Tier column on every table below records that slice, using a closed vocabulary of seven values:
| Tier | Meaning | Who reaches for it |
|---|---|---|
| front-porch | The tight set a new app developer needs to build a working app — register / dispatch / subscribe, the frame basics, the everyday reads. Loaded by default by the Guide and the skills. | Every app author, day one. |
| advanced | Power-user surfaces that solve real problems but are not first-reach — epoch-listener registration, trace projection, flows internals, the *-twin fn forms, the lower-level lifecycle (install-adapter!, init-platform). Opt-in. |
Experienced authors, library authors, niche cases. |
| tooling | Dev / inspection surfaces consumed by Story, Xray, the pair-MCP servers, and other Spec 009 / Tool-Pair tools — trace listeners, the registrar query API, off-box-egress projections, the epoch query surface. Not for application logic. | Tool and instrumentation authors. |
| adapter | The substrate-adapter hooks — the per-substrate adapter Var, the hooks and set-*! seams a substrate adapter implements or installs. |
Adapter authors (Reagent / UIx / Helix / custom). |
| testing | Test-only helpers — fixture machinery, assertion helpers, view-tree walkers, the HTTP-stub surfaces. Elided or simply absent from production paths. | Test authors. |
| internal-public | Stable, exported, and safe to call, but not an application surface — provided for a specific tool/host integration, not for app logic. The Xray mount-<panel>! family is the canonical instance (a host that builds its own Xray chrome may call them; an app never should). |
Specific host/tool integrations only. |
| deprecated | On the way out; a replacement exists. Retained only long enough for callers to migrate. (Pre-alpha currently carries none — removed surfaces live in §Removed / not shipped, not here.) | Nobody new — migrate off. |
The Guide and the skills load FRONT-PORCH only by default. A new app developer reading the Guide, or an AI agent operating through the skills, sees the front-porch tier and nothing else unless they opt in. Advanced, tooling, adapter, testing, and internal-public surfaces are opt-in — reached by explicitly pulling in the relevant chapter, the relevant artefact, or the relevant tool. The acceptance bar for this taxonomy: a new app developer can read the one-page front-porch list (the union of all front-porch rows below) and never trip over trace projection, epoch-listener registration, adapter hooks, or Xray internals.
Tier vs Status. Tier and Status are orthogonal axes. Status records shipping lineage (v1 / v1 (preserved) / post-v1 lib / …). Tier records who-reaches-for-it. A surface can be v1 and advanced (register-epoch-listener!), or post-v1 lib and front-porch (the Story run verb is post-v1 lib/tooling; a hypothetical post-v1 ergonomic core helper would be post-v1 lib/front-porch). Read the two columns together.
The front-porch / back-room split is the first instance, generalised. The multi-frame surface (§View ergonomics, per 002 §The multi-frame surface) was already organised as a front-porch / back-room split — dispatch / subscribe / with-frame on the porch, frame-bound-fn* and the {:frame …} override in the back room. The Tier column generalises that split across the whole API: front-porch is the porch; advanced / tooling / adapter / testing / internal-public are the back rooms.
Closed vocabulary, restrictive-by-default. The seven values above are the complete set — no eighth tier is added without an explicit governance decision. When a surface is genuinely ambiguous between two tiers, pick the more restrictive one (advanced over front-porch; internal-public over advanced) and note the call in the row's Notes. The downstream manifest (rf2-3nbl5.2) consumes Tier as a first-class field, so the value must come from this closed set.
What the Tier column covers. Tier is a property of a public var (a fn / macro / Var). The tables that enumerate keyword-addressed registrations — standard events (:rf.route/navigate, :rf/hydrate, …), standard subs (:rf/route, :rf/response, …), standard fx (:rf.nav/push-url, :rf.http/managed, …), standard cofx, reserved fx-ids, the :fx-entry catalogue, the spec-internal schemas, the configure keys, and the error/trace-event catalogues — are not vars and carry no Tier column. They are part of the contract of whichever artefact owns them; their availability follows that artefact's tier (e.g. the :rf.route/* events ship with the advanced routing artefact). The front-porch one-page list is the union of front-porch-tiered var rows.
Tiering of cross-tool surfaces (Story, Xray, pair-MCP)¶
Story, Xray, and the MCP support namespaces ship their own public-var rows in their own specs (007-Stories.md, tools/xray/spec, Tool-Pair.md); this projection rows only the slices that surface through re-frame.core or the per-feature artefacts. The Tier taxonomy is nonetheless repo-wide and authoritative over those surfaces — the per-tool specs classify against this closed vocabulary rather than inventing local terms. The standing classifications (resolved here, so the per-tool specs reference rather than re-litigate):
- Story facade — the public execution verbs
run/is/explainand the registration macros (reg-story/reg-variant/reg-workspace/reg-tag/reg-decorator/reg-story-panel) are tooling (a Storybook-shaped dev surface, not application logic). Therun-variant/is-variant/run-plan/is-plan/watch-variant/reset-variantimplementation/migration vocabulary is not public — unrowed, not a tier. (Absorbs story F-8.) - Xray
mount-<panel>!family (mount-epoch-panel!,mount-app-db-diff!,mount-trace!,mount-machine-inspector!,mount-routing!,mount-segment-inspector!, the mastermount-shell!, …) — internal-public. They are exported and stable, but they are a host-embed surface, not an application or even general-tool surface: a host that builds its own Xray chrome may call amount-<panel>!; an app never should. This resolves the prior "public vs internal-but-stable" question against the closed vocabulary — the answer isinternal-public. (Resolves xray M2.) - Xray panel-helper functions (the per-panel render/projection helpers beneath the mount-fns) — tooling where they are a documented panel-author surface, otherwise unrowed-internal. (Absorbs xray H6.)
- pair-MCP support namespaces — the trace/egress surfaces they consume (
elide-wire-value,sensitive?, the registrar query API, the epoch query surface) are tooling, tiered at theirre-frame.core/ artefact home rows below.
Not-rowed internal carve-outs¶
The only internal (unrowed, MUST-NOT-depend-on) carve-outs in re-frame.core are two JVM-only macro-helpers re-exposed purely so pre-split tests can reach them:
re-frame.core/expand-reg-view—^:no-doc; the canonical home isre-frame.core-reg-view-macro/expand-reg-view.re-frame.core/parse-reg-view-args—^:no-doc; the canonical home isre-frame.core-reg-view-macro/parse-reg-view-args.
Neither is rowed in this projection. Applications and tools MUST NOT depend on these re-exports; reach the canonical homes directly. These are not a tier — they are below the public surface entirely.
Registration¶
Return value. Every
reg-*row below returns its primary id — the keyword (or path, forreg-app-schema) the caller registered with.reg-flowreturns the:idvalue of its flow-map (the primary id is carried by the map, not a separate arg). Per Conventions §reg-*return-value convention.
| API | M/Fn | Signature | Status | Tier | Spec | Notes |
|---|---|---|---|---|---|---|
reg-event-db |
M | (reg-event-db id ?metadata-or-interceptors handler) |
v1 (preserved + extended) | front-porch | 002 | Macro for source-coord capture. See MIGRATION §M-5 for higher-order-use migration. |
reg-event-fx |
M | (reg-event-fx id ?metadata-or-interceptors handler) |
v1 (preserved + extended) | front-porch | 002 | Handler accepts (fn [m] ...) or (fn [m event-vec] ...). |
reg-event-ctx |
M | (reg-event-ctx id ?metadata-or-interceptors handler) |
v1 (preserved + extended) | advanced | 002 | Lower-level than reg-event-db/-fx — the full-context handler form; reach for it when you need to manipulate the interceptor context directly. |
reg-sub |
M | (reg-sub id ?metadata signal-fn? computation-fn) |
v1 (preserved + extended) | front-porch | 002 | :<- sugar preserved. The only sub-registration form in v2. |
reg-fx |
M | (reg-fx id ?metadata handler) |
v1 (preserved + extended) | front-porch | 002 | Unary or binary handler. |
reg-cofx |
M | (reg-cofx id ?metadata handler) |
v1 (preserved) | front-porch | 002 | Reading a sub's value from a handler is by cofx-wrapping — see Guide ch.05 §Reading a subscription from a handler. Inline-interceptor alternative. An interceptor map {:id ... :before (fn [ctx] (assoc-in ctx [:coeffects k] v))} is a legal participant in any event's interceptor vector and may inject coeffects without going through the registry. reg-cofx + inject-cofx is the canonical path (id-addressable: test-stub by id, REPL hot-rebind, Xray enumeration, parameterised reuse); the inline form is the escape hatch for define-once/use-locally/never-stubbed cases. Rubric and worked example: Guide ch.05 §When ceremony outweighs benefit. Design decision. |
reg-frame |
M | (reg-frame id metadata) |
v1 | front-porch | 002 | Atomic create + register. |
make-frame |
Fn | (make-frame opts) → :rf.frame/<id> |
v1 | advanced | 002 | Anonymous frame; gensym'd id. The named reg-frame is the front-porch path; make-frame is for tools / tests / dynamic frames. |
reg-view |
M | (reg-view sym [args] body+) / (reg-view sym docstring [args] body+) / (reg-view ^{:rf/id :explicit/id} sym [args] body+) |
v1 | front-porch | 004 | Defn-shape; auto-defs the symbol; auto-derives id from (keyword *ns* sym); auto-injects dispatch / subscribe as lexical bindings; rejects non-defn-shape bodies at macroexpand. |
reg-view* |
Fn | (reg-view* id render-fn) / (reg-view* id metadata render-fn) |
v1 | advanced | 004 | Plain-fn surface beneath reg-view. No auto-def, no auto-inject, no compile check. Use for computed ids, library-generated views, Reagent Form-3 (create-class), or registration without a Var. The * follows Clojure's let/let*, fn/fn* idiom (per Conventions). |
reg-machine |
M | (reg-machine machine-id machine-spec) |
v1 | advanced | 005 | Optional re-frame.machines artefact. Walks the literal spec form at expansion time; co-locates per-element source on each :guards / :actions entry + a reference-site :source-coords on each :states-tree map node. Top-level call-site coords land on handler-meta. |
reg-machine* |
Fn | (reg-machine* machine-id machine-spec) |
v1 | advanced | 005 | Plain-fn surface beneath reg-machine. No source-coord walking. Use for code-gen pipelines, REPL workflows, or conformance harnesses that synthesise specs from data. |
defmachine |
M | (defmachine name spec) / (defmachine name docstring spec) |
v1 | advanced | 005 | def-shape for the def-then-register pattern. Walks the literal spec at the definition site, stamping per-element source onto the def'd value so it travels into a later (reg-machine id name). Does not register. |
reg-app-schema |
M | (reg-app-schema path schema) / (reg-app-schema path schema opts) |
v1 | advanced | 010 | Optional re-frame.schemas artefact. Path is the registration id. App-db schemas are path-keyed and live in the schemas artefact's per-frame side-table (per rf2-cq1ak — app-db schemas are NOT a registrar kind). Every other reg-* is keyword-id-keyed; here the first arg is the path vector (e.g. [:user]) and (app-schema-at [:user]) / (app-schema-meta-at [:user]) look up by the same vector. The asymmetry is principled (paths are first-class in get-in / assoc-in grain — schemas-at-paths matches the dataflow grain), not accidental. Per Conventions §reg-* return-value rule. |
reg-app-schemas |
M | (reg-app-schemas {path-1 schema-1, path-2 schema-2, ...}) / (reg-app-schemas {…} opts) — bulk plural form for feature-modular apps that register 5–20 paths against the same prefix (per Conventions §Feature-modularity prefix convention). Each entry routes through the singular reg-app-schema and is stamped with this call's source-coords. Returns the vector of paths registered |
v1 | advanced | 010 | |
add-marks |
Fn | (add-marks frame-id {path mark, ...}) |
v1 | advanced | 015 | Frame-scoped path-marks for data classification — :sensitive paths render as :rf/redacted in observation surfaces; :large paths surface as :rf/large summaries. Per 015 §App-db marks (per frame) — add-marks / set-marks. Additively merges into the frame's existing mark-set — paths not mentioned keep their prior state. Schema-attached marks per reg-app-schema :sensitive? / :large? are preserved and union at lookup time. Pure declaration — does not mutate app-db. Returns frame-id. |
set-marks |
Fn | (set-marks frame-id {path mark, ...}) |
v1 | advanced | 015 | Frame-scoped path-marks for data classification. Per 015 §App-db marks (per frame). Wholesale replaces the frame's prior mark-set — paths not mentioned are CLEARED. Schema-attached marks are preserved. Pure declaration. Returns frame-id. |
reg-flow |
Fn | (reg-flow flow) / (reg-flow flow opts) |
v1 | advanced | 013 | Optional flows artefact. opts is a map (currently {:frame frame-id}). The shipped surface is opts-map only — same shape as dispatch / subscribe / clear-flow. Returns the flow's :id (per Conventions §reg-* return-value convention). The :flow registrar slot is last-registration-wins across frames (the same id registered against multiple frames shares one registrar slot, keyed by flow-id only); for full per-frame discovery use @re-frame.flows/flows — the per-frame runtime registry is the source of truth for evaluation. Per 013-Flows.md §Frame-scoping and. |
reg-route |
M | (reg-route id metadata) |
v1 | advanced | 012 | Optional routing artefact. |
reg-head |
M | (reg-head id ?metadata head-fn) |
v1 | advanced | 011 | Optional SSR artefact. New registry kind :head; routes name a registered head via :head route metadata. Captures source-coords; under the optional-artefact wrapper convention the surface routes through the :ssr/reg-head late-bind hook. |
reg-error-projector |
M | (reg-error-projector id ?metadata projector-fn) |
v1 | advanced | 011 | Optional SSR artefact. New registry kind :error-projector; named per-frame via the frame's :ssr {:public-error-id ...} metadata (per reg-frame / make-frame). |
Clearing registrations¶
| API | M/Fn | Signature | Status | Tier |
|---|---|---|---|---|
clear-event |
Fn | (clear-event) / (clear-event id) |
v1 (preserved) | advanced |
clear-sub |
Fn | (clear-sub) / (clear-sub id) |
v1 (preserved) | advanced |
clear-fx |
Fn | (clear-fx) / (clear-fx id) |
v1 (preserved) | advanced |
clear-flow |
Fn | (clear-flow id) / (clear-flow id opts) |
v1 | advanced |
destroy-frame! |
Fn | (destroy-frame! frame-id) — the normative teardown boundary. Per-feature artefacts (flows, machines, schemas, SSR, epoch) hang their frame-scoped cleanup off this call; flows release per 013 §Frame-destroy teardown. |
v1 | front-porch |
reset-frame! |
Fn | (reset-frame! frame-id) |
v1 | advanced |
clear-sub-cache! |
Fn | (clear-sub-cache! frame-id?) |
v1 (preserved) | advanced |
Dispatch and subscribe¶
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
dispatch |
M | (dispatch event) / (dispatch event opts) |
v1 (preserved + extended); macro per — captures call-site for :rf.trace/call-site |
front-porch | 002 |
dispatch* |
Fn | (dispatch* event) / (dispatch* event opts) |
— fn form for HoF / programmatic dispatch (no call-site stamping) | advanced | 002 |
dispatch-sync |
M | (dispatch-sync event) / (dispatch-sync event opts) |
v1 (preserved + extended); macro per | front-porch | 002 |
dispatch-sync* |
Fn | (dispatch-sync* event) / (dispatch-sync* event opts) |
— fn form for HoF / programmatic sync dispatch | advanced | 002 |
subscribe |
M | (subscribe query-v) / (subscribe frame-id query-v) |
v1 (preserved + extended); macro per | front-porch | 002 |
subscribe* |
Fn | (subscribe* query-v) / (subscribe* frame-id query-v) |
— fn form for HoF / programmatic subscribe | advanced | 002 |
subscribe-once |
Fn | (subscribe-once query-v) / (subscribe-once frame-id query-v) → value (subscribe + deref + immediate unsubscribe; one-shot, non-reactive read for handler bodies, machine actions, REPL) |
v1 | advanced | 006 |
unsubscribe |
Fn | (unsubscribe query-v) / (unsubscribe frame-id query-v) → nil (decrement the cache ref-count; ref-count→0 schedules disposal after the configured :sub-cache grace-period). Carved out from the Conventions §Tear-down verb axis — clear-sub is already taken by the symmetric inverse of reg-sub (the registrar decrement), so un- is reserved as the singular form for the sub-cache ref-count decrement. |
v1 | advanced | 006 |
sub-machine |
Fn | (sub-machine machine-id) → reaction over snapshot. Sugar over (subscribe [:rf/machine machine-id]). |
v1 | advanced | 005 |
opts map keys: :frame, :fx-overrides, :interceptor-overrides, :trace-id, :source. Envelope shape and semantics: see 002 §Routing: the dispatch envelope.
Canonical event / query-v shape (best practice). [<id>] (trivial), [<id> <single-scalar>] (single-arg), [<id> {<k> <v>}] (multi-arg → single map payload). Variadic [<id> a b c] is tolerated by the runtime for v1-migration and caller convenience; the linter nudges new code toward the map form. Full rationale and cross-refs: Conventions §Canonical event-vector shape.
dispatch-* family taxonomy¶
Per audit-of-audits state-machines #10, the dispatch-* family has two sub-shapes that look alike on first read but answer different questions. Both are dispatch operations — the family-prefix is honest — but they sit in different sub-families.
Stamping-pair sub-family (dispatch / dispatch-sync macros + dispatch* / dispatch-sync* fn variants).
The pair-shape question is "do you want call-site stamping or not?" The macro form captures :rf.trace/call-site from the surrounding source position so tooling can navigate from a trace event back to the originating expression. The * fn-form skips the stamping — needed for HoF composition ((map dispatch* events)) where a macro can't sit inside the higher-order call. Both shapes route through the same dispatcher; only the trace stamping differs.
Named-target sugar sub-family (dispatch-to-system, per 005 §Cross-machine messaging by name).
This question is "do you have a :system-id instead of a target machine-id?" dispatch-to-system is sugar over (when-let [m (machine-by-system-id system-id)] (dispatch [m event])) — it dispatches just like the macros, but it resolves the target through the per-frame [:rf/runtime :machines :system-ids] reverse index first. It is not outside the dispatch family; it's a named-addressing sugar built on top of dispatch. The hyphen-after-dispatch reads as "dispatch with extra routing logic on top," not "different kind of dispatch."
The two sub-families compose: dispatch-to-system ultimately calls dispatch, so the same :rf.trace/call-site stamping fires (call-site at the dispatch-to-system invocation, since that's the macro the user wrote). When future named-addressing variants land (per actor-model patterns) they slot into the same sub-family; new call-site-stamping variants slot into the same pair.
View ergonomics¶
The multi-frame surface is organised by intent, not mechanism (a front-porch / back-room split — per 002 §The multi-frame surface):
- Single-frame (no frames in play):
dispatch,dispatch-sync,subscribe. - Scope:
with-frame,with-new-frame,frame-provider. - Hold (carry a frame's ops as a value, across async):
frame-handle(common),frame-bound-fn/frame-bound-fn*(advanced). - Override: the
{:frame …}opt — first-class explicit routing for tools / tests / SSR / fx handlers. - Reads / lifecycle:
app-db-value,current-frame-id,snapshot-of,destroy-frame!,make-frame,reg-frame,frame-ids,frame-meta(see §Public registrar query API).
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
frame-provider |
Component (Reagent) | [rf/frame-provider {:frame :todo} & children] |
v1 | front-porch | 002 |
with-frame |
M | (with-frame :keyword body) — pin to an existing frame-id. Vector arg is a compile-time error (use with-new-frame) |
v1 | front-porch | 002 |
with-new-frame |
M | (with-new-frame [sym expr] body) — eval expr, bind sym, run body, destroy frame on exit. Keyword arg is a compile-time error (use with-frame) |
v1 | front-porch | 002 |
frame-handle |
Fn | (frame-handle) or (frame-handle frame-id) → {:frame :dispatch :dispatch-sync :subscribe} — the keystone OPERATION BUNDLE. Captures the frame at CREATION; its ops always target the captured frame and survive async. A per-call :frame opt MUST NOT override the captured frame (the handle is locked). Read app-db via (app-db-value (:frame h)), not the handle |
v1 | front-porch | 002, 004 |
frame-bound-fn |
M | (frame-bound-fn [args] body) → frame-rebinding closure (fn-syntax sugar over frame-bound-fn*). Re-establishes *current-frame* (captured at lex-binding) for the body |
v1 | advanced | 002 |
frame-bound-fn* |
Fn | (frame-bound-fn* f) or (frame-bound-fn* frame-id f) → frame-rebinding closure. The *-twin of the frame-bound-fn macro — wrap an existing callback so its body always runs with *current-frame* re-bound. Advanced; prefer frame-handle for the common dispatch / subscribe case |
v1 | advanced | 002 |
view |
Fn | (view view-id) → render-fn (runtime-lookup handle; returns the registered render-fn, not hiccup). Use in hiccup as [(rf/view :id) args...] — the lookup form for late-binding a registered view by id. |
v1 | advanced | 001, 004 |
with-frame (pin) and with-new-frame (eval-bind-run-destroy) are documented in 002 §with-frame and with-new-frame. The macros are non-overlapping: each rejects the other's argument shape at compile time, with :recovery pointing the caller at the right sibling. Per rf2-twoc5.
frame-handle is the keystone affordance — it replaces the removed dispatcher / subscriber nouns and is the single answer to "carry a frame's dispatch/subscribe ops across an async boundary." frame-bound-fn is a CLJS-only macro; CLJS users either reach it via rf/frame-bound-fn (after (:require [re-frame.core :as rf])) or :require-macros [re-frame.core :refer [frame-bound-fn]]. frame-bound-fn* is a plain fn, available on both CLJS and JVM (no macro indirection).
UIx adapter (Spec 006)¶
UIx-specific surfaces live in re-frame.adapter.uix (artefact day8/re-frame2-uix) — they are NOT re-exported from re-frame.core because core has no static dependency on the adapter (the dependency direction is adapter → core per Conventions §Adapter shipping convention). Apps targeting UIx :require [re-frame.adapter.uix :as uix-adapter] and call the surfaces directly.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
uix-adapter/adapter |
Var (map) | {:make-state-container … :render … :dispose-adapter! …} |
v1 | adapter | 006 |
uix-adapter/use-subscribe |
Fn (UIx hook) | (use-subscribe query-v) / (use-subscribe frame-kw query-v) → current sub value |
v1 | adapter | 006 |
uix-adapter/use-current-frame |
Fn (UIx hook) | (use-current-frame) → frame-kw |
v1 | adapter | 006 |
uix-adapter/frame-provider |
Fn (UIx component) | ($ uix-adapter/frame-provider {:frame :session} child-1 child-2) — idiomatic $ trailing children (rf2-7kii2) |
v1 | adapter | 002, 006 |
uix-adapter/wrap-view |
Fn | (wrap-view id metadata user-fn) → wrapped fn (source-coord injection per Spec 006 §Source-coord annotation) |
v1 | adapter | 006 |
uix-adapter/flush-views! |
Fn | (flush-views!) / (flush-views! f) — wraps React's act() for tests |
v1 | adapter | 006, 008 |
uix-adapter/set-hiccup-emitter! |
Fn | (set-hiccup-emitter! f) — install render-tree → HTML fn (parity with the Reagent adapter's late-bind seam) |
v1 | adapter | 006, 011 |
Per Decision 1 the hook is named use-subscribe (matching the React/UIx idiom). Per Decision 3 there is no auto-injection — UIx components call the hook and (:dispatch (rf/frame-handle)) directly. Per Decision 4 reg-view (the Reagent macro) does NOT cover UIx; UIx users register with rf/reg-view* if they need registry-keyed view addressing.
The shared React Context that backs frame-provider lives in re-frame.adapter.context (CLJS-only file in core, factored out per Decision 2) — both the Reagent adapter and the UIx adapter consume the same createContext object so a future mixed-substrate app's frame-provider chain composes across substrates.
Helix adapter (Spec 006)¶
Helix-specific surfaces live in re-frame.adapter.helix (artefact day8/re-frame2-helix) — they are NOT re-exported from re-frame.core because core has no static dependency on the adapter (the dependency direction is adapter → core per Conventions §Adapter shipping convention). Apps targeting Helix :require [re-frame.adapter.helix :as helix-adapter] and call the surfaces directly. The Helix adapter mirrors the UIx adapter exactly — the eight rf2-3yij decisions transfer one-for-one to rf2-2qit.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
helix-adapter/adapter |
Var (map) | {:make-state-container … :render … :dispose-adapter! …} |
v1 | adapter | 006 |
helix-adapter/use-subscribe |
Fn (Helix hook) | (use-subscribe query-v) / (use-subscribe frame-kw query-v) → current sub value |
v1 | adapter | 006 |
helix-adapter/use-current-frame |
Fn (Helix hook) | (use-current-frame) → frame-kw |
v1 | adapter | 006 |
helix-adapter/frame-provider |
Fn (Helix component) | ($ helix-adapter/frame-provider {:frame :session} child-1 child-2) — idiomatic $ trailing children (rf2-7kii2) |
v1 | adapter | 002, 006 |
helix-adapter/wrap-view |
Fn | (wrap-view id metadata user-fn) → wrapped fn (source-coord injection per Spec 006 §Source-coord annotation) |
v1 | adapter | 006 |
helix-adapter/flush-views! |
Fn | (flush-views!) / (flush-views! f) — wraps React's act() for tests |
v1 | adapter | 006, 008 |
helix-adapter/set-hiccup-emitter! |
Fn | (set-hiccup-emitter! f) — install render-tree → HTML fn (parity with the Reagent and UIx adapters' late-bind seam) |
v1 | adapter | 006, 011 |
Per (transferring Decision 1) the hook is named use-subscribe. Per Decision 3 there is no auto-injection — Helix components call the hook and (:dispatch (rf/frame-handle)) directly. Per Decision 4 reg-view (the Reagent macro) does NOT cover Helix; Helix users register with rf/reg-view* if they need registry-keyed view addressing.
The shared React Context that backs frame-provider lives in re-frame.adapter.context (CLJS-only file in core, factored out per Decision 2) — the Reagent, UIx, and Helix adapters all consume the same createContext object so a mixed-substrate app's frame-provider chain composes across substrates.
Routing (Spec 012)¶
reg-route is rowed canonically in §Registration.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
match-url |
Fn | (match-url url) → {:route-id :params :query :validation-failed?} or nil |
v1 | advanced | 012 |
route-url |
Fn | (route-url route-id path-params) / (route-url route-id path-params query-params) → URL string |
v1 | advanced | 012 |
route-link |
Fn (registered view at :route/link) |
[rf/route-link {:to :route-id :params {...} :query {...} :fragment "..." & html-attrs} & children] |
v1 | advanced | 012 |
reg-route metadata reserved keys: :doc, :path, :params, :query, :query-defaults, :query-retain, :tags, :parent, :on-match, :on-error, :can-leave, :scroll. Canonical detail in 012-Routing.md; shape in Spec-Schemas §:rf/route-metadata.
route-link click rules: a plain primary-button click (no modifier keys, no defaultPrevented) calls .preventDefault and dispatches [:rf/url-requested {:url <synthesised> :to <route-id> :params {...} :query {...} :fragment "..."}]. Modifier-key clicks (cmd / ctrl / shift / alt) and auxiliary-button clicks (middle-click) defer to the browser so the native href opens in a new tab. A caller-supplied :on-click runs first; if it calls .preventDefault (or otherwise leaves defaultPrevented true) the framework's interception is skipped. Keys other than :to / :params / :query / :fragment / :on-click pass through to the underlying <a> element. Detailed semantics in 012-Routing.md §Linking from views.
Standard route-related events:
| Event | Notes | Spec |
|---|---|---|
:rf.route/navigate |
Navigate to a registered route. | 012 |
:rf.route/handle-url-change |
Default handler for :rf.route/transitioned. |
012 |
:rf.route/transitioned |
The browser URL changed. | 012 |
:rf/url-requested |
The user clicked a framework-owned link. | 012 |
:rf.route/navigation-blocked |
A :can-leave guard rejected a navigation. |
012 |
:rf.route/continue |
User-dispatched event proceeding a blocked navigation. | 012 |
:rf.route/cancel |
User-dispatched event abandoning a blocked navigation. | 012 |
Standard route-related subs:
| Sub | Returns | Spec |
|---|---|---|
:rf/route |
The full :rf/route slice {:id :params :query :transition :error} |
012 |
:rf.route/id |
Current route id | 012 |
:rf.route/params |
Current path params | 012 |
:rf.route/query |
Current query params | 012 |
:rf.route/transition |
:idle / :loading / :error |
012 |
:rf.route/error |
Current error map (when :transition = :error) |
012 |
:rf.route/fragment |
Current URL fragment (string or nil) | 012 |
:rf.route/chain |
Vector of route ids from parent-most to current (per :parent links) |
012 |
:rf/pending-navigation |
The pending-nav slot (per :rf/pending-navigation schema) when a navigation is blocked; nil otherwise |
012 |
Standard route-related fx (canonical detail in 012-Routing.md):
| Fx | Args | Platforms |
|---|---|---|
:rf.nav/push-url |
URL string | :client |
:rf.nav/replace-url |
URL string | :client |
:rf.nav/scroll |
scroll-spec map | :client |
:rf.route/with-nav-token |
{:do <fx-entry> :nav-token <token>} |
universal |
Standard route-related cofx (canonical detail in 012-Routing.md):
| Cofx | Injects | Spec |
|---|---|---|
:nav-token |
The active navigation epoch token (read from [:rf/runtime :routing :current :nav-token]) under the coeffect key :nav-token — declare via (inject-cofx :nav-token) in an :on-match-reached handler to capture the epoch live at scheduling time for stale-result suppression. Per 012 §Navigation tokens. |
012 |
SSR (Spec 011)¶
Namespace: the surfaces below live in
re-frame.ssr(artefactday8/re-frame2-ssr); consumers(:require [re-frame.ssr :as ssr]). The Ring host-adapter lives inre-frame.ssr.ring(artefactday8/re-frame2-ssr-ring). Neither is re-exported fromre-frame.core— apps targeting SSR add the artefacts to their deps and require the namespace directly. Per the §Conventions per-artefact namespace table.
reg-head and reg-error-projector are rowed canonically in §Registration. The head-fn signature is (fn [db route] head-model); the projector-fn signature is (fn [trace-event] :rf/public-error).
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
render-to-string |
Fn | (render-to-string view-or-hiccup opts) → HTML string |
v1 | advanced | 011 |
render-tree-hash |
Fn | (render-tree-hash render-tree) → 32-bit FNV-1a structural hash (lowercase hex). Identical output on JVM and CLJS for the same canonical-EDN representation. Per 011 §Hydration-mismatch detection. |
v1 | advanced | 011 |
project-error |
Fn | (project-error frame-id trace-event) → :rf/public-error. Applies the active error-projector (selected by the frame's :ssr {:public-error-id ...} metadata) for the named frame. Per 011 §Server error projection. |
v1 | advanced | 011 |
render-head |
Fn | (render-head head-id opts) → :rf/head-model |
v1 | advanced | 011 |
active-head |
Fn | (active-head) / (active-head frame-id) → :rf/head-model |
v1 | advanced | 011 |
head-model->html |
Fn | (head-model->html head-model) / (head-model->html head-model {:wrap? bool}) → inner-head HTML string |
v1 | advanced | 011 |
head-snapshot |
Fn | (head-snapshot frame-id) → {head-id → :rf/head-model}. Read the per-frame snapshot of last-produced head-models. Returns {} for a frame that has never seen a render-head call (or whose snapshot has been cleared via per-request frame teardown). Useful for tests, introspection, and tools. Re-exported as rf/head-snapshot. Per 011 §Head/meta contract. |
v1 | advanced | 011 |
streaming-render-shell |
Fn | (streaming-render-shell root-hiccup) → {:shell-html "…" :continuations [{:id <id> :subtree <hiccup>} …]}. Walks the tree once; at each :rf/suspense-boundary emits a <template …suspense-fallback> placeholder + records a continuation. Per 011 §Streaming SSR — (a). |
v1 | advanced | 011 |
streaming-render-continuation |
Fn | (streaming-render-continuation frame-id entry) → {:id … :html "…" :delta {…} :failed? bool}. Drains one continuation against frame-id's app-db; snapshots before-db / after-db and computes the per-subtree delta. Catches throws and surfaces the original fallback HTML inline (per 011 §Failure semantics — inline fallback). |
v1 | advanced | 011 |
streaming-build-final-payload |
Fn | (streaming-build-final-payload frame-id render-hash opts) → canonical :rf/hydration-payload. Called after all continuations drain to populate the __rf_payload final chunk. |
v1 | advanced | 011 |
Standard SSR-related events:
| Event | What it does | Spec |
|---|---|---|
:rf/server-init |
Per-request server-side initialisation. Reads request cofx; dispatches setup events. :platforms #{:server}. |
011 |
:rf/hydrate |
Seed the client-side app-db from the server-supplied payload. Runs once on client bootstrap. |
011 |
Standard SSR-related fx (server-only; :platforms #{:server}):
| Fx | Args | Spec |
|---|---|---|
:rf.server/set-status |
:int (per :rf.fx.server/set-status-args) |
011 |
:rf.server/set-header |
{:name :value} (per :rf.fx.server/set-header-args) |
011 |
:rf.server/append-header |
{:name :value} (per :rf.fx.server/append-header-args) |
011 |
:rf.server/set-cookie |
:rf.server/cookie map |
011 |
:rf.server/delete-cookie |
{:name ?:path ?:domain} |
011 |
:rf.server/redirect |
{:location ?:status} (default :status 302); truncates HTML |
011 |
Standard SSR-related subs:
| Sub | Returns | Spec |
|---|---|---|
:rf/response |
The current request's response accumulator (status / headers / cookies / redirect) | 011 |
:rf/head |
The head model for the active route (resolved via (active-head)) |
011 |
:rf/public-error |
The sanitised public-error projection when an error page is being rendered; nil otherwise |
011 |
Standard cofx (server-only):
| Cofx | Returns | Spec |
|---|---|---|
:rf.server/request |
The active HTTP request map | 011 |
reg-fx's :platforms metadata key (a set containing :server and/or :client) gates fx execution by active platform; default #{:server :client} (universal) when the key is absent. Skipped fx emit a :rf.fx/skipped-on-platform trace event. Detail in 011 §:platforms metadata on reg-fx.
SSR error-projection policy is per-frame metadata (see Conventions §Configuration surfaces bucket 3): a frame opts in via the :ssr {:public-error-id ... :dev-error-detail? ...} map on its reg-frame / make-frame metadata. See 011 §Server error projection for the keys.
HTTP requests (Spec 014)¶
:rf.http/managed is the canonical, optional HTTP-request fx — v1 (optional capability). CLJS reference ships it on Fetch (browser) and java.net.http.HttpClient (JVM). Args, behaviours, decode pipeline, retry semantics, abort surface, failure taxonomy, and reply addressing are normatively defined in 014-HTTPRequests.md; the surface below is the API-level summary.
| API | Kind | Signature / shape | Status | Tier | Spec |
|---|---|---|---|---|---|
:rf.http/managed |
fx | [:rf.http/managed args-map] — args per 014 §The args map and :rf.fx/managed-args |
v1 (optional capability) | — (fx-id; follows the advanced HTTP artefact) |
014 |
:rf.http/managed-abort |
fx | [:rf.http/managed-abort request-id] — abort the in-flight request with the given :request-id |
v1 (optional capability) | — (fx-id) | 014 |
:rf.http/managed-canned-success |
fx | [:rf.http/managed-canned-success {:value v}] — synthesises the canonical success reply (per 014 §Testing). Registered at load of re-frame.http-test-support (NOT re-frame.http-managed); the stubbing macros (rows below) ship in the same namespace per (audit-of-audits #15). |
v1 (optional capability, dev/test) | — (fx-id; test) | 014 |
:rf.http/managed-canned-failure |
fx | [:rf.http/managed-canned-failure {:kind <:rf.http/*> :tags {...}}] — synthesises the canonical failure reply. Same registration gate (re-frame.http-test-support) and same co-location with the stubbing macros. |
v1 (optional capability, dev/test) | — (fx-id; test) | 014 |
with-managed-request-stubs |
M | (with-managed-request-stubs route-map body+) — route-map {[<method> <url>] {:reply ...}} per 014 §Testing. Lives in re-frame.http-test-support (the single home for HTTP test surfaces per — audit-of-audits #15 closed the prior split that placed the macros in re-frame.http-managed); see Spec 008 §HTTP test surfaces. |
v1 (optional capability, dev/test) | testing | 014 |
with-managed-request-stubs* |
Fn | (with-managed-request-stubs* route-map body-fn) — plain-fn surface beneath with-managed-request-stubs; the * follows the Clojure let/let*, fn/fn* idiom (per Conventions). Ships in re-frame.http-test-support. Use for computed route-maps or non-literal bodies. Per 014 §Testing. |
v1 (optional capability, dev/test) | testing | 014 |
install-managed-request-stubs! |
Fn | (install-managed-request-stubs! route-map) — install the stub routes; persists until uninstall-managed-request-stubs! is called. Lower-level than with-managed-request-stubs; use when stubs span multiple deftests. Ships in re-frame.http-test-support. Per 014 §Testing. |
v1 (optional capability, dev/test) | testing | 014 |
uninstall-managed-request-stubs! |
Fn | (uninstall-managed-request-stubs!) — drop any installed stubs and restore real-request routing. Idempotent. Ships in re-frame.http-test-support. Per 014 §Testing. |
v1 (optional capability, dev/test) | testing | 014 |
reg-http-interceptor |
Fn | (reg-http-interceptor id interceptor-map) — register an HTTP interceptor on a frame's :rf.http/managed middleware chain (per 014 §Middleware). id is a keyword; interceptor-map carries at least one of :before (fn [ctx] ctx') and :after (fn [ctx response] response'), plus optional :frame (default :rf/default) and any :rf/registration-metadata (:doc / :tags / :schema / :sensitive?). Per rf2-uheqq (shape iii) the surface mirrors the event-interceptor {:id :before :after} shape — symmetric request/response sides; :before chain in registration order, :after chain in reverse. |
v1 (optional capability) | advanced | 014 |
clear-http-interceptor |
Fn | (clear-http-interceptor id) / (clear-http-interceptor frame id) — unregister an interceptor by id (per 014 §Middleware). Single-arity targets :rf/default. |
v1 (optional capability) | advanced | 014 |
re-frame.http/get |
Fn | (rf.http/get url) / (rf.http/get url args) — build a [:rf.http/managed {:request {:method :get :url url} ...}] fx vector (per 014 §Call-site helpers). |
v1 (optional capability) | advanced | 014 |
re-frame.http/post |
Fn | (rf.http/post url) / (rf.http/post url args) — POST helper; same shape as get. |
v1 (optional capability) | advanced | 014 |
re-frame.http/put |
Fn | (rf.http/put url) / (rf.http/put url args) — PUT helper. |
v1 (optional capability) | advanced | 014 |
re-frame.http/delete |
Fn | (rf.http/delete url) / (rf.http/delete url args) — DELETE helper. |
v1 (optional capability) | advanced | 014 |
re-frame.http/patch |
Fn | (rf.http/patch url) / (rf.http/patch url args) — PATCH helper. |
v1 (optional capability) | advanced | 014 |
re-frame.http/head |
Fn | (rf.http/head url) / (rf.http/head url args) — HEAD helper. |
v1 (optional capability) | advanced | 014 |
re-frame.http/options |
Fn | (rf.http/options url) / (rf.http/options url args) — OPTIONS helper. |
v1 (optional capability) | advanced | 014 |
Public API surface in re-frame.core for ports that ship Spec 014. Ports that omit it MUST NOT register :rf.http/* for any other purpose (per Conventions §Reserved namespaces).
The verb helpers (get / post / put / delete / patch / head / options) live in re-frame.http — users (:require [re-frame.http :as rf.http]) alongside re-frame.core. They're pure synthesis fns that produce the canonical [:rf.http/managed args-map] fx vector; the namespace ships in day8/re-frame2-http (same artefact as the fx they reference) so loading the helpers and the fx are a single dep decision.
Reply-payload shape¶
Every reply lands as {:rf/reply {:kind :success :value v}} or {:rf/reply {:kind :failure :failure {:kind <:rf.http/*> ...}}}. Default reply addressing dispatches [<originating-event-id> (assoc original-msg :rf/reply ...)] back to the same handler; explicit :on-success / :on-failure targets append the reply payload as the last event-vector arg. Both shapes detailed in 014 §Reply addressing.
Failure categories (closed set)¶
The eight :kind values inside a failure reply, all reserved under :rf.http/* (per Conventions §Reserved namespaces). See 014 §Failure categories for tags-by-kind:
:kind |
Meaning |
|---|---|
:rf.http/transport |
Network / DNS / connection error pre-HTTP |
:rf.http/cors |
CORS preflight rejected (CLJS-only) |
:rf.http/timeout |
Per-attempt timeout fired |
:rf.http/http-4xx |
Non-2xx 4xx response |
:rf.http/http-5xx |
Non-2xx 5xx response |
:rf.http/decode-failure |
2xx response but decode rejected the body |
:rf.http/accept-failure |
:accept returned {:failure user-map} |
:rf.http/aborted |
Request aborted via :request-id or :abort-signal |
Trace events emitted by :rf.http/managed¶
:operation |
:op-type |
When |
|---|---|---|
:rf.http/retry-attempt |
:info |
Per intermediate attempt that matched :retry :on; carries :attempt, :max-attempts, :failure, :next-backoff-ms |
:rf.warning/decode-defaulted |
:warning |
The request relied on :decode :auto (default); informational, not an error |
:rf.http.interceptor/registered |
:info |
A reg-http-interceptor succeeded; carries :frame, :id (per 014 §Middleware) |
:rf.http.interceptor/cleared |
:info |
A clear-http-interceptor removed an existing slot; carries :frame, :id |
:rf.error/http-interceptor-failed |
:error |
A request-interceptor :before threw; carries :frame, :interceptor-id, :url, :cause. The request is NOT dispatched (per 014 §Middleware §Failure mode) |
Schema-reflection metadata¶
Handlers may declare :rf.http/decode-schemas [<schema> ...] in their reg-event-fx metadata-map; pair tools and generators read it via (rf/handler-meta :event id). Optional, never enforced — see 014 §Schema reflection.
Effect-map shape¶
Closed: :db + :fx only. See Spec-Schemas §:rf/effect-map. Top-level :dispatch / :dispatch-later / :dispatch-n from v1 migrate via MIGRATION.md §M-8.
| Key | Notes |
|---|---|
:db |
New app-db (replaces). |
:fx |
Vector of [fx-id args] pairs. |
Standard :fx entries:
[fx-id args] |
Args | Status | Spec | Notes |
|---|---|---|---|---|
[:dispatch [event-id ...]] |
event vector | v1 | 002 | |
[:dispatch-later {:ms ms :dispatch event-vec}] |
options map | v1 | 002 | |
[:http args] |
impl-specific | — | — | user-registered via reg-fx. |
[:rf.http/managed args-map] |
args per 014 §The args map | v1 (optional capability) | 014 | Framework-provided when the implementation ships Spec 014. CLJS reference: ships on Fetch + JVM HttpClient. See also :rf.http/managed-abort, :rf.http/managed-canned-success, :rf.http/managed-canned-failure. |
[:rf.nav/push-url url-string] |
URL string | v1 | 012 | |
[:raise event-vec] |
event vector | v1 | 005 | machine-only: reserved fx-id recognised by the machine handler; routes the event back into the same machine, atomic and pre-commit. Outside a machine action's :fx, this fx-id is unbound. |
[:rf.machine/spawn spawn-spec] |
spawn-spec map (per :rf.fx/spawn-args: :machine-id/:definition, :id-prefix, :data, :on-spawn, :start) |
v1 | 005 | Canonical actor-lifecycle fx (registered globally by re-frame.machines); registers a new dynamic actor (whose snapshot lives at [:rf/runtime :machines :snapshots <gensym'd-id>]) and (via :on-spawn) records its id into the parent's :data. Emitted from any event handler's :fx (including machine actions and the :spawn desugar). |
[:rf.machine/destroy actor-id] |
actor id (keyword) | v1 | 005 | Canonical actor-destroy fx (registered globally by re-frame.machines); runs the actor's :exit action, dissociates [:rf/runtime :machines :snapshots <actor-id>], and clears the actor's event-handler registration. Symmetric counterpart to :rf.machine/spawn. |
Public registrar query API¶
For tooling, agents, story tools, 10x.
| API | M/Fn | Signature | Status | Tier | JVM-runnable? | Spec |
|---|---|---|---|---|---|---|
registrations |
Fn | (registrations kind) / (registrations kind pred-fn) → {id metadata-map}. Use when you want metadata — registry walks that read source-coords, :rf/sensitive, :rf/machine?, :platforms, etc. |
v1 | tooling | ✓ | 002 |
handler-ids |
Fn | (handler-ids kind) → id set (canonical alias for (-> (registrations kind) keys set)). Use when you only need to enumerate — completion lists, existence checks, set-shaped intersections; saves both the metadata-map allocations and the keys walk. |
v1 | tooling | ✓ | 002 |
handler-meta |
Fn | (handler-meta kind id) → registration-metadata map. View registrations include source-coord keys (:ns / :line / :column / :file) per :rf/source-coord-meta (Spec-Schemas); pair tools resolve data-rf2-source-coord DOM annotations to :file via this lookup. |
v1 | tooling | ✓ | 002 |
machines |
Fn | (machines) → seq of machine-ids. Derived view over (registrations :event) filtered by :rf/machine? true. |
v1 | tooling | ✓ | 005 |
machine-meta |
Fn | (machine-meta machine-id) → registration-metadata map (transition table, doc, schemas). Equivalent to (handler-meta :event machine-id). |
v1 | tooling | ✓ | 005 |
frame-ids |
Fn | (frame-ids) / (frame-ids ns-prefix) |
v1 | tooling | ✓ | 002 |
frame-meta |
Fn | (frame-meta frame-id) |
v1 | tooling | ✓ | 002 |
app-db-value |
Fn | (app-db-value frame-id) → app-db value (plain map) — the out-of-band value read (renamed from get-frame-db). The front-porch read is subscribe; app-db-value is the non-reactive snapshot read for tools, tests, REPL, and fx/handler bodies. |
v1 | advanced | ✓ | 002 |
snapshot-of |
Fn | (snapshot-of path) / (snapshot-of path opts) |
v1 | tooling | ✓ | 002 |
sub-topology |
Fn | (sub-topology) → {sub-id {:inputs [<input-sub-ids>] :doc :ns :line :file}} — static dependency graph from :<- declarations. Pure data over the registrar; :inputs always present (empty for layer-1); the per-entry :doc / :ns / :line / :file keys are present when registration carries them. |
v1 | tooling | ✓ | 002 |
sub-cache |
Fn | (sub-cache frame-id) → live cache state |
v1 | tooling | ✗ (CLJS-only) | 002 |
Schema-introspection accessors — app-schemas, app-schema-at, app-schemas-digest — are rowed canonically in §Schemas.
compute-sub is rowed canonically in §Testing (pure sub computation against an app-db value).
Schemas¶
Namespace: the introspection surfaces below live in
re-frame.schemas(artefactday8/re-frame2-schemas); consumers(:require [re-frame.schemas :as schemas]). They are not re-exported fromre-frame.core— apps targeting schemas add the artefact and require the namespace directly. The registration macros (reg-app-schema/reg-app-schemas) live inre-frame.coreand route through the schemas artefact at registration time. Per the §Conventions per-artefact namespace table.
reg-app-schema is rowed canonically in §Registration.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
app-schemas |
Fn | (app-schemas) / (app-schemas {:frame frame-id}) |
v1 | tooling | 010 |
app-schema-at |
Fn | (app-schema-at path) / (app-schema-at path {:frame frame-id}) |
v1 | tooling | 010 |
app-schema-meta-at |
Fn | (app-schema-meta-at path) / (app-schema-meta-at path opts-or-frame-id) — return the full registration-metadata map (:path, :schema, :frame, plus source-coords :ns / :line / :file and the rest of :rf/registration-metadata) for a registered app-db schema, or nil. Pair-tool and 10x consumers reach for this when they need the registration anchor (e.g. click-back-to-code); the lighter app-schema-at is the right call when only the schema value is needed. Per 010 §Schemas as a tooling/agent surface and Spec-Schemas §:rf/app-schema-meta. |
v1 | tooling | 010 |
app-schemas-digest |
Fn | (app-schemas-digest) / (app-schemas-digest {:frame frame-id}) → string |
v1 | tooling | 010 |
set-schema-validator! |
Fn | (set-schema-validator! validate-fn) — install the validator ((fn [schema value] truthy?), same shape as malli.core/validate) every dev-time schema-validation site routes through. Swaps only the validator — the explainer/printer are left untouched (use set-schema-fns! to install all three atomically). nil disables validation entirely. Default ships Malli's validate; this seam lets apps swap in their own validator to drop the Malli dep. Last-write-wins; returns the installed validator. Per 010 §Default validator and the validator-fn extension point. |
v1 | advanced | 010 |
set-schema-fns! |
Fn | (set-schema-fns! {:validate validate-fn :explain explain-fn :print print-fn}) — atomically install any subset of the validator / explainer / printer bundle from one map. Each key is optional; an absent key leaves the existing registration in place. The honest one-call substitute-Malli boot pattern (a Zod / clojure.spec port installs all three together so they never drift mid-boot). :print nil coerces to the default EDN canonicaliser (parity with set-schema-printer!); :validate nil / :explain nil disable that fn. Last-write-wins per key; returns the installed validator. Per 010 §Default validator and the validator-fn extension point. |
v1 | advanced | 010 |
set-schema-explainer! |
Fn | (set-schema-explainer! explain-fn) — install the explainer used to enrich :rf.error/schema-validation-failure traces' :explain key. Companion to set-schema-validator!. Per 010 §Default validator and the validator-fn extension point. |
v1 | advanced | 010 |
set-schema-printer! |
Fn | (set-schema-printer! print-fn) — install the schema-print companion the digest pipeline (per 010 §Schema digest) hashes. print-fn is (fn [schema-value] canonical-string) and MUST be pure + deterministic across runtimes. nil falls back to the default EDN canonicaliser so the digest is never undefined. Parallel to set-schema-validator! / set-schema-explainer!: non-Malli ports register their own serialiser so cross-runtime digest comparison reflects their port's contract. Per 010 §Default validator and the validator-fn extension point. |
v1 | advanced | 010 |
validate-at-boundary-interceptor |
Var (interceptor value) | validate-at-boundary-interceptor — a pre-built interceptor value, not a fn (interceptor :id is :rf.schema/at-boundary). Add it to a reg-event-*'s positional interceptor vector for production-boundary validation; do not call it as a fn (it has no fn arity — invoking (rf/validate-at-boundary-interceptor ...) raises ArityException). |
v1 | advanced | 010 |
See 010 §Schemas for :schema metadata, validation timing, and dev/prod elision. (v1's :spec metadata key was renamed to :schema — a breaking rename with no back-compat alias; the framework no longer accepts :spec on reg-* metadata. Per MIGRATION §M-54.)
Event-emit (always-on, production-survivable)¶
Per 009 §What IS available in production (#2). A minimal always-on listener surface that survives :advanced + goog.DEBUG=false and delivers one tight record per processed event. The intended consumers are hosted observability back-ends (Datadog, Honeycomb, Sentry, …). Parallel to (not a fallback for) the dev-only trace surface; per-event only — no per-sub, per-fx, or per-:rf.event/db-changed records. Record shape {:event :event-id :frame :time :outcome :elapsed-ms} (the always-on event-emit substrate's own record fields, distinct from trace :tags keys); the :event slot is passed through rf/elide-wire-value once before fan-out, so schema-marked :sensitive? paths land as :rf/redacted and :large? paths land as :rf.size/large-elided.
Sensitive data marking is path-based per the upcoming data-classification mechanism (separate spec doc; in progress). The legacy handler-meta
:sensitive?annotation that previously drove substrate-level record drop has been removed.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
register-event-listener! |
Fn | (register-event-listener! id listener-fn) — listener-fn receives one event-record per processed event (shape above). Re-registering the same id replaces. Returns id. Always-on: survives CLJS :advanced + goog.DEBUG=false. |
v1 | tooling | 009 |
unregister-event-listener! |
Fn | (unregister-event-listener! id) → nil |
v1 | tooling | 009 |
Error-emit (always-on, production-survivable)¶
Per 009 §What IS available in production (#2 second paragraph). Sibling of the event-emit surface above; runs through the SAME always-on error-emit substrate as the per-frame :on-error slot (002-Frames.md) but along an INDEPENDENT corpus-wide fan-out path. Survives :advanced + goog.DEBUG=false. Intended consumers are hosted error monitors (Sentry, Honeybadger, Rollbar). One tight record per :rf.error/* event the router emits through the handler-exception path; record shape {:error :event :event-id :frame :time :exception :elapsed-ms}. The :event slot is passed through rf/elide-wire-value once before fan-out, so schema-marked :sensitive? paths land as :rf/redacted and :large? paths land as :rf.size/large-elided. The two paths from the substrate (corpus-wide listeners AND the per-frame :on-error policy fn) are mutually isolated; either may throw without affecting the other.
Sensitive data marking on the error-emit substrate is path-based per the upcoming data-classification mechanism (separate spec doc; in progress). The legacy handler-meta
:sensitive?annotation that previously drove substrate-level event redaction has been removed — the per-path elision wire-walker is now the sole redaction surface on this path.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
register-error-listener! |
Fn | (register-error-listener! id listener-fn) — listener-fn receives one error-record per :rf.error/* event (shape above). Re-registering the same id replaces. Returns id. Always-on: survives CLJS :advanced + goog.DEBUG=false. |
v1 | tooling | 009 |
unregister-error-listener! |
Fn | (unregister-error-listener! id) → nil |
v1 | tooling | 009 |
Tracing¶
All tracing is dev-only (elided in production). See 009 §Tracing for emit semantics and synchronous listener delivery.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
register-listener! |
Fn | (register-listener! key callback-fn) — callback-fn receives one trace event per call |
v1 (dev-only) | tooling | 009 |
unregister-listener! |
Fn | (unregister-listener! key) → nil |
v1 (dev-only) | tooling | 009 |
emit-trace-event! |
Fn | (emit-trace-event! op-type operation tags) → nil |
v1 (dev-only) | tooling | 009 |
re-frame.interop/debug-enabled? |
Var | ^boolean. CLJS: alias of goog.DEBUG — constant-folded by Closure under :advanced, so :advanced + goog.DEBUG=false builds DCE every (when interop/debug-enabled? ...) branch. JVM: a def read ONCE at ns-load from the Java system property -Dre-frame.debug (winning on conflict) or the environment variable RE_FRAME_DEBUG; defaults true (dev parity). Accepts the conventional false-y vocabulary case-insensitively (false, 0, no, off, empty string) with whitespace trimmed; anything else leaves the flag at true. Set BEFORE re-frame.interop loads. SSR / webhook receivers / long-running JVMs facing untrusted input MUST set the gate false explicitly — per 009 §JVM builds and Security §Production gates. |
v1 | tooling | 009 |
re-frame.performance/enabled? |
Var | ^boolean goog-defined (CLJS) / ^:const false (JVM). Set via :closure-defines {re-frame.performance/enabled? true} to bracket event dispatch / sub recompute / fx walk / view render in performance.mark + performance.measure calls (User-Timing entries rf:event:*, rf:sub:*, rf:fx:*, rf:render:*). Compile-time only — not a (rf/configure! ...) knob; runtime mutation has no effect. Default false; under :advanced + default the bracket DCEs and shipped binaries carry zero User-Timing instrumentation. CLJS-only — JVM is a no-op. See 009 §Performance instrumentation and Tool-Pair §Performance API consumption |
v1 | tooling | 009 |
trace-buffer |
Fn | (trace-buffer) / (trace-buffer opts) → vector of trace events, oldest-first |
v1 (dev-only) | tooling | 009 |
clear-trace-buffer! |
Fn | (clear-trace-buffer!) → nil |
v1 (dev-only) | tooling | 009 |
(rf/configure! :trace-buffer {:depth N}) |
— | See §Configure keys. | v1 (dev-only) | — (configure key) | 009 |
group-cascades |
Fn | (group-cascades events) → vector of cascade records {:dispatch-id :event :handler :fx :effects :subs :renders :other}, sorted by emission order. Pure data; JVM-runnable. Re-exported from re-frame.trace.projection (see 009 §Cascade projection). |
v1 (dev-only) | tooling | 009 |
domino-bucket |
Fn | (domino-bucket trace-event) → #{:event :handler :fx :effect :sub :render :other}. Classifies a raw trace event into the six-domino slot used by group-cascades. Pure data. |
v1 (dev-only) | tooling | 009 |
Trace-emission opt-out (per-handler metadata)¶
Event-handler registration accepts a :rf.trace/no-emit? true metadata flag. When set, the runtime suppresses every trace emission and event-emit record within the handler's scope — the handler runs invisibly to the trace surface, the event-emit substrate, and (transitively) the epoch buffer. Used by framework-internal bookkeeping handlers (Xray, Story, re-frame2-pair-mcp, story-mcp) that would otherwise saturate the trace stream. Per Conventions §Reserved namespaces the :rf.trace/* namespace is framework-owned.
| Metadata key | Where | Value | Default | Effect |
|---|---|---|---|---|
:rf.trace/no-emit? |
reg-event-db / reg-event-fx / reg-event-ctx metadata map |
boolean | false |
When true, suppresses all trace + event-emit emissions inside the handler's scope. Per 009 §Trace-emission opt-out. |
:rf.trace/frame-no-emit? |
reg-frame config map |
boolean | false |
When true, marks the frame a tool / inspector frame: the runtime suppresses every trace emission tagged with that frame, so the inspector's own reactivity does not flood the shared ring it inspects. The frame-scoped sibling of :rf.trace/no-emit?. Per 009 §Frame-level trace-emission opt-out. |
Epoch history (per Tool-Pair)¶
Per-frame epoch snapshots, recorded on each drain-completion in dev builds. Used by pair-shaped tools for time-travel and post-mortem analysis. Production builds elide entirely.
All rows below are the Tool-Pair time-travel surface — pair-shaped dev tools (Xray, the pair-MCP servers), so they tier tooling (the classification guidance also calls epoch-listener registration "advanced power-user"; either way the surface is back-room and opt-in — never front-porch). reset-frame-db! is the write/state-injection member of the same surface.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
epoch-history |
Fn | (epoch-history frame-id) → vector of epoch records. Returns [] for an unknown / destroyed frame (per Tool-Pair §Surface behaviour against destroyed frames). |
v1 (dev-only) | tooling | Tool-Pair |
restore-epoch |
Fn | (restore-epoch frame-id epoch-id) → boolean (true on success). Emits :rf.error/no-such-handler (kind :frame) and returns false for an unknown / destroyed frame (per Tool-Pair §Surface behaviour against destroyed frames). |
v1 (dev-only) | tooling | Tool-Pair |
reset-frame-db! |
Fn | (reset-frame-db! frame-id new-db) → boolean (true on success) — pair-tool write surface (state injection). Emits :rf.error/no-such-handler (kind :frame) and returns false for an unknown / destroyed frame. |
v1 (dev-only) | tooling | Tool-Pair |
register-epoch-listener! |
Fn | (register-epoch-listener! key callback-fn) — assembled-epoch listener. Process-global; a callback whose previously-observed frame is destroyed receives a one-shot :rf.epoch.cb/silenced-on-frame-destroy trace (per Tool-Pair §Surface behaviour against destroyed frames). |
v1 (dev-only) | tooling | Tool-Pair, 009 |
unregister-epoch-listener! |
Fn | (unregister-epoch-listener! key) |
v1 (dev-only) | tooling | Tool-Pair, 009 |
(rf/configure! :epoch-history {:depth N}) |
— | See §Configure keys. | v1 (dev-only) | — (configure key) | Tool-Pair |
app-db-value (cross-ref to §Public registrar query API) |
Fn | Returns nil for an unknown / destroyed frame (per Tool-Pair §Surface behaviour against destroyed frames). |
v1 | advanced | 002 |
Trace events emitted by epoch-history machinery:
:operation |
Tags |
|---|---|
:rf.epoch/snapshotted |
:frame, :epoch-id, :event-id |
:rf.epoch/restored |
:frame, :epoch-id |
:rf.epoch/db-replaced |
:frame, :epoch-id |
:rf.epoch/restore-unknown-epoch |
:frame, :epoch-id, :history-size |
:rf.epoch/restore-schema-mismatch |
:frame, :epoch-id, :schema-digest-recorded, :schema-digest-current, :failing-paths |
:rf.epoch/restore-missing-handler |
:frame, :epoch-id, :missing |
:rf.epoch/restore-version-mismatch |
:frame, :epoch-id, :machine-id, :version-recorded, :version-current |
:rf.epoch/restore-during-drain |
:frame, :epoch-id |
:rf.epoch/restore-non-ok-record |
:frame, :epoch-id, :outcome, :halt-reason |
:rf.epoch/reset-frame-db-during-drain |
:frame |
:rf.epoch/reset-frame-db-schema-mismatch |
:frame, :failing-paths |
:rf.epoch.cb/silenced-on-frame-destroy |
:frame, :cb-id |
Size-elision wire-boundary walker¶
Cross-reference: see Security.md §Privacy / secret handling —
elide-wire-valueis named there as the single normative emission site for the:rf/redactedsentinel. Every off-box egress (trace forwarders, MCP servers, error monitors) routes through this walker; the trust-boundary surfaces catalogued in Security.md compose against this primitive.
The framework primitive that walks tree-shaped values at the wire boundary and substitutes elision markers for sensitive or large slots. Consumed by every tool that emits wire data (the off-box error-monitor forwarders, the Xray-MCP / re-frame2-pair-mcp / story-mcp servers per Tool-Pair.md, the on-box dev panels). The walker is the single normative emission site for the :rf/redacted sensitive sentinel and the :rf.size/large-elided marker; per-tool reimplementation is prohibited.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
elide-wire-value |
Fn | (elide-wire-value v opts) → v or an elision-marker substitution. opts is a map: {:rf.size/include-large? <bool> :rf.size/include-sensitive? <bool> :rf.size/include-digests? <bool> :rf.size/threshold-bytes <int> :path [...] :frame <frame-id>}. Defaults: both include-* flags false (maximum elision); :rf.size/threshold-bytes falls back to (rf/configure! :elision ...) then 16384. Walks v consulting [:rf/runtime :elision :declarations] and [:rf/runtime :elision :sensitive-declarations] of the named frame's app-db; substitutes :rf/redacted for sensitive slots and :rf.size/large-elided markers for large slots. Composition rule (normative): when both predicates match the sensitive drop wins — the size marker is suppressed because it would leak :path / :bytes / :digest. Per 009 §Size elision in traces and Spec-Schemas §:rf/elision-marker. |
v1 | tooling | 009 |
elision-declarations |
Fn | (elision-declarations) / (elision-declarations frame-id) → the current [:rf/runtime :elision :declarations] map for the frame (or {}). Pair-tool / introspection reader for paths nominated for elision (schema-sourced). Default frame is :rf/default. Per 009 §Size elision in traces. |
v1 | tooling | 009 |
populate-elision-from-schemas! |
Fn | (populate-elision-from-schemas!) / (populate-elision-from-schemas! frame-id) → vector of paths populated (possibly empty). Boot-time hydrator that walks the frame's registered app-schemas and writes {:large? true :source :schema} declarations for every path whose Malli schema carries :large? true. Idempotent. No-op when the schemas artefact (day8/re-frame2-schemas) is not on the classpath. Per 009 §Size elision in traces. |
v1 | advanced | 009 |
Schema-only declaration path. The [:rf/runtime :elision] registry has exactly two slots: :declarations (schema-derived :large? paths, populated by populate-elision-from-schemas!) and :sensitive-declarations (schema-derived :sensitive? paths). There is no runtime declaration API — apps declare :large? / :sensitive? on the Malli schema and rf/reg-app-schema it; the boot-time hydrator does the rest. Per docs/guide/23-privacy-and-large-things.md — the canonical statement of "schemas are the only path" — and implementation/core/src/re_frame/elision.cljc L4-6.
DOM source-coord annotations (mandatory)¶
Per Spec 006 §Source-coord annotation and Tool-Pair §Source-mapping, every adapter whose host has a DOM-attribute concept MUST inject data-rf2-source-coord="<ns>:<sym>:<line>:<col>" on the rendered root DOM element of each registered view. Format and exemptions (Fragments, non-DOM roots) are documented in Spec 006 §Source-coord annotation. Annotation is gated on interop/debug-enabled? (the CLJS mirror of goog.DEBUG); production :advanced builds elide the attribute via dead-code elimination — there is no DOM-bytes cost in shipped bundles. The JVM SSR emitter mirrors the same contract per Spec 011 §Source-coord annotation under SSR.
Error contract¶
Errors are emitted as structured trace events with :op-type :error (or :warning / :info / :fx / :flow / :frame) and a per-category :operation keyword. The complete normative catalogue — every :rf.error/*, :rf.warning/*, :rf.fx/*, :rf.cofx/*, :rf.ssr/*, :rf.epoch/*, :rf.flow/*, :rf.http/*, :rf.http.interceptor/*, :rf.frame/*, and :rf.route.nav-token/* event the runtime emits — lives at 009 §Error event catalogue (single source of truth for category names, :op-type discriminator, trigger conditions, default :recovery, and :tags payload keys). Per-category Malli :tags schemas are canonicalised at Spec-Schemas §Per-category :tags schemas — one schema per catalogue row.
Recent additions consumers should be aware of: :rf.ssr/version-mismatch, :rf.ssr/schema-digest-mismatch, :rf.ssr/compatibility-check-skipped (the SSR hydration compatibility-check trio,), and :rf.cofx/skipped-on-platform (the platform-gating mirror of :rf.fx/skipped-on-platform). The catalogue at 009 is the single source of truth — do not duplicate the table here.
Per-Spec emit-sites: 002-Frames, 005-StateMachines, 006-ReactiveSubstrate, 010-Schemas, 011-SSR, 012-Routing, 013-Flows, 014-HTTPRequests, Tool-Pair. Each catalogue row's "Per [N]" cross-link names the owning Spec section.
Privacy (Spec 009 §Privacy / sensitive data in traces)¶
Cross-reference: see Security.md §Privacy / secret handling for the framework-wide pattern-level posture and the two composition sites (
redact-interceptor+ per-slot schema meta); the trust-boundary catalogue lives in Security.md. The cross-artefact inventory + composition order (every privacy surface inre-frame.core,re-frame.http,re-frame.schemas,re-frame.epoch,tools/mcp-base, with the data-flow from handler exit to off-box wire) lives in Privacy.md.
Per Spec 009 §Privacy the runtime stamps :sensitive? true at the top level of every trace event emitted inside the scope of a handler whose schema-derived path overlap declares sensitivity. (The legacy handler-meta :sensitive? annotation has been removed; sensitive data marking is path-based per the upcoming data-classification mechanism — separate spec doc; in progress.) Framework-published trace consumers (Sentry/Honeybadger forwarders, re-frame2-pair server, Xray, Story, story-mcp, re-frame2-pair-mcp) MUST default-drop the stamped events at their egress boundary. redact-interceptor is the in-place payload scrub composed alongside the stamp.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
sensitive? |
Fn | (sensitive? trace-event) → boolean. True iff trace-event is a map carrying :sensitive? true at the top level (not under :tags). The framework-published predicate every consumer composes against — replaces per-consumer reimplementations of the same five-token check. |
v1 | tooling | 009 |
redact-interceptor |
Fn | (redact-interceptor paths) → interceptor. Build a positional interceptor that overwrites the named keys in the event vector's payload map with the :rf/redacted sentinel before the handler chain runs. The handler body itself sees the UNREDACTED payload via the regular :event coeffect slot; the redaction is for the trace surface only. paths is a vector of get-in-style key paths into the payload map. |
post-v1 (planned) | advanced | 009 |
Spec-internal schemas¶
Per Spec-Schemas.md, the spec's own runtime shapes are described as Malli schemas registered at runtime. These are the conformance contract an implementation validates against.
| Schema | Describes | Spec |
|---|---|---|
:rf/dispatch-envelope |
Internal envelope wrapping every dispatch | 002 |
:rf/dispatch-opts |
The user-facing opts map for dispatch / dispatch-sync / subscribe |
002 |
:rf/registration-metadata |
Common metadata-map shape across reg-* |
001 / 010 |
:rf/effect-map |
Return value of reg-event-fx handlers — closed: only :db and :fx |
002 |
:rf/trace-event |
Universal trace event shape | 009 |
:rf/error-event |
Refinement of :rf/trace-event for :op-type :error / :warning (unified error/warning envelope) |
009 |
:rf/handler-body-dsl |
Conformance corpus handler-body DSL (host-agnostic event/sub bodies; small-DSL grammar) | 008 / Spec-Schemas |
:rf/transition-table |
State-machine transition table grammar | 005 |
:rf/machine-snapshot |
Runtime snapshot of a machine instance | 005 |
:rf/hydration-payload |
Wire format for SSR hydration | 011 |
:rf/response |
HTTP-response accumulator owned by the request frame during SSR | 011 |
:rf.server/cookie |
Structured-cookie shape for :rf.server/set-cookie / :rf.server/delete-cookie |
011 |
:rf/head-model |
SSR head/meta data model (title, meta, link, json-ld, html/body attrs) | 011 |
:rf/public-error |
Sanitised, client-safe projection of an internal error trace event | 011 |
:rf.fx.server/set-status-args / :rf.fx.server/set-header-args / :rf.fx.server/append-header-args / :rf.fx.server/set-cookie-args / :rf.fx.server/delete-cookie-args / :rf.fx.server/redirect-args |
Args of standard :rf.server/* SSR fx |
011 |
:rf/frame-meta |
Returned by (frame-meta frame-id) |
002 |
:rf/variant |
Story-variant artefact contract (post-v1 lib) — variants are data, no fn-valued slots | 007 |
:rf/epoch-record |
Per-frame epoch snapshot record (Tool-Pair) | Tool-Pair |
:rf.fx/dispatch-args |
Args of standard :dispatch fx (and :raise, same shape) |
002 / 005 |
:rf.fx/dispatch-later-args |
Args of standard :dispatch-later fx |
002 |
:rf.fx/http-args |
Args of :http fx (user-owned recommendation) |
Pattern-RemoteData |
:rf.fx/nav/push-url-args |
Args of :rf.nav/push-url fx |
012 |
:rf.fx/nav/replace-url-args |
Args of :rf.nav/replace-url fx |
012 |
:rf.fx/nav/scroll-args |
Args of :rf.nav/scroll fx |
012 |
:rf.fx/with-nav-token-args |
Args of :rf.route/with-nav-token fx wrapper |
012 |
:rf.fx/spawn-args |
Args of :rf.machine/spawn fx (the canonical actor-lifecycle fx-id; emitted from any event handler's :fx) |
005 |
:rf.fx/managed-args |
Args of :rf.http/managed fx (request envelope, decode, accept, retry, timeout-ms, on-success/on-failure, request-id, abort-signal) |
014 |
:rf.fx/managed-abort-args |
Args of :rf.http/managed-abort fx (request-id) |
014 |
:rf.http/reply |
Reply-payload envelope {:kind :success :value v} / {:kind :failure :failure {:kind <:rf.http/*> ...}} lands under :rf/reply |
014 |
:rf/route-rank |
Structural-rank tuple for route-precedence sorting | 012 |
:rf/pending-navigation |
Pending-navigation slot when :can-leave guard rejects |
012 |
:rf/elision-registry |
Per-frame size-elision declaration registry under the reserved app-db root [:rf/runtime :elision] |
009 |
:rf/elision-marker |
Wire shape rf/elide-wire-value substitutes for an elided large value (:rf.size/large-elided) |
009 |
Schemas are open by default (consumers tolerate unknown keys; producers grow shapes additively); :closed true is opt-in at boundary-validation sites and on the effect-map.
Testing¶
The testing surface lives across three namespaces. re-frame.core carries the production primitives that double as testing entry points (make-frame, with-frame, with-new-frame, dispatch-sync, with-fx-overrides, app-db-value, snapshot-of, compute-sub, machine-transition, sub-topology). re-frame.test-support ships the test-only fixture machinery and test-flavoured helpers. re-frame.test-helpers ships the view-assertion helpers (hiccup-walk + testid authoring). re-frame.test-support does not re-export from re-frame.core — a test file requires both [re-frame.core :as rf] and [re-frame.test-support :as ts], and additionally [re-frame.test-helpers :as th] for view-assertion tests. See 008-Testing.md for fixtures, framework adapters, and re-frame-test compatibility.
| API | M/Fn | Signature | Status | Tier | Spec | Notes |
|---|---|---|---|---|---|---|
dispatch-sequence |
Fn | (dispatch-sequence events) / (dispatch-sequence events opts) |
v1 | testing | 008 | opts: :after-each (fn [db ev] ...), :frame. Returns final app-db. Lives in re-frame.test-support. |
assert-path-equals |
Fn | (assert-path-equals path expected-val) / (assert-path-equals path expected-val opts) |
v1 | testing | 008 | Path-form sync assertion. Mismatch fires clojure.test/is-style failure via do-report. Lives in re-frame.test-support. Mirrors the :rf.assert/path-equals event used inside a Story :play block — same name root so the fn-side and event-side are navigable without a translation table. The wider sibling event family (:rf.assert/sub-equals, :rf.assert/state-is, :rf.assert/dispatched?, :rf.assert/no-warnings, :rf.assert/effect-emitted, :rf.assert/path-matches) lives in 007 §Play functions; runner and reporting channel differ. Choose by test surface: assert-path-equals from a deftest body, :rf.assert/path-equals from a story variant's :play vector. |
assert-db-equals |
Fn | (assert-db-equals expected-db) / (assert-db-equals expected-db opts) |
v1 | testing | 008 | Full-db sync assertion (no :rf.assert/* event analog — the event family is path-keyed). Mismatch fires clojure.test/is-style failure via do-report. Companion to assert-path-equals. Lives in re-frame.test-support. |
poll-until |
Fn | (poll-until pred) / (poll-until pred opts) |
v1 | testing | 008 | Bounded-deadline poll. JVM: synchronous — returns the truthy value, throws ex-info with :rf.test/poll-timeout true on timeout. CLJS: returns a js/Promise resolving with the truthy value or rejecting on timeout. Opts: :timeout-ms (default 2000), :interval-ms (default 5), :label. Lives in re-frame.test-support. |
with-fx-overrides |
M | (with-fx-overrides {fx-id -> override, …} body+) |
v1 | testing | 002, 008 | Lexical-scope :fx-overrides binding. Every dispatch / dispatch-sync inside the body merges the supplied map into its envelope's :fx-overrides. Precedence: per-call opt > lexical with-fx-overrides > per-frame :fx-overrides. Composes with with-frame. Lives in re-frame.core. Renamed from with-overrides per MIGRATION §M-50. |
compute-sub |
Fn | (compute-sub query-v db) |
v1 | testing | 008 | Pure sub computation against an app-db value. Lives in re-frame.core. |
snapshot-registrar / restore-registrar! / with-fresh-registrar / make-reset-runtime-fixture |
Fn | per docstring | v1 | testing | 008 | Fixture machinery. Lives in re-frame.test-support. |
Testing — view-assertion helpers¶
re-frame.test-helpers ships the hiccup-walk view-assertion surface — call the view-fn directly, walk the returned hiccup, assert on content or invoke a handler. JVM-runnable; no JSDOM, no React, no act(). Pairs with render-to-string (the HTML-string view-test path per Spec 011): hiccup-walk for structure / handler assertions, render-to-string for HTML-markup assertions. Per 008-Testing §View-assertion helpers.
| API | M/Fn | Signature | Status | Tier | Spec | Notes |
|---|---|---|---|---|---|---|
expand-tree |
Fn | (expand-tree tree) → tree |
v1 | testing | 008 | Recursively expand fn-components and Form-3 class components inside a hiccup tree. After expansion every vector's first element is a keyword tag or a non-component value. Lives in re-frame.test-helpers. |
attrs |
Fn | (attrs node) → map? |
v1 | testing | 008 | Return the attrs map of a hiccup node, or nil. Lives in re-frame.test-helpers. |
children |
Fn | (children node) → vector |
v1 | testing | 008 | Return the child elements — everything after the tag (and optional attrs map). Lives in re-frame.test-helpers. |
text-content |
Fn | (text-content node) → string |
v1 | testing | 008 | Recursively collect string leaves under node and join. Numbers coerce to strings; nils are skipped. Lives in re-frame.test-helpers. |
extract-handler |
Fn | (extract-handler node event-key) → fn? |
v1 | testing | 008 | Return the value of event-key from node's attrs map, or nil. Lives in re-frame.test-helpers. |
find-by-attr |
Fn | (find-by-attr tree attr val) → node? |
v1 | testing | 008 | First hiccup node whose attrs map carries attr == val, or nil. Generic over the attribute keyword (:data-testid, :data-test, :id, custom). Lives in re-frame.test-helpers. |
find-all-by-attr |
Fn | (find-all-by-attr tree attr val) → vector |
v1 | testing | 008 | Every matching node, in depth-first order. Lives in re-frame.test-helpers. |
find-by-attr-prefix |
Fn | (find-by-attr-prefix tree attr prefix) → vector |
v1 | testing | 008 | Every node whose attr value (a string) STARTS with prefix. Non-string attr values do not match. Lives in re-frame.test-helpers. |
find-by-testid |
Fn | (find-by-testid tree test-id) → node? |
v1 | testing | 008 | Convenience over find-by-attr keyed on :data-testid. Lives in re-frame.test-helpers. |
find-all-by-testid |
Fn | (find-all-by-testid tree test-id) → vector |
v1 | testing | 008 | Convenience over find-all-by-attr keyed on :data-testid. Lives in re-frame.test-helpers. |
find-by-testid-prefix |
Fn | (find-by-testid-prefix tree prefix) → vector |
v1 | testing | 008 | Convenience over find-by-attr-prefix keyed on :data-testid. Lives in re-frame.test-helpers. |
invoke-handler |
Fn | (invoke-handler node event-key & args) → any |
v1 | testing | 008 | Find the handler under event-key on node and call it with args. Returns the handler's return value. THROWS when node is not a hiccup vector, the node has no attrs map, or no handler is registered — the throwing failure mode is deliberate (a missing handler is almost always a test bug). Lives in re-frame.test-helpers. |
testid |
Fn | (testid id) / (testid id extra) → map |
v1 | testing | 008 | Build an attrs map carrying :data-testid id. The 2-arity merges extra into the map; :data-testid always wins on collision. Authoring helper at the view call site. Lives in re-frame.test-helpers. |
Standard interceptors¶
The v2 std-interceptor surface is three specific helpers plus the ->interceptor primitive. The principled line: keep helpers that do specific, non-trivial work; drop helpers that are just (->interceptor :before f) or (->interceptor :after f) with no other logic. Five interceptors removed: debug, trim-v, on-changes, enrich, after (per MIGRATION §M-21).
| API | M/Fn | Signature | Tier | Purpose |
|---|---|---|---|---|
inject-cofx |
M | (inject-cofx id) / (inject-cofx id value) |
front-porch | Inject a registered cofx into the handler's coeffect map. Macro per — captures call-site for :rf.trace/call-site on errors emitted from inside the cofx body. Specific work — :cofx registry lookup, not subsumable by ->interceptor. |
inject-cofx* |
Fn | (inject-cofx* id) / (inject-cofx* id value) |
advanced | Fn form for HoF / programmatic interceptor construction — no call-site stamping. |
path |
Fn | (path & path) |
front-porch | Focus a handler on an app-db sub-slice. Specific work — :before focuses, :after splices the result back into parent db. |
unwrap |
(val) | unwrap |
front-porch | Assert [id payload-map] event shape; replace :event coeffect with the payload map; restore on :after. Sugar over the M-19 canonical map-payload form. |
->interceptor |
M | (->interceptor & {:keys [id before after]}) |
front-porch | The primitive. Build a custom interceptor with :before and/or :after slots. Use this for any work not covered by the three specific helpers above — analytics, logging, validation, ad-hoc context manipulation. The resulting interceptor is named, addressable, and queryable like any other artefact. Macro per — captures the definition-site :source-coord from (meta &form) (rf2-siheh) so tools jump to the interceptor's source when it throws (Xray Epoch INTERCEPTOR row). |
->interceptor* |
Fn | (->interceptor* & {:keys [id before after source-coord]}) |
advanced | Fn form for HoF / programmatic / REPL interceptor construction — no definition-site coord capture (pass :source-coord explicitly if you have one). |
Removed in v2 (see MIGRATION §M-21):
| Removed API | Replaced by |
|---|---|
debug |
Trace surface (009) + 10x / re-frame-pair |
trim-v |
Canonical map-payload call shape (M-19) |
on-changes |
Flows (Spec 013) |
enrich |
Flows (derived state) / :schema (validation) / custom ->interceptor (escape hatch) |
after |
Registered fx (:fx [[:my-fx ...]]) for side-effects; custom ->interceptor for context-shaped work; vendor from v1 if the helper is wanted as a local utility |
reg-flow / clear-flow (Spec 013)¶
reg-flow is rowed canonically in §Registration; required flow-map keys are :id, :inputs, :output, :path. Both surfaces take an optional opts map ({:frame frame-id}) selecting the owning frame: (reg-flow flow) / (reg-flow flow opts) and (clear-flow id) / (clear-flow id opts). clear-flow is rowed canonically in §Clearing registrations; it deregisters the flow from the named frame and dissoc-ins its :path from that frame's app-db only (per Spec 013 §Frame-scoping).
Frame-destroy teardown. destroy-frame! releases every per-frame piece of flow state (registry slot, last-inputs rows, registrar entries for ids the destroyed frame was last owner of) per Spec 013 §Frame-destroy teardown. Sibling frames' state is preserved.
Flow-eval failures in production. A throw inside a flow's :output fn surfaces as :rf.error/flow-eval-exception on the always-on error-emit substrate — registered :on-error policy fns and register-error-listener! callbacks fire under CLJS :advanced + goog.DEBUG=false. The error is NOT trace-only. Per Spec 013 §Failure semantics rule 4 and 009 §Production builds.
Reserved fx-ids for runtime flow management via :fx:
| Name | Kind | Signature | Status |
|---|---|---|---|
:rf.fx/reg-flow |
Reserved fx-id | [:rf.fx/reg-flow flow-map] — register a flow at runtime via :fx |
v2 |
:rf.fx/clear-flow |
Reserved fx-id | [:rf.fx/clear-flow id] — clear a registered flow via :fx |
v2 |
Interceptor / context plumbing¶
| API | M/Fn | Signature | Status | Tier |
|---|---|---|---|---|
get-coeffect |
Fn | (get-coeffect ctx) / (get-coeffect ctx key) / (get-coeffect ctx key not-found) |
v1 (preserved) | advanced |
assoc-coeffect |
Fn | (assoc-coeffect ctx key value) |
v1 (preserved) | advanced |
get-effect |
Fn | (get-effect ctx) / (get-effect ctx key) / (get-effect ctx key not-found) |
v1 (preserved) | advanced |
assoc-effect |
Fn | (assoc-effect ctx key value) |
v1 (preserved) | advanced |
Lifecycle / utility¶
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
init! |
Fn | (init! adapter-map) — idempotent boot. Required arg: the adapter spec map. Each adapter ns exports an adapter Var; consumers require the ns and pass the Var, e.g. (rf/init! reagent/adapter). Calling (init!) with no args raises a language-level ArityException at compile/load time ( — the no-arg arity was cut so the missing-adapter mistake surfaces before runtime). Calling (init! nil) or (init! :reagent) raises :rf.error/no-adapter-specified at runtime. Per 006 §Adapter selection at boot and. Ensures :rf/default frame is present |
v1 | front-porch | 006 |
init-platform |
Fn | (init-platform p) — set the host-wide active-platform marker (:server or :client). The runtime tracks the active platform so reg-fx/reg-cofx :platforms metadata can gate execution (per 011 §Effect handling on the server). CLJS hosts default to :client, JVM hosts to :server; call at boot to override (CLJS-on-Node SSR runtime: (rf/init-platform :server); JVM-runnable test simulating browser: (rf/init-platform :client)). Per-frame :config :platform (set by the :ssr-server preset) still wins over this host-wide marker. p must be :server or :client; anything else raises :rf.error/invalid-platform. Idempotent / re-callable. Sibling boot call to init! — independent (adapter install vs platform marker). |
v1 | advanced | 011 |
install-adapter! |
Fn | (install-adapter! adapter-map) — must be called before any frame is created. Lower-level than init!; most consumers call init! instead |
v1 | advanced | 006 |
destroy-adapter! |
Fn | (destroy-adapter!) — tear down the installed adapter. Calls the adapter spec's :dispose-adapter! fn (if present), clears the install slot so a new adapter can install, and flips the adapter-disposed? breadcrumb. Per Conventions §Tear-down verb axis — destroy- cluster (lifecycle boundary; symmetric with install-adapter! and with destroy-frame!). The adapter-spec map key :dispose-adapter! (an internal contract slot adapters implement) is unchanged. |
v2 | advanced | 006 |
current-adapter |
Fn | (current-adapter) → discriminator keyword (:rf.adapter/reagent / :rf.adapter/reagent-slim / :rf.adapter/uix / :rf.adapter/helix / :rf.adapter/plain-atom / :rf.adapter/ssr / :custom) or nil when no adapter is installed. Answers "what substrate am I on?" — predicate / branch code. For the spec map (fn handles, identity checks), use current-adapter-spec. |
v1 | advanced | 006 |
current-adapter-spec |
Fn | (current-adapter-spec) → the installed adapter spec map (the value passed to (rf/init! ...)) or nil when no adapter is installed. Answers "give me the adapter fns to call" — tools, routing, identity checks across the install/dispose lifecycle. For the discriminator keyword, use current-adapter. |
v1 | advanced | 006 |
adapter-disposed? |
Fn | (adapter-disposed?) → true iff the most recent lifecycle event was a successful destroy-adapter! and no subsequent install-adapter! has fired. false for never-installed (fresh process) and after a fresh install. Read-only — the breadcrumb is owned by the install / destroy pair. Use to distinguish :rf.error/no-adapter-installed (fresh process) from :rf.error/adapter-disposed (torn down). Per 006 §Disposed-vs-never-installed. |
v1 | advanced | 006 |
configure! |
Fn | (configure! key opts) — runtime config; key vocabulary in §Configure keys. One of three orthogonal configuration surfaces per Conventions §Configuration surfaces (configure! for process-level data knobs; set-! / install-! for adapter-pluggable hooks; per-frame metadata for frame-scoped overrides). |
v1 | front-porch | — |
Feature inspection¶
re-frame2's optional capabilities ship as separate Maven artefacts (day8/re-frame2-<feature>) whose implementation namespaces core reaches through the late-bind hook registry at call time (per Conventions §Facade re-export, artefact require). The upside is bundle-isolation — an app that omits a feature does not carry its code. The downside this front-porch closes: the late-binding is otherwise invisible, so a developer who forgets to :require the impl artefact calls a re-exported fn that exists and is met with an opaque artefact-missing error. These three surfaces make the optional-feature inventory self-explaining.
The known optional features are :schemas, :machines, :routing, :flows, :http, :ssr, :epoch (the closed per-feature split set per Conventions §Artefact tiers).
These three fns ship to production. They are runtime queries, not dev-time instrumentation, so — unlike the trace / epoch surfaces — they are NOT gated on interop/debug-enabled? and do NOT elide under :advanced + goog.DEBUG=false. A production caller may legitimately probe (rf/feature-loaded? :routing) before taking a routing-dependent path. The feature→coordinate mapping is static data in the always-loaded re-frame.core facade (a plain table of {:feature {:maven … :require … :spec …}} strings), never a live :require reaching into the optional impl namespaces — a live reach-in would create a hard facade→optionals reference that pulls every optional artefact into every production bundle, breaking bundle-isolation. feature-loaded? detects presence by a pure keyword lookup in the always-loaded late-bind hooks atom against a representative key the impl publishes at ns-load — no reach into the optional namespace.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
features |
Fn | (features) → map of every optional feature keyword to its inspection entry: the static coordinate data (:maven / :require / :spec) merged with the live :loaded? boolean. E.g. {:epoch {:maven "day8/re-frame2-epoch" :require "re-frame.epoch" :spec "Tool-Pair (Time-travel / epoch)" :loaded? true} …}. Ships to production (NOT elided). |
v1 | advanced | — |
feature-loaded? |
Fn | (feature-loaded? feature) → true when the feature's impl artefact is on the classpath, false otherwise (including for an unknown feature keyword). Pure keyword lookup against the always-loaded late-bind hooks atom — no require into the optional namespace. Ships to production (NOT elided). |
v1 | advanced | — |
require-feature! |
Fn | (require-feature! feature) → true when the feature is loaded; otherwise throws :rf.error/feature-not-loaded carrying the EXACT copy-pasteable Maven coordinate (:maven) + require namespace (:require-ns) and a :reason string naming both (an unknown feature keyword throws :rf.error/unknown-feature with the :known set). Use as a self-explaining early guard at the top of feature-dependent code. Ships to production (NOT elided). |
v1 | advanced | — |
Artefact-missing errors carry the require. This front-porch is paired with a hard rule: every artefact-missing error in the framework — including the existing late-bind facade throws (
:rf.error/<feature>-artefact-missing, raised viare-frame.late-bind/require-fn!from there-frame.core-<feature>wrappers) — carries the exact copy-pasteable Maven coordinate and the namespace to require at app boot in its:reasonslot. The named pattern is documented once at Conventions §Facade re-export, artefact require.
Configure keys¶
Runtime configuration is uniformly via (rf/configure! <key> <opts>). Every framework-owned key is enumerated here. Keys are plural-noun-shaped; opts are an open map of per-key settings.
| Key | Opts shape | Default | Status | Spec |
|---|---|---|---|---|
:epoch-history |
{:depth N :trace-events-keep N :redact-fn (fn [record] …)} — :depth non-negative integer (0 disables the ring); :trace-events-keep non-negative integer caps raw :trace-events retention (per Security §Epoch privacy posture); :redact-fn is fn? or nil — invoked once per assembled record at build-time (between build-record and ring-append / listener fan-out) so ring + listeners see the same redacted shape, with the :rf.epoch/sensitive? rollup computed first, throws caught and surfaced as :rf.warning/epoch-redact-fn-exception with fallback to the raw record. Per Tool-Pair §Time-travel. |
{:depth 50, :redact-fn nil} |
v1 (dev-only) | Tool-Pair |
:trace-buffer |
{:depth N} — non-negative integer; 0 disables |
{:depth 200} |
v1 (dev-only) | 009 |
:elision |
{:rf.size/threshold-bytes N} — non-negative integer; 0 disables runtime auto-detect (only declared / schema entries elide) |
{:rf.size/threshold-bytes 16384} |
v1 | 009 |
Retired key (rf2-cmfln). The earlier
:sub-cache {:grace-period-ms N}knob is gone. Sub-cache disposal is now synchronous on derefer-count → 0 (per 006 §Reference counting and disposal); there is no deferred-grace timer to configure.
SSR error-projection policy (:public-error-id, :dev-error-detail?) is not a configure key — it is per-frame metadata on the frame's :ssr map (see Conventions §Configuration surfaces bucket 3 and 011 §Server error projection). Different frames in the same process can carry different projector / dev-detail settings, so the natural lifetime is per-frame, not process-global.
Opts-key naming rule¶
The opts map for any configure key mixes two shapes deliberately, and the choice is not stylistic — it encodes which contract owns the sub-key:
- Framework-owned semantic sub-keys use a namespaced keyword under a reserved
:rf.<area>/*sub-namespace (per Conventions §Reserved namespaces). The namespace identifies the cross-spec policy area the sub-key participates in — the same key shape appears verbatim wherever that policy is consumed, not only insideconfigure. Example::elisioncarries{:rf.size/threshold-bytes N}because:rf.size/threshold-bytesis the same per-call policy key consumed byrf/elide-wire-valueand the wire-elision walker (per Conventions §Reserved namespaces —:rf.size/*). The namespaced form makes the cross-surface identity grep-visible and prevents collision with adjacent per-knob settings. - Ergonomic per-knob sub-keys are unqualified bare keywords (
:depth,:trace-events-keep,:redact-fn). These sub-keys are local to a singleconfigurekey's opts map — they do not appear elsewhere in the framework's vocabulary, so a framework-owned namespace would add noise without adding identity. The bare form is the default at this leaf position; reach for it whenever the knob is unique to oneconfigurekey.
The discriminator is whether the sub-key names a cross-surface policy slot or a one-off knob. A sub-key earns a :rf.<area>/* namespace when it names a contract that lives in more than one place (:rf.size/threshold-bytes is read by :elision, by elide-wire-value, and by the MCP wire walker). A sub-key stays bare when it is local to its parent configure key (:depth under :trace-buffer has nothing to do with :depth under :epoch-history — same English word, separate knobs, no shared contract).
New configure keys MUST apply the same rule: if a sub-key participates in a cross-spec policy area, qualify it under the area's reserved namespace; otherwise leave it bare. The rule is closed — there is no third shape (no :configure/depth, no :rf.configure/* prefix). A sub-key that would want a third shape is evidence the proposed knob is doing two things and should be split.
Fixed-and-additive¶
The configure-keys vocabulary is fixed-and-additive (Spec-ulation): existing keys cannot be renamed or removed; new keys are added by extending the table. User code that wraps configure should pattern-match on known keys and ignore unknown ones.
Machines¶
Split between the v1 machine-as-event-handler foundation and the post-v1 re-frame.machines scaffolding library — see 005-StateMachines.md §Disposition. The machine is the event handler: a machine is registered as one reg-event-fx whose body comes from make-machine-handler.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
reg-machine |
M | (reg-machine machine-id machine-spec) — registers a machine as an event handler. Walks the literal spec form at expansion time and co-locates per-element source on each :guards / :actions entry + a reference-site :source-coords on each :states-tree map node. |
v1 | advanced | 005 |
reg-machine* |
Fn | (reg-machine* machine-id machine-spec) — plain-fn surface beneath the macro. No source-coord walking. |
v1 | advanced | 005 |
defmachine |
M | (defmachine name [docstring] spec) — def-shape that walks the literal spec at the definition site and co-locates per-element source onto the def'd value, for the def-then-register shape (defmachine m {…}) / (reg-machine :id m). Does not register. |
v1 | advanced | 005 |
make-machine-handler |
Fn | (make-machine-handler spec) → event-handler fn |
v1 | advanced | 005 |
machine-transition |
Fn | (machine-transition definition snapshot event) → [next-snapshot effects] |
v1 | advanced | 005 |
sub-machine |
Fn | (sub-machine machine-id) → reaction over snapshot |
v1 | advanced | 005 |
machines |
Fn | (machines) → seq of registered machine-ids |
v1 | tooling | 005 |
machine-meta |
Fn | (machine-meta machine-id) → registration metadata; :guards / :actions entries carry co-located :source-coords / :source-code and each :states-tree map node carries its own reference-site :source-coords when registered via the macro |
v1 | tooling | 005 |
machine-by-system-id |
Fn | (machine-by-system-id system-id) / (machine-by-system-id system-id frame-id) → spawned-machine id bound to system-id in the frame's [:rf/runtime :machines :system-ids] reverse index (or nil). Per 005 §Named addressing via :system-id. |
v1 | advanced | 005 |
dispatch-to-system |
Fn | (dispatch-to-system system-id event) / (dispatch-to-system system-id event frame-id) — sugar over (when-let [m (machine-by-system-id system-id)] (dispatch [m event])); no-op when the system-id is unbound. Per 005 §Cross-machine messaging by name. |
v1 | advanced | 005 |
machine-has-tag? |
Fn | (machine-has-tag? machine-id tag) → reaction whose value is true iff the machine's snapshot's :tags set contains tag. Sugar over (subscribe [:rf/machine-has-tag? machine-id tag]). Per 005 §State tags. |
v1 | advanced | 005 |
:rf.machine/spawn (fx) |
— | Canonical actor-lifecycle fx (registered globally by re-frame.machines). Args per :rf.fx/spawn-args. |
v1 | — (fx-id) | 005 |
:rf.machine/destroy (fx) |
— | Canonical actor-destroy fx (registered globally by re-frame.machines). Args: an actor id. |
v1 | — (fx-id) | 005 |
:raise (fx) |
— | Reserved fx-id inside a machine action's :fx (machine-internal, routed pre-commit). Args: an event vector. |
v1 | — (fx-id) | 005 |
:final? / :output-key (state-node keys) |
— | :final? marks a leaf state as terminal — entering it auto-destroys the machine. :output-key (requires :final?) designates the child's :data slot reported back via the parent's :on-done. Capability axis :fsm/final-states. Per ; see 005 §Final states. |
v1 | — (spec key) | 005 |
:on-done (:spawn spec key) |
— | (fn [{:keys [data result]}] new-data) on the parent's :spawn map. Fires synchronously when the spawned child enters a :final? state; result is the child's :data slot named by the final state's :output-key (or nil). Returns the parent's new :data map. Per and. |
v1 | — (spec key) | 005 |
:child-machine (transition-table key) |
— | Declarative state-scoped child-machine binding. | post-v1 lib | — (spec key) | 005 |
machine->xstate-json |
Fn | (machine->xstate-json definition) → JSON |
post-v1 lib | tooling | 005 |
machine->mermaid |
Fn | (machine->mermaid definition) → string |
post-v1 lib | tooling | 005 |
Canonical descriptions (factory purity, spec keys, snapshot location, registration-time validation, etc.) in 005-StateMachines.md and Spec-Schemas.
v1 transition-table grammar subset is enumerated in 005 §Capability matrix; shape in Spec-Schemas §:rf/transition-table.
Standard registered subs (machines)¶
| Standard sub | Returns | Spec |
|---|---|---|
[:rf/machine <machine-id>] |
The machine's snapshot {:state :data} (or nil if not yet initialised) |
005 |
sub-machine is sugar over the registered :rf/machine sub — see 005 §Subscribing to machines.
Story / variant / workspace library (post-v1)¶
See 007-Stories.md.
All Story surfaces are tooling (a Storybook-shaped dev surface — registration, execution, introspection — not application logic). Absorbs story F-8; see §Tiering of cross-tool surfaces.
| API | M/Fn | Signature | Status | Tier | Spec |
|---|---|---|---|---|---|
reg-story |
M | (reg-story id metadata) |
post-v1 lib | tooling | 007 |
reg-variant |
M | (reg-variant id metadata) |
post-v1 lib | tooling | 007 |
reg-workspace |
M | (reg-workspace id metadata) |
post-v1 lib | tooling | 007 |
reg-tag |
M | (reg-tag id metadata) |
post-v1 lib | tooling | 007 |
reg-decorator |
M | (reg-decorator id metadata) |
post-v1 lib | tooling | 007 |
reg-story-panel |
M | (reg-story-panel id metadata) |
post-v1 lib | tooling | 007 |
run |
Fn | (run target) / (run target opts) → promise/future of the unified run-result. target is a keyword (registered variant) OR a map (inline plan). The single execution verb. |
post-v1 lib | tooling | 007 |
is |
Fn | (is target) / (is target opts) → runs target and reports each assertion to clojure.test / cljs.test. JVM blocks (bounded by :timeout-ms, default 30000) and returns the run-result; CLJS returns the run promise. |
post-v1 lib | tooling | 007 |
explain |
Fn | (explain target) / (explain target opts) → the plan's :explain map (args-validation, sub-overrides, decorators, …) without running. |
post-v1 lib | tooling | 007 |
variants-with-tags |
Fn | (variants-with-tags tag-set) → seq of variant ids |
post-v1 lib | tooling | 007 |
snapshot-identity |
Fn | (snapshot-identity variant-id) → {:variant-id ... :content-hash "..."} |
post-v1 lib | tooling | 007 |
story-view |
Fn | (story-view variant-id) → hiccup |
post-v1 lib | tooling | 007 |
The public execution surface is exactly the three verbs run / is / explain (each accepts a registered-variant keyword OR an inline-plan map). run-variant / is-variant / run-plan / is-plan / watch-variant / reset-variant are implementation / migration vocabulary, NOT the P1 public surface — they are not rowed here. The unified run-result is the single execution-record boundary; read its verdict via result-status / result-passed? (:pass / :fail / :cannot-run / :error — there is no :passing? boolean). Canonical execution model: story spec 017 §Public execution API (the re-frame2-story library spec, the normative home for the verbs + run-result shape).
Removed / not shipped¶
These surfaces are removed or renamed — not part of the public projection and not a tier (the deprecated tier is reserved for surfaces still shipping while on the way out; pre-alpha carries none). This is a migration table: each row names what to use instead.
| API | What to do | Reference |
|---|---|---|
dispatch-with (master) |
Use (dispatch event {:fx-overrides {...}}) |
MIGRATION M-4 |
dispatch-sync-with (master) |
Use (dispatch-sync event {:fx-overrides {...}}) |
MIGRATION M-4 |
dispatch-to (proposed earlier) |
Use (dispatch event {:frame :todo}) |
002 |
subscribe-to (proposed earlier) |
Use (subscribe query-v {:frame :todo}) |
002 |
frame-dispatcher / bound-dispatcher / bound-subscriber (proposed earlier) |
Use (rf/frame-handle) (the keystone OPERATION BUNDLE — captures the frame at creation; safe during render and from async callbacks) |
002 |
bound-fn (CLJS macro) |
Use frame-bound-fn (the macro is renamed; same fn-syntax + frame-capture) |
002 |
dispatcher |
Use (:dispatch (rf/frame-handle)) or the dispatch injected in a reg-view body |
002 |
subscriber |
Use (:subscribe (rf/frame-handle)) or the subscribe injected in a reg-view body |
002 |
frame-bound-fn (fn form, 1- and 2-arity) |
Renamed to frame-bound-fn* (the *-twin); frame-bound-fn is now the macro |
002 |
current-frame |
Renamed to current-frame-id (returns a frame-id keyword) |
002 |
get-frame-db |
Renamed to app-db-value (returns the app-db VALUE, a plain map) |
002 |
enable-performance-api-tracing! (proposed earlier) |
Performance-API instrumentation is gated on the compile-time re-frame.performance/enabled? goog-define, not a runtime toggle (see 009 §Performance instrumentation) |
009 |
add-trace-listener / remove-trace-listener (proposed earlier) |
Use register-listener! / unregister-listener! |
009 |
register-trace-listener / unregister-trace-listener (no-bang, proposed earlier) |
Renamed to register-listener! / unregister-listener! (bang form matches the side-effecting nature of listener registration) |
009 |
Bare [:my-view "args"] keyword-tagged hiccup |
Use the Var form [my-view "args"] (canonical) or [(rf/view :my-view) "args"] for late-binding by id |
004 |
h macro (proposed earlier) |
Removed. Use the Var form [my-view "args"] or [(rf/view :my-view) "args"] |
004 |
reg-global-interceptor |
Use reg-frame :interceptors (frame-level is the canonical "global within this frame"). For cross-frame observation use register-listener!. |
MIGRATION M-17 |
clear-global-interceptor |
No replacement needed — re-register reg-frame with an updated :interceptors vector (absent-key semantics clear it). |
MIGRATION M-17 |
reg-sub-raw |
Use reg-sub (app-db reads), Pattern-AsyncEffect (non-app-db sources), state machines (lifecycle), or the 006 adapter contract (bridging external reactivity). |
MIGRATION M-18 |
re-frame.alpha/reg |
Per-kind macros: reg-event-db / reg-event-fx / reg-event-ctx / reg-sub / reg-fx / reg-cofx / reg-flow. |
MIGRATION M-23 |
re-frame.alpha/sub |
Vector-form (rf/subscribe [::id arg]). |
MIGRATION M-23 |
re-frame.alpha/reg-sub-lifecycle and built-in lifecycle policies (:safe, :no-cache, :reactive, :forever) |
Sub-cache uses a single algorithm — synchronous ref-counting (dispose on derefer-count → 0), per Spec 006 §Reference counting and disposal. For specific edge cases file a follow-up bead. | MIGRATION M-23 |
Cross-references¶
- 000-Vision.md — principles and design decisions
- 002-Frames.md — frames, dispatch envelope, drain semantics, overrides, machine foundations
- 004-Views.md — view registration, hiccup forms
- 005-StateMachines.md — machine library design (post-v1)
- 007-Stories.md — story/variant/workspace library design (post-v1)
- 008-Testing.md — testing API and patterns
- 009-Instrumentation.md — trace event stream, listeners, error contract
- 010-Schemas.md — Malli schemas
- 014-HTTPRequests.md —
:rf.http/managedrequest fx (optional capability) - MIGRATION.md — AI-driven migration spec