re-frame.schemas¶
Use schemas to catch data of the wrong shape where it is written. Attach a Malli schema to an app-db path with reg-app-schema, or to an event, effect or subscription with the :schema key of its registration. Development builds validate proposed app-db writes, event vectors, fx arguments and subscription values that have schemas. A mismatch emits :rf.error/schema-validation-failure. These checks are removed in production; an event with :boundary? true keeps its input validation in every build.
This namespace ships in the optional artefact day8/re-frame2-schemas. Require re-frame.schemas once at boot: loading it installs the default Malli validator. Without it, rf/reg-app-schema and rf/reg-app-schemas throw :rf.error/schemas-artefact-missing.
(rf/with-frame :rf/default
(rf/reg-app-schema [:user]
[:map [:id :uuid] [:name :string]]))
(rf/reg-event :user/rename
{:schema [:cat [:= :user/rename] :string]}
(fn [{:keys [db]} [_ new-name]]
{:db (assoc-in db [:user :name] new-name)}))
Register app-db schemas through the re-frame.core facade, as rf/reg-app-schema and rf/reg-app-schemas; their full contract is on this page. Everything else here is called on re-frame.schemas itself.
Validate with schemas teaches the model.
What is validated¶
In a development build each schema is checked where its data is produced, and a value that fails goes no further:
| Schema on | Declared with | Checked | On failure (:where) |
|---|---|---|---|
An app-db path |
reg-app-schema or reg-app-schemas |
After an event handler returns :db, before the new app-db installs. Every schema in the frame is checked, including those at paths the handler did not write. |
:app-db. The event's whole transition is rejected: app-db keeps its pre-handler value and the event's :fx do not run. |
| An event | :schema on reg-event, matched against the whole event vector, id included |
Before the handler runs. | :event. The handler does not run; the queue moves on to the next event. |
| An effect | :schema on reg-fx, matched against the effect's argument |
Before the effect handler runs. | :fx-args. That effect is skipped; the other effects in the same :fx vector still run. |
| A subscription | :schema on reg-sub, matched against the computed value |
After each recompute. | :sub-return. The subscription yields nil. |
Other checks that run through this validator report the same id with their own :where: :flow-output (a flow's :schema; see reg-flow), :machine-data and :machine-output (a machine's [:schemas :data] and [:schemas :output]; see reg-machine), and :sub-override (a Story subscription override whose value fails the subscription's :schema; the override yields nil).
Routes (:params, :query; reg-route) and resources (:params-schema; reg-resource) also validate through this validator but report their own errors; each of those entries says when its check runs and what a failure does. A {:recordable? true} coeffect's :schema (reg-cofx) is checked in every build as the value is recorded, and a failure emits :rf.error/cofx-value-invalid and throws.
Failure reports¶
The four checks in the table, and the flow, machine and :sub-override checks, report a failure rather than throw it; see Errors for how a reported id reaches you.
- The trace. Each failure emits an
:rf.error/schema-validation-failuretrace, which Xray shows on the event that caused it. Its tags carry:where,:failing-id(the event, effect or subscription id),:value(the value that failed),:explain(the validator's explanation, with Malli's:explain-humanizedbeside it) and:reason; anapp-dbfailure adds:path(the failing leaf),:registered-pathand:rollback? true. The trace event carries:recoveryat its top level, beside:operationand:tags, and:sensitive? truethere when the failure was redacted. :recoveryby:where::no-recoveryfor:app-db,:event,:flow-output,:machine-dataand:machine-output;:skippedfor:fx-args;:replaced-with-defaultfor:sub-returnand:sub-override.- The always-on
:errorsrecord. Two failures are also reported on the always-on:errorsstream, so they reach the frame's:observability :errorssinks, or the browser console when no sink handles them. Neither record carries the failing value:- An
app-dbrejection's record carries:error,:where :app-db,:registered-path,:event-id,:failing-id,:frame,:rollback? true,:recovery,:reasonand:time, and never the leaf path or the schema. - A
:boundary? trueevent's failure sends a record with a fixed key set::error,:where,:source :boundary,:event-id,:failing-id,:schema-id,:frame,:recoveryand:time.
- An
- A malformed schema. A schema form is not checked when it is registered. A malformed one, such as a childless
[:vector]or an unknown operator, makes the validator throw the first time it runs. That check counts as a failure, with the usual recovery, and emits:rf.error/malformed-schemain place of:rf.error/schema-validation-failure; the trace names the schema and the validator's message but carries no value. Anapp-dbschema in that state rejects every commit that returns:db, and each rejection is also reported on the:errorsstream, until the schema is fixed. - Redaction. When the schema marks a slot
{:sensitive? true}, the trace's value-bearing tags are replaced with:rf/redacted; see Schema classification walkers. These props change only what the validation-failure trace carries, except on a managed-HTTP request's:decodeschema, whose props also classify the response body (Response-body classification). They do not classifyapp-db, a machine's:dataor a resource's cached data, and a:sensitive?slot in a resource's:data-schemaredacts nothing, because that schema is never run. Where to declare, by owner says where each kind of data is classified.
Event, effect and subscription failures appear only in the trace, so without Xray, tap the trace yourself:
;; Development only: log every schema failure.
(rf/register-listener! :trace :my-app/schema-failures
(fn [{:keys [operation tags]}]
(when (= operation :rf.error/schema-validation-failure)
(js/console.warn (:where tags) (:failing-id tags) (:reason tags)))))
Validation in production¶
A production build (:advanced with goog.DEBUG false) removes the four checks above. The schemas stay registered, so the introspection functions still return them, but nothing is checked. For an event whose payload comes from outside the app, such as an HTTP reply, a websocket message or a postMessage, set :boundary? true beside its :schema, and that event is checked in every build:
(rf/reg-event :ws/message-received
{:schema [:cat [:= :ws/message-received]
[:map [:type :string] [:body :string]]]
:boundary? true}
(fn [{:keys [db]} [_ msg]]
{:db (update db :messages (fnil conj []) msg)}))
- A failure skips the handler, marks the dispatch
:outcome :rejected, and sends an:rf.error/schema-validation-failurerecord with:source :boundaryto the always-on:errorsstream. Its keys are fixed, so it never carries the payload (see Failure reports); a development build also emits the full trace. - The check runs through the validator installed here, so the production build must load
re-frame.schemas. With no validator installed, including after(set-schema-fns! {:validate nil}), the check passes. - A validator that throws counts as a failure.
:boundary? truewithout a:schemakey throws:rf.error/at-boundary-missing-schemaat registration. Seereg-event.- See In production: what goes, what stays.
Registration¶
reg-app-schema¶
- Kind: macro (
rf/reg-app-schema); also a function,re-frame.schemas/reg-app-schema, which records no source coordinates - Signature:
- Description: Attaches a Malli schema to an
app-dbpath. In development builds, after each event handler returns:db, the proposedapp-dbis validated before it is installed. A write that fails keeps the pre-handlerapp-db, and the event's:fxdo not run (see What is validated).- Every registered path is checked on every commit, and a path nothing has written yet reads
nil. So a schema registered before its slice is seeded rejects every commit until the seed lands. Seed all such slices in one:dbwrite, or allow the empty state with[:maybe …]. See Every registered path is checked on every commit. - This is a development-build assertion. A production build still registers the schema, so
app-schemasandapp-schema-metareturn it, but never checks it: a violatingapp-dbinstalls with no rejection, rollback or trace. Keep invariants that must hold in production in the handler, and set:boundary? trueon the event registration where untrusted input must be validated in production too. - The schema is the last positional argument, as in the rest of the
reg-*family. The optional middle metadata map carries the frame under:frame(a frame-id keyword or a frame value), plus:docand open:my/*keys. A frame value passed in place of the metadata map names that frame, as{:frame fv}does. Without:frame, the schema registers against the frame in scope, for example insiderf/with-frame. - The frame need not exist yet. A schema registered against a frame id before
make-framecreates that frame applies once it does, so schemas can be registered at boot ahead of their frames; a mistyped frame id is not detected. Destroying the frame removes its schemas. - The path is the registration id. App-db schemas are not a registrar kind: they live in the schemas artefact's per-frame table, and
(schemas/app-schema-meta {:frame f :path [:user]})looks one up by the same path. pathis a sequentialget-inpath of concrete segments, normalised to a vector.[]registers a schema for the wholeapp-db. Returns the normalised path.- A segment can be a keyword, string, symbol, boolean, UUID, instant,
nil, or an integer from −9,007,199,254,740,991 to 9,007,199,254,740,991. Composite values, functions and host handles are rejected. Anilsegment is a literal map key; anilpath is invalid. Use a non-negative integer to address a vector element. - In development builds, re-registering a different schema at a path whose live
app-dbvalue fails it emits an:rf.schema/violationwarning trace (:path,:pre-reload-schema,:post-reload-schema,:mismatching-value,:frame).app-dbis left as it is, so every later event that returns:dbis rejected until one writes a conforming value there or the schema changes again. - A schema the walkers cannot inspect for
:sensitive?/:large?flags warns once per process with:rf.warning/schema-walker-opaque: a compiledm/schemavalue, at the root or nested inside a vector form, a local:registry, or a[:ref …]. A failure against such a schema redacts every value-bearing trace slot, as if the whole schema were sensitive; register the plain vector form to keep per-slot redaction.
- Every registered path is checked on every commit, and a path nothing has written yet reads
- Errors:
:rf.error/app-schema-bad-metadata— the middle argument of the three-argument form is not a map.:rf.error/app-schema-bad-path— the path is not sequential, or has a non-concrete segment.:rf.error/app-schema-runtime-path— the first segment reaches theruntime-dbpartition (:rf.runtime/*,:rf.db/runtime, or the:rf/runtimeroot).:rf.error/app-schemas-bad-arg—:framedoes not resolve to a keyword frame id.:rf.error/no-frame-context— no:frameand no frame in scope.
- Example:
reg-app-schemas¶
- Kind: macro (
rf/reg-app-schemas); also a function,re-frame.schemas/reg-app-schemas, which records no source coordinates - Signature:
- Description: Registers several
app-dbschemas in one call; each entry behaves as areg-app-schemacall.- A development-build assertion, as for the singular form: every entry registers in a production build but is never checked there.
- Each entry is stamped with this call's source coords.
- The optional second argument names the frame for every entry: an opts map (
{:frame target}), a frame-id keyword, or a frame value. One frame per call. - Returns the vector of paths registered, in map-iteration order.
{}registers nothing. - Every path is checked before anything is stored, so a batch with one invalid path (
:rf.error/app-schema-bad-pathor:rf.error/app-schema-runtime-path) registers nothing. - A
nilor non-map first argument raises:rf.error/app-schemas-bad-batch. - A second argument that is not an opts map, a frame-id keyword or a frame value, or a
:framethat does not resolve to a keyword frame id, raises:rf.error/app-schemas-bad-arg. With no frame named and none in scope the call raises:rf.error/no-frame-context. Either way nothing is registered.
- Example:
Introspection¶
re-frame.core does not re-export these reads. Each takes one opts map whose :frame is required: a frame-id keyword or a frame value (normalised to its id), the same targets rf/registrations accepts. There is no default frame and no positional frame argument, because a frame value is itself a map and could not be told apart from an opts map. Query your schemas shows them in use.
- A map without
:frame, or a non-map argument, raises:rf.error/no-frame-context, with a message naming the{:frame f}form. A:framethat does not resolve to a keyword raises:rf.error/app-schemas-bad-arg. - A frame with no schemas returns
{},nilor the empty-set digest rather than raising, and the frame need not be live.
app-schemas¶
- Kind: function
- Signature:
- Description: Returns every schema registered in a frame as a
{path → registration-metadata}map, or{}.- This is the same
{id → meta}shaperf/registrationsreturns for registrar kinds. - Each value is the metadata recorded by
reg-app-schema::path,:schema,:frame, source coords (:ns/:line/:column/:file), and any other registration metadata. - Map
:schemaover the values when you want only the schemas.
- This is the same
- Example:
;; every registration in the default frame (schemas/app-schemas {:frame :rf/default}) ;; => {[:user] {:path [:user] :schema [:map [:id :uuid]] ;; :frame :rf/default :ns my.app.schema :line 12 :column 3 :file "..."}} ;; just the schema values (update-vals (schemas/app-schemas {:frame :rf/default}) :schema) ;; => {[:user] [:map [:id :uuid]]}
app-schema-meta¶
- Kind: function
- Signature:
- Description: Returns the registration metadata for one path in a frame, or
nilwhen nothing is registered there.- The map contains
:path,:schema,:frame, source coords (:ns/:line/:column/:file), and any other registration metadata.(:schema …)is the schema itself. - Both
:frameand:pathare required. A missing or malformed:pathraises:rf.error/bad-path.
- The map contains
- Example:
;; schema plus the source coords a tool uses to jump to the registration (schemas/app-schema-meta {:frame :rf/default :path [:user]}) ;; => {:path [:user] :schema [:map [:id :uuid]] ;; :frame :rf/default :ns my.app.schema :line 12 :column 3 :file "..."} ;; the schema alone (:schema (schemas/app-schema-meta {:frame :tenant/a :path [:articles]}))
app-schemas-digest¶
- Kind: function
- Signature:
- Description: Returns one hash over all the schemas registered in a frame, so you can tell whether the set changed without comparing schemas one by one.
- The format is
"sha256:"followed by the first 16 lowercase hex characters. - A frame with no schemas has a stable digest. Schemas made only of EDN data digest identically on every runtime. A function inside a schema (
[:map [:n pos-int?]]) is hashed by the host's name for it, so its digest is stable across restarts of one runtime but differs between the JVM and ClojureScript and can differ between builds; a floating-point value other than a whole number in the safe-integer range carries no cross-runtime guarantee either. An SSR server on the JVM and a browser client therefore report:rf.ssr/schema-digest-mismatchfor such a schema set; write those constraints as EDN data where the digests must match. - It is computed over the
{path → schema}projection ofapp-schemas, so other registration metadata does not affect it. - SSR hydration uses it to check that server and client registered the same schemas.
- The format is
- Example:
Validator bundle¶
The default validator is Malli's validate / explain pair, plus an EDN printer that canonicalises schemas for the digest. To use a different schema library, install your own bundle: one map whose :validate decides whether a value is valid, :explain describes a failure, and :print gives each schema a stable canonical string for the digest. set-schema-fns! installs a bundle, schema-fns reads the installed one, and default-schema-fns is the framework's own. See Swap the validator.
set-schema-fns!¶
- Kind: function
- Signature:
- Description: Installs any subset of the validator, explainer and printer from one map. It is the only way to change them.
- Each key is optional. An absent key leaves that function in place, so a one-key map swaps a single function. An explicit
nilis a write:nil:validateor:explaindisables that function. With:validatenil, every check that uses the validator passes, including the production:boundary? truecheck and route:params/:queryvalidation. - A
nil:printfalls back to the default EDN canonicaliser, so the digest always has a printer. validate-fnis(fn [schema value] truthy?), themalli.core/validateshape.explain-fnis(fn [schema value] explanation), themalli.core/explainshape.print-fnis(fn [schema-value] canonical-string)and must be pure and give the same string on every runtime.- Last write wins per key; a call is not transactional.
- Returns the installed bundle
{:validate … :explain … :print …}after the call, including keys the call did not touch. Select one key to get back the function you installed. - Install all three in one call at boot so they never disagree mid-boot. The same call restores a saved bundle: pass it a value from
schema-fnsordefault-schema-fns.
- Each key is optional. An absent key leaves that function in place, so a one-key map swaps a single function. An explicit
- Example:
;; install validator + explainer together (e.g. a clojure.spec or Zod port) (schemas/set-schema-fns! {:validate my-validate :explain my-explain}) ;; swap just one fn — the others are untouched (schemas/set-schema-fns! {:validate my-validate}) ;; a non-Malli port registers its own canonical schema serialiser (schemas/set-schema-fns! {:print (fn [schema] (pr-str schema))}) ;; nil turns validation off everywhere, :boundary? checks included (schemas/set-schema-fns! {:validate nil})
schema-fns¶
- Kind: function
- Signature:
- Description: Returns the installed validator, explainer and printer as one map, in the shape
set-schema-fns!accepts and returns.(set-schema-fns! (schema-fns))changes nothing.:validateand:explainmay benil;:printnever is.- Tests use it to save the bundle, install a stub, and restore the saved value, as in the example. The equivalent for registered schemas is
snapshot-schemas-by-frame/restore-schemas-by-frame!.
- Example:
default-schema-fns¶
- Kind: var
- Signature:
- Description: The framework's own bundle: the map installed before an app installs anything, with exactly
:validate,:explainand:print.(set-schema-fns! default-schema-fns)restores the defaults.:validateand:explaincall Malli;:printis the framework's EDN canonicaliser, which Malli does not supply.
- Example:
Framework integration¶
Not for application code — used by adapters, tools and the test harness.
Validation entry points¶
The runtime calls these four functions for you: validate-event! before an event handler runs, validate-fx! before an fx handler runs, validate-app-schema! before the handler's proposed :db is installed, and validate-sub! after a subscription recomputes. They are public so that tools and conformance tests can call them directly; application code registers a :schema instead.
- They exist in development builds only. In a production build the code is dead-code-eliminated and each function returns
truewithout checking anything. - A malformed registered schema (one that makes the validator throw) emits
:rf.error/malformed-schemaand counts as a failure (false), so a broken schema never passes silently. That trace carries only locator slots, no values. - Each function's longest arity takes a trailing
continue?predicate, which the runtime passes. It is checked around each validator call; once it returns false, the function stops, emits nothing more, and returns:rf/stale-incarnation, meaning the event's frame was destroyed or re-created while the event ran and the verdict no longer applies. The shorter arities supply the predicate themselves.
validate-app-schema!¶
- Kind: function
- Signature:
- Description: Validates the handler's proposed
app-dbbefore installation, against every schema registered for the frame. Schemas registered in other frames are ignored.- Each failing schema emits its own
:rf.error/schema-validation-failuretrace, with the explainer's output attached.:value(the failing leaf) is redacted when that leaf, an ancestor or a descendant is:sensitive?;:explainand:explain-humanizedcarry the whole registered value, so they are redacted when any slot in the schema is:sensitive?. - A malformed schema emits
:rf.error/malformed-schemafor that entry, counts asfalse, and does not stop the other schemas validating. event-id(optional) names the handler whose commit is being checked; it appears in the trace as:failing-id.- Returns
truewhen every schema conformed, and also when no validator or no schema is registered for the frame, or the build elided validation. - Returns
falsewhen at least one schema failed. The router then rejects the transition:app-dbkeeps its pre-handler value and the event's:fxare skipped. - Does nothing for any schema when
set-schema-fns!has installed anil:validate.
- Each failing schema emits its own
validate-event!¶
- Kind: function
- Signature:
- Description: Validates an event vector against the
:schemain its handler's registration metadata, before the handler runs.- On failure it emits
:rf.error/schema-validation-failurewith:where :event, and the caller skips the handler (recovery:no-recovery). - A
:sensitive?slot anywhere in the event schema redacts the trace's value-bearing slots. The common case is a:cat/:catnpayload slot, such as the:passwordin a login event's schema. - The optional
frameargument stamps:frameon the trace, so the failure is captured in the in-flight epoch's:trace-events(which the Xray Issues and Schema-timeline lenses read). The runtime passes it; direct callers may omit it. - Returns
trueorfalse.
- On failure it emits
validate-fx!¶
- Kind: function
- Signature:
- Description: Validates an fx handler's args against the
:schemain its registration metadata, before the fx runs.- On failure it emits
:rf.error/schema-validation-failurewith:where :fx-args, and only that fx is skipped (recovery:skipped). The other fx in the same:fxvector still run, and queued events still drain. - The optional
frameargument stamps:frameon the trace for epoch capture. - Returns
trueorfalse.
- On failure it emits
validate-sub!¶
- Kind: function
- Signature:
- Description: Validates a subscription's value against the
:schemain its registration metadata, after the subscription recomputes.- On failure it emits
:rf.error/schema-validation-failurewith:where :sub-return, and the caller replaces the value withnil(recovery:replaced-with-default). - The optional
frameargument stamps:frameon the trace for epoch capture. - Returns
trueorfalse.
- On failure it emits
Boundary validation and redaction¶
Other parts of the framework call these three functions in every build. validate-with-registered-fn and explain-with-registered-fn run the installed validator and explainer for the checks outside the four entry points above, among them the {:boundary? true} event check and route :params / :query validation. Every validation-failure trace emitted outside this namespace is redacted by redact-validation-tags.
validate-with-registered-fn¶
- Kind: function
- Signature:
- Description: Validates
valueagainstschemawith the installed validator. The:boundary? truecheck calls it, and it runs in production builds as well as development ones.- Returns
truewhen the value conforms. - Returns
falsewhen it does not, including when a malformed schema makes the validator throw (fail closed). - Returns
truewhen no validator is installed: no validator means no validation, as on the development-build path. - Emits no trace, because the boundary check emits the failure itself.
- Returns
explain-with-registered-fn¶
- Kind: function
- Signature:
- Description: Explains why
valuefailsschema, using the installed explainer. The boundary check uses it besidevalidate-with-registered-fn.- Returns the explanation when the value fails.
- Returns
nilwhen the value conforms, when no explainer is installed, or when the explainer throws. A throwing explainer degrades tonilbecause a diagnostic must never change the verdict.
redact-validation-tags¶
- Kind: function
- Signature:
- Description: Redacts a validation-failure trace's
tagsmap according to theschemathe failing value was checked against.- Its callers are the always-on boundary check, machine
:datavalidation, the:sub-overridepath, flow output validation, and the recordable-coeffect:rf.error/cofx-value-invalidemit. - For a
:sensitive?schema, the value-bearing slots (:value,:received,:explain,:explain-humanized,:rf.fx/args,:rf.sub/query-v) are replaced with:rf/redacted, and:sensitive? trueis added. An opaque compiled schema that the walker cannot inspect is treated as sensitive. - For a
:large?schema that is not sensitive, the same slots except:rf.sub/query-vare replaced with the:rf.size/large-elidedmarker, and:large? trueis added. - Otherwise the tags are returned unchanged.
- Idempotent. Callers outside the artefact reach it through the
:schemas/redact-validation-tagslate-bind hook; when the schemas artefact is absent, the tags pass through unchanged.
- Its callers are the always-on boundary check, machine
Schema classification walkers¶
Pure functions that walk a Malli vector-form schema and report which slots carry :sensitive? true or :large? true props. The validation-failure redactor uses them, and so do two projectors that classify a transient payload by its schema: managed HTTP's response-body projector, which reads the request's :decode schema, and story-mcp's tool-egress projector.
A compiled m/schema value is opaque to the walkers, so register the vector form when per-slot flags need to be visible.
extract-large-paths-from-schema¶
- Kind: function
- Signature:
- Description: Returns a
{path declaration}map of every:large? trueslot in a Malli schema form, with each path prefixed bybase-path.- Each declaration carries
:source :schema, plus the slot's:hintwhen its props carry one. - Size-elision code that projects a payload by its own schema reads it directly. It does not feed the frame's
app-dbegress registry.
- Each declaration carries
- Example:
extract-sensitive-paths-from-schema¶
- Kind: function
- Signature:
- Description: The
:sensitive? truecounterpart ofextract-large-paths-from-schema. The validation-failure redactor uses it to decide which value-bearing slots to scrub. Results are memoised by(schema, base-path);clear-sensitive-paths-cache!clears the memo between tests.
schema-has-sensitive?¶
- Kind: function
- Signature:
- Description: Returns
truewhen any part of the schema is sensitive: its container-level props carry:sensitive? true, or a nested slot does. The redactor uses this whole-schema check, so if any slot is sensitive, all of the trace's value-bearing slots are redacted. - Example:
schema-has-large?¶
- Kind: function
- Signature:
- Description: Returns
truewhen any slot is:large? true; the:large?counterpart ofschema-has-sensitive?. When it istrue, the validation trace carries the:rf.size/large-elidedmarker in place of the value-bearing slots. A slot that is both sensitive and large is treated as sensitive.
schema-sensitive-at?¶
- Kind: function
- Signature:
- Description: Returns
truewhen the slot atin-pathis sensitive: either a slot along the path is:sensitive?(the failing slot sits inside a sensitive container), or a slot inside it is (its value holds a sensitive child).in-pathis the value-relative path Malli reports as:in. Anilor emptyin-pathis equivalent to(or (schema-has-sensitive? schema) (schema-has-opaque-child? schema)).- It fails closed: an opaque child the walker reaches, or cannot align past, counts as sensitive.
- The
app-dbvalidation path uses it to redact the narrowed:valueslot of a failure, so a non-sensitive failing leaf whose sibling is sensitive is not over-redacted.
schema-opaque?¶
- Kind: function
- Signature:
- Description: Returns
truewhenschemais a value the walker cannot inspect for per-slot flags: anything that is neither a vector nor a keyword, such as a compiledmalli.core/schemaobject, a map or a fn.- A bare keyword (
:int,:string, a registry ref) is not opaque. An explicit[:ref …]form is treated as opaque byschema-has-opaque-child?. - Redaction fails closed on an opaque schema: it redacts as if the schema were sensitive, since Malli may honour a
:sensitive?slot the walker cannot see. Register the vector form to make per-slot flags visible.
- A bare keyword (
schema-has-opaque-child?¶
- Kind: function
- Signature:
- Description: The recursive form of
schema-opaque?. Returnstruewhen the root is opaque, or when a vector-form schema contains, at any depth, an opaque value in a child-schema position, a local:registryor an explicit[:ref …](the walker resolves neither), or an operator the walker does not classify.- Literal operands (
[:= 42],[:enum 1 2],:repatterns, comparator bounds) are data rather than children, so they never count. redact-validation-tagsand the:rf.warning/schema-walker-opaqueregistration warning use it, so a compiled child cannot hide inside a walkable root.
- Literal operands (
- Example:
Test support¶
These functions reset the artefact's state between tests. re-frame.test-support's make-reset-runtime-fixture calls the snapshot, restore and clear functions for you; the latch and cache resetters let each test case start with no remembered warnings and empty caches. on-frame-destroyed! is called by frame teardown.
snapshot-schemas-by-frame¶
- Kind: function
- Signature:
- Description: Returns a snapshot of the per-frame schema registry; restore it with
restore-schemas-by-frame!. It is the registry-level counterpart ofschema-fns.
restore-schemas-by-frame!¶
- Kind: function
- Signature:
- Description: Resets the per-frame schema registry to a snapshot taken by
snapshot-schemas-by-frame.
clear-schemas-by-frame!¶
- Kind: function
- Signature:
- Description: Empties the per-frame schema registry (
{}). The registry is the artefact's only mutable registration state.make-reset-runtime-fixturecalls it at every reset, through the:schemas/clear-by-frame!hook. It takes asnapshot-schemas-by-framefirst and restores it withrestore-schemas-by-frame!when the test ends, so a test body starts with no per-frame schemas.
on-frame-destroyed!¶
- Kind: function
- Signature:
- Description: Removes every schema registered against a destroyed frame, so a later
make-framewith the same id starts with no schemas. Frame teardown calls it. Idempotent: a frame with no entry is a no-op.
clear-validator-unavailable-warned!¶
- Kind: function
- Signature:
- Description: Resets the once-per-process
:rf.warning/schema-validator-unavailablelatch, so each test case can see the warning afresh. The firstreg-app-schemaemits that warning when the Malli adapter is unbound and the framework-default validator is still installed, so every check passes.
clear-walker-opaque-warned!¶
- Kind: function
- Signature:
- Description: Resets the once-per-process
:rf.warning/schema-walker-opaquelatch, the warning emitted when a schema is registered as an opaque compiled value.
clear-edn-print-cache!¶
- Kind: function
- Signature:
- Description: Empties the printer memo behind
app-schemas-digest. Returnsnil.- For tests only. The memo lives for the whole process but is bounded by the number of registered schemas, which register once at boot, so production never needs to clear it.
- A test suite that registers many distinct fresh schemas clears it in fixture teardown so the cache does not grow across the suite.
clear-sensitive-paths-cache!¶
- Kind: function
- Signature:
- Description: Empties the
extract-sensitive-paths-from-schemamemo. Returnsnil. For tests only, for the same reason asclear-edn-print-cache!.
See also¶
- re-frame.core — the
reg-app-schema/reg-app-schemasfacade entries, the:boundary? trueregistration flag, and the commit-plane classification effects (:sensitive/:large/:clear-sensitive/:clear-large) that classify durableapp-dbpaths. - Keep secrets out of traces — data classification,
:sensitive?, and large values.