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/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:
| 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 the frame resolves 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 failure is reported and the subscription still yields the computed value. |
Other checks that run through this validator report the same id with their own :where: :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 still renders as pinned).
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 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 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, subscription or machine id),:frame,:value(the value that failed),:explain(the validator's explanation, with Malli's:explain-humanizedbeside it in development builds) and:reason, in the same shape whichever check found the failure; 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,:machine-data,:machine-output,:sub-returnand:sub-override;:skippedfor:fx-args.- 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:rf.error/id,: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::rf.error/id,:where,:source :boundary,:event-id,:failing-id,:schema-id,:frame,:recoveryand:time.
- An
- A malformed schema. In development builds
reg-app-schemaandreg-app-schemascompile each schema through the bundle's:compileand throw on a malformed one, such as a childless[:vector]or an unknown operator. A malformed schema that registers anyway, under a bundle with no:compileor as a:schemaon another registration, 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, whichever check ran it; 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. Anapp-dbfailure's trace is redacted by the path's classification instead: the:sensitiveeffect that declares a path sensitive covers its validation failures too, so the path is declared once. A:sensitive?prop on anapp-dbschema is legal and still redacts that schema's failures, but it is not how anapp-dbpath is declared sensitive. 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).- The schema constrains the value only where its path exists: a path the proposed
app-dblacks is not checked, and a presentnilis. So a slice that nothing has seeded yet blocks no commit. To require that a key exists, say so in an enclosing schema, usually the root:(rf/reg-app-schema [] [:map [:cart CartSchema]]). Adissocof a registered slice installs unless such a schema requires the key. - 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:docand open:my/*keys. - Registration names no frame. It is a top-level program registration, like
reg-event, so it needs no frame in scope and can run at namespace load. Every frame validates against the schemas it resolves, as it resolves any other registration: live from the registrar for a frame made without:images, through its sealed image generation for a frame made with them. A schema therefore applies to every frame that resolves it — an SSR per-request frame, a test frame from(rf/make-frame {}), a Story variant built from the app's image — with no per-frame step. - A frame that should validate differently selects a later image that shadows the path: register the narrower schema in a namespace that image selects, and that image's frames resolve it instead.
- The path is the registration id, under the registrar kind
:app-schema.(schemas/app-schema-meta {:frame f :path [:user]})and(rf/registrations {:frame f :kind :app-schema})read what a frame resolves, and(rf/clear :app-schema [:user])removes the registration. A hot reload drops a schema its reloaded namespace no longer registers. - A path registered from two namespaces is ambiguous. A frame made without
:imagesrefuses it: validation fails loudly with:rf.error/ambiguous-registrationwhen a candidate puts a value at that path, and the introspection reads raise the same error. The schema is never silently skipped. 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, registering a schema checks the live
app-dbof each running frame that resolves it: a value present at the path that fails it emits an:rf.schema/violationwarning trace (:path,:pre-reload-schema,:post-reload-schema,:mismatching-value,:frame). That covers a first registration, whose trace has no:pre-reload-schema, and a re-registration with a different schema; an absent path and an unchanged re-registration emit nothing.app-dbis left as it is: an event that leaves the slice in place commits, and one that writes it is rejected until the value it writes conforms. - 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.
- The schema constrains the value only where its path exists: a path the proposed
- 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/*or:rf.db/runtime).:rf.error/app-schema-frame-target— the metadata map carries:frame, or a frame value sits in its place. The message points at the image route described above.
- 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, so each is its own frame-agnostic:app-schemaregistration.- 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.
- 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. - The optional second argument names no frame. A frame-id keyword, a frame value, or an opts map carrying
:frameraises:rf.error/app-schema-frame-target; any other non-map value raises:rf.error/app-schemas-bad-arg. 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.
- Each read answers from that frame's own resolution: the schemas its images select, or the live registrar for a frame made without
:images.(rf/registrations {:frame f :kind :app-schema})answers the same. - 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 that resolves no schemas returns
{},nilor the empty-set digest rather than raising. The frame need not be live: an id naming no live frame answers from each path's latest registration.
app-schemas¶
- Kind: function
- Signature:
- Description: Returns every schema a frame resolves 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,:docwhen given, 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 schema the default frame resolves (schemas/app-schemas {:frame :rf/default}) ;; => {[:user] {:path [:user] :schema [:map [:id :uuid]] ;; :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 the frame resolves for one path, or
nilwhen it resolves nothing there.- The map contains
:path,:schema,:docwhen given, 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]] ;; :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 a frame resolves, 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 the server's frame and the client's frame resolve the same schemas.
- The format is
- Example:
Validator bundle¶
The default validator is Malli's validate / explain pair, with Malli's humanizer in development builds, plus an EDN printer that canonicalises schemas for the digest. The default :validate compiles each schema once and keeps the compiled validator only while the schema itself is alive, so there is nothing to configure or clear. To use a different schema library, install your own bundle: one map whose :validate decides whether a value is valid, :explain describes a failure, :humanize turns that description into readable messages, :compile checks a schema when it is registered, 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, humanizer, compiler 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. The one exception is
:compile: a call that installs:validateand names no:compileclears it, because a compiler belongs to the validator it compiles for. An explicitnilis a write:nil:validate,:explain,:humanizeor:compiledisables 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.humanize-fnis(fn [explanation] readable), themalli.error/humanizeshape, and fills a failure trace's:explain-humanized.compile-fnis(fn [schema] _): development-buildreg-app-schemaandreg-app-schemascall it before registering, so a malformed schema throws there; its return is ignored.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 … :humanize … :compile … :print …}after the call, including keys the call did not touch. Select one key to get back the function you installed. - Install them 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. The one exception is
- 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, ;; and clears :compile, so registration checks nothing either (schemas/set-schema-fns! {:validate nil})
schema-fns¶
- Kind: function
- Signature:
- Description: Returns the installed validator, explainer, humanizer, compiler and printer as one map, in the shape
set-schema-fns!accepts and returns.(set-schema-fns! (schema-fns))changes nothing.:validate,:explain,:humanizeand:compilemay benil;:printnever is.- Tests use it to save the bundle, install a stub, and restore the saved value, as in the example. Registered schemas are registrar state, so
re-frame.test-support's registrar snapshot restores them after each test, and its reset clears them for the test's length.
- Example:
default-schema-fns¶
- Kind: var
- Signature:
- Description: The framework's own bundle: the map installed before an app installs anything.
(set-schema-fns! default-schema-fns)restores the defaults.:validateand:explaincall Malli,:validatecompiling each schema once and:compilecompiling into the same cache.:humanizeis Malli's humanizer in development builds andnilin production.: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.
validate-app-schema!¶
- Kind: function
- Signature:
- Description: Validates the handler's proposed
app-dbbefore installation, against every schema the frame resolves whose path the proposedapp-dbhas. A path it lacks is skipped in every arity, and a presentnilis checked. A schema the frame's images do not select is ignored.- Each failing schema emits its own
:rf.error/schema-validation-failuretrace, with the explainer's output attached. When any slot in the schema is:sensitive?, or the walker cannot fully inspect it, every value-bearing slot is redacted —:value(the failing leaf) as well as:explainand:explain-humanized(the whole registered value) — and:pathkeeps only the leading keys the schema declares on a:map, with:rf/redactedin place of every later segment. - 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.db-before(optional, and what the runtime passes) is theapp-dbthe candidate replaces. A schema whose slice the candidate leavesidentical?to it is skipped, since the commit does not write that slice. A parent path is still checked when a write lands beneath it, and[]whenever the db changed.- A failure is projected through the frame's classification, so a path a
reg-classificationdeclares:sensitiveor:largeis redacted or size-elided in the trace. - Returns
truewhen every schema conformed, and also when no validator is installed, the frame resolves no schema, 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-returnand recovery:no-recovery; the caller still delivers the value. - The optional
frameargument stamps:frameon the trace for epoch capture. - Returns
trueorfalse.
- On failure it emits
Boundary validation and redaction¶
Every check outside the four entry points above — the {:boundary? true} event check, a :sub-override pin, machine :data and :output, a recordable coeffect's :schema, route :params / :query and a resource's :params-schema — runs through one seam, the :schemas/check! late-bind hook, which the entry points use too. It runs in every build, returns the verdict and the failure's tags in one shape, and emits the standard trace unless the check reports the failure under its own recovery. The seam is a hook, not a var on this namespace.
- A validator that throws on a malformed schema answers
:rf.error/malformed-schema, never a pass. - The tags are redacted against the whole schema. A
:sensitive?schema, or one the walker cannot fully inspect, replaces the value-bearing slots (:value,:received,:explain,:explain-humanized,:rf.fx/args,:rf.sub/query-v) with:rf/redactedand adds:sensitive? true. A:large?schema that is not sensitive replaces the same slots except:rf.sub/query-vwith the:rf.size/large-elidedmarker and adds:large? true.
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. It walks the schema afresh on every call and retains nothing.
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-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. - The validation seam's redaction and the
:rf.warning/schema-walker-opaqueregistration warning use it, so a compiled child cannot hide inside a walkable root.
- Literal operands (
- Example:
Test support¶
This function resets the artefact's once-per-process warning latch between tests, so each test case starts with no remembered warning. Registered schemas need no reset of their own: make-reset-runtime-fixture clears every one before each test and its registrar snapshot restores them afterwards, so a test checks only the schemas it registers, and a schema registered in a test body is rolled back after it like any other registration.
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.
See also¶
- re-frame.core — the
reg-app-schema/reg-app-schemasfacade entries, the:boundary? trueregistration flag, andreg-classification, which classifies durableapp-dbpaths. - Keep secrets out of traces — data classification,
:sensitive?, and large values.