@glance-apps/sync 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/merge.js +16 -8
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.2] - 2026-06-14
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`mergeRoutineDefinitions` chip collisions are now ownership-aware.** Routine
|
|
15
|
+
chips can be claimed by a user (stamping `ownerSyncId`), and claiming is a
|
|
16
|
+
one-way transition. Previously collisions were resolved purely by newer
|
|
17
|
+
`lastModified`, so an unclaimed copy of a chip could beat a claimed copy when
|
|
18
|
+
it carried an equal-or-newer timestamp — a claim made on one device would
|
|
19
|
+
never reach a device still holding the unclaimed copy. Resolution now prefers
|
|
20
|
+
the claimed side when exactly one side is claimed, regardless of
|
|
21
|
+
`lastModified`; when both sides share the same ownership status (both claimed
|
|
22
|
+
or both unclaimed), the newer `lastModified` still wins. Ordering and
|
|
23
|
+
tombstone behavior are unchanged.
|
|
24
|
+
|
|
10
25
|
## [1.3.1] - 2026-06-14
|
|
11
26
|
|
|
12
27
|
### Fixed
|
package/package.json
CHANGED
package/src/merge.js
CHANGED
|
@@ -115,11 +115,14 @@ export const mergeArrayById = (localItems, remoteItems, deletedIds, syncHorizon
|
|
|
115
115
|
*
|
|
116
116
|
* Each side's bucket is an array of { id, name } chips. The merge preserves
|
|
117
117
|
* local ordering and appends any remote-only chips at the end — mirroring the
|
|
118
|
-
* mergeArrayById strategy. When the same chip id exists on both sides,
|
|
118
|
+
* mergeArrayById strategy. When the same chip id exists on both sides, a
|
|
119
|
+
* claimed chip (one carrying an `ownerSyncId`) always beats an unclaimed copy —
|
|
120
|
+
* claiming is a one-way transition, so the unclaimed side is stale regardless of
|
|
121
|
+
* its `lastModified`. When both sides share the same ownership status, the
|
|
119
122
|
* version with the newer `lastModified` wins (field-level last-write-wins), so
|
|
120
|
-
* edits like renames
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
+
* edits like renames and time changes still propagate across devices. Chips
|
|
124
|
+
* present in the deletedChipIds tombstone map are excluded from the merged
|
|
125
|
+
* result.
|
|
123
126
|
*
|
|
124
127
|
* @param {Object} localDefs - Local routine definitions (bucket → chip[])
|
|
125
128
|
* @param {Object} remoteDefs - Remote routine definitions (bucket → chip[])
|
|
@@ -147,17 +150,22 @@ export const mergeRoutineDefinitions = (localDefs, remoteDefs, deletedChipIds =
|
|
|
147
150
|
const remoteMap = new Map(remoteChips.map(c => [String(c.id), c]));
|
|
148
151
|
const localMap = new Map(localChips.map(c => [String(c.id), c]));
|
|
149
152
|
const lm = (c) => c?.lastModified ? new Date(c.lastModified).getTime() : 0;
|
|
153
|
+
const owned = (c) => !!c?.ownerSyncId;
|
|
154
|
+
// A claimed chip (has ownerSyncId) always overrides an unclaimed copy of the
|
|
155
|
+
// same id — claiming is one-way, so the unclaimed side is stale regardless of
|
|
156
|
+
// its lastModified. Same ownership status on both sides → newer wins.
|
|
157
|
+
const overrides = (x, y) => owned(x) !== owned(y) ? owned(x) : lm(x) > lm(y);
|
|
150
158
|
|
|
151
159
|
const bucketMerged = [];
|
|
152
|
-
// Local chips in order; when a chip also exists remotely, take the
|
|
160
|
+
// Local chips in order; when a chip also exists remotely, take the winner.
|
|
153
161
|
for (const chip of localChips) {
|
|
154
162
|
if (isTombstoned(chip)) { localChanged = true; continue; }
|
|
155
163
|
const r = remoteMap.get(String(chip.id));
|
|
156
|
-
if (r && !isTombstoned(r) &&
|
|
157
|
-
bucketMerged.push(r); localChanged = true; // remote
|
|
164
|
+
if (r && !isTombstoned(r) && overrides(r, chip)) {
|
|
165
|
+
bucketMerged.push(r); localChanged = true; // remote version wins
|
|
158
166
|
} else {
|
|
159
167
|
bucketMerged.push(chip);
|
|
160
|
-
if (r &&
|
|
168
|
+
if (r && overrides(chip, r)) remoteChanged = true; // local version wins → push
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
// Append remote-only chips (skip tombstoned)
|