json-diff-ts 5.0.0-alpha.2 → 5.0.0-alpha.4
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/README.md +137 -137
- package/dist/index.cjs +188 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -58
- package/dist/index.d.ts +58 -58
- package/dist/index.js +173 -137
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -125,22 +125,18 @@ var atomizeChangeset = (obj, path = "$", embeddedKey) => {
|
|
|
125
125
|
const valueType = getTypeOfObj(obj.value);
|
|
126
126
|
let finalPath = path;
|
|
127
127
|
if (!finalPath.endsWith(`[${obj.key}]`)) {
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (filterStartIdx !== -1) {
|
|
136
|
-
const filterValue = path.slice(filterStartIdx + 2, filterEndIdx).replaceAll(/(^'|'$)/g, "");
|
|
137
|
-
endsWithFilterValue = filterValue === String(obj.key);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (!endsWithFilterValue) {
|
|
141
|
-
finalPath = append(path, obj.key);
|
|
128
|
+
let endsWithFilterValue = false;
|
|
129
|
+
const filterEndIdx = path.lastIndexOf(")]");
|
|
130
|
+
if (filterEndIdx !== -1) {
|
|
131
|
+
const filterStartIdx = path.lastIndexOf("==", filterEndIdx);
|
|
132
|
+
if (filterStartIdx !== -1) {
|
|
133
|
+
const filterValue = path.slice(filterStartIdx + 2, filterEndIdx).replaceAll(/(^'|'$)/g, "");
|
|
134
|
+
endsWithFilterValue = filterValue === String(obj.key);
|
|
142
135
|
}
|
|
143
136
|
}
|
|
137
|
+
if (!endsWithFilterValue) {
|
|
138
|
+
finalPath = append(path, obj.key);
|
|
139
|
+
}
|
|
144
140
|
}
|
|
145
141
|
return [
|
|
146
142
|
{
|
|
@@ -194,19 +190,19 @@ var unatomizeChangeset = (changes) => {
|
|
|
194
190
|
} else {
|
|
195
191
|
for (let i = 1; i < segments.length; i++) {
|
|
196
192
|
const segment = segments[i];
|
|
197
|
-
const result = /^([^[\]]+)\[\?\(
|
|
193
|
+
const result = /^([^[\]]+)\[\?\(@(?:\.?([^=[]*)|(?:\['([^']*)'\]))=+'([^']+)'\)\]$|^(.+)\[(\d+)\]$/.exec(segment);
|
|
198
194
|
if (result) {
|
|
199
195
|
let key;
|
|
200
196
|
let embeddedKey;
|
|
201
197
|
let arrKey;
|
|
202
198
|
if (result[1]) {
|
|
203
199
|
key = result[1];
|
|
204
|
-
embeddedKey = result[2] || "$value";
|
|
205
|
-
arrKey = result[
|
|
200
|
+
embeddedKey = result[3] || result[2] || "$value";
|
|
201
|
+
arrKey = result[4];
|
|
206
202
|
} else {
|
|
207
|
-
key = result[
|
|
203
|
+
key = result[5];
|
|
208
204
|
embeddedKey = "$index";
|
|
209
|
-
arrKey = Number(result[
|
|
205
|
+
arrKey = Number(result[6]);
|
|
210
206
|
}
|
|
211
207
|
if (i === segments.length - 1) {
|
|
212
208
|
ptr.key = key;
|
|
@@ -650,9 +646,11 @@ var revertBranchChange = (obj, change) => {
|
|
|
650
646
|
function append(basePath, nextSegment) {
|
|
651
647
|
return nextSegment.includes(".") ? `${basePath}[${nextSegment}]` : `${basePath}.${nextSegment}`;
|
|
652
648
|
}
|
|
649
|
+
var IDENT_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
653
650
|
function filterExpression(basePath, filterKey, filterValue) {
|
|
654
651
|
const value = typeof filterValue === "number" ? filterValue : `'${filterValue}'`;
|
|
655
|
-
|
|
652
|
+
const memberAccess = typeof filterKey === "string" && !IDENT_RE.test(filterKey) ? `['${filterKey}']` : `.${filterKey}`;
|
|
653
|
+
return `${basePath}[?(@${memberAccess}==${value})]`;
|
|
656
654
|
}
|
|
657
655
|
|
|
658
656
|
// src/jsonCompare.ts
|
|
@@ -765,7 +763,7 @@ var comparisonToDict = (node) => {
|
|
|
765
763
|
}
|
|
766
764
|
return result;
|
|
767
765
|
};
|
|
768
|
-
var
|
|
766
|
+
var IDENT_RE2 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
769
767
|
var comparisonToFlatList = (node, options = {}) => {
|
|
770
768
|
const results = [];
|
|
771
769
|
flattenNode(node, "$", options.includeUnchanged ?? false, results);
|
|
@@ -784,7 +782,7 @@ function flattenNode(node, path, includeUnchanged, results) {
|
|
|
784
782
|
node.value
|
|
785
783
|
)) {
|
|
786
784
|
if (child == null) continue;
|
|
787
|
-
const childPath =
|
|
785
|
+
const childPath = IDENT_RE2.test(key) ? `${path}.${key}` : `${path}['${key.replace(/'/g, "''")}']`;
|
|
788
786
|
flattenNode(child, childPath, includeUnchanged, results);
|
|
789
787
|
}
|
|
790
788
|
}
|
|
@@ -803,7 +801,7 @@ function flattenNode(node, path, includeUnchanged, results) {
|
|
|
803
801
|
results.push(entry);
|
|
804
802
|
}
|
|
805
803
|
|
|
806
|
-
// src/
|
|
804
|
+
// src/atomPath.ts
|
|
807
805
|
function formatFilterLiteral(value) {
|
|
808
806
|
if (value === null) return "null";
|
|
809
807
|
if (typeof value === "boolean") return String(value);
|
|
@@ -874,6 +872,9 @@ function parseFilter(inner) {
|
|
|
874
872
|
const eq = inner.indexOf("==");
|
|
875
873
|
if (eq === -1) throw new Error(`Invalid filter: missing '==' in ${inner}`);
|
|
876
874
|
const key = inner.slice(2, eq);
|
|
875
|
+
if (!key || !SIMPLE_PROPERTY_RE.test(key)) {
|
|
876
|
+
throw new Error(`Invalid property name in filter: '${key}'. Use bracket notation for non-identifier keys: @['${key}']`);
|
|
877
|
+
}
|
|
877
878
|
return { type: "keyFilter", property: key, value: parseFilterLiteral(inner.slice(eq + 2)) };
|
|
878
879
|
}
|
|
879
880
|
if (inner.startsWith("@['")) {
|
|
@@ -886,7 +887,7 @@ function parseFilter(inner) {
|
|
|
886
887
|
}
|
|
887
888
|
throw new Error(`Invalid filter expression: ${inner}`);
|
|
888
889
|
}
|
|
889
|
-
function
|
|
890
|
+
function parseAtomPath(path) {
|
|
890
891
|
if (!path.startsWith("$")) {
|
|
891
892
|
throw new Error(`Path must start with '$': ${path}`);
|
|
892
893
|
}
|
|
@@ -939,7 +940,7 @@ function formatMemberAccess(name) {
|
|
|
939
940
|
}
|
|
940
941
|
return `['${name.replace(/'/g, "''")}']`;
|
|
941
942
|
}
|
|
942
|
-
function
|
|
943
|
+
function buildAtomPath(segments) {
|
|
943
944
|
let result = "";
|
|
944
945
|
for (const seg of segments) {
|
|
945
946
|
switch (seg.type) {
|
|
@@ -964,7 +965,41 @@ function buildDeltaPath(segments) {
|
|
|
964
965
|
}
|
|
965
966
|
return result;
|
|
966
967
|
}
|
|
967
|
-
function
|
|
968
|
+
function canonicalizeFilterForAtom(inner) {
|
|
969
|
+
if (inner.startsWith("@.")) {
|
|
970
|
+
const eqIdx = inner.indexOf("==");
|
|
971
|
+
if (eqIdx === -1) return `[?(${inner})]`;
|
|
972
|
+
const key = inner.slice(2, eqIdx);
|
|
973
|
+
const valuePart = inner.slice(eqIdx);
|
|
974
|
+
if (SIMPLE_PROPERTY_RE.test(key)) {
|
|
975
|
+
return `[?(@.${key}${valuePart})]`;
|
|
976
|
+
}
|
|
977
|
+
return `[?(@['${key.replace(/'/g, "''")}']${valuePart})]`;
|
|
978
|
+
}
|
|
979
|
+
return `[?(${inner})]`;
|
|
980
|
+
}
|
|
981
|
+
function canonicalizeFilterForV4(inner) {
|
|
982
|
+
if (inner.startsWith("@['")) {
|
|
983
|
+
const [key, endIdx] = extractQuotedString(inner, 3);
|
|
984
|
+
const rest = inner.slice(endIdx + 2);
|
|
985
|
+
if (rest.startsWith("==")) {
|
|
986
|
+
const literal = rest.slice(2);
|
|
987
|
+
const normalizedLiteral = normalizeToStringQuoted(literal);
|
|
988
|
+
return `[?(@.${key}==${normalizedLiteral})]`;
|
|
989
|
+
}
|
|
990
|
+
return `[?(${inner})]`;
|
|
991
|
+
}
|
|
992
|
+
return normalizeFilterToStringLiterals(`[?(${inner})]`);
|
|
993
|
+
}
|
|
994
|
+
function normalizeToStringQuoted(literal) {
|
|
995
|
+
if (literal.startsWith("'") && literal.endsWith("'")) {
|
|
996
|
+
return literal;
|
|
997
|
+
}
|
|
998
|
+
const value = parseFilterLiteral(literal);
|
|
999
|
+
const stringValue = String(value).replace(/'/g, "''");
|
|
1000
|
+
return `'${stringValue}'`;
|
|
1001
|
+
}
|
|
1002
|
+
function atomicPathToAtomPath(atomicPath) {
|
|
968
1003
|
if (atomicPath === "$.$root") return "$";
|
|
969
1004
|
if (atomicPath.startsWith("$.$root.")) return "$" + atomicPath.slice(7);
|
|
970
1005
|
if (!atomicPath.startsWith("$")) {
|
|
@@ -985,7 +1020,8 @@ function atomicPathToDeltaPath(atomicPath) {
|
|
|
985
1020
|
if (atomicPath[i + 1] === "?") {
|
|
986
1021
|
const closingIdx = findFilterClose(atomicPath, i + 2);
|
|
987
1022
|
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${atomicPath}`);
|
|
988
|
-
|
|
1023
|
+
const inner = atomicPath.slice(i + 3, closingIdx);
|
|
1024
|
+
result += canonicalizeFilterForAtom(inner);
|
|
989
1025
|
i = closingIdx + 2;
|
|
990
1026
|
} else if (atomicPath[i + 1] === "'" || /\d/.test(atomicPath[i + 1])) {
|
|
991
1027
|
const end = atomicPath.indexOf("]", i);
|
|
@@ -1005,45 +1041,45 @@ function atomicPathToDeltaPath(atomicPath) {
|
|
|
1005
1041
|
}
|
|
1006
1042
|
return result;
|
|
1007
1043
|
}
|
|
1008
|
-
function
|
|
1009
|
-
if (!
|
|
1010
|
-
throw new Error(`
|
|
1044
|
+
function atomPathToAtomicPath(atomPath) {
|
|
1045
|
+
if (!atomPath.startsWith("$")) {
|
|
1046
|
+
throw new Error(`Atom path must start with '$': ${atomPath}`);
|
|
1011
1047
|
}
|
|
1012
|
-
if (
|
|
1048
|
+
if (atomPath === "$") {
|
|
1013
1049
|
return "$.$root";
|
|
1014
1050
|
}
|
|
1015
1051
|
let result = "$";
|
|
1016
1052
|
let i = 1;
|
|
1017
|
-
while (i <
|
|
1018
|
-
if (
|
|
1053
|
+
while (i < atomPath.length) {
|
|
1054
|
+
if (atomPath[i] === ".") {
|
|
1019
1055
|
i += 1;
|
|
1020
1056
|
const start = i;
|
|
1021
|
-
while (i <
|
|
1057
|
+
while (i < atomPath.length && /[a-zA-Z0-9_]/.test(atomPath[i])) {
|
|
1022
1058
|
i += 1;
|
|
1023
1059
|
}
|
|
1024
|
-
result += "." +
|
|
1025
|
-
} else if (
|
|
1026
|
-
if (
|
|
1027
|
-
const closingIdx = findFilterClose(
|
|
1028
|
-
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${
|
|
1029
|
-
const
|
|
1030
|
-
result +=
|
|
1060
|
+
result += "." + atomPath.slice(start, i);
|
|
1061
|
+
} else if (atomPath[i] === "[") {
|
|
1062
|
+
if (atomPath[i + 1] === "?") {
|
|
1063
|
+
const closingIdx = findFilterClose(atomPath, i + 2);
|
|
1064
|
+
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${atomPath}`);
|
|
1065
|
+
const inner = atomPath.slice(i + 3, closingIdx);
|
|
1066
|
+
result += canonicalizeFilterForV4(inner);
|
|
1031
1067
|
i = closingIdx + 2;
|
|
1032
|
-
} else if (
|
|
1033
|
-
const [key, endIdx] = extractQuotedString(
|
|
1034
|
-
if (
|
|
1068
|
+
} else if (atomPath[i + 1] === "'") {
|
|
1069
|
+
const [key, endIdx] = extractQuotedString(atomPath, i + 2);
|
|
1070
|
+
if (atomPath[endIdx + 1] !== "]") throw new Error(`Expected ']' in: ${atomPath}`);
|
|
1035
1071
|
result += `[${key}]`;
|
|
1036
1072
|
i = endIdx + 2;
|
|
1037
|
-
} else if (/\d/.test(
|
|
1038
|
-
const end =
|
|
1039
|
-
if (end === -1) throw new Error(`Unterminated bracket in: ${
|
|
1040
|
-
result +=
|
|
1073
|
+
} else if (/\d/.test(atomPath[i + 1])) {
|
|
1074
|
+
const end = atomPath.indexOf("]", i);
|
|
1075
|
+
if (end === -1) throw new Error(`Unterminated bracket in: ${atomPath}`);
|
|
1076
|
+
result += atomPath.slice(i, end + 1);
|
|
1041
1077
|
i = end + 1;
|
|
1042
1078
|
} else {
|
|
1043
|
-
throw new Error(`Unexpected character after '[' in: ${
|
|
1079
|
+
throw new Error(`Unexpected character after '[' in: ${atomPath}`);
|
|
1044
1080
|
}
|
|
1045
1081
|
} else {
|
|
1046
|
-
throw new Error(`Unexpected character '${
|
|
1082
|
+
throw new Error(`Unexpected character '${atomPath[i]}' in atom path: ${atomPath}`);
|
|
1047
1083
|
}
|
|
1048
1084
|
}
|
|
1049
1085
|
return result;
|
|
@@ -1093,15 +1129,15 @@ function extractKeyFromAtomicPath(atomicPath) {
|
|
|
1093
1129
|
return atomicPath;
|
|
1094
1130
|
}
|
|
1095
1131
|
|
|
1096
|
-
// src/
|
|
1097
|
-
function
|
|
1132
|
+
// src/jsonAtom.ts
|
|
1133
|
+
function validateAtom(atom) {
|
|
1098
1134
|
const errors = [];
|
|
1099
|
-
if (typeof
|
|
1100
|
-
return { valid: false, errors: ["
|
|
1135
|
+
if (typeof atom !== "object" || atom === null) {
|
|
1136
|
+
return { valid: false, errors: ["Atom must be a non-null object"] };
|
|
1101
1137
|
}
|
|
1102
|
-
const d =
|
|
1103
|
-
if (d.format !== "json-
|
|
1104
|
-
errors.push(`Invalid or missing format: expected 'json-
|
|
1138
|
+
const d = atom;
|
|
1139
|
+
if (d.format !== "json-atom") {
|
|
1140
|
+
errors.push(`Invalid or missing format: expected 'json-atom', got '${d.format}'`);
|
|
1105
1141
|
}
|
|
1106
1142
|
if (typeof d.version !== "number") {
|
|
1107
1143
|
errors.push(`Missing or invalid version: expected number, got '${typeof d.version}'`);
|
|
@@ -1139,7 +1175,7 @@ function validateDelta(delta) {
|
|
|
1139
1175
|
}
|
|
1140
1176
|
return { valid: errors.length === 0, errors };
|
|
1141
1177
|
}
|
|
1142
|
-
function
|
|
1178
|
+
function diffAtom(oldObj, newObj, options = {}) {
|
|
1143
1179
|
const changeset = diff(oldObj, newObj, {
|
|
1144
1180
|
...options,
|
|
1145
1181
|
treatTypeChangeAsReplace: true
|
|
@@ -1148,7 +1184,7 @@ function diffDelta(oldObj, newObj, options = {}) {
|
|
|
1148
1184
|
const operations = [];
|
|
1149
1185
|
walkChanges(changeset, "$", oldObj, newObj, operations, options);
|
|
1150
1186
|
return {
|
|
1151
|
-
format: "json-
|
|
1187
|
+
format: "json-atom",
|
|
1152
1188
|
version: 1,
|
|
1153
1189
|
operations
|
|
1154
1190
|
};
|
|
@@ -1328,10 +1364,10 @@ function findElementByFn(oldArr, newArr, fn, changeKey, opType) {
|
|
|
1328
1364
|
}
|
|
1329
1365
|
return void 0;
|
|
1330
1366
|
}
|
|
1331
|
-
function
|
|
1367
|
+
function toAtom(changeset, options = {}) {
|
|
1332
1368
|
let atoms;
|
|
1333
1369
|
if (changeset.length === 0) {
|
|
1334
|
-
return { format: "json-
|
|
1370
|
+
return { format: "json-atom", version: 1, operations: [] };
|
|
1335
1371
|
}
|
|
1336
1372
|
if ("path" in changeset[0]) {
|
|
1337
1373
|
atoms = changeset;
|
|
@@ -1339,7 +1375,7 @@ function toDelta(changeset, options = {}) {
|
|
|
1339
1375
|
atoms = atomizeChangeset(changeset);
|
|
1340
1376
|
}
|
|
1341
1377
|
const rawOps = atoms.map((atom) => {
|
|
1342
|
-
const path =
|
|
1378
|
+
const path = atomicPathToAtomPath(atom.path);
|
|
1343
1379
|
switch (atom.type) {
|
|
1344
1380
|
case "ADD" /* ADD */:
|
|
1345
1381
|
return { op: "add", path, value: atom.value };
|
|
@@ -1363,7 +1399,7 @@ function toDelta(changeset, options = {}) {
|
|
|
1363
1399
|
}
|
|
1364
1400
|
});
|
|
1365
1401
|
const operations = mergeConsecutiveOps(rawOps);
|
|
1366
|
-
return { format: "json-
|
|
1402
|
+
return { format: "json-atom", version: 1, operations };
|
|
1367
1403
|
}
|
|
1368
1404
|
function mergeConsecutiveOps(ops) {
|
|
1369
1405
|
const result = [];
|
|
@@ -1387,13 +1423,13 @@ function mergeConsecutiveOps(ops) {
|
|
|
1387
1423
|
}
|
|
1388
1424
|
return result;
|
|
1389
1425
|
}
|
|
1390
|
-
function
|
|
1391
|
-
const validation =
|
|
1426
|
+
function fromAtom(atom) {
|
|
1427
|
+
const validation = validateAtom(atom);
|
|
1392
1428
|
if (!validation.valid) {
|
|
1393
|
-
throw new Error(`Invalid
|
|
1429
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1394
1430
|
}
|
|
1395
|
-
return
|
|
1396
|
-
const atomicPath =
|
|
1431
|
+
return atom.operations.map((op) => {
|
|
1432
|
+
const atomicPath = atomPathToAtomicPath(op.path);
|
|
1397
1433
|
const key = extractKeyFromAtomicPath(atomicPath);
|
|
1398
1434
|
switch (op.op) {
|
|
1399
1435
|
case "add": {
|
|
@@ -1406,11 +1442,11 @@ function fromDelta(delta) {
|
|
|
1406
1442
|
}
|
|
1407
1443
|
case "replace": {
|
|
1408
1444
|
const valueType = getValueType(op.value);
|
|
1409
|
-
const
|
|
1445
|
+
const atom2 = { type: "UPDATE" /* UPDATE */, key, path: atomicPath, valueType, value: op.value };
|
|
1410
1446
|
if (op.oldValue !== void 0) {
|
|
1411
|
-
|
|
1447
|
+
atom2.oldValue = op.oldValue;
|
|
1412
1448
|
}
|
|
1413
|
-
return
|
|
1449
|
+
return atom2;
|
|
1414
1450
|
}
|
|
1415
1451
|
/* istanbul ignore next -- exhaustive switch */
|
|
1416
1452
|
default:
|
|
@@ -1425,21 +1461,21 @@ function getValueType(value) {
|
|
|
1425
1461
|
const type = typeof value;
|
|
1426
1462
|
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
1427
1463
|
}
|
|
1428
|
-
function
|
|
1429
|
-
const validation =
|
|
1464
|
+
function invertAtom(atom) {
|
|
1465
|
+
const validation = validateAtom(atom);
|
|
1430
1466
|
if (!validation.valid) {
|
|
1431
|
-
throw new Error(`Invalid
|
|
1467
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1432
1468
|
}
|
|
1433
|
-
for (let i = 0; i <
|
|
1434
|
-
const op =
|
|
1469
|
+
for (let i = 0; i < atom.operations.length; i++) {
|
|
1470
|
+
const op = atom.operations[i];
|
|
1435
1471
|
if (op.op === "replace" && !("oldValue" in op)) {
|
|
1436
|
-
throw new Error(`operations[${i}]: replace operation missing oldValue \u2014
|
|
1472
|
+
throw new Error(`operations[${i}]: replace operation missing oldValue \u2014 atom is not reversible`);
|
|
1437
1473
|
}
|
|
1438
1474
|
if (op.op === "remove" && !("oldValue" in op)) {
|
|
1439
|
-
throw new Error(`operations[${i}]: remove operation missing oldValue \u2014
|
|
1475
|
+
throw new Error(`operations[${i}]: remove operation missing oldValue \u2014 atom is not reversible`);
|
|
1440
1476
|
}
|
|
1441
1477
|
}
|
|
1442
|
-
const invertedOps = [...
|
|
1478
|
+
const invertedOps = [...atom.operations].reverse().map((op) => {
|
|
1443
1479
|
const extensions = {};
|
|
1444
1480
|
for (const key of Object.keys(op)) {
|
|
1445
1481
|
if (!["op", "path", "value", "oldValue"].includes(key)) {
|
|
@@ -1458,25 +1494,25 @@ function invertDelta(delta) {
|
|
|
1458
1494
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1459
1495
|
}
|
|
1460
1496
|
});
|
|
1461
|
-
const envelope = { format: "json-
|
|
1462
|
-
for (const key of Object.keys(
|
|
1497
|
+
const envelope = { format: "json-atom", version: atom.version, operations: invertedOps };
|
|
1498
|
+
for (const key of Object.keys(atom)) {
|
|
1463
1499
|
if (!["format", "version", "operations"].includes(key)) {
|
|
1464
|
-
envelope[key] =
|
|
1500
|
+
envelope[key] = atom[key];
|
|
1465
1501
|
}
|
|
1466
1502
|
}
|
|
1467
1503
|
return envelope;
|
|
1468
1504
|
}
|
|
1469
|
-
function
|
|
1470
|
-
const validation =
|
|
1505
|
+
function applyAtom(obj, atom) {
|
|
1506
|
+
const validation = validateAtom(atom);
|
|
1471
1507
|
if (!validation.valid) {
|
|
1472
|
-
throw new Error(`Invalid
|
|
1508
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1473
1509
|
}
|
|
1474
1510
|
let result = obj;
|
|
1475
|
-
for (const op of
|
|
1511
|
+
for (const op of atom.operations) {
|
|
1476
1512
|
if (op.path === "$") {
|
|
1477
1513
|
result = applyRootOp(result, op);
|
|
1478
1514
|
} else {
|
|
1479
|
-
const atomicChange =
|
|
1515
|
+
const atomicChange = atomOpToAtomicChange(op);
|
|
1480
1516
|
const miniChangeset = unatomizeChangeset([atomicChange]);
|
|
1481
1517
|
applyChangeset(result, miniChangeset);
|
|
1482
1518
|
}
|
|
@@ -1504,8 +1540,8 @@ function applyRootOp(obj, op) {
|
|
|
1504
1540
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1505
1541
|
}
|
|
1506
1542
|
}
|
|
1507
|
-
function
|
|
1508
|
-
const atomicPath =
|
|
1543
|
+
function atomOpToAtomicChange(op) {
|
|
1544
|
+
const atomicPath = atomPathToAtomicPath(op.path);
|
|
1509
1545
|
const key = extractKeyFromAtomicPath(atomicPath);
|
|
1510
1546
|
switch (op.op) {
|
|
1511
1547
|
case "add":
|
|
@@ -1526,14 +1562,14 @@ function deltaOpToAtomicChange(op) {
|
|
|
1526
1562
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1527
1563
|
}
|
|
1528
1564
|
}
|
|
1529
|
-
function
|
|
1530
|
-
const inverse =
|
|
1531
|
-
return
|
|
1565
|
+
function revertAtom(obj, atom) {
|
|
1566
|
+
const inverse = invertAtom(atom);
|
|
1567
|
+
return applyAtom(obj, inverse);
|
|
1532
1568
|
}
|
|
1533
1569
|
|
|
1534
|
-
// src/
|
|
1570
|
+
// src/atomHelpers.ts
|
|
1535
1571
|
var OP_SPEC_KEYS = /* @__PURE__ */ new Set(["op", "path", "value", "oldValue"]);
|
|
1536
|
-
var
|
|
1572
|
+
var ATOM_SPEC_KEYS = /* @__PURE__ */ new Set(["format", "version", "operations"]);
|
|
1537
1573
|
function operationSpecDict(op) {
|
|
1538
1574
|
const result = { op: op.op, path: op.path };
|
|
1539
1575
|
if ("value" in op) result.value = op.value;
|
|
@@ -1550,44 +1586,44 @@ function operationExtensions(op) {
|
|
|
1550
1586
|
return result;
|
|
1551
1587
|
}
|
|
1552
1588
|
function leafProperty(op) {
|
|
1553
|
-
const segments =
|
|
1589
|
+
const segments = parseAtomPath(op.path);
|
|
1554
1590
|
if (segments.length === 0) return null;
|
|
1555
1591
|
const last = segments[segments.length - 1];
|
|
1556
1592
|
return last.type === "property" ? last.name : null;
|
|
1557
1593
|
}
|
|
1558
|
-
function
|
|
1594
|
+
function atomSpecDict(atom) {
|
|
1559
1595
|
return {
|
|
1560
|
-
format:
|
|
1561
|
-
version:
|
|
1562
|
-
operations:
|
|
1596
|
+
format: atom.format,
|
|
1597
|
+
version: atom.version,
|
|
1598
|
+
operations: atom.operations.map(operationSpecDict)
|
|
1563
1599
|
};
|
|
1564
1600
|
}
|
|
1565
|
-
function
|
|
1601
|
+
function atomExtensions(atom) {
|
|
1566
1602
|
const result = /* @__PURE__ */ Object.create(null);
|
|
1567
|
-
for (const key of Object.keys(
|
|
1568
|
-
if (!
|
|
1569
|
-
result[key] =
|
|
1603
|
+
for (const key of Object.keys(atom)) {
|
|
1604
|
+
if (!ATOM_SPEC_KEYS.has(key)) {
|
|
1605
|
+
result[key] = atom[key];
|
|
1570
1606
|
}
|
|
1571
1607
|
}
|
|
1572
1608
|
return result;
|
|
1573
1609
|
}
|
|
1574
|
-
function
|
|
1575
|
-
return { ...
|
|
1610
|
+
function atomMap(atom, fn) {
|
|
1611
|
+
return { ...atom, operations: atom.operations.map((op, i) => fn(op, i)) };
|
|
1576
1612
|
}
|
|
1577
|
-
function
|
|
1578
|
-
return
|
|
1613
|
+
function atomStamp(atom, extensions) {
|
|
1614
|
+
return atomMap(atom, (op) => ({ ...op, ...extensions }));
|
|
1579
1615
|
}
|
|
1580
|
-
function
|
|
1616
|
+
function atomGroupBy(atom, keyFn) {
|
|
1581
1617
|
const groups = /* @__PURE__ */ Object.create(null);
|
|
1582
|
-
for (const op of
|
|
1618
|
+
for (const op of atom.operations) {
|
|
1583
1619
|
const k = keyFn(op);
|
|
1584
1620
|
if (!groups[k]) groups[k] = [];
|
|
1585
1621
|
groups[k].push(op);
|
|
1586
1622
|
}
|
|
1587
1623
|
const envelope = /* @__PURE__ */ Object.create(null);
|
|
1588
|
-
for (const key of Object.keys(
|
|
1624
|
+
for (const key of Object.keys(atom)) {
|
|
1589
1625
|
if (key !== "operations") {
|
|
1590
|
-
envelope[key] =
|
|
1626
|
+
envelope[key] = atom[key];
|
|
1591
1627
|
}
|
|
1592
1628
|
}
|
|
1593
1629
|
const result = /* @__PURE__ */ Object.create(null);
|
|
@@ -1599,18 +1635,18 @@ function deltaGroupBy(delta, keyFn) {
|
|
|
1599
1635
|
function deepClone(obj) {
|
|
1600
1636
|
return JSON.parse(JSON.stringify(obj));
|
|
1601
1637
|
}
|
|
1602
|
-
function
|
|
1638
|
+
function squashAtoms(source, atoms, options = {}) {
|
|
1603
1639
|
const { target, verifyTarget = true, ...diffOptions } = options;
|
|
1604
1640
|
let final;
|
|
1605
|
-
if (target !== void 0 &&
|
|
1641
|
+
if (target !== void 0 && atoms.length > 0 && verifyTarget) {
|
|
1606
1642
|
let computed = deepClone(source);
|
|
1607
|
-
for (const d of
|
|
1608
|
-
computed =
|
|
1643
|
+
for (const d of atoms) {
|
|
1644
|
+
computed = applyAtom(computed, d);
|
|
1609
1645
|
}
|
|
1610
|
-
const verification =
|
|
1646
|
+
const verification = diffAtom(computed, target, diffOptions);
|
|
1611
1647
|
if (verification.operations.length > 0) {
|
|
1612
1648
|
throw new Error(
|
|
1613
|
-
"
|
|
1649
|
+
"squashAtoms: provided target does not match sequential application of atoms to source"
|
|
1614
1650
|
);
|
|
1615
1651
|
}
|
|
1616
1652
|
final = target;
|
|
@@ -1618,14 +1654,14 @@ function squashDeltas(source, deltas, options = {}) {
|
|
|
1618
1654
|
final = target;
|
|
1619
1655
|
} else {
|
|
1620
1656
|
final = deepClone(source);
|
|
1621
|
-
for (const d of
|
|
1622
|
-
final =
|
|
1657
|
+
for (const d of atoms) {
|
|
1658
|
+
final = applyAtom(final, d);
|
|
1623
1659
|
}
|
|
1624
1660
|
}
|
|
1625
|
-
const result =
|
|
1626
|
-
for (const d of
|
|
1661
|
+
const result = diffAtom(source, final, diffOptions);
|
|
1662
|
+
for (const d of atoms) {
|
|
1627
1663
|
for (const key of Object.keys(d)) {
|
|
1628
|
-
if (!
|
|
1664
|
+
if (!ATOM_SPEC_KEYS.has(key)) {
|
|
1629
1665
|
Object.defineProperty(result, key, {
|
|
1630
1666
|
value: d[key],
|
|
1631
1667
|
writable: true,
|
|
@@ -1640,38 +1676,38 @@ function squashDeltas(source, deltas, options = {}) {
|
|
|
1640
1676
|
export {
|
|
1641
1677
|
CompareOperation,
|
|
1642
1678
|
Operation,
|
|
1679
|
+
applyAtom,
|
|
1643
1680
|
applyChangelist,
|
|
1644
1681
|
applyChangeset,
|
|
1645
|
-
|
|
1682
|
+
atomExtensions,
|
|
1683
|
+
atomGroupBy,
|
|
1684
|
+
atomMap,
|
|
1685
|
+
atomSpecDict,
|
|
1686
|
+
atomStamp,
|
|
1646
1687
|
atomizeChangeset,
|
|
1647
|
-
|
|
1688
|
+
buildAtomPath,
|
|
1648
1689
|
compare2 as compare,
|
|
1649
1690
|
comparisonToDict,
|
|
1650
1691
|
comparisonToFlatList,
|
|
1651
1692
|
createContainer,
|
|
1652
1693
|
createValue,
|
|
1653
|
-
deltaExtensions,
|
|
1654
|
-
deltaGroupBy,
|
|
1655
|
-
deltaMap,
|
|
1656
|
-
deltaSpecDict,
|
|
1657
|
-
deltaStamp,
|
|
1658
1694
|
diff,
|
|
1659
|
-
|
|
1695
|
+
diffAtom,
|
|
1660
1696
|
enrich,
|
|
1661
1697
|
formatFilterLiteral,
|
|
1662
|
-
|
|
1698
|
+
fromAtom,
|
|
1663
1699
|
getTypeOfObj,
|
|
1664
|
-
|
|
1700
|
+
invertAtom,
|
|
1665
1701
|
leafProperty,
|
|
1666
1702
|
operationExtensions,
|
|
1667
1703
|
operationSpecDict,
|
|
1668
|
-
|
|
1704
|
+
parseAtomPath,
|
|
1669
1705
|
parseFilterLiteral,
|
|
1706
|
+
revertAtom,
|
|
1670
1707
|
revertChangeset,
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
toDelta,
|
|
1708
|
+
squashAtoms,
|
|
1709
|
+
toAtom,
|
|
1674
1710
|
unatomizeChangeset,
|
|
1675
|
-
|
|
1711
|
+
validateAtom
|
|
1676
1712
|
};
|
|
1677
1713
|
//# sourceMappingURL=index.js.map
|