Entity / foldkit-entity
Mental model
Entity
├── fields intrinsic values: the properties of entity.schema
├── relations navigation edges: one / many of another Entity (Entity.relate)
├── derived readable values an interpreter supplies, counts or finds
├── members all three under one namespace; keys never collide
├── sets members the write's context fills (who asks, when)
└── invariants what every stored row must hold (kept in its metadata)
over it, as data:
Selection which members a consumer reads Entity.select
Query which rows, in which order Query.from · Expr
Write what one operation does to one row Write.update / insert / delete
interpreter ──(foldkit-metadata)──► Entity / member attaches what it needs
interpreter ◄── reads all of the above, and runs it (SQL, the client's store)An Entity has one set of members. Entity.relate, Entity.derived and
Entity.sets add to it, and every descriptor of the Entity reads it: the one
declared before relate, the one a relation's target() returns, the one a
query captured. Metadata is the exception: Entity.annotate,
Entity.annotateMembers and Entity.invariant return a new descriptor that
carries its own, so one reader's view (a form's labels) stays its own. Compare
descriptors with Entity.same(a, b), not a === b.
identity ── relations, derived, sets one per Entity; relate / derived / sets add
├── descriptor fields, metadata define, relate, derived, sets return it
└── descriptor fields, metadata' annotate / annotateMembers / invariant make oneA descriptor's type still shows only the steps it came through: read typed
members from the descriptor relate returned, or a later one.
Everything here is checked where it is declared: a type error where the types can see it, and a throw for what they cannot. An interpreter that cannot run something refuses it by name rather than skipping it.
Install
pnpm add effect foldkit-entity