@korajs/merge 1.0.0-beta.11 → 1.0.0-beta.13

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/dist/index.d.cts CHANGED
@@ -209,22 +209,31 @@ interface LWWResult {
209
209
  declare function lastWriteWins(localValue: unknown, remoteValue: unknown, localTimestamp: HLCTimestamp, remoteTimestamp: HLCTimestamp): LWWResult;
210
210
 
211
211
  /**
212
- * Add-wins set merge strategy for array fields.
212
+ * Add-wins set merge strategy for array fields (three-way, base-relative).
213
213
  *
214
- * When two sides concurrently modify an array, this strategy preserves all
215
- * additions from both sides. An element is only removed from the result if
216
- * BOTH sides independently removed it. This prevents data loss: if one side
217
- * adds an element while another removes a different element, both changes
218
- * are preserved.
214
+ * When two sides concurrently modify an array, every element either side ADDED
215
+ * survives, and every base element either side REMOVED is removed. An element
216
+ * one side left unchanged never resurrects an element the other side removed
217
+ * (MERGE-1, NEW-MERGE-1): "unchanged" is not a vote to keep it.
219
218
  *
220
219
  * Algorithm:
221
- * added_local = local - base
222
- * added_remote = remote - base
223
- * removed_local = base - local
224
- * removed_remote = base - remote
225
- * result = (base ∪ added_local ∪ added_remote) - (removed_local ∩ removed_remote)
220
+ * result = (local ∩ remote) ∪ (local − base) ∪ (remote − base)
221
+ *
222
+ * Equivalently: a base element is kept only if BOTH sides kept it; a non-base
223
+ * element is kept if EITHER side added it.
224
+ *
225
+ * Ordering is role-independent (the two devices performing this merge call
226
+ * opposite sides "local"): kept base elements first, in base order, then every
227
+ * other element sorted by its serialized form.
226
228
  *
227
229
  * Uses JSON.stringify for element comparison to handle primitives and objects.
230
+ * Delegates to `mergeArraySet` in `@korajs/core`.
231
+ *
232
+ * Interim (S1): this is still a pairwise merge. W7 replaces it with a per-element
233
+ * LWW set whose result is independent of merge order.
234
+ *
235
+ * @deprecated W7 (beta.13): arrays fold as an occurrence-indexed LWW element
236
+ * multiset in `@korajs/core`. Kept for `experimental.legacyMerge` (one beta).
228
237
  *
229
238
  * @param localArray - The local array after local modifications
230
239
  * @param remoteArray - The remote array after remote modifications
@@ -442,6 +451,11 @@ declare function resolveConstraintViolation(violation: ConstraintViolation, merg
442
451
  /**
443
452
  * Three-tier merge engine for resolving concurrent operations.
444
453
  *
454
+ * @deprecated W7 (beta.13): records are materialized by the per-field CRDT fold in
455
+ * `@korajs/core` (`mergeOp` / `foldRecord`), which produces the MergeTraces. This
456
+ * pairwise engine only backs `experimental.legacyMerge` for one beta and is
457
+ * removed afterwards. Constraint and referential checks stay in this package.
458
+ *
445
459
  * Tier 1: Auto-merge per field kind (LWW, add-wins set, CRDT)
446
460
  * Tier 3: Custom resolvers override Tier 1 for specific fields
447
461
  * Tier 2: Constraint validation against the candidate merged state
package/dist/index.d.ts CHANGED
@@ -209,22 +209,31 @@ interface LWWResult {
209
209
  declare function lastWriteWins(localValue: unknown, remoteValue: unknown, localTimestamp: HLCTimestamp, remoteTimestamp: HLCTimestamp): LWWResult;
210
210
 
211
211
  /**
212
- * Add-wins set merge strategy for array fields.
212
+ * Add-wins set merge strategy for array fields (three-way, base-relative).
213
213
  *
214
- * When two sides concurrently modify an array, this strategy preserves all
215
- * additions from both sides. An element is only removed from the result if
216
- * BOTH sides independently removed it. This prevents data loss: if one side
217
- * adds an element while another removes a different element, both changes
218
- * are preserved.
214
+ * When two sides concurrently modify an array, every element either side ADDED
215
+ * survives, and every base element either side REMOVED is removed. An element
216
+ * one side left unchanged never resurrects an element the other side removed
217
+ * (MERGE-1, NEW-MERGE-1): "unchanged" is not a vote to keep it.
219
218
  *
220
219
  * Algorithm:
221
- * added_local = local - base
222
- * added_remote = remote - base
223
- * removed_local = base - local
224
- * removed_remote = base - remote
225
- * result = (base ∪ added_local ∪ added_remote) - (removed_local ∩ removed_remote)
220
+ * result = (local ∩ remote) ∪ (local − base) ∪ (remote − base)
221
+ *
222
+ * Equivalently: a base element is kept only if BOTH sides kept it; a non-base
223
+ * element is kept if EITHER side added it.
224
+ *
225
+ * Ordering is role-independent (the two devices performing this merge call
226
+ * opposite sides "local"): kept base elements first, in base order, then every
227
+ * other element sorted by its serialized form.
226
228
  *
227
229
  * Uses JSON.stringify for element comparison to handle primitives and objects.
230
+ * Delegates to `mergeArraySet` in `@korajs/core`.
231
+ *
232
+ * Interim (S1): this is still a pairwise merge. W7 replaces it with a per-element
233
+ * LWW set whose result is independent of merge order.
234
+ *
235
+ * @deprecated W7 (beta.13): arrays fold as an occurrence-indexed LWW element
236
+ * multiset in `@korajs/core`. Kept for `experimental.legacyMerge` (one beta).
228
237
  *
229
238
  * @param localArray - The local array after local modifications
230
239
  * @param remoteArray - The remote array after remote modifications
@@ -442,6 +451,11 @@ declare function resolveConstraintViolation(violation: ConstraintViolation, merg
442
451
  /**
443
452
  * Three-tier merge engine for resolving concurrent operations.
444
453
  *
454
+ * @deprecated W7 (beta.13): records are materialized by the per-field CRDT fold in
455
+ * `@korajs/core` (`mergeOp` / `foldRecord`), which produces the MergeTraces. This
456
+ * pairwise engine only backs `experimental.legacyMerge` for one beta and is
457
+ * removed afterwards. Constraint and referential checks stay in this package.
458
+ *
445
459
  * Tier 1: Auto-merge per field kind (LWW, add-wins set, CRDT)
446
460
  * Tier 3: Custom resolvers override Tier 1 for specific fields
447
461
  * Tier 2: Constraint validation against the candidate merged state
package/dist/index.js CHANGED
@@ -9,69 +9,9 @@ function lastWriteWins(localValue, remoteValue, localTimestamp, remoteTimestamp)
9
9
  }
10
10
 
11
11
  // src/strategies/add-wins-set.ts
12
+ import { mergeArraySet } from "@korajs/core";
12
13
  function addWinsSet(localArray, remoteArray, baseArray) {
13
- const serialize = (v) => JSON.stringify(v);
14
- const baseSet = new Set(baseArray.map(serialize));
15
- const localSet = new Set(localArray.map(serialize));
16
- const remoteSet = new Set(remoteArray.map(serialize));
17
- const addedLocal = /* @__PURE__ */ new Set();
18
- for (const s of localSet) {
19
- if (!baseSet.has(s)) {
20
- addedLocal.add(s);
21
- }
22
- }
23
- const addedRemote = /* @__PURE__ */ new Set();
24
- for (const s of remoteSet) {
25
- if (!baseSet.has(s)) {
26
- addedRemote.add(s);
27
- }
28
- }
29
- const removedLocal = /* @__PURE__ */ new Set();
30
- for (const s of baseSet) {
31
- if (!localSet.has(s)) {
32
- removedLocal.add(s);
33
- }
34
- }
35
- const removedRemote = /* @__PURE__ */ new Set();
36
- for (const s of baseSet) {
37
- if (!remoteSet.has(s)) {
38
- removedRemote.add(s);
39
- }
40
- }
41
- const removedByBoth = /* @__PURE__ */ new Set();
42
- for (const s of removedLocal) {
43
- if (removedRemote.has(s)) {
44
- removedByBoth.add(s);
45
- }
46
- }
47
- const resultSerialized = /* @__PURE__ */ new Set();
48
- const result = [];
49
- const addIfNew = (serialized, value) => {
50
- if (!resultSerialized.has(serialized) && !removedByBoth.has(serialized)) {
51
- resultSerialized.add(serialized);
52
- result.push(value);
53
- }
54
- };
55
- for (const item of baseArray) {
56
- addIfNew(serialize(item), item);
57
- }
58
- const additions = /* @__PURE__ */ new Map();
59
- for (const item of localArray) {
60
- const s = serialize(item);
61
- if (addedLocal.has(s) && !additions.has(s)) {
62
- additions.set(s, item);
63
- }
64
- }
65
- for (const item of remoteArray) {
66
- const s = serialize(item);
67
- if (addedRemote.has(s) && !additions.has(s)) {
68
- additions.set(s, item);
69
- }
70
- }
71
- for (const s of [...additions.keys()].sort()) {
72
- addIfNew(s, additions.get(s));
73
- }
74
- return result;
14
+ return mergeArraySet(localArray, remoteArray, baseArray);
75
15
  }
76
16
 
77
17
  // src/strategies/object-merge.ts