canon 0.31.1
canon: ^0.31.1 copied to clipboard
Application runtime context from a single grammar tree: navigation, identity, and state as one compile-safe spec.
0.31.1 #
- Lints on
package:lintsrecommended; README explains the pure model.
0.31.0 #
- BREAKING:
.cycledremoved — cycle folding is the consumer's to wire with checks on the live stack; the universal no-op tap remains. - BREAKING:
.stackedrenamed.again— the recurring back-edge, each visit a fresh frame. - BREAKING:
NavGraphconstruction is spec-only (grammar + optionalseedChain);root/chrome/pageOf/observerslive on canon_flutter'sScreenGraph.NavGraphisbase. - regent ^0.14.0.
0.30.1 #
- regent ^0.13.0.
- Docs: consumer-named const row globals, feature grafts.
0.30.0 #
- regent ^0.12.0 (
Regencygraphs, role mixins, CRUD bricks) — the @regents enum tier retires with generator 0.36.
0.29.0 #
- regent ^0.11.0 (guards are launchers:
.forward/.mintjudgments;Envelopegone). NavUnitreduces the sealedNavOp(the shadow law: no row reduces rootMsg).
0.28.0 #
- The engine folds: navigation state is a pure value (
NavState) and every verb aNavOpFACT (a regentMsg) folded bynavReduce— journalable, judgeable, replayable;NavGraphinterprets (history, host, batching). - The ledger can OWN the stack:
NavUnit(a regent citizen),graph.routeOps/applyState, andSeedOpfor inbound restores/system pops;Navigation.modeis per-op now (a replace claims exactly the op that consumed it). IdNav<K>— the deictic navigation claim (this identity, from where the claiming widget stands); the generator hangs typed forward verbs on it, one set per id node.- regent ^0.10.0 (pure-fold memories).
0.27.0 #
- regent ^0.9.0 (BREAKING via re-export: verdicts and awaits state become consumer rows; unit merge edges; CoveredRanges).
0.26.0 #
- regent ^0.8.0 (BREAKING via re-export: guards judge through
read, no stores facade).
0.25.0 #
- The entity grammar (
Entities,EntityNode,EntityGraph, …) moves in from identifiable — a spec grammar, sibling of the screen tiers.
0.24.0 #
@canon— one spec mark, dispatched by node mixin; link domain declared in the tree viaDomain('…').- Affix segments via concat codec (
slot(id + Codec.literal('_thumb'))) — structuralsuffix:removed; slots stay single, alternatives are unions.
0.23.0 #
- Fragment path: callable face,
operator /on Codec, strict decode, persistence snapshot.
0.22.0 #
- The regent 0.4 citizens surface, re-exported.
0.21.1 #
- Allow regent 0.3.
0.21.0 #
LinkNode: the fieldless links-only grammar tier; one graph — spec-onlyNavGraph(no root/seedChain).widgetoptional on screen nodes: widget-less rows are link-only.inheritchains through a typedInheritedwrapper; back-edges carry the id lock.- A declared
.stackedself-edge overrides the same-id no-op guard. chromehook: the host wrapsScreenScopearound consumer page chrome.setResolverboot fallback — the resolver always fires at cold start.
0.20.0 #
- BREAKING: pure-Dart core — the Flutter presentation layer moves behind the
NavHostseam (seecanon_flutter). - canon is the runtime facade: re-exports the
regentengine (@stores,Store/Unit, guards,Awaits, merge edges). @IDsid space +@entitiesownership graph annotations.- Generated files are
.canon.dart; path URL strategy owned by the nav host. inheritwidens to 16 slots.
0.19.1 #
- README:
Screen.pop()returns a sealedPopDestPlacementyou switch on directly;Screen.go(Hop)returns the typed nav.
0.19.0 #
- Add
requireAllOf/requireOneOf: a link query/fragment group that REJECTS a URL missing it (e.g. an OAuth/callbackwithoutcode+state), vs the optionalallOf/oneOf. Link branches only — rejected on screen view-state.
0.18.1 #
- README: sharpen positioning and document the unified web/mobile model —
Screen.manager, theUrl→Place/Link/RootUrlresolver, andurl.domain.
0.18.0 #
- Full browser back/forward history on web: multi-entry History API, refresh-survival, and a floor model with returnable (
anchor) vs bounce-exit (passthrough) roots. - Grammar
root→trunk; the base/initial concept claimsroot(root:boot widget,Screen.rootUrl,Screen.rootcontrols,BootScreen.root). Breaking.
0.17.0 #
- Single navigation resolver: external URLs / deep-links (web address bar + mobile app-links) route through one resolver via
setNewRoutePath, with a consume-once cold-start replay. - Web history honors REPLACE mode (first-commit-out-of-boot +
Screen.replace.*), restores back/forward silently (forward stack preserved), and makes re-navigating to the current top an idempotent no-op. - URL mirrors the TOP screen's forward grammar path only —
.stacked/.cycledback-edges add stack depth (blob), never URL segments. parseLinkaccepts path-only URLs instead of throwing; the boot URL is captured eagerly forScreen.initialUrl.
0.16.0 #
- Reactive, surgical view-state reads:
context.on/context.atandQuery.of/Fragment.ofrebuild only on the keys they reference. - Sealed
AnyPlacementfoundation forScreen.current/on/atandsurface(). - Nav-mirror + link URL building: new
encodeNavUrl, andencodeLinknow carries?query/#fragment. - Widget-form link id leads its union (
[id, …declared]). - Sim-safe
popTo; screen lifecycle callbacks removed. Requirescanon_codec ^0.1.3.
0.15.1 #
- Docs:
Screen.go(Hop)returns aKickstartNav(.atnarrows to the landed target) as of canon_generator 0.19.0.
0.15.0 #
pageOfandobserversare now optional (pageOfdefaults toMaterialPage).- Removed the unused
maybePop; enginego/pop/forgetare now@internal— navigate via the generated typed verbs.
0.14.0 #
Screen.manager()restoration is now always on by default (restorationIddefaults to'nav'); pass it only to override the storage key. Drop-in: existingmanager()calls keep working,manager()with no args now restores.
0.13.0 #
- Add
Screen.manager()/NavGraph.manager()— a standalone nav host forMaterialApp(home: ...), no Router/RouteInformation channel (URLs/deep-links never drive the stack). Owns system back; with a restorationId, persists/restores the snapshot.Screen.delegate(Router/MaterialApp.router) stays for URL/web integration.
0.12.0 #
- Breaking: ids are read only through the typed
context.idOf(...).ScreenScopeis now@internaland canon wraps every page in it itself, sopageOfno longer wraps it andPageCtx.entryis gone (usePageCtx.screen). The rawObject? idis never exposed to screen code; the generatedcontext.idOf(ScreenId.x)(id-bearing screens only) is the single sanctioned, non-null reader. ScreenScope.of(context)now returns the screenEnum;ScreenScope.idOf<T>(context, spec)is the internal typed seam the generated extension delegates to.- Pairs with canon_generator ^0.15.0 (emits
ScreenId<I>+ the delegating extension).
0.11.0 #
- Built-in state restoration.
NavGraph.toState()snapshots the full multi-scope stack (restoration-serializable) andrestore(state)rebuilds it — best-effort: any mid-stack failure (illegal edge, unknown screen, or a token its codec rejects) truncates there, dropping that screen and everything above it, keeping the valid prefix. A stale-graph snapshot is rejected outright. No codegen, no wiring. - Id codecs on the screen.
ScreenNodeBaseexposesCodec? get id(default null); declarefinal Codec? id;on the enum and the engine round-trips ids for restoration by reading the codec directly. - Re-exports
Codecfrom the newcanon_codecpackage, sopackage:canon/canon.dartis the only import needed for the nav DSL and id codecs.
0.10.0 #
- Breaking: engine erased to
Enumso one graph can hold screens from several enums.NavGraph<S, I>→NavGraph<I>,PageCtx<S>→PageCtx,pageOfis now(Widget widget, PageCtx ctx, LocalKey key)(the resolved widget; the screen + id are inctx.entry), andobservetakes(Enum from, Enum to). The authoring DSL stays typed per family. graft— split a large graph across enums.graft(Sub.subtree)mounts another screen family's subtree into the tree (the one explicit cross-family edge); the runtime splices it into one virtual tree, so the surface is blind to the split.keep/forget— live content across tab parks.screen.keep({...})keeps a placement and its subtree mounted when its tab parks (content-swapped, not rebuilt);forget()carves a region back out.NavGraph.forget(keep)frees a parked keep.- Shared screens via refs.
SubScreenNode's widget is now optional: a null-widget row is a ref that collapses to its same-named owner (exactly one owner per name, no dangling ref), so a screen owned in one enum can be referenced in-family from another forinherit/cycled.
0.9.1 #
- Fix: a
.stackedback-edge now pushes a fresh instance even when the target is an exact duplicate of the current top (e.g.userProfile(x)fromuserProfile(x)). The universal p==1 exact-duplicate no-op no longer applies to non-collapsing edges, matching.stacked's "fresh instance every revisit" semantics. Collapsing/.cycledand forward edges keep the duplicate guard.
0.9.0 #
- Breaking:
initial:is now a typedInitialScreen.NavGraphisNavGraph<S, I extends InitialScreenBase<S>>andinitialtakes anI— the generatedInitialScreen(e.g.NavGraph<_Screens, InitialScreen>(initial: .home.settings.about)). It seeds the entire root..target chain, so the start can be any reachable stack, not just a root. Only anInitialScreenis accepted, so a navigatingScreen.goXor a live-stackScreen.on(...)can't be passed as the initial. - Removed
initialId(shipped in 0.8.0) — superseded by the typedinitial. An id-bearing initial is expressed asinitial: .someRoot(id)/ a chain.
0.8.0 #
- Roots can carry ids.
go(root, id)now seeds (or reseeds, when the id differs) the root scope with that id instead of dropping it, andNavGraph(initialId: …)seeds the initial root. An id-bearing root is identified by its id (entering it with a different id reseeds; id-free roots pass null and resume their parked stack unchanged). This closes the gap where aninheritchain rooted at an id-bearing root couldn't stamp the root's id —inherit(home)-style chains and the kick-start rescue from a root source now work.
0.7.0 #
inherit— a placement's id is structurally an ancestor's.ad({editAd.inherit(ad)})declareseditAd.id == ad.id; the generated chained push verb takes no id (Screen.on(.ad)?.goEditAd()/goAd(id).goEditAd()) and reads the live ancestor id, so the two ids can never diverge. Runtime adds a no-opinherit(S ancestor)marker onScreenNodeBase.- Breaking: tree sets are now
Set<TreeNode<S>>. A newsealed class TreeNode<S>is the grammar set element type; the spec enum implements it via theScreenNodemixin, andcycled/stackednow return a first-class_BackEdge<S>instead of the screen. This (a) makes chaining after a back-edge (.cycled.inherit(…)) a compile error, (b) rejects foreign/other-graph elements in a tree set, and (c) removes the per-family stash for back-edges. Tree literals are unaffected (the element type is inferred); only code that named theSet<S>root/children types directly needsSet<TreeNode<S>>. - Pairs with canon_generator ^0.8.0 (adds
Screen.on(.parentOf.x)?.go(…)scope-agnostic push).
0.6.0 #
- Breaking:
ScreenNodenow dictates the widget. The grammar engine stays pure-Dart by being generic over the widget type (mixin ScreenNodeBase<S extends ScreenNodeBase<S, W>, W extends Object> on Enum { W get widget; ... }), and the Flutter layer binds it:typedef ScreenNode<S extends ScreenNodeBase<S, Widget>> = ScreenNodeBase<S, Widget>. Consumers now write the one-argenum _Screens with ScreenNode<_Screens>and must providefinal Widget widget. The vestigial id type paramIis dropped (per-screen id types come from the generator, not the mixin). Engine generics bound<S, Object>and accept consumer<S, Widget>via covariance.
0.5.0 #
- Add
NavGraph.observe(fn)— a side-effect listener fired after each navigation commits (new top settled, before its transition animates), with(from, to)screens; returns a disposer. Pure observation (no veto/reroute), meant to be wired where state lives (e.g. a provider). The generator surfaces it typed asScreen.observe((Screen from, Screen to) {...}). The "after transition settled" phase is deferred to the origin collaboration.
0.4.0 #
- Self-pop:
popToXxnow reaches the previous occurrence of the screen you're on (resolvePopskips the current top) instead of no-opping, and it's chainable through a cycle —popToProfile().popToProfile()steps back two. Cycle-member pops are now on union navs too so chains keep a handle that still exposes them. (Relative/absolute "by n / at depth n" are just chaining + thedepthgetter — no extra verbs.) Generator no longer emits an unused_endsWithhelper for trees that don't use.under.
0.3.0 #
- Cycle navigation:
NavGraph.countOf(screen, [id])for cycle depth, adepthgetter on cyclic nav handles (Screen.at case XNav(:depth) when depth > 1), the one-shotScreen.on(.x([id]).depth(n))exact-match gate (compile-gated to cyclic screens), and throwing cycle-memberpopToXverbs guarded by those depth checks.
0.2.1 #
- Handle verbs (
goX/go(Hop)) are edge-required: a target unreachable from the live top throws instead of silently teleporting via the canonical fallback, and guaranteedpop/popTothrow when impossible — both release-active (replacing debug-only asserts). Stale-but-still-legal navigations still resolve; the globalScreen.goXteleport is unchanged.
0.2.0 #
- Stale-codegen guard:
Screen.isCodegenFresh+ a boot-time assert flag a tree that was re-parented without regenerating. - Replace the
againback-edge withcycled(folds a completed duplicate cycle) andstacked(stacks a fresh instance, preserving history).
0.1.0 #
- Initial release.