docs/research/decisions/slate-v2-read-update-runtime-architecture.md
Slate v2 should expose:
editor.read(fn)
editor.update(fn, options?)
as the public runtime lifecycle.
tx.resolveTarget() remains internal. It is not normal plugin/app DX.
The strongest cross-editor evidence points to the same discipline:
Slate v2 should combine those lessons without copying their models:
Slate model + operations
read/update lifecycle
transaction-owned target freshness
commit metadata
React-optimized live reads and dirty regions
extension namespace ergonomics
browser gauntlet proof
editor.read((state) => {
state.selection.get()
state.value.get()
state.marks.get()
})
editor.update((tx) => {
tx.value.replace({
children,
marks: null,
selection: null,
})
tx.nodes.unwrap({ match: isList })
tx.nodes.set({ type: 'list-item' })
tx.nodes.wrap({ type: 'bulleted-list', children: [] })
})
Grouped state and tx methods stay primitive and flexible. This is closer to
Slate's durable value than adding a semantic method for every custom node type,
without keeping flat editor mutation methods as normal public DX.
Whole-document replacement is also a transaction write. The public shape is
editor.update((tx) => tx.value.replace(input)). Static Editor.replace,
public editor.replace, and public editor.reset are not part of the target
app-author API.
editor.update
-> active transaction
-> implicit target resolves once if needed
-> internal write registry uses the transaction target when `at` is omitted
-> operations
-> EditorCommit
-> history/collaboration/render/DOM repair
editor.selection, editor.children, editor.marks,
editor.operationsTransforms.* as primary docs/examples APIeditor.apply and editor.onChange as extension pointsReactEditor.runCommandUse extension namespaces and optional product-layer chain sugar:
defineEditorExtension({
key: 'todo',
tx: {
todo(tx) {
return {
toggle() {
tx.nodes.set({ type: 'todo', checked: true })
},
}
},
},
})
Optional later:
editor.chain().setNodes(props).wrapNodes(wrapper).run()
chain().run() must be sugar over editor.update, not a separate runtime.
React consumes:
EditorCommitReact does not own:
No release-quality claim without generated browser gauntlets that assert:
Accepted as the final architecture direction for the current Slate v2 runtime plan.
The read/update lifecycle is no longer just architecture prose: focused
write-boundary contracts, public-surface hard cuts, generated destructive
browser gauntlets, and persistent-profile soak now exercise the current
runtime. Final closure still requires the full bun test:integration-local
sweep and keeps raw Android/iOS mobile proof scoped unless a device-lab gate
provides direct artifacts.