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.cjs
CHANGED
|
@@ -22,39 +22,39 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
CompareOperation: () => CompareOperation,
|
|
24
24
|
Operation: () => Operation,
|
|
25
|
+
applyAtom: () => applyAtom,
|
|
25
26
|
applyChangelist: () => applyChangelist,
|
|
26
27
|
applyChangeset: () => applyChangeset,
|
|
27
|
-
|
|
28
|
+
atomExtensions: () => atomExtensions,
|
|
29
|
+
atomGroupBy: () => atomGroupBy,
|
|
30
|
+
atomMap: () => atomMap,
|
|
31
|
+
atomSpecDict: () => atomSpecDict,
|
|
32
|
+
atomStamp: () => atomStamp,
|
|
28
33
|
atomizeChangeset: () => atomizeChangeset,
|
|
29
|
-
|
|
34
|
+
buildAtomPath: () => buildAtomPath,
|
|
30
35
|
compare: () => compare2,
|
|
31
36
|
comparisonToDict: () => comparisonToDict,
|
|
32
37
|
comparisonToFlatList: () => comparisonToFlatList,
|
|
33
38
|
createContainer: () => createContainer,
|
|
34
39
|
createValue: () => createValue,
|
|
35
|
-
deltaExtensions: () => deltaExtensions,
|
|
36
|
-
deltaGroupBy: () => deltaGroupBy,
|
|
37
|
-
deltaMap: () => deltaMap,
|
|
38
|
-
deltaSpecDict: () => deltaSpecDict,
|
|
39
|
-
deltaStamp: () => deltaStamp,
|
|
40
40
|
diff: () => diff,
|
|
41
|
-
|
|
41
|
+
diffAtom: () => diffAtom,
|
|
42
42
|
enrich: () => enrich,
|
|
43
43
|
formatFilterLiteral: () => formatFilterLiteral,
|
|
44
|
-
|
|
44
|
+
fromAtom: () => fromAtom,
|
|
45
45
|
getTypeOfObj: () => getTypeOfObj,
|
|
46
|
-
|
|
46
|
+
invertAtom: () => invertAtom,
|
|
47
47
|
leafProperty: () => leafProperty,
|
|
48
48
|
operationExtensions: () => operationExtensions,
|
|
49
49
|
operationSpecDict: () => operationSpecDict,
|
|
50
|
-
|
|
50
|
+
parseAtomPath: () => parseAtomPath,
|
|
51
51
|
parseFilterLiteral: () => parseFilterLiteral,
|
|
52
|
+
revertAtom: () => revertAtom,
|
|
52
53
|
revertChangeset: () => revertChangeset,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
toDelta: () => toDelta,
|
|
54
|
+
squashAtoms: () => squashAtoms,
|
|
55
|
+
toAtom: () => toAtom,
|
|
56
56
|
unatomizeChangeset: () => unatomizeChangeset,
|
|
57
|
-
|
|
57
|
+
validateAtom: () => validateAtom
|
|
58
58
|
});
|
|
59
59
|
module.exports = __toCommonJS(index_exports);
|
|
60
60
|
|
|
@@ -185,22 +185,18 @@ var atomizeChangeset = (obj, path = "$", embeddedKey) => {
|
|
|
185
185
|
const valueType = getTypeOfObj(obj.value);
|
|
186
186
|
let finalPath = path;
|
|
187
187
|
if (!finalPath.endsWith(`[${obj.key}]`)) {
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (filterStartIdx !== -1) {
|
|
196
|
-
const filterValue = path.slice(filterStartIdx + 2, filterEndIdx).replaceAll(/(^'|'$)/g, "");
|
|
197
|
-
endsWithFilterValue = filterValue === String(obj.key);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (!endsWithFilterValue) {
|
|
201
|
-
finalPath = append(path, obj.key);
|
|
188
|
+
let endsWithFilterValue = false;
|
|
189
|
+
const filterEndIdx = path.lastIndexOf(")]");
|
|
190
|
+
if (filterEndIdx !== -1) {
|
|
191
|
+
const filterStartIdx = path.lastIndexOf("==", filterEndIdx);
|
|
192
|
+
if (filterStartIdx !== -1) {
|
|
193
|
+
const filterValue = path.slice(filterStartIdx + 2, filterEndIdx).replaceAll(/(^'|'$)/g, "");
|
|
194
|
+
endsWithFilterValue = filterValue === String(obj.key);
|
|
202
195
|
}
|
|
203
196
|
}
|
|
197
|
+
if (!endsWithFilterValue) {
|
|
198
|
+
finalPath = append(path, obj.key);
|
|
199
|
+
}
|
|
204
200
|
}
|
|
205
201
|
return [
|
|
206
202
|
{
|
|
@@ -254,19 +250,19 @@ var unatomizeChangeset = (changes) => {
|
|
|
254
250
|
} else {
|
|
255
251
|
for (let i = 1; i < segments.length; i++) {
|
|
256
252
|
const segment = segments[i];
|
|
257
|
-
const result = /^([^[\]]+)\[\?\(
|
|
253
|
+
const result = /^([^[\]]+)\[\?\(@(?:\.?([^=[]*)|(?:\['([^']*)'\]))=+'([^']+)'\)\]$|^(.+)\[(\d+)\]$/.exec(segment);
|
|
258
254
|
if (result) {
|
|
259
255
|
let key;
|
|
260
256
|
let embeddedKey;
|
|
261
257
|
let arrKey;
|
|
262
258
|
if (result[1]) {
|
|
263
259
|
key = result[1];
|
|
264
|
-
embeddedKey = result[2] || "$value";
|
|
265
|
-
arrKey = result[
|
|
260
|
+
embeddedKey = result[3] || result[2] || "$value";
|
|
261
|
+
arrKey = result[4];
|
|
266
262
|
} else {
|
|
267
|
-
key = result[
|
|
263
|
+
key = result[5];
|
|
268
264
|
embeddedKey = "$index";
|
|
269
|
-
arrKey = Number(result[
|
|
265
|
+
arrKey = Number(result[6]);
|
|
270
266
|
}
|
|
271
267
|
if (i === segments.length - 1) {
|
|
272
268
|
ptr.key = key;
|
|
@@ -710,9 +706,11 @@ var revertBranchChange = (obj, change) => {
|
|
|
710
706
|
function append(basePath, nextSegment) {
|
|
711
707
|
return nextSegment.includes(".") ? `${basePath}[${nextSegment}]` : `${basePath}.${nextSegment}`;
|
|
712
708
|
}
|
|
709
|
+
var IDENT_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
713
710
|
function filterExpression(basePath, filterKey, filterValue) {
|
|
714
711
|
const value = typeof filterValue === "number" ? filterValue : `'${filterValue}'`;
|
|
715
|
-
|
|
712
|
+
const memberAccess = typeof filterKey === "string" && !IDENT_RE.test(filterKey) ? `['${filterKey}']` : `.${filterKey}`;
|
|
713
|
+
return `${basePath}[?(@${memberAccess}==${value})]`;
|
|
716
714
|
}
|
|
717
715
|
|
|
718
716
|
// src/jsonCompare.ts
|
|
@@ -825,7 +823,7 @@ var comparisonToDict = (node) => {
|
|
|
825
823
|
}
|
|
826
824
|
return result;
|
|
827
825
|
};
|
|
828
|
-
var
|
|
826
|
+
var IDENT_RE2 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
829
827
|
var comparisonToFlatList = (node, options = {}) => {
|
|
830
828
|
const results = [];
|
|
831
829
|
flattenNode(node, "$", options.includeUnchanged ?? false, results);
|
|
@@ -844,7 +842,7 @@ function flattenNode(node, path, includeUnchanged, results) {
|
|
|
844
842
|
node.value
|
|
845
843
|
)) {
|
|
846
844
|
if (child == null) continue;
|
|
847
|
-
const childPath =
|
|
845
|
+
const childPath = IDENT_RE2.test(key) ? `${path}.${key}` : `${path}['${key.replace(/'/g, "''")}']`;
|
|
848
846
|
flattenNode(child, childPath, includeUnchanged, results);
|
|
849
847
|
}
|
|
850
848
|
}
|
|
@@ -863,7 +861,7 @@ function flattenNode(node, path, includeUnchanged, results) {
|
|
|
863
861
|
results.push(entry);
|
|
864
862
|
}
|
|
865
863
|
|
|
866
|
-
// src/
|
|
864
|
+
// src/atomPath.ts
|
|
867
865
|
function formatFilterLiteral(value) {
|
|
868
866
|
if (value === null) return "null";
|
|
869
867
|
if (typeof value === "boolean") return String(value);
|
|
@@ -934,6 +932,9 @@ function parseFilter(inner) {
|
|
|
934
932
|
const eq = inner.indexOf("==");
|
|
935
933
|
if (eq === -1) throw new Error(`Invalid filter: missing '==' in ${inner}`);
|
|
936
934
|
const key = inner.slice(2, eq);
|
|
935
|
+
if (!key || !SIMPLE_PROPERTY_RE.test(key)) {
|
|
936
|
+
throw new Error(`Invalid property name in filter: '${key}'. Use bracket notation for non-identifier keys: @['${key}']`);
|
|
937
|
+
}
|
|
937
938
|
return { type: "keyFilter", property: key, value: parseFilterLiteral(inner.slice(eq + 2)) };
|
|
938
939
|
}
|
|
939
940
|
if (inner.startsWith("@['")) {
|
|
@@ -946,7 +947,7 @@ function parseFilter(inner) {
|
|
|
946
947
|
}
|
|
947
948
|
throw new Error(`Invalid filter expression: ${inner}`);
|
|
948
949
|
}
|
|
949
|
-
function
|
|
950
|
+
function parseAtomPath(path) {
|
|
950
951
|
if (!path.startsWith("$")) {
|
|
951
952
|
throw new Error(`Path must start with '$': ${path}`);
|
|
952
953
|
}
|
|
@@ -999,7 +1000,7 @@ function formatMemberAccess(name) {
|
|
|
999
1000
|
}
|
|
1000
1001
|
return `['${name.replace(/'/g, "''")}']`;
|
|
1001
1002
|
}
|
|
1002
|
-
function
|
|
1003
|
+
function buildAtomPath(segments) {
|
|
1003
1004
|
let result = "";
|
|
1004
1005
|
for (const seg of segments) {
|
|
1005
1006
|
switch (seg.type) {
|
|
@@ -1024,7 +1025,41 @@ function buildDeltaPath(segments) {
|
|
|
1024
1025
|
}
|
|
1025
1026
|
return result;
|
|
1026
1027
|
}
|
|
1027
|
-
function
|
|
1028
|
+
function canonicalizeFilterForAtom(inner) {
|
|
1029
|
+
if (inner.startsWith("@.")) {
|
|
1030
|
+
const eqIdx = inner.indexOf("==");
|
|
1031
|
+
if (eqIdx === -1) return `[?(${inner})]`;
|
|
1032
|
+
const key = inner.slice(2, eqIdx);
|
|
1033
|
+
const valuePart = inner.slice(eqIdx);
|
|
1034
|
+
if (SIMPLE_PROPERTY_RE.test(key)) {
|
|
1035
|
+
return `[?(@.${key}${valuePart})]`;
|
|
1036
|
+
}
|
|
1037
|
+
return `[?(@['${key.replace(/'/g, "''")}']${valuePart})]`;
|
|
1038
|
+
}
|
|
1039
|
+
return `[?(${inner})]`;
|
|
1040
|
+
}
|
|
1041
|
+
function canonicalizeFilterForV4(inner) {
|
|
1042
|
+
if (inner.startsWith("@['")) {
|
|
1043
|
+
const [key, endIdx] = extractQuotedString(inner, 3);
|
|
1044
|
+
const rest = inner.slice(endIdx + 2);
|
|
1045
|
+
if (rest.startsWith("==")) {
|
|
1046
|
+
const literal = rest.slice(2);
|
|
1047
|
+
const normalizedLiteral = normalizeToStringQuoted(literal);
|
|
1048
|
+
return `[?(@.${key}==${normalizedLiteral})]`;
|
|
1049
|
+
}
|
|
1050
|
+
return `[?(${inner})]`;
|
|
1051
|
+
}
|
|
1052
|
+
return normalizeFilterToStringLiterals(`[?(${inner})]`);
|
|
1053
|
+
}
|
|
1054
|
+
function normalizeToStringQuoted(literal) {
|
|
1055
|
+
if (literal.startsWith("'") && literal.endsWith("'")) {
|
|
1056
|
+
return literal;
|
|
1057
|
+
}
|
|
1058
|
+
const value = parseFilterLiteral(literal);
|
|
1059
|
+
const stringValue = String(value).replace(/'/g, "''");
|
|
1060
|
+
return `'${stringValue}'`;
|
|
1061
|
+
}
|
|
1062
|
+
function atomicPathToAtomPath(atomicPath) {
|
|
1028
1063
|
if (atomicPath === "$.$root") return "$";
|
|
1029
1064
|
if (atomicPath.startsWith("$.$root.")) return "$" + atomicPath.slice(7);
|
|
1030
1065
|
if (!atomicPath.startsWith("$")) {
|
|
@@ -1045,7 +1080,8 @@ function atomicPathToDeltaPath(atomicPath) {
|
|
|
1045
1080
|
if (atomicPath[i + 1] === "?") {
|
|
1046
1081
|
const closingIdx = findFilterClose(atomicPath, i + 2);
|
|
1047
1082
|
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${atomicPath}`);
|
|
1048
|
-
|
|
1083
|
+
const inner = atomicPath.slice(i + 3, closingIdx);
|
|
1084
|
+
result += canonicalizeFilterForAtom(inner);
|
|
1049
1085
|
i = closingIdx + 2;
|
|
1050
1086
|
} else if (atomicPath[i + 1] === "'" || /\d/.test(atomicPath[i + 1])) {
|
|
1051
1087
|
const end = atomicPath.indexOf("]", i);
|
|
@@ -1065,45 +1101,45 @@ function atomicPathToDeltaPath(atomicPath) {
|
|
|
1065
1101
|
}
|
|
1066
1102
|
return result;
|
|
1067
1103
|
}
|
|
1068
|
-
function
|
|
1069
|
-
if (!
|
|
1070
|
-
throw new Error(`
|
|
1104
|
+
function atomPathToAtomicPath(atomPath) {
|
|
1105
|
+
if (!atomPath.startsWith("$")) {
|
|
1106
|
+
throw new Error(`Atom path must start with '$': ${atomPath}`);
|
|
1071
1107
|
}
|
|
1072
|
-
if (
|
|
1108
|
+
if (atomPath === "$") {
|
|
1073
1109
|
return "$.$root";
|
|
1074
1110
|
}
|
|
1075
1111
|
let result = "$";
|
|
1076
1112
|
let i = 1;
|
|
1077
|
-
while (i <
|
|
1078
|
-
if (
|
|
1113
|
+
while (i < atomPath.length) {
|
|
1114
|
+
if (atomPath[i] === ".") {
|
|
1079
1115
|
i += 1;
|
|
1080
1116
|
const start = i;
|
|
1081
|
-
while (i <
|
|
1117
|
+
while (i < atomPath.length && /[a-zA-Z0-9_]/.test(atomPath[i])) {
|
|
1082
1118
|
i += 1;
|
|
1083
1119
|
}
|
|
1084
|
-
result += "." +
|
|
1085
|
-
} else if (
|
|
1086
|
-
if (
|
|
1087
|
-
const closingIdx = findFilterClose(
|
|
1088
|
-
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${
|
|
1089
|
-
const
|
|
1090
|
-
result +=
|
|
1120
|
+
result += "." + atomPath.slice(start, i);
|
|
1121
|
+
} else if (atomPath[i] === "[") {
|
|
1122
|
+
if (atomPath[i + 1] === "?") {
|
|
1123
|
+
const closingIdx = findFilterClose(atomPath, i + 2);
|
|
1124
|
+
if (closingIdx === -1) throw new Error(`Unterminated filter in: ${atomPath}`);
|
|
1125
|
+
const inner = atomPath.slice(i + 3, closingIdx);
|
|
1126
|
+
result += canonicalizeFilterForV4(inner);
|
|
1091
1127
|
i = closingIdx + 2;
|
|
1092
|
-
} else if (
|
|
1093
|
-
const [key, endIdx] = extractQuotedString(
|
|
1094
|
-
if (
|
|
1128
|
+
} else if (atomPath[i + 1] === "'") {
|
|
1129
|
+
const [key, endIdx] = extractQuotedString(atomPath, i + 2);
|
|
1130
|
+
if (atomPath[endIdx + 1] !== "]") throw new Error(`Expected ']' in: ${atomPath}`);
|
|
1095
1131
|
result += `[${key}]`;
|
|
1096
1132
|
i = endIdx + 2;
|
|
1097
|
-
} else if (/\d/.test(
|
|
1098
|
-
const end =
|
|
1099
|
-
if (end === -1) throw new Error(`Unterminated bracket in: ${
|
|
1100
|
-
result +=
|
|
1133
|
+
} else if (/\d/.test(atomPath[i + 1])) {
|
|
1134
|
+
const end = atomPath.indexOf("]", i);
|
|
1135
|
+
if (end === -1) throw new Error(`Unterminated bracket in: ${atomPath}`);
|
|
1136
|
+
result += atomPath.slice(i, end + 1);
|
|
1101
1137
|
i = end + 1;
|
|
1102
1138
|
} else {
|
|
1103
|
-
throw new Error(`Unexpected character after '[' in: ${
|
|
1139
|
+
throw new Error(`Unexpected character after '[' in: ${atomPath}`);
|
|
1104
1140
|
}
|
|
1105
1141
|
} else {
|
|
1106
|
-
throw new Error(`Unexpected character '${
|
|
1142
|
+
throw new Error(`Unexpected character '${atomPath[i]}' in atom path: ${atomPath}`);
|
|
1107
1143
|
}
|
|
1108
1144
|
}
|
|
1109
1145
|
return result;
|
|
@@ -1153,15 +1189,15 @@ function extractKeyFromAtomicPath(atomicPath) {
|
|
|
1153
1189
|
return atomicPath;
|
|
1154
1190
|
}
|
|
1155
1191
|
|
|
1156
|
-
// src/
|
|
1157
|
-
function
|
|
1192
|
+
// src/jsonAtom.ts
|
|
1193
|
+
function validateAtom(atom) {
|
|
1158
1194
|
const errors = [];
|
|
1159
|
-
if (typeof
|
|
1160
|
-
return { valid: false, errors: ["
|
|
1195
|
+
if (typeof atom !== "object" || atom === null) {
|
|
1196
|
+
return { valid: false, errors: ["Atom must be a non-null object"] };
|
|
1161
1197
|
}
|
|
1162
|
-
const d =
|
|
1163
|
-
if (d.format !== "json-
|
|
1164
|
-
errors.push(`Invalid or missing format: expected 'json-
|
|
1198
|
+
const d = atom;
|
|
1199
|
+
if (d.format !== "json-atom") {
|
|
1200
|
+
errors.push(`Invalid or missing format: expected 'json-atom', got '${d.format}'`);
|
|
1165
1201
|
}
|
|
1166
1202
|
if (typeof d.version !== "number") {
|
|
1167
1203
|
errors.push(`Missing or invalid version: expected number, got '${typeof d.version}'`);
|
|
@@ -1199,7 +1235,7 @@ function validateDelta(delta) {
|
|
|
1199
1235
|
}
|
|
1200
1236
|
return { valid: errors.length === 0, errors };
|
|
1201
1237
|
}
|
|
1202
|
-
function
|
|
1238
|
+
function diffAtom(oldObj, newObj, options = {}) {
|
|
1203
1239
|
const changeset = diff(oldObj, newObj, {
|
|
1204
1240
|
...options,
|
|
1205
1241
|
treatTypeChangeAsReplace: true
|
|
@@ -1208,7 +1244,7 @@ function diffDelta(oldObj, newObj, options = {}) {
|
|
|
1208
1244
|
const operations = [];
|
|
1209
1245
|
walkChanges(changeset, "$", oldObj, newObj, operations, options);
|
|
1210
1246
|
return {
|
|
1211
|
-
format: "json-
|
|
1247
|
+
format: "json-atom",
|
|
1212
1248
|
version: 1,
|
|
1213
1249
|
operations
|
|
1214
1250
|
};
|
|
@@ -1388,10 +1424,10 @@ function findElementByFn(oldArr, newArr, fn, changeKey, opType) {
|
|
|
1388
1424
|
}
|
|
1389
1425
|
return void 0;
|
|
1390
1426
|
}
|
|
1391
|
-
function
|
|
1427
|
+
function toAtom(changeset, options = {}) {
|
|
1392
1428
|
let atoms;
|
|
1393
1429
|
if (changeset.length === 0) {
|
|
1394
|
-
return { format: "json-
|
|
1430
|
+
return { format: "json-atom", version: 1, operations: [] };
|
|
1395
1431
|
}
|
|
1396
1432
|
if ("path" in changeset[0]) {
|
|
1397
1433
|
atoms = changeset;
|
|
@@ -1399,7 +1435,7 @@ function toDelta(changeset, options = {}) {
|
|
|
1399
1435
|
atoms = atomizeChangeset(changeset);
|
|
1400
1436
|
}
|
|
1401
1437
|
const rawOps = atoms.map((atom) => {
|
|
1402
|
-
const path =
|
|
1438
|
+
const path = atomicPathToAtomPath(atom.path);
|
|
1403
1439
|
switch (atom.type) {
|
|
1404
1440
|
case "ADD" /* ADD */:
|
|
1405
1441
|
return { op: "add", path, value: atom.value };
|
|
@@ -1423,7 +1459,7 @@ function toDelta(changeset, options = {}) {
|
|
|
1423
1459
|
}
|
|
1424
1460
|
});
|
|
1425
1461
|
const operations = mergeConsecutiveOps(rawOps);
|
|
1426
|
-
return { format: "json-
|
|
1462
|
+
return { format: "json-atom", version: 1, operations };
|
|
1427
1463
|
}
|
|
1428
1464
|
function mergeConsecutiveOps(ops) {
|
|
1429
1465
|
const result = [];
|
|
@@ -1447,13 +1483,13 @@ function mergeConsecutiveOps(ops) {
|
|
|
1447
1483
|
}
|
|
1448
1484
|
return result;
|
|
1449
1485
|
}
|
|
1450
|
-
function
|
|
1451
|
-
const validation =
|
|
1486
|
+
function fromAtom(atom) {
|
|
1487
|
+
const validation = validateAtom(atom);
|
|
1452
1488
|
if (!validation.valid) {
|
|
1453
|
-
throw new Error(`Invalid
|
|
1489
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1454
1490
|
}
|
|
1455
|
-
return
|
|
1456
|
-
const atomicPath =
|
|
1491
|
+
return atom.operations.map((op) => {
|
|
1492
|
+
const atomicPath = atomPathToAtomicPath(op.path);
|
|
1457
1493
|
const key = extractKeyFromAtomicPath(atomicPath);
|
|
1458
1494
|
switch (op.op) {
|
|
1459
1495
|
case "add": {
|
|
@@ -1466,11 +1502,11 @@ function fromDelta(delta) {
|
|
|
1466
1502
|
}
|
|
1467
1503
|
case "replace": {
|
|
1468
1504
|
const valueType = getValueType(op.value);
|
|
1469
|
-
const
|
|
1505
|
+
const atom2 = { type: "UPDATE" /* UPDATE */, key, path: atomicPath, valueType, value: op.value };
|
|
1470
1506
|
if (op.oldValue !== void 0) {
|
|
1471
|
-
|
|
1507
|
+
atom2.oldValue = op.oldValue;
|
|
1472
1508
|
}
|
|
1473
|
-
return
|
|
1509
|
+
return atom2;
|
|
1474
1510
|
}
|
|
1475
1511
|
/* istanbul ignore next -- exhaustive switch */
|
|
1476
1512
|
default:
|
|
@@ -1485,21 +1521,21 @@ function getValueType(value) {
|
|
|
1485
1521
|
const type = typeof value;
|
|
1486
1522
|
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
1487
1523
|
}
|
|
1488
|
-
function
|
|
1489
|
-
const validation =
|
|
1524
|
+
function invertAtom(atom) {
|
|
1525
|
+
const validation = validateAtom(atom);
|
|
1490
1526
|
if (!validation.valid) {
|
|
1491
|
-
throw new Error(`Invalid
|
|
1527
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1492
1528
|
}
|
|
1493
|
-
for (let i = 0; i <
|
|
1494
|
-
const op =
|
|
1529
|
+
for (let i = 0; i < atom.operations.length; i++) {
|
|
1530
|
+
const op = atom.operations[i];
|
|
1495
1531
|
if (op.op === "replace" && !("oldValue" in op)) {
|
|
1496
|
-
throw new Error(`operations[${i}]: replace operation missing oldValue \u2014
|
|
1532
|
+
throw new Error(`operations[${i}]: replace operation missing oldValue \u2014 atom is not reversible`);
|
|
1497
1533
|
}
|
|
1498
1534
|
if (op.op === "remove" && !("oldValue" in op)) {
|
|
1499
|
-
throw new Error(`operations[${i}]: remove operation missing oldValue \u2014
|
|
1535
|
+
throw new Error(`operations[${i}]: remove operation missing oldValue \u2014 atom is not reversible`);
|
|
1500
1536
|
}
|
|
1501
1537
|
}
|
|
1502
|
-
const invertedOps = [...
|
|
1538
|
+
const invertedOps = [...atom.operations].reverse().map((op) => {
|
|
1503
1539
|
const extensions = {};
|
|
1504
1540
|
for (const key of Object.keys(op)) {
|
|
1505
1541
|
if (!["op", "path", "value", "oldValue"].includes(key)) {
|
|
@@ -1518,25 +1554,25 @@ function invertDelta(delta) {
|
|
|
1518
1554
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1519
1555
|
}
|
|
1520
1556
|
});
|
|
1521
|
-
const envelope = { format: "json-
|
|
1522
|
-
for (const key of Object.keys(
|
|
1557
|
+
const envelope = { format: "json-atom", version: atom.version, operations: invertedOps };
|
|
1558
|
+
for (const key of Object.keys(atom)) {
|
|
1523
1559
|
if (!["format", "version", "operations"].includes(key)) {
|
|
1524
|
-
envelope[key] =
|
|
1560
|
+
envelope[key] = atom[key];
|
|
1525
1561
|
}
|
|
1526
1562
|
}
|
|
1527
1563
|
return envelope;
|
|
1528
1564
|
}
|
|
1529
|
-
function
|
|
1530
|
-
const validation =
|
|
1565
|
+
function applyAtom(obj, atom) {
|
|
1566
|
+
const validation = validateAtom(atom);
|
|
1531
1567
|
if (!validation.valid) {
|
|
1532
|
-
throw new Error(`Invalid
|
|
1568
|
+
throw new Error(`Invalid atom: ${validation.errors.join(", ")}`);
|
|
1533
1569
|
}
|
|
1534
1570
|
let result = obj;
|
|
1535
|
-
for (const op of
|
|
1571
|
+
for (const op of atom.operations) {
|
|
1536
1572
|
if (op.path === "$") {
|
|
1537
1573
|
result = applyRootOp(result, op);
|
|
1538
1574
|
} else {
|
|
1539
|
-
const atomicChange =
|
|
1575
|
+
const atomicChange = atomOpToAtomicChange(op);
|
|
1540
1576
|
const miniChangeset = unatomizeChangeset([atomicChange]);
|
|
1541
1577
|
applyChangeset(result, miniChangeset);
|
|
1542
1578
|
}
|
|
@@ -1564,8 +1600,8 @@ function applyRootOp(obj, op) {
|
|
|
1564
1600
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1565
1601
|
}
|
|
1566
1602
|
}
|
|
1567
|
-
function
|
|
1568
|
-
const atomicPath =
|
|
1603
|
+
function atomOpToAtomicChange(op) {
|
|
1604
|
+
const atomicPath = atomPathToAtomicPath(op.path);
|
|
1569
1605
|
const key = extractKeyFromAtomicPath(atomicPath);
|
|
1570
1606
|
switch (op.op) {
|
|
1571
1607
|
case "add":
|
|
@@ -1586,14 +1622,14 @@ function deltaOpToAtomicChange(op) {
|
|
|
1586
1622
|
throw new Error(`Unknown operation: ${op.op}`);
|
|
1587
1623
|
}
|
|
1588
1624
|
}
|
|
1589
|
-
function
|
|
1590
|
-
const inverse =
|
|
1591
|
-
return
|
|
1625
|
+
function revertAtom(obj, atom) {
|
|
1626
|
+
const inverse = invertAtom(atom);
|
|
1627
|
+
return applyAtom(obj, inverse);
|
|
1592
1628
|
}
|
|
1593
1629
|
|
|
1594
|
-
// src/
|
|
1630
|
+
// src/atomHelpers.ts
|
|
1595
1631
|
var OP_SPEC_KEYS = /* @__PURE__ */ new Set(["op", "path", "value", "oldValue"]);
|
|
1596
|
-
var
|
|
1632
|
+
var ATOM_SPEC_KEYS = /* @__PURE__ */ new Set(["format", "version", "operations"]);
|
|
1597
1633
|
function operationSpecDict(op) {
|
|
1598
1634
|
const result = { op: op.op, path: op.path };
|
|
1599
1635
|
if ("value" in op) result.value = op.value;
|
|
@@ -1610,44 +1646,44 @@ function operationExtensions(op) {
|
|
|
1610
1646
|
return result;
|
|
1611
1647
|
}
|
|
1612
1648
|
function leafProperty(op) {
|
|
1613
|
-
const segments =
|
|
1649
|
+
const segments = parseAtomPath(op.path);
|
|
1614
1650
|
if (segments.length === 0) return null;
|
|
1615
1651
|
const last = segments[segments.length - 1];
|
|
1616
1652
|
return last.type === "property" ? last.name : null;
|
|
1617
1653
|
}
|
|
1618
|
-
function
|
|
1654
|
+
function atomSpecDict(atom) {
|
|
1619
1655
|
return {
|
|
1620
|
-
format:
|
|
1621
|
-
version:
|
|
1622
|
-
operations:
|
|
1656
|
+
format: atom.format,
|
|
1657
|
+
version: atom.version,
|
|
1658
|
+
operations: atom.operations.map(operationSpecDict)
|
|
1623
1659
|
};
|
|
1624
1660
|
}
|
|
1625
|
-
function
|
|
1661
|
+
function atomExtensions(atom) {
|
|
1626
1662
|
const result = /* @__PURE__ */ Object.create(null);
|
|
1627
|
-
for (const key of Object.keys(
|
|
1628
|
-
if (!
|
|
1629
|
-
result[key] =
|
|
1663
|
+
for (const key of Object.keys(atom)) {
|
|
1664
|
+
if (!ATOM_SPEC_KEYS.has(key)) {
|
|
1665
|
+
result[key] = atom[key];
|
|
1630
1666
|
}
|
|
1631
1667
|
}
|
|
1632
1668
|
return result;
|
|
1633
1669
|
}
|
|
1634
|
-
function
|
|
1635
|
-
return { ...
|
|
1670
|
+
function atomMap(atom, fn) {
|
|
1671
|
+
return { ...atom, operations: atom.operations.map((op, i) => fn(op, i)) };
|
|
1636
1672
|
}
|
|
1637
|
-
function
|
|
1638
|
-
return
|
|
1673
|
+
function atomStamp(atom, extensions) {
|
|
1674
|
+
return atomMap(atom, (op) => ({ ...op, ...extensions }));
|
|
1639
1675
|
}
|
|
1640
|
-
function
|
|
1676
|
+
function atomGroupBy(atom, keyFn) {
|
|
1641
1677
|
const groups = /* @__PURE__ */ Object.create(null);
|
|
1642
|
-
for (const op of
|
|
1678
|
+
for (const op of atom.operations) {
|
|
1643
1679
|
const k = keyFn(op);
|
|
1644
1680
|
if (!groups[k]) groups[k] = [];
|
|
1645
1681
|
groups[k].push(op);
|
|
1646
1682
|
}
|
|
1647
1683
|
const envelope = /* @__PURE__ */ Object.create(null);
|
|
1648
|
-
for (const key of Object.keys(
|
|
1684
|
+
for (const key of Object.keys(atom)) {
|
|
1649
1685
|
if (key !== "operations") {
|
|
1650
|
-
envelope[key] =
|
|
1686
|
+
envelope[key] = atom[key];
|
|
1651
1687
|
}
|
|
1652
1688
|
}
|
|
1653
1689
|
const result = /* @__PURE__ */ Object.create(null);
|
|
@@ -1659,18 +1695,18 @@ function deltaGroupBy(delta, keyFn) {
|
|
|
1659
1695
|
function deepClone(obj) {
|
|
1660
1696
|
return JSON.parse(JSON.stringify(obj));
|
|
1661
1697
|
}
|
|
1662
|
-
function
|
|
1698
|
+
function squashAtoms(source, atoms, options = {}) {
|
|
1663
1699
|
const { target, verifyTarget = true, ...diffOptions } = options;
|
|
1664
1700
|
let final;
|
|
1665
|
-
if (target !== void 0 &&
|
|
1701
|
+
if (target !== void 0 && atoms.length > 0 && verifyTarget) {
|
|
1666
1702
|
let computed = deepClone(source);
|
|
1667
|
-
for (const d of
|
|
1668
|
-
computed =
|
|
1703
|
+
for (const d of atoms) {
|
|
1704
|
+
computed = applyAtom(computed, d);
|
|
1669
1705
|
}
|
|
1670
|
-
const verification =
|
|
1706
|
+
const verification = diffAtom(computed, target, diffOptions);
|
|
1671
1707
|
if (verification.operations.length > 0) {
|
|
1672
1708
|
throw new Error(
|
|
1673
|
-
"
|
|
1709
|
+
"squashAtoms: provided target does not match sequential application of atoms to source"
|
|
1674
1710
|
);
|
|
1675
1711
|
}
|
|
1676
1712
|
final = target;
|
|
@@ -1678,14 +1714,14 @@ function squashDeltas(source, deltas, options = {}) {
|
|
|
1678
1714
|
final = target;
|
|
1679
1715
|
} else {
|
|
1680
1716
|
final = deepClone(source);
|
|
1681
|
-
for (const d of
|
|
1682
|
-
final =
|
|
1717
|
+
for (const d of atoms) {
|
|
1718
|
+
final = applyAtom(final, d);
|
|
1683
1719
|
}
|
|
1684
1720
|
}
|
|
1685
|
-
const result =
|
|
1686
|
-
for (const d of
|
|
1721
|
+
const result = diffAtom(source, final, diffOptions);
|
|
1722
|
+
for (const d of atoms) {
|
|
1687
1723
|
for (const key of Object.keys(d)) {
|
|
1688
|
-
if (!
|
|
1724
|
+
if (!ATOM_SPEC_KEYS.has(key)) {
|
|
1689
1725
|
Object.defineProperty(result, key, {
|
|
1690
1726
|
value: d[key],
|
|
1691
1727
|
writable: true,
|
|
@@ -1701,38 +1737,38 @@ function squashDeltas(source, deltas, options = {}) {
|
|
|
1701
1737
|
0 && (module.exports = {
|
|
1702
1738
|
CompareOperation,
|
|
1703
1739
|
Operation,
|
|
1740
|
+
applyAtom,
|
|
1704
1741
|
applyChangelist,
|
|
1705
1742
|
applyChangeset,
|
|
1706
|
-
|
|
1743
|
+
atomExtensions,
|
|
1744
|
+
atomGroupBy,
|
|
1745
|
+
atomMap,
|
|
1746
|
+
atomSpecDict,
|
|
1747
|
+
atomStamp,
|
|
1707
1748
|
atomizeChangeset,
|
|
1708
|
-
|
|
1749
|
+
buildAtomPath,
|
|
1709
1750
|
compare,
|
|
1710
1751
|
comparisonToDict,
|
|
1711
1752
|
comparisonToFlatList,
|
|
1712
1753
|
createContainer,
|
|
1713
1754
|
createValue,
|
|
1714
|
-
deltaExtensions,
|
|
1715
|
-
deltaGroupBy,
|
|
1716
|
-
deltaMap,
|
|
1717
|
-
deltaSpecDict,
|
|
1718
|
-
deltaStamp,
|
|
1719
1755
|
diff,
|
|
1720
|
-
|
|
1756
|
+
diffAtom,
|
|
1721
1757
|
enrich,
|
|
1722
1758
|
formatFilterLiteral,
|
|
1723
|
-
|
|
1759
|
+
fromAtom,
|
|
1724
1760
|
getTypeOfObj,
|
|
1725
|
-
|
|
1761
|
+
invertAtom,
|
|
1726
1762
|
leafProperty,
|
|
1727
1763
|
operationExtensions,
|
|
1728
1764
|
operationSpecDict,
|
|
1729
|
-
|
|
1765
|
+
parseAtomPath,
|
|
1730
1766
|
parseFilterLiteral,
|
|
1767
|
+
revertAtom,
|
|
1731
1768
|
revertChangeset,
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
toDelta,
|
|
1769
|
+
squashAtoms,
|
|
1770
|
+
toAtom,
|
|
1735
1771
|
unatomizeChangeset,
|
|
1736
|
-
|
|
1772
|
+
validateAtom
|
|
1737
1773
|
});
|
|
1738
1774
|
//# sourceMappingURL=index.cjs.map
|