Skip to content

feat: support mergeable map child containers#85

Merged
zxch3n merged 10 commits into
mainfrom
codex/mergeable-map-child-containers
Jun 15, 2026
Merged

feat: support mergeable map child containers#85
zxch3n merged 10 commits into
mainfrom
codex/mergeable-map-child-containers

Conversation

@lodystage

@lodystage lodystage Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add opt-in support for Loro mergeable child containers under LoroMap keys.
  • Prefer parent Map schema configuration via schema.LoroMap(..., { mergeableMapChildContainers: true }) and schema.LoroMapRecord(..., { mergeableMapChildContainers: true }), with inferOptions.mergeableMapChildContainers as the schema-less/global fallback.
  • Preserve backward compatibility by reusing existing same-type child containers, including regular setContainer data, and by keeping the default behavior disabled.

Why

Loro's mergeable container APIs avoid concurrent first-creation races for child containers under the same Map key. Loro Mirror previously always used regular setContainer creation for Map child containers, so two offline peers could create distinct children for the same logical field and one child would become hidden after sync.

Behavior Notes

  • New direct child containers under an opted-in parent LoroMap use ensureMergeableMap/List/MovableList/Text/Tree.
  • Existing same-type child containers are updated in place instead of migrated or overwritten.
  • If a Map slot is occupied by a primitive or a different container type, Mirror deletes the slot before ensuring the mergeable container to avoid Loro's occupied-slot error.
  • List items and tree node data keep existing non-mergeable creation semantics.

Validation

  • pnpm --filter loro-mirror test tests/mergeable-containers.test.ts
  • pnpm --filter loro-mirror build
  • pnpm typecheck
  • pnpm lint (passes with existing warnings)
  • pnpm --filter loro-mirror test -- --testTimeout=10000

@zxch3n zxch3n changed the title [codex] support mergeable map child containers feat: support mergeable map child containers Jun 15, 2026
@zxch3n
zxch3n marked this pull request as ready for review June 15, 2026 04:01
zxch3n and others added 9 commits June 15, 2026 04:07
Document the problem mergeable map child containers solve (concurrent
child creation under the same Map key being dropped by last-writer-wins),
what enabling the option does (logical-position-derived identity so peers
converge on one CRDT object), and how to configure it. Adds a usage example
to the core README and expands the JSDoc on both `SchemaOptions` and
`InferContainerOptions`, with links to the Loro blog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
toNormalizedJson built each map via LoroMap.getShallowValue(), which leaks
mergeable child containers as raw binary markers instead of resolving them.
As a result the public helper (and the schema-less buildRootStateSnapshot
fallback) returned garbage (e.g. {0:0,1:76,...}) for any mergeable Map/List/
Text/MovableList/Tree child, even though getState() and doc.toJSON() were
correct.

Resolve containers explicitly via get()/toJSON() so both regular and mergeable
children are normalized uniformly with $cid injected on every map. Because
toJsonWithReplacer re-resolves mergeable container-id strings found in the
returned structure (corrupting nested $cid), inject $cid behind a sentinel
prefix during the pass and strip it in restoreCidDescriptors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
loro-crdt 1.13.2 panics (unreachable at container_store/container_wrapper.rs)
when importing an out-of-order op on a mergeable child container; fixed
upstream in 1.13.3. Raise the peer dependency floor across core/react/jotai
and update the docs/JSDoc version requirement accordingly so the mergeable
feature only runs against a loro-crdt build that has the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vitest typecheck (CI) flagged the object-literal setState calls: the record
value omitted a required field and the tree node omitted its required `id`.
Drop the unused optional field from the schema and add the tree node id so
the test type-checks while exercising the same runtime behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zxch3n
zxch3n merged commit 370f6f0 into main Jun 15, 2026
1 check passed
@zxch3n
zxch3n deleted the codex/mergeable-map-child-containers branch June 15, 2026 17:20
@github-actions github-actions Bot mentioned this pull request Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant