Skip to content

fix(compare): emit replace when a root container type changes from an empty value - #333

Open
maximilliangrand wants to merge 1 commit into
Starcounter-Jack:masterfrom
maximilliangrand:fix/compare-empty-container-type-change
Open

fix(compare): emit replace when a root container type changes from an empty value#333
maximilliangrand wants to merge 1 commit into
Starcounter-Jack:masterfrom
maximilliangrand:fix/compare-empty-container-type-change

Conversation

@maximilliangrand

Copy link
Copy Markdown

Problem

compare()/generate() produce an empty or corrupt patch when one document root is an empty container and the other is a container of the opposite kind:

compare([], {a: 1})   // => [{op:'add', path:'/a', value:1}]  — applyPatch throws OPERATION_PATH_ILLEGAL_ARRAY_INDEX
compare({}, [1, 2])   // => two 'add' ops that apply to {} as {0:1,1:2}, not [1,2]
compare([], {})       // => []  — applying it leaves the doc as []

This is the exact case reported in #239 and called out earlier in #31 (compare([], {}) === []). The array/object-substitution fix in #205 emits the corrective replace from inside the loop over the source's existing keys, so it never runs when the source container is empty.

Fix

Handle the array-vs-object mismatch once at the top of _generate (after toJSON): emit a single replace of the whole subtree (plus a matching test op under invertible) and return. This also covers the non-empty case previously handled in the loop, so the now-redundant branch is removed. Because _generate recurses, this also fixes the nested cases in #239 (e.g. compare({a:[]}, {a:{}})).

compare([], {a: 1})   // => [{op:'replace', path:'', value:{a:1}}]  ✓ round-trips

Tests

Added compare specs for empty-array-root→object and empty-object-root→array (both invertible variants), each asserting the emitted patch and a round-trip via applyPatch. They fail on master and pass with this change; the rest of the suite is unchanged.

Fixes #239. Also resolves the case documented in #31 that #205 left open.

… empty value

`compare()`/`generate()` produced an empty (or corrupt) patch when one document
root was an empty container and the other was a container of the other type,
e.g. `compare([], {a:1})` returned `[]` and applying it left the document as
`[]`. The array/object substitution fix in Starcounter-Jack#205 only emitted the `replace`
from inside the old-keys loop, so it never ran when the source was empty.

Handle the array-vs-object mismatch once at the top of `_generate` (after
`toJSON`), emitting a single `replace` of the whole subtree (with a matching
`test` op under `invertible`). This also covers the non-empty case previously
handled in the loop, so the now-redundant branch is removed.

Fixes the case documented in Starcounter-Jack#31 (`compare([], {}) === []`) that Starcounter-Jack#205 left open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

compare() is not returning any diff in case of diffrent object type

1 participant