canon library

Classes

AllOf
Co-occurrence: all members present (→ record) or all absent. A partial set is a parse failure. mandatory true forbids the all-absent case too — the link only matches when every member is present (link branches only).
AnyProjection
The common face of Projection and UnitProjection — what a Regency.merges set holds.
AnyStore<S>
What a @stores row may hold: a keyed Store or a Unit. S is the state a read of the regent returns — the keyed collection for a store, the value for a unit — so one typed lookup serves both kinds.
At<Handle>
A POSITION in the queue that answers ledger.at(...) with its typed Handle — the spec instance carries the handle type, so the lookup is fully typed with no name in between. Every regent kind is a position (store → StoreMemory, unit → UnitMemory, guard → GuardMemory), and the two positions nobody declares are the static sentinels:
Bus
The message bus — the RICH tier's transport. Dispatch messages through guards to typed subscribers. Transport-agnostic: feed it from WS, HTTP, a local DB, or a local optimistic dispatch(..., optimistic: true). Decoupled from canon and from Flutter; a StoreMemory subscribes to it, and a riverpod notifier can subscribe via on too — neither owns the other.
Canon
THE spec mark: one annotation for every canon spec enum — ids, entities, regents, screens, link trees. The row MIXIN is the source of truth (IdNode, entity rows, RegentNode, ScreenNode, LinkNode); the generator dispatches on it, so the mark only says "generate here". The main screens enum is the one that declares the NavGraph; a link domain is declared IN the tree (Domain('https://…')), not here.
Codec<T>
A strict, bidirectional codec for a single string token. decode returns null when the token is not valid for this codec — that null is what lets the caller fall through (URL match fallthrough, or best-effort restore). encode is the exact inverse for any value the codec can produce, so a value round-trips to the same canonical token.
CompositeCodec
A composite codec (see Codec.composite); const-constructible. Components are individual fields — a const constructor can't build a list — so the signature itself caps the arity at 16. The value is a positional RECORD of the components' decoded values: a record's runtime type comes from its fields' runtime types, so a 2-part composite of string codecs decodes to a value that IS a (String, String) — typed casts and structural equality (repeat-collapse, prefix reuse) work as if it were declared that shape.
CompositeId
See IdNode.compose. Component nodes are individual fields — a const constructor can't build a list — so the signature itself caps the arity at 16 (à la Object.hash); generators read the n* fields directly.
ConcatCodec
An ordered sequence of codecs occupying ONE path segment: exactly one VARIABLE member (carries the value) framed by LiteralCodec members (fixed text). Invertible because the literals anchor the boundaries — decode strips the leading/trailing literals, delegates the remainder to the variable. A second variable has no recoverable boundary, so it is rejected.
CoveredRanges<C extends Comparable<C>>
The cursor intervals an authority has covered — a plain folded VALUE, pure and axis-blind (the consumer decides what C means: a DateTime, an int, a name). Absence WITHIN a covered interval is a fact ("gone"); absence outside is silence.
CsvCodec<T>
Comma-joined list in a single token (see Codec.csv); const-constructible. A normal scalar codec — no repeated-key protocol.
Deleted<K, E>
Domain
A domain root — a URL prefix (scheme + optional host). The first domain in the tree is the one used for output. Inlined (not an enum): root-only by type. Also placeable bare in a NavGraph's trunk set — the screens tree's canonical output domain, declared IN the grammar.
DomainNode
A domain root: a URL prefix (scheme + optional host + optional base path) and its subtree. The prefix is parsed once into components the matcher compares structurally (not by string prefix), so example.com never matches example.com.evil.com.
DomainPlacement
Edge
A path edge: a static literal, or a slot trying codecs in precedence order.
Entities
Marks the hand-written enum that is an app's ENTITY SPACE: each row binds an entity TYPE to its id-NODE. A static final graph = EntityGraph({...}) declares OWNERSHIP as a tree — review({comment}) means a comment belongs to exactly one review (its state lives inside it; removing the review removes its comments by construction). The graph carries ownership ONLY: an unlisted row is a root, and an app whose entities are all flat omits the graph entirely. The same child kind may appear under several parents (image({reactor}), moment({reactor})): instances still have exactly one owner, of either kind. Roots are the aggregate boundaries — stores attach to roots only.
EntityBranch
A row plus its owned children — the tree-building wrapper.
EntityGraph
The declared ownership tree. Structure is read both at build time (the generator derives store legality, nested-map machinery, and path types from it) and at runtime (ownersOf/childrenOf — the entity-scope resolution surface).
EntityMerge
A row carrying merge edges (and optionally children) — the tree-building wrapper EntityNode.merge returns.
EntityTreeNode
Authoring marker: what an EntityGraph set literal may hold — a bare row (a leaf) or a row with children (review({comment})).
EntryPosition
The sentinel for the queue's ingress — see At.entry.
ExitPosition
The sentinel for the queue's end — see At.exit.
Feed
The stream-only face of a sentinel position: typed message taps, no dispatch — nobody injects mid-queue.
ForgetOp
ForwardJudgment
Continue msg at the next row — judged by everything above this guard.
FragmentFace<S>
The fragment declaration surface: ONE fragment per screen, ONE scheme — calling it declares the PAIRS scheme (#k=v&k2=v2, exactly the query machinery); path declares the PATH scheme (#<seg>/<seg>). The two are mutually exclusive by assignment: whichever is declared last would overwrite, so declaring both asserts.
FragmentNode
One position of a PATH-scheme fragment: a codec (open domain = a slug, a Codec.literal singleton = a fixed segment) and the positions that may follow it. Compose with /: .product / {thumb, gallery / {imageIndex}}.
GoOp
Guard<M extends Msg>
A PURE judge standing at its row of the queue: every traversing message of the M family is submitted to judge, whose returned set IS its verdict — one verb for every shape of judgment:
GuardEvent<M extends Msg>
One judgment's full story, emitted AFTER the verdict ran: the submitted message and what the guard launched for it, atomically — for a Veto, an empty verdict IS the block. Observation only (ledger.at(spec)); the flow itself never depends on it.
GuardMemory<M extends Msg>
The live handle at a guard's row (ledger.at(const CachedGate())): the judged input and the verdict, observable — a judge holds no state, so its memory is pure story. Plural members are streams, all derived from the atomic events:
IdNav<K>
A deictic navigation claim: "this identity, from where the claiming widget stands." Minted by the binding layer (canon_flutter's IdScope.of<K>(context)); the generator hangs the typed forward verbs on it as extensions, one set per id NODE — ids resolve ambiently (the scope's own id plus ids locked on the chain), so item code never passes the id it is standing on. Each verb anchors at screen on the live chain and takes the edge with edgeRequired enforcement: navigating somewhere the placement can't reach is a programmer error the fold throws on, never a silent kick-start fold.
IdNode
The contract an @ids enum wears: every row carries a codec. The node IS a Codec (it delegates to its inner one), so a screen can bind it straight into a Codec? id field (id: .user) and a store can key by it — the SAME node across both grammar trees. Generators read node.codec to recover the value type (the node itself erases to Codec<Object?>).
IDs
Marks the HAND-WRITTEN enum that is an app's id-space: each row is an identity carrying its Codec — how its key serialises in a URL. Nothing is generated; the enum IS the holder. Other grammar trees (canon's @screens, ledger's @stores) reference these rows by dot-shorthand and read row.codec to encode/decode and to validate a screen id or a store key against them.
Inherited<S>
The chained form of .inherit(...): carries its placement node DIRECTLY (never stashed), so chaining is explicit in the type system — one expression, one node, no orphan to mis-claim. Usable as:
Inserted<K, E>
Judgment
One LAUNCH — the element of a guard's verdict. A guard at index x may target exactly TWO indices, the only two that preserve the system's theorem (no row ever sees a message that skipped a guard above it):
KeyDef
One key. codec null = a flag (presence only); list true = repeated key carrying an ordered List<elem> (codec is then the ELEMENT codec).
Ledger
The queue of positions, behind TWO doors:
LedgerRows
The registration face Regent.mount dispatches into — Ledger implements it; the indirection keeps the regent tiers free of the ledger's own import.
LinkBranch<S>
A link-grammar branch placed in the tree (a trunk, or nested in a .call). It is GENERATOR-READ ONLY: the runtime nav engine ignores it (links don't seed the stack). Carries the link DSL node the generator walks to emit Link.
LinkMatch
A successful parse. path is the ordered dynamic slot values (statics are structural, omitted); query/fragment are decoded params by key name. Unmodeled query keys are ignored, never captured.
LinkMatcher
Strict, bidirectional URL ↔ match codec over a LinkSpec.
LinkSpec
LinkTreeNode
Authoring DSL: a runtime-built tree the matcher walks and the generator reads (via AST). Positional by construction — the same literal can sit at many positions with different types.
LiteralCodec
A fixed-token codec (Codec.literal). Exposed so link-tree assembly can detect literal branches (e.g. to order an injected id codec after them).
MarkReplaceOp
MintJudgment
Derive msg as a NEW round from the top of the queue, after the current round completes. Unjournaled: re-derived on replay.
Msg
A FACT. The journal stores it, replay re-delivers it — so it must never mutate after construction. The annotation makes the analyzer enforce final fields on every subclass.
One entry of a live navigation stack — a screen and its id. T is the screen representation: the raw Enum internally, or the public Screen<Object?> wrapper for the consumer-facing stack.
What the FLUTTER layer plugs into the pure engine (canon_flutter's delegate implements it): rebuild triggers and frame scheduling. Every hook is a no-op until a host attaches — headless (server, test) graphs simply never attach one.
An immutable snapshot of one committed navigation, delivered to NavGraph.navigations. Safe to hold past the commit: it captures the transition rather than reading live state, so async (stream) delivery never sees a stale position. from and to are the full active-scope stacks (bottom-to-top, each entry a (screen, id)); the generated layer retypes them into the public ScreenEntry stack.
A navigation VERB as a FACT — a Msg, so a ledger can journal, judge, and replay navigation like any other truth; navReduce folds it. Placement legality still runs in the pure resolvers; an illegal op throws (a programmer error, not a state).
A live navigation stack, trunk-first: the full record (screens + ids) plus derived views.
The navigation STATE as a pure value: per-scope stacks, the active trunk, and the batch's commit mode. Verbs are NavOp facts folded by navReduce; the graph holds a pending state per microtask batch and commits the diff once. Immutable — every fold returns a new value, so the fold replays.
The navigation stack as a REGENT — hold it as the LAST row of a regents enum, route the graph's verbs through the ledger (graph.routeOps(dispatch)), and mirror folds back with graph.applyState. Gates above it judge outbound navigation like any fact; replay carries the session's navigation. Inert (null) until a SeedOp arrives — the graph seeds on wiring and on every inbound restore. Reads the one live grammar via NavGraph.boundSpec: immutable config, one graph per app.
OneOf
Mutual exclusion: exactly one member present (→ sealed union), or none. Two or more present is a parse failure. mandatory true forbids the none case — the link only matches when exactly one member is present (link branches only).
ParamSchema
A query (or fragment) schema: a list of terms. Unmodeled keys are always ignored — what isn't in the schema isn't data. Capture arbitrary query by modeling an explicit raw key.
PathNode
One path position. Statics are tried before the (at most one) slot. A node resolves (is an endpoint) when endpoint is set, when it has a query or fragment schema (params imply resolution), or when it is a leaf.
PopOp
PopToOp
Projection<S extends Identifiable<K>, K, E>
A READ RESOLVER for a merge edge (user.merge(viewer, const ViewerSupportsUser()) in the entities graph): the SOURCE store's state answers the TARGET surface's per-key reads at the source's OWN identity — S extends Identifiable<K> IS the claim; there is no key method anywhere.
Pure
Marks a method (or class) as PURE — its body may read only its parameters, locals, pattern-bound variables, and compile-time constants, and may call only other pure functions. No ambient or mutable state, no clock, no randomness, no I/O. Purity is what makes a fold deterministic and therefore replay-able: replay(a) == replay(b) is a law only because the folds between cannot reach outside their arguments.
QueryTerm
A query/fragment term — a key (flag or value/list) or an allOf/oneOf group.
Record2Codec<A, B>
A 2-field record codec (see Codec.record2); const so it works as an enum-constant id, e.g. Record2Codec(Codec.string, Codec.integer).
Record3Codec<A, B, C>
A 3-field record codec (see Codec.record3); const-constructible.
Regency
The app as a VALUE: an ordered set of regents plus merge edges. A graph IS a regent, and a regent is a one-row graph — Ledger.root takes a single Regent, so the smallest ledger is Ledger.root(const NavUnit()) and the largest is a const tree:
Regent
A REGENT — a row of the ledger — anything that occupies a row of the regents enum: stores, units, guards, vetoes. Row order is traversal order: a message walks the rows top to bottom.
RootScreenBase
A direct stack seed (trunk..target chain of (screen, id)) for the seedChain: constructor arg — used by engine/restore code and tests to start at a specific stack. Consumers instead pass root: (the boot widget) and let the resolver drive the first navigation; see BootScreen.
RowChange<K, E>
One row's movement inside one fold — the store's change feed at row grain, derived from StoreEvent.changed (no map walk).
Screens
Marks the library-private spec enum the nav generator reads. Rows: identifier = screen name, first positional = const widget, optional second positional = id Type (named types only).
SeedOp
An inbound wholesale state (a restore, a URL apply, a system pop already applied by the interpreter) — the fold adopts it verbatim.
SegBuilder
SlotBuilder
SlotEdge
StackEntry
One page on the runtime stack, as the grammar sees it.
StaticEdge
Store<K, E extends Identifiable<K>, M extends Msg>
The PURE, const registry descriptor: how a message folds into an entry's state. No mutable state, no ref, const — so it can sit in a spec. The live store (StoreMemory) is created separately and wired to a Bus.
StoreEvent<K, E extends Identifiable<K>, M extends Msg>
One fold's full story, emitted by a store AFTER the reduce ran: the cause and its consequence, atomically — an effect filtering these can never race the fold. Filters recover every narrower feed: structural (the list shell), changed.contains(id) (per key), a before/after delta (state TRANSITIONS), a msg-type check (post-fold message observation).
StoreMemory<K, E extends Identifiable<K>, M extends Msg>
The live store for a Store: the folded collection driven off a Bus, plus read-time merge edges and the change/event feeds. It holds NO other state — optimism, in-flight status, freshness, and settlement all live in consumer ROWS (docks, in-flight units, coverage), where they replay.
StreamSubscription<T>
A subscription on events from a Stream.
Term
The runtime link spec the matcher walks. The authoring DSL (Domain/Seg/slot) builds this; the generator reads the SAME source via AST to emit the typed Link surface. Plain data — no Flutter, no nav. A node in a query/fragment schema: a single key, or a typed combinator.
TreeNode<S>
Authoring marker: what a grammar set literal may hold — a screen (via ScreenNodeBase), a again back-edge, or a graft of another family. <S> keeps a native literal typed to one family; graft is the one explicit cross-family bridge.
UnionCodec<T>
The flattened branches of an a | b | c union. Decode tries them left-to- right (first non-null wins); encode uses the left/canonical branch. The nav/link layer reads branches to recover the ordered union the spec wrote.
Unit<S, M extends Msg>
The UNIT sibling of Store: one value, cardinality one — for entities whose identity is the session (the wire sends their facts KEYLESS: a viewer profile, a requests+unseen state). Same purity contract.
UnitEvent<S, M extends Msg>
The unit form of StoreEvent.
UnitMemory<S, M extends Msg>
The live memory for a Unit: the value driven off a Bus.
UnitProjection<S, T>
The UNIT form of Projection — a unit-target merge edge (viewer.from(viewerPending, const ApplyPending()) in the regents merges set): the SOURCE unit's state answers the TARGET unit's read. Keyless — a unit has cardinality one, so the edge always applies; resolve no-ops itself when the source carries nothing. Read-time only: the fold and guards' read never see it.
Updated<K, E>
Veto<M extends Msg>
The refusing specialization — a guard that only ever passes or drops. TRUE from block drops the message.
ViewCond
One view-state condition term in a selector (.category('books'), .not.byFav). The generated per-screen …Cond types implement this; Screen.on/context.on gate on test(liveValue), and key is the aspect a reactive read subscribes to.
WidgetScreen<W>
The host-facing face of a screen: its widget — NULLABLE, and never forced: a row with no widget is a LINK-ONLY row (grammar/URL presence, nothing to render; nav verbs to it are the generator's concern to omit). The engine stays Flutter-free — W is abstract here; the Flutter alias binds it to Widget. Lets the host read a widget off an erased (Enum) screen; the name comes from Enum directly.

Enums

BootScreen
The synthetic boot placement. When a graph is built with no seedChain, the stack is seeded as [(BootScreen.root, null)] — so the always-non-empty invariant holds — and current/Screen.at report it until the first commit, which the engine auto-replaces (the boot entry leaves no history). Never part of a consumer tree; the generated Screen.at maps it to Initial.
CommitMode
How a committed navigation maps to history: push adds a new entry, replace overwrites the current one (no back-target). Default is push; the generated Screen.replace flips a batch to replace, and the engine forces replace for the first commit out of the boot state. The web Router delegate reads it (pushState/replaceState); the bare stack engine, which has no history, ignores it.
FloorKind
The kind of synthetic base entry sitting at history index 0 (null = none):
The shape of a committed navigation, derived from the active scope's stack delta: forward grew the stack, backward shrank it, roundTrip did both (a popTo(...).go(...) chain), jump switched scope/trunk (a kick-start).

Mixins

EntityNode<Self extends EntityNode<Self>>
The contract an @entities enum wears: every row carries the entity type and the id-node (key) its instances are identified by. call({children}) declares the row's OWNED children in the graph.
Identifiable<I>
Entities are VALUES: folds produce new instances; a mutated entity corrupts before/after events and optimistic refolds — enforced here for every class that wears the mixin.
LinkNode<S extends LinkNode<S>>
A URL-addressable grammar node with NO presentation and NO fields — the enum NAME is the whole declaration. The links-only tier of the screens grammar: a server or API surface authors enum _Links with LinkNode<_Links> and builds the same tree (user({product})) with the same link/view-state vocabulary, and nothing to render. A ScreenNodeBase row is conceptually a LinkNode plus a widget; the hierarchy unification (placement-as-link, matcher/generator support) is a coming pass — today this is the authoring surface and the validated tree (LinkGraph).
NameableCodec<T>
Adds (#name) to a codec, overriding a generated field name at the use site (slot(.uuid(#productId))). The returned codec drops the mixin, so a name can be applied at most once. Battery codecs mix this in; custom codecs may.
QueryKeyBase
Query/fragment key names — enum values mixing this in. A bare value is a flag; Key(codec) is a value/list.
ScreenNodeBase<S extends ScreenNodeBase<S, W>, W>
SegBase
Path literals. The literal derives from the enum name (camelCase → kebab).

Extensions

CodecConcat on Codec<Object?>
+ is ORDERED concatenation within one segment — the string forms of the members joined in order (Ids.image + Codec.literal('_thumb'){imageId}_thumb). Non-commutative on purpose (prefix ≠ suffix), unlike the commutative |. Exactly one member carries a value; literals frame it.
CodecUnion on Codec<T>
a | b — a union: either token addresses the same T. Decode tries the branches left-to-right (strict codecs return null on a non-match, so the first that accepts the token wins); encode delegates to the left branch (the canonical form). The nav/link generator reads the branches structurally; this runtime form lets a spec written with | compile and round-trip. An extension, not an interface member, so existing implements Codec classes are unaffected.
FragmentPathComposition on Codec<Object?>
/ composes a codec with what may follow it. The right side is a single codec, a FragmentNode, or a SET of either (a branch point).
IdentifiableIterable on Iterable<T>
IdentifiableList on List<T>
IdentifiableMapExtension on Map<I, T>
QueryTermAlgebra on QueryTerm
The codec algebra over query/fragment terms: a & b is co-occurrence (all present together → record), a | b is mutual exclusion (exactly one → sealed union). Dart binds & tighter than |, so a & b | c reads as (a & b) | c — use parentheses to group the other way. An extension, so the QueryTerm interface (and its implementers) stay untouched.
StoreEventStream on Stream<StoreEvent<K, E, M>>
The keyed-store counterpart of UnitEventStream.
UnitEventStream on Stream<UnitEvent<S, M>>
Pure sugar over a UNIT's post-fold event stream — the recurring effect idioms as verbs. Extensions on the STREAM (not the memory), so they compose after any filter and over replayed feeds alike.

Constants

canon → const Canon
The spec mark — @canon enum _Screens with ScreenNode<_Screens> {…}.
entities → const Entities
pure → const Pure
The @pure marker — mirrors @immutable's lowercase-const convention.
screens → const Screens
The arg-less default; use @Screens(domain: '…') to declare a link domain.

Functions

decodeFragmentPath(Set<FragmentNode> roots, String raw) List<Object?>?
Decode a raw fragment string against roots. Returns the decoded positions in order, or null if any position rejects (strict). Tolerates (strips) a :~: directive tail — that's user-agent territory.
encodeFragmentPath(Set<FragmentNode> roots, List<Object?> values) String?
Encode decoded positions back to the raw fragment string, walking the same tree. Returns null if the values don't fit the tree (a write of an illegal path). Percent-encodes embedded '/' — the splitter owns it.
fragmentRoots(Object tree) Set<FragmentNode>
Normalize a path(...) argument — a bare codec, one node, or a root BRANCH SET (alternative first segments: {deals, .product / {...}}).
graft<P, C>(TreeNode<C> child) TreeNode<P>
Mounts a child from ANOTHER screen family into this family's tree — the one explicit cross-family edge. Pass a screen, a built Sub.x({...}), or a reusable static final subtree. Inferred P (the parent family) comes from the target set's element type; C from the grafted node.
linkRoot(Set<LinkTreeNode?> branches) PathNode
Builds a root PathNode from the .links branches gathered off the @screens graph (each a screen-rooted SegBuilder) — the runtime link tree the matcher walks. Domain-agnostic: the caller wraps it in a DomainNode built from the URL's own origin at parse time.
The PURE navigation fold — the verb semantics as a function of (state, op): idempotent-tap no-ops, boot exit, trunk park/seed, the tap>edge>canonical ladder, targeted pops, keep maintenance. Throws are programmer errors (stale handles, unprovable pops), not states. warn hosts the canonical-fallback diagnostic.
replay(Regent root, List<Msg> order) Map<Object, Object?>
The state the WHOLE ledger folds from order — its replay. Builds a pure ledger from root (a Regency or any single regent), folds the messages synchronously, and returns every regent's state keyed by SPEC INSTANCE — z[const Todos()]. Deterministic: the folds are pure, so the same messages always yield the same snapshot — replay is the operation purity buys you.
replayStore<K, E extends Identifiable<K>, M extends Msg>(Store<K, E, M> store, List<Msg> order) IdentifiableMap<K, E>
A single store's replayed collection — the narrow form of replay for a store whose reduce reads only its own state (no guards, no merge edges).
requireAllOf(Set<QueryTerm> members) QueryTerm
REQUIRED co-occurrence: the link only matches when all members are present — a URL missing them is rejected, not resolved with a null group. Link branches (.link/slot) only; rejected on screen view-state, where a query is decoration, not part of the route's identity.
requireOneOf(Set<QueryTerm> members) QueryTerm
REQUIRED mutual exclusion: the link only matches when exactly one member is present — a URL with none is rejected. Link branches only (see requireAllOf).
slot(Codec<Object?> codec) SlotBuilder
A path slot: the next segment is one value of codec. A union is a single codec built with |slot(.uuid(#userId) | .username) — whose branches are expanded here, tried in order (first match wins), generating a sealed type at the use-site. An affix segment ({imageId}_thumb) is a concat codec — slot(Ids.image + Codec.literal('_thumb')).
slots(Set<Codec<Object?>> codecs) SlotBuilder
A union slot from an explicit codec set — slots({literal('me'), uuid}), tried in order (first match wins).
tree(Set<DomainPlacement> domains) LinkSpec
Builds the whole link spec from a set of domain placements.
viewSchema(Set<QueryTerm> terms) Map<String, Codec<Object?>?>
Flattens a query/fragment term set into a key→codec schema (codec null = a flag). View-state placements (screen(...).query({...})) use this so the engine can encode/decode the stored values against the URL.

Typedefs

IdentifiableMap<K, E extends Identifiable<K>> = Map<K, E>
Identity = Identifiable<String>
IdentityMap<T extends Identity> = Map<String, T>
ReadStore = S Function<S>(AnyStore<S> spec)
A guard's view of the world: this ledger's own state, looked up by regent IDENTITY — read(const BrowseDeck()), read(const AuthMachine()). Const canonicalization makes the constructor expression the regent's canonical NAME: const X() written in a judge IS the instance the row holds (same class, different args = a different regent; two rows may not hold identical instances — enforced at registration). Bound to the ledger the guard stands in, so a replayed ledger reads itself. Throws when no row holds the instance (wrong args, or a missing const).