@vorplex/core 0.0.32 → 0.0.34
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}
|
|
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
|
}
|
|
@@ -147,7 +176,10 @@ export class $Changes {
|
|
|
147
176
|
else if ($Value.equals(aValue, bValue)) {
|
|
148
177
|
similarities[key] = aValue;
|
|
149
178
|
}
|
|
150
|
-
else if (
|
|
179
|
+
else if (/^\$\d+\+$/.test(key)) {
|
|
180
|
+
differences[key] = aValue;
|
|
181
|
+
}
|
|
182
|
+
else if ($Value.isObject(aValue) && $Value.isObject(bValue)) {
|
|
151
183
|
const nested = $Changes.compareChanges(aValue, bValue);
|
|
152
184
|
if (nested.differences !== undefined)
|
|
153
185
|
differences[key] = nested.differences;
|
|
@@ -173,9 +205,19 @@ export class $Changes {
|
|
|
173
205
|
const updates = new Map();
|
|
174
206
|
const inserts = new Map();
|
|
175
207
|
for (const [key, value] of Object.entries(changes)) {
|
|
208
|
+
const idMatch = key.match(/^\$\{(?<id>.+)\}$/);
|
|
176
209
|
const insertMatch = key.match(/^\$(?<index>\d+)\+$/);
|
|
177
210
|
const updateMatch = key.match(/^\$(?<index>\d+)$/);
|
|
178
|
-
if (
|
|
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) {
|
|
179
221
|
inserts.set(Number.parseInt(insertMatch.groups.index, 10), value);
|
|
180
222
|
}
|
|
181
223
|
else if (updateMatch) {
|
|
@@ -210,11 +252,13 @@ export class $Changes {
|
|
|
210
252
|
return false;
|
|
211
253
|
if (!Object.keys(change).length)
|
|
212
254
|
return false;
|
|
213
|
-
for (const
|
|
214
|
-
if (!
|
|
255
|
+
for (const key of Object.keys(change)) {
|
|
256
|
+
if (!key.startsWith('$'))
|
|
215
257
|
return false;
|
|
216
|
-
|
|
217
|
-
|
|
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)))
|
|
218
262
|
return false;
|
|
219
263
|
}
|
|
220
264
|
return true;
|
|
@@ -276,17 +320,15 @@ export class $Changes {
|
|
|
276
320
|
return { result };
|
|
277
321
|
}
|
|
278
322
|
const sourceWithDifferences = $Changes.apply(source, remoteCompare.differences, localCompare.differences, remoteCompare.similarities, localCompare.similarities);
|
|
279
|
-
const mergedWithLocalDifferences = $Changes.apply(remote, localCompare.differences);
|
|
280
323
|
return {
|
|
281
|
-
result: $Changes.apply(
|
|
324
|
+
result: $Changes.apply(remote, local),
|
|
282
325
|
conflict: {
|
|
283
326
|
local: localCompare,
|
|
284
327
|
remote: remoteCompare,
|
|
285
328
|
merge: {
|
|
286
329
|
source: sourceWithDifferences,
|
|
287
330
|
remote: $Changes.apply(sourceWithDifferences, remoteCompare.conflicts),
|
|
288
|
-
local: $Changes.apply(sourceWithDifferences, localCompare.conflicts)
|
|
289
|
-
result: mergedWithLocalDifferences,
|
|
331
|
+
local: $Changes.apply(sourceWithDifferences, localCompare.conflicts)
|
|
290
332
|
},
|
|
291
333
|
},
|
|
292
334
|
};
|