@glance-apps/sync 1.3.0 → 1.3.1
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 +13 -0
- package/package.json +1 -1
- package/src/merge.js +23 -27
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.1] - 2026-06-14
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`mergeRoutineDefinitions` now applies last-write-wins on chip id collisions.**
|
|
15
|
+
Previously, when the same chip id existed on both sides, the local chip was
|
|
16
|
+
kept verbatim and the remote one skipped — `lastModified` was only consulted
|
|
17
|
+
for tombstone resurrection. As a result, any edit to an existing routine chip
|
|
18
|
+
(rename, reorder, time change, or stamping `ownerSyncId`) failed to propagate
|
|
19
|
+
across devices. Colliding chips are now resolved by the newer `lastModified`,
|
|
20
|
+
matching the behavior of `mergeArrayById` / `mergeHabits`. Ordering and
|
|
21
|
+
tombstone behavior are preserved.
|
|
22
|
+
|
|
10
23
|
## [1.3.0] - 2026-06-14
|
|
11
24
|
|
|
12
25
|
### Added
|
package/package.json
CHANGED
package/src/merge.js
CHANGED
|
@@ -115,8 +115,11 @@ 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.
|
|
119
|
-
*
|
|
118
|
+
* mergeArrayById strategy. When the same chip id exists on both sides, the
|
|
119
|
+
* version with the newer `lastModified` wins (field-level last-write-wins), so
|
|
120
|
+
* edits like renames, time changes, or ownership stamps propagate across
|
|
121
|
+
* devices. Chips present in the deletedChipIds tombstone map are excluded from
|
|
122
|
+
* the merged result.
|
|
120
123
|
*
|
|
121
124
|
* @param {Object} localDefs - Local routine definitions (bucket → chip[])
|
|
122
125
|
* @param {Object} remoteDefs - Remote routine definitions (bucket → chip[])
|
|
@@ -141,40 +144,33 @@ export const mergeRoutineDefinitions = (localDefs, remoteDefs, deletedChipIds =
|
|
|
141
144
|
for (const bucket of allBuckets) {
|
|
142
145
|
const localChips = localDefs[bucket] || [];
|
|
143
146
|
const remoteChips = remoteDefs[bucket] || [];
|
|
144
|
-
const localIds = new Set(localChips.map(c => String(c.id)));
|
|
145
147
|
const remoteMap = new Map(remoteChips.map(c => [String(c.id), c]));
|
|
148
|
+
const localMap = new Map(localChips.map(c => [String(c.id), c]));
|
|
149
|
+
const lm = (c) => c?.lastModified ? new Date(c.lastModified).getTime() : 0;
|
|
146
150
|
|
|
147
|
-
// Start with local chips in order, filtering out tombstoned chips
|
|
148
151
|
const bucketMerged = [];
|
|
152
|
+
// Local chips in order; when a chip also exists remotely, take the newer one.
|
|
149
153
|
for (const chip of localChips) {
|
|
150
|
-
if (isTombstoned(chip)) {
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
if (isTombstoned(chip)) { localChanged = true; continue; }
|
|
155
|
+
const r = remoteMap.get(String(chip.id));
|
|
156
|
+
if (r && !isTombstoned(r) && lm(r) > lm(chip)) {
|
|
157
|
+
bucketMerged.push(r); localChanged = true; // remote edit wins
|
|
158
|
+
} else {
|
|
159
|
+
bucketMerged.push(chip);
|
|
160
|
+
if (r && lm(chip) > lm(r)) remoteChanged = true; // local edit is newer
|
|
153
161
|
}
|
|
154
|
-
bucketMerged.push(chip);
|
|
155
162
|
}
|
|
156
|
-
|
|
157
163
|
// Append remote-only chips (skip tombstoned)
|
|
158
|
-
for (const
|
|
159
|
-
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
remoteChanged = true; // tell remote this was deleted
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
bucketMerged.push(remoteChip);
|
|
166
|
-
localChanged = true;
|
|
164
|
+
for (const r of remoteChips) {
|
|
165
|
+
if (localMap.has(String(r.id))) continue;
|
|
166
|
+
if (isTombstoned(r)) { remoteChanged = true; continue; }
|
|
167
|
+
bucketMerged.push(r); localChanged = true;
|
|
167
168
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (isTombstoned(localChip)) continue; // don't flag deleted chips as needing push
|
|
173
|
-
if (!remoteMap.has(id)) {
|
|
174
|
-
remoteChanged = true;
|
|
175
|
-
}
|
|
169
|
+
// Local-only chips → remote needs them
|
|
170
|
+
for (const c of localChips) {
|
|
171
|
+
if (isTombstoned(c)) continue;
|
|
172
|
+
if (!remoteMap.has(String(c.id))) remoteChanged = true;
|
|
176
173
|
}
|
|
177
|
-
|
|
178
174
|
merged[bucket] = bucketMerged;
|
|
179
175
|
}
|
|
180
176
|
|