@vorplex/core 0.0.33 → 0.0.35

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.
@@ -1,4 +1,4 @@
1
- export type ArrayChange = Record<`$${number}` | `$${number}+`, any | typeof $Changes.deleted>;
1
+ export type ArrayChange = Record<`$${number}` | `$${number}+` | `${'$'}{${string | number}}`, any | typeof $Changes.deleted>;
2
2
  export interface ChangeCompareResult {
3
3
  differences: any;
4
4
  similarities: any;
@@ -29,15 +29,12 @@ export interface ChangeRebase<T = any> {
29
29
  * The `.source` with local conflicting changes applied
30
30
  */
31
31
  local: T;
32
- /**
33
- * The remote with local non-conflicting changes applied
34
- */
35
- result: T;
36
32
  };
37
33
  };
38
34
  }
39
35
  export declare class $Changes {
40
36
  static readonly deleted: "[deleted]";
37
+ private static valueHasId;
41
38
  /**
42
39
  * Compute a minimal change object between `a` (base) and `b` (target).
43
40
  * Returns `undefined` when there is no difference.
@@ -1,6 +1,9 @@
1
1
  import { $Value } from '../value/value.util';
2
2
  export class $Changes {
3
3
  static deleted = '[deleted]';
4
+ static valueHasId(value) {
5
+ return value !== null && typeof value === 'object' && !Array.isArray(value) && 'id' in value;
6
+ }
4
7
  /**
5
8
  * Compute a minimal change object between `a` (base) and `b` (target).
6
9
  * Returns `undefined` when there is no difference.
@@ -25,6 +28,30 @@ export class $Changes {
25
28
  return Object.keys(changes).length ? changes : undefined;
26
29
  }
27
30
  function getArrayChanges(base, target) {
31
+ const idKey = (id) => '$' + '{' + id + '}';
32
+ function getIdBasedArrayChanges(base, target) {
33
+ const changes = {};
34
+ const targetById = new Map(target.filter($Changes.valueHasId).map((item) => [item.id, item]));
35
+ const baseIds = new Set(base.filter($Changes.valueHasId).map((item) => item.id));
36
+ for (const item of base) {
37
+ if (!$Changes.valueHasId(item))
38
+ continue;
39
+ const targetItem = targetById.get(item.id);
40
+ if (targetItem === undefined) {
41
+ changes[idKey(item.id)] = $Changes.deleted;
42
+ }
43
+ else {
44
+ const diff = $Changes.get(item, targetItem);
45
+ if (diff !== undefined)
46
+ changes[idKey(item.id)] = diff;
47
+ }
48
+ }
49
+ for (let i = 0; i < target.length; i++) {
50
+ if ($Changes.valueHasId(target[i]) && !baseIds.has(target[i].id))
51
+ changes[`$${i}+`] = target[i];
52
+ }
53
+ return Object.keys(changes).length ? changes : undefined;
54
+ }
28
55
  function getArrayOperations(base, target) {
29
56
  function getLcsMatrix(a, b) {
30
57
  const matrix = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
@@ -60,12 +87,14 @@ export class $Changes {
60
87
  }
61
88
  if (!target.length)
62
89
  return [];
90
+ if (base.every($Changes.valueHasId) && target.every($Changes.valueHasId))
91
+ return getIdBasedArrayChanges(base, target);
63
92
  const operations = getArrayOperations(base, target);
64
93
  const paired = [];
65
94
  for (let i = 0; i < operations.length; i++) {
66
95
  const current = operations[i];
67
96
  const next = operations[i + 1];
68
- if (current.type === 'delete' && next?.type === 'insert') {
97
+ if (current.type === 'delete' && next?.type === 'insert' && $Value.isPrimitive(base[current.sourceIndex])) {
69
98
  paired.push({ type: 'update', sourceIndex: current.sourceIndex, targetIndex: next.targetIndex, value: next.value });
70
99
  i++;
71
100
  }
@@ -176,9 +205,19 @@ export class $Changes {
176
205
  const updates = new Map();
177
206
  const inserts = new Map();
178
207
  for (const [key, value] of Object.entries(changes)) {
208
+ const idMatch = key.match(/^\$\{(?<id>.+)\}$/);
179
209
  const insertMatch = key.match(/^\$(?<index>\d+)\+$/);
180
210
  const updateMatch = key.match(/^\$(?<index>\d+)$/);
181
- if (insertMatch) {
211
+ if (idMatch) {
212
+ const index = base.findIndex((item) => $Changes.valueHasId(item) && String(item.id) === idMatch.groups.id);
213
+ if (index !== -1) {
214
+ if (value === $Changes.deleted)
215
+ deletes.push(index);
216
+ else
217
+ updates.set(index, value);
218
+ }
219
+ }
220
+ else if (insertMatch) {
182
221
  inserts.set(Number.parseInt(insertMatch.groups.index, 10), value);
183
222
  }
184
223
  else if (updateMatch) {
@@ -213,11 +252,13 @@ export class $Changes {
213
252
  return false;
214
253
  if (!Object.keys(change).length)
215
254
  return false;
216
- for (const k of Object.keys(change)) {
217
- if (!k.startsWith('$'))
255
+ for (const key of Object.keys(change)) {
256
+ if (!key.startsWith('$'))
218
257
  return false;
219
- const key = k.endsWith('+') ? k.slice(1, -1) : k.slice(1);
220
- if (Number.isNaN(Number.parseInt(key, 10)))
258
+ if (/^\$\{.+\}$/.test(key))
259
+ continue;
260
+ const index = key.endsWith('+') ? key.slice(1, -1) : key.slice(1);
261
+ if (Number.isNaN(Number.parseInt(index, 10)))
221
262
  return false;
222
263
  }
223
264
  return true;
@@ -279,17 +320,15 @@ export class $Changes {
279
320
  return { result };
280
321
  }
281
322
  const sourceWithDifferences = $Changes.apply(source, remoteCompare.differences, localCompare.differences, remoteCompare.similarities, localCompare.similarities);
282
- const mergedWithLocalDifferences = $Changes.apply(remote, localCompare.differences);
283
323
  return {
284
- result: $Changes.apply(mergedWithLocalDifferences, localCompare.conflicts),
324
+ result: $Changes.apply(remote, localCompare.differences, localCompare.conflicts),
285
325
  conflict: {
286
326
  local: localCompare,
287
327
  remote: remoteCompare,
288
328
  merge: {
289
329
  source: sourceWithDifferences,
290
330
  remote: $Changes.apply(sourceWithDifferences, remoteCompare.conflicts),
291
- local: $Changes.apply(sourceWithDifferences, localCompare.conflicts),
292
- result: mergedWithLocalDifferences,
331
+ local: $Changes.apply(sourceWithDifferences, localCompare.conflicts)
293
332
  },
294
333
  },
295
334
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vorplex/core",
3
- "version": "0.0.33",
3
+ "version": "0.0.35",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "files": [