Composition / foldkit-composition
Migrations and unknown Blocks
A Block that a deployment no longer has does not make its pages unreadable. Its
nodes load, round-trip, render as a placeholder once a renderer exists, and can
be reordered where they are or removed. validate reports them, so the strict
check refuses to publish until they are dealt with. Migrations deal with them:
const { document, applied, unused } = Composition.migrate(stored, [
Composition.renameBlock('OldHeading', 'Heading'),
Composition.renameProp('Heading', 'alignment', 'align'),
Composition.promoteUnknown('LegacyVideo to Embed', 'LegacyVideo', Embed),
Composition.migration('DangerToCritical', 'Callout', node =>
node.props['tone'] === 'danger' ? { ...node, props: { ...node.props, tone: 'critical' } } : undefined,
),
])
// applied: [{ name, node }] in the order made; unused: names that rewrote nothing- The list is the chain. A later migration sees what an earlier one made.
- A migration rewrites a node, never its id, and the Document's structure is checked after each one that changed something: a Region pointing at nothing, a node placed twice, a cycle or a stranded node throws, naming the migration. What was already wrong before it ran is not held against it.
- The result is still content. A rewritten node must decode as a Node.
- Declining is allowed. Returning
undefinedkeeps the node.promoteUnknowndeclines for a node whose props do not decode as the target Block's, so it stays as it was rather than half converted. - They run where you choose, such as on load, before a publish, or as an explicit upgrade, never on every read.
A Block the Catalog does not know can be reordered within the Region or the roots it is in, and removed. It cannot be moved elsewhere, and its props cannot be set, since nothing could check either.
Performance
pnpm bench measures a page of 1,000 nodes (bench/operations.bench.ts). On
the machine the design's budgets were checked on, as means:
apply one setProp 0.40 ms budget 1 ms
apply one move between sections 0.39 ms budget 1 ms
validate the whole page 1.14 ms budget 10 ms
index a page not seen before 0.09 ms budget 5 msapply shares every node it does not change, and copies the record of nodes
once per Operation, which is most of its cost at this size.
Relationship to rich text
A page and a rich-text document are two documents on purpose: rich text
addresses edits by text position, a page by node and Region. They meet in one
direction, with rich text held as a Block's prop. This package follows
foldkit-richtext's rules where they apply: props are
JSON when stored and checked by the vocabulary, content a deployment does not
know is kept, and the vocabulary is a module-level value, never Model state.
Limits
- A condition is one of four operations over one context key; there is no
orand nonot, as inExpr. - Only a token choice is responsive; a variant is one value for every viewport.