0001. ProseMirror-shaped JSON is the document model
- Status: Accepted
- Date: 2026-10-01 (recorded with the initial engine, commit
13a251b)
Context
Folio's first consumer stores book chapters as ProseMirror/Tiptap JSON (contentJson), saves them with top-level "blocks-patch" splices and renders them to HTML on the backend. Any new model would need a migration for existing content, a second renderer, and new save semantics. We also need a model that layout can cache cheaply, that crosses worker boundaries, and that hosts can extend with their own node types.
Alternatives considered: an OOXML-backed model (Word's structure natively, but a huge surface and a lossy round-trip with existing content) and a custom block/run model (clean, but every existing document would need conversion).
Decision
A Folio document is plain JSON in the ProseMirror shape: { type, attrs, content, text, marks }. @nextgensoftwares/folio-model defines FolioNode / FolioDocument, a Schema with ProseMirror-style content expressions and groups, validate and normalize, and a standardSchema covering everything the existing editor uses plus Word-native pagination attributes (pageBreakBefore, keepWithNext, keepLinesTogether). Hosts add types with schema.extend(). Nodes are immutable.
Consequences
- Existing content loads unchanged; top-level block indexing, blocks-patch saves and the backend HTML renderer keep working. Tiptap is gone; the data format is not.
- The model is plain data: it serializes without loss and survives a
postMessageround-trip, so layout can run in a worker. - Immutability lets every cache key on object identity (layout flows, page reuse, position index, formatter, checksums). The cost is discipline: hosts must never mutate nodes in place.
- The editor can convert to and from ProseMirror nodes 1:1 (see 0003).
- Word's richer structure (sections with their own page setup, numbering, footnotes) has to be grown into this model deliberately, as new node types and attributes, rather than inherited.