Mirror / Mirrored state
One owner per datum
A mirror is not an owner. It observes fields and owns nothing, which is what separates it from the two packages that do own state:
| State | Owner | Store |
|---|---|---|
| Filter, sort, page, search text, an open panel | the local Model | the URL, via Mirror.url |
| Preferences, a draft, a collapsed sidebar | the local Model | KeyValueStore, via Mirror.kv |
| Client-authored state that must converge across devices | foldkit-sync | a durable ordered log |
| A cache of another owner's facts | foldkit-remote | the server |
mirror.contract is of kind mirror, so Module.manifest shows a mirrored
field as local with the mirror beside it, and Module.validate reports a
key-value mirror whose MirrorRestored the union does not declare. A URL key
belongs to one mirror per application.
Recovery
A mirror holds no unsent user edits, so its policy is Remote's, not Sync's: a key-value document of another version or scope, or a malformed one, is removed rather than restored; a store failure is absorbed and never fails the application; a slice back at its defaults removes the document. Outside a browser the URL store writes nothing, so a server render is safe.
When not to use a mirror
- Two tabs must agree. A mirror is last-write-wins with no log; use
foldkit-sync. - The value is a server fact. Use
foldkit-remote; the URL should carry the identity (?projectId=p1), not the entity. - The key is a route. A Foldkit router owns the path; a mirror touches only
its own query keys and composes with
route.query(schema)when they differ.
See it working
examples/todo-app keeps the filter in the URL
(/?filter=active, linkable and read back on navigation) and the composer's
draft in Web Storage; pnpm demo prints both in section 12 of the transcript,
and test/runtime.test.ts round-trips the URL. The
package README documents the API.