fix(core): drop doc events targeting schema.Ignore fields - #97
Merged
Merged
Conversation
A root field declared schema.Ignore() was honored at initialization but not by the incremental event path: doc events under an ignored root were still normalized, their containers registered, and their deltas materialized into state — rebuilding a streaming ignored list item by item with wrong indices and growing the container registry with every container. Consumers had to monkeypatch private Mirror methods (normalizeLoroEventBatch / registerContainersFromLoroEvent) to drop those events. Mirror now filters such events up front in handleLoroEvent and in the local capture paths (applyLocalLoroChanges, finalizeEphemeralPatches): events whose path[0] is an ignored root key, whose target's root container maps to one, or whose path resolves to a nested Ignore map field are dropped before registration/application. A batch containing only ignored events causes no state change and no subscriber notification; a mixed batch notifies once for the kept part. setState writes to ignored keys remain memory-only, unchanged. Also widens RootSchemaDefinition (new RootFieldSchemaType) so a root schema.Ignore() no longer needs a cast.
This was referenced Sep 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A root field declared
schema.Ignore()was honored bybuildRootStateSnapshot(the initial snapshot skips it) but not by the incremental event path. A doc event under an ignored root (event.path[0] === key, orevent.targetresolving to the ignored root container) was still normalized, its containers registered, andapplyEventBatchToStatematerialized the delta into state. Over a streaming session an "ignored" list gets rebuilt item by item as a partial array with wrong indices, and the registry grows with every container.Downstream (Lody) currently works around this by monkeypatching the private
normalizeLoroEventBatch/registerContainersFromLoroEventmethods on the Mirror instance to drop those events.Fix
Mirror now filters such events up front, before any registration/application, in:
handleLoroEvent(remote/local doc subscription path)applyLocalLoroChanges(captureLocalDocEventconsumer)finalizeEphemeralPatchesAn event is dropped when:
event.path[0]is an ignored root key, orgetChildSchema) to a nestedignoremap field at any depth, orevent.target's root container (via theparent()chain + inverteddoc.getShallowValue()) maps to an ignored root key.Semantics:
Ignorefield return the batch unchanged (zero overhead).setStatewrites to ignored keys stay memory-only — unchanged (the diff already skipped them).Typing
RootSchemaDefinitionnow acceptsIgnoreSchemaTypeat the root via the new exportedRootFieldSchemaType = ContainerSchemaType | IgnoreSchemaType, so a rootschema.Ignore()no longer needs a cast.InferTypeof the field isunknown.Tests
New
packages/core/tests/ignore-root-events.test.ts:history: schema.Ignore()+ peer appending items tohistory→ state has nohistory, registry unchanged, subscriber not called.historyappend and asessionmap set → state updatessessiononly, subscriber called exactly once, onlysessionregistered.setStatewriting to the ignored key stays memory-only (state has the value,doc.toJSON()does not).Ignoremap field: events inside it are dropped (no state/registry/notify), while the parent-path creation event is still applied (unchanged pre-existing semantics).Plus a type test in
inferType.test-d.tsproving a rootschema.Ignore()compiles without a cast.Full suite: 528 tests pass, lint/typecheck clean.
Published loro-crdt 1.16.0 follow-up
Fixed the nested Ignore consistency regression: the comparison snapshot now carries the in-memory Ignore value, excluding both ignored document values and their container identities from comparison. Ordinary sibling values and identities remain checked. The regression fails before the fix and passes after it, including a normal setState after a committed ignored-list append. Updated core/react/jotai loro-crdt requirements and lockfile to ^1.16.0 / 1.16.0. Build, 529 tests, lint (0 errors), and typecheck pass.