Repository navigation
fix(loro.js): export the latest state in a snapshot of a detached document - #1130
Conversation
…ument A detached document wrote its checked-out state and version alongside the full history, producing an inconsistent snapshot. Match Rust's `encode_snapshot_inner`: encode the latest state, then restore the checkout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WASM Size Report
|
…ed snapshots The lazily imported snapshot branch returned early with the checked-out version and stale lazy state. Take it only when the state is at the latest version. Build the latest state with the same incremental transition that diff() uses and transition back; fall back to one replay on a staging doc. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Addressed the adversarial review in 52710c1. P1: the lazy snapshot branch returned early. No more double replay. The new 10k map keys, 100 edits after the checkout, median of 21 runs per process, 3 alternating rounds (ms):
The export test now also deletes text, checks that a held Checks: |
Resolution: a detached snapshot export (#1130) builds its latest state with the same prepared transition as checkout (#prepareSnapshotTransition, #settleSnapshotSequences) and encodes it with the lazy-aware state encoder. Lazily imported documents therefore also use the O(delta) transition instead of a staging replay, and snapshot states that a replay does not reproduce stay exact in the export. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
Found while auditing
loro.jsagainst the recent Rust snapshot fixes: some probes exported a snapshot after acheckout.On
main,export({ mode: "snapshot" })on a detachedloro.jsdocument writes the checked-out state, the checked-out version vector, and frontiers next to the full history. The resulting blob is inconsistent:{"m":{"a":1}}with frontiers0@1while the history goes up to1@1.checkouton the importer throwssnapshot version does not match its history.Rust's
encode_snapshot_innerchecks out the latest version, encodes, and checks the old version back out.loro.jsnow does the same: it encodes the latest state, writes the oplog version vector and frontiers, and then restores the checkout. The exporting document stays detached at the same version.Validation
loro-js/tests/detached-snapshot.test.ts: the export keeps the checkout; the importer sees the latest value and frontiers and can check out the old version; updates imported while detached are included. Both tests fail onmain.mainimports aloro.jssnapshot exported while detached and gets the latest value{"m":{"a":2},"t":"x"}and frontiers2@1.pnpm --dir loro-js test(18 files, 242 tests),typecheck,lint,format:checkall pass.bench:b4 259778 3) snapshot export is 118.9 ms here vs 112.2 ms onmain, with overlapping samples (109–123 vs 107–122 ms) and identical output (309,780 bytes). Only a detached export pays for rebuilding the latest state and restoring the checkout, which is O(history), as in Rust's two checkouts.The test history avoids a list delete, because retreating a delete on snapshot-imported state is a separate bug fixed in #1126.
🤖 Generated with Claude Code