@fgv/ts-json 5.0.0-2 → 5.0.0-21

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.
Files changed (94) hide show
  1. package/.vscode/launch.json +16 -0
  2. package/.vscode/settings.json +32 -0
  3. package/config/api-extractor.json +343 -0
  4. package/config/rig.json +16 -0
  5. package/dist/ts-json.d.ts +719 -28
  6. package/dist/tsdoc-metadata.json +1 -1
  7. package/lib/index.d.ts +2 -0
  8. package/lib/index.js +25 -0
  9. package/lib/packlets/diff/detailedDiff.d.ts +375 -0
  10. package/lib/packlets/diff/detailedDiff.js +342 -0
  11. package/lib/packlets/diff/index.d.ts +3 -0
  12. package/lib/packlets/diff/index.js +40 -0
  13. package/lib/packlets/diff/threeWayDiff.d.ts +263 -0
  14. package/lib/packlets/diff/threeWayDiff.js +261 -0
  15. package/lib/packlets/diff/utils.d.ts +6 -0
  16. package/lib/packlets/diff/utils.js +62 -0
  17. package/lib/packlets/editor/common.d.ts +6 -0
  18. package/lib/packlets/editor/jsonEditor.d.ts +57 -28
  19. package/lib/packlets/editor/jsonEditor.js +101 -32
  20. package/lib/test/legacy/jsonConverter.conditional.test.d.ts +2 -0
  21. package/lib/test/legacy/jsonEditor/rules/conditional.test.d.ts +2 -0
  22. package/lib/test/legacy/jsonEditor/rules/multivalue.test.d.ts +2 -0
  23. package/lib/test/legacy/jsonEditor/rules/references.test.d.ts +2 -0
  24. package/lib/test/legacy/jsonEditor/rules/templates.test.d.ts +2 -0
  25. package/lib/test/unit/contextHelpers.test.d.ts +2 -0
  26. package/lib/test/unit/converters.test.d.ts +2 -0
  27. package/lib/test/unit/diff/jsonDiff.test.d.ts +2 -0
  28. package/lib/test/unit/jsonConverter.test.d.ts +2 -0
  29. package/lib/test/unit/jsonEditor/jsonEditor.test.d.ts +2 -0
  30. package/lib/test/unit/jsonEditor/templateContext.test.d.ts +2 -0
  31. package/lib/test/unit/jsonReferenceMap.test.d.ts +2 -0
  32. package/package.json +11 -11
  33. package/src/index.ts +29 -0
  34. package/src/packlets/context/compositeJsonMap.ts +120 -0
  35. package/src/packlets/context/contextHelpers.ts +221 -0
  36. package/src/packlets/context/index.ts +33 -0
  37. package/src/packlets/context/jsonContext.ts +133 -0
  38. package/src/packlets/converters/converters.ts +117 -0
  39. package/src/packlets/converters/index.ts +26 -0
  40. package/src/packlets/converters/jsonConverter.ts +476 -0
  41. package/src/packlets/diff/detailedDiff.ts +585 -0
  42. package/src/packlets/diff/index.ts +24 -0
  43. package/src/packlets/diff/threeWayDiff.ts +420 -0
  44. package/src/packlets/diff/utils.ts +66 -0
  45. package/src/packlets/editor/common.ts +125 -0
  46. package/src/packlets/editor/index.ts +36 -0
  47. package/src/packlets/editor/jsonEditor.ts +523 -0
  48. package/src/packlets/editor/jsonEditorRule.ts +117 -0
  49. package/src/packlets/editor/jsonEditorState.ts +225 -0
  50. package/src/packlets/editor/jsonReferenceMap.ts +516 -0
  51. package/src/packlets/editor/rules/conditional.ts +222 -0
  52. package/src/packlets/editor/rules/index.ts +25 -0
  53. package/src/packlets/editor/rules/multivalue.ts +206 -0
  54. package/src/packlets/editor/rules/references.ts +177 -0
  55. package/src/packlets/editor/rules/templates.ts +159 -0
  56. package/CHANGELOG.md +0 -115
  57. package/lib/index.d.ts.map +0 -1
  58. package/lib/index.js.map +0 -1
  59. package/lib/packlets/context/compositeJsonMap.d.ts.map +0 -1
  60. package/lib/packlets/context/compositeJsonMap.js.map +0 -1
  61. package/lib/packlets/context/contextHelpers.d.ts.map +0 -1
  62. package/lib/packlets/context/contextHelpers.js.map +0 -1
  63. package/lib/packlets/context/index.d.ts.map +0 -1
  64. package/lib/packlets/context/index.js.map +0 -1
  65. package/lib/packlets/context/jsonContext.d.ts.map +0 -1
  66. package/lib/packlets/context/jsonContext.js.map +0 -1
  67. package/lib/packlets/converters/converters.d.ts.map +0 -1
  68. package/lib/packlets/converters/converters.js.map +0 -1
  69. package/lib/packlets/converters/index.d.ts.map +0 -1
  70. package/lib/packlets/converters/index.js.map +0 -1
  71. package/lib/packlets/converters/jsonConverter.d.ts.map +0 -1
  72. package/lib/packlets/converters/jsonConverter.js.map +0 -1
  73. package/lib/packlets/editor/common.d.ts.map +0 -1
  74. package/lib/packlets/editor/common.js.map +0 -1
  75. package/lib/packlets/editor/index.d.ts.map +0 -1
  76. package/lib/packlets/editor/index.js.map +0 -1
  77. package/lib/packlets/editor/jsonEditor.d.ts.map +0 -1
  78. package/lib/packlets/editor/jsonEditor.js.map +0 -1
  79. package/lib/packlets/editor/jsonEditorRule.d.ts.map +0 -1
  80. package/lib/packlets/editor/jsonEditorRule.js.map +0 -1
  81. package/lib/packlets/editor/jsonEditorState.d.ts.map +0 -1
  82. package/lib/packlets/editor/jsonEditorState.js.map +0 -1
  83. package/lib/packlets/editor/jsonReferenceMap.d.ts.map +0 -1
  84. package/lib/packlets/editor/jsonReferenceMap.js.map +0 -1
  85. package/lib/packlets/editor/rules/conditional.d.ts.map +0 -1
  86. package/lib/packlets/editor/rules/conditional.js.map +0 -1
  87. package/lib/packlets/editor/rules/index.d.ts.map +0 -1
  88. package/lib/packlets/editor/rules/index.js.map +0 -1
  89. package/lib/packlets/editor/rules/multivalue.d.ts.map +0 -1
  90. package/lib/packlets/editor/rules/multivalue.js.map +0 -1
  91. package/lib/packlets/editor/rules/references.d.ts.map +0 -1
  92. package/lib/packlets/editor/rules/references.js.map +0 -1
  93. package/lib/packlets/editor/rules/templates.d.ts.map +0 -1
  94. package/lib/packlets/editor/rules/templates.js.map +0 -1
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2025 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.jsonDiff = jsonDiff;
25
+ exports.jsonEquals = jsonEquals;
26
+ const ts_json_base_1 = require("@fgv/ts-json-base");
27
+ const ts_utils_1 = require("@fgv/ts-utils");
28
+ const utils_1 = require("./utils");
29
+ /**
30
+ * Internal recursive diff function that builds the change list.
31
+ */
32
+ function diffRecursive(obj1, obj2, path, options, changes) {
33
+ const pathPrefix = path ? `${path}${options.pathSeparator}` : '';
34
+ // Handle primitive values
35
+ if ((0, ts_json_base_1.isJsonPrimitive)(obj1) || (0, ts_json_base_1.isJsonPrimitive)(obj2)) {
36
+ if (!(0, utils_1.deepEquals)(obj1, obj2)) {
37
+ changes.push({
38
+ path,
39
+ type: 'modified',
40
+ oldValue: obj1,
41
+ newValue: obj2
42
+ });
43
+ }
44
+ else if (options.includeUnchanged) {
45
+ changes.push({
46
+ path,
47
+ type: 'unchanged',
48
+ oldValue: obj1,
49
+ newValue: obj2
50
+ });
51
+ }
52
+ return;
53
+ }
54
+ // Handle arrays
55
+ if ((0, ts_json_base_1.isJsonArray)(obj1) && (0, ts_json_base_1.isJsonArray)(obj2)) {
56
+ if (options.arrayOrderMatters) {
57
+ // Ordered array comparison
58
+ const maxLength = Math.max(obj1.length, obj2.length);
59
+ for (let i = 0; i < maxLength; i++) {
60
+ const itemPath = `${pathPrefix}${i}`;
61
+ if (i >= obj1.length) {
62
+ changes.push({
63
+ path: itemPath,
64
+ type: 'added',
65
+ newValue: obj2[i]
66
+ });
67
+ }
68
+ else if (i >= obj2.length) {
69
+ changes.push({
70
+ path: itemPath,
71
+ type: 'removed',
72
+ oldValue: obj1[i]
73
+ });
74
+ }
75
+ else {
76
+ diffRecursive(obj1[i], obj2[i], itemPath, options, changes);
77
+ }
78
+ }
79
+ }
80
+ else {
81
+ // Unordered array comparison - simplified approach
82
+ // This is a basic implementation; a more sophisticated approach would use
83
+ // algorithms like longest common subsequence for better matching
84
+ const processed = new Set();
85
+ // Find matching elements
86
+ for (let i = 0; i < obj1.length; i++) {
87
+ let found = false;
88
+ for (let j = 0; j < obj2.length; j++) {
89
+ if (!processed.has(j) && (0, utils_1.deepEquals)(obj1[i], obj2[j])) {
90
+ processed.add(j);
91
+ found = true;
92
+ if (options.includeUnchanged) {
93
+ changes.push({
94
+ path: `${pathPrefix}${i}`,
95
+ type: 'unchanged',
96
+ oldValue: obj1[i],
97
+ newValue: obj2[j]
98
+ });
99
+ }
100
+ break;
101
+ }
102
+ }
103
+ if (!found) {
104
+ changes.push({
105
+ path: `${pathPrefix}${i}`,
106
+ type: 'removed',
107
+ oldValue: obj1[i]
108
+ });
109
+ }
110
+ }
111
+ // Find added elements
112
+ for (let j = 0; j < obj2.length; j++) {
113
+ if (!processed.has(j)) {
114
+ changes.push({
115
+ path: `${pathPrefix}${j}`,
116
+ type: 'added',
117
+ newValue: obj2[j]
118
+ });
119
+ }
120
+ }
121
+ }
122
+ return;
123
+ }
124
+ // Handle one array, one non-array
125
+ if ((0, ts_json_base_1.isJsonArray)(obj1) || (0, ts_json_base_1.isJsonArray)(obj2)) {
126
+ changes.push({
127
+ path,
128
+ type: 'modified',
129
+ oldValue: obj1,
130
+ newValue: obj2
131
+ });
132
+ return;
133
+ }
134
+ // Handle objects
135
+ if ((0, ts_json_base_1.isJsonObject)(obj1) && (0, ts_json_base_1.isJsonObject)(obj2)) {
136
+ const keys1 = new Set(Object.keys(obj1));
137
+ const keys2 = new Set(Object.keys(obj2));
138
+ const allKeys = new Set([...keys1, ...keys2]);
139
+ for (const key of allKeys) {
140
+ const keyPath = path ? `${path}${options.pathSeparator}${key}` : key;
141
+ if (!keys1.has(key)) {
142
+ changes.push({
143
+ path: keyPath,
144
+ type: 'added',
145
+ newValue: obj2[key]
146
+ });
147
+ }
148
+ else if (!keys2.has(key)) {
149
+ changes.push({
150
+ path: keyPath,
151
+ type: 'removed',
152
+ oldValue: obj1[key]
153
+ });
154
+ }
155
+ else {
156
+ diffRecursive(obj1[key], obj2[key], keyPath, options, changes);
157
+ }
158
+ }
159
+ return;
160
+ }
161
+ /* c8 ignore next 9 - defensive code path that should never be reached with valid JsonValue types */
162
+ // Handle mixed object types
163
+ changes.push({
164
+ path,
165
+ type: 'modified',
166
+ oldValue: obj1,
167
+ newValue: obj2
168
+ });
169
+ }
170
+ /**
171
+ * Performs a deep diff comparison between two JSON values.
172
+ *
173
+ * This function provides detailed change tracking by analyzing every difference
174
+ * between two JSON structures. It returns a list of specific changes with paths,
175
+ * making it ideal for debugging, logging, change analysis, and understanding
176
+ * exactly what has changed between two data states.
177
+ *
178
+ * **Key Features:**
179
+ * - Deep recursive comparison of nested objects and arrays
180
+ * - Precise path tracking using dot notation (e.g., "user.profile.name")
181
+ * - Support for all JSON value types: objects, arrays, primitives, null
182
+ * - Configurable array comparison (ordered vs unordered)
183
+ * - Optional inclusion of unchanged values for complete comparisons
184
+ *
185
+ * **Use Cases:**
186
+ * - Debugging data changes in applications
187
+ * - Generating change logs or audit trails
188
+ * - Validating API responses against expected data
189
+ * - Creating detailed diff reports for data synchronization
190
+ *
191
+ * @param obj1 - The first JSON value to compare (often the "before" state)
192
+ * @param obj2 - The second JSON value to compare (often the "after" state)
193
+ * @param options - Optional configuration for customizing diff behavior
194
+ * @returns A Result containing the diff result with all detected changes
195
+ *
196
+ * @example Basic usage with objects
197
+ * ```typescript
198
+ * const before = { name: "John", age: 30, city: "NYC" };
199
+ * const after = { name: "Jane", age: 30, country: "USA" };
200
+ *
201
+ * const result = jsonDiff(before, after);
202
+ * if (result.success) {
203
+ * result.value.changes.forEach(change => {
204
+ * console.log(`${change.path}: ${change.type}`);
205
+ * // Output:
206
+ * // name: modified
207
+ * // city: removed
208
+ * // country: added
209
+ * });
210
+ * }
211
+ * ```
212
+ *
213
+ * @example With arrays and nested structures
214
+ * ```typescript
215
+ * const user1 = {
216
+ * profile: { name: "John", hobbies: ["reading"] },
217
+ * settings: { theme: "dark" }
218
+ * };
219
+ * const user2 = {
220
+ * profile: { name: "John", hobbies: ["reading", "gaming"] },
221
+ * settings: { theme: "light", notifications: true }
222
+ * };
223
+ *
224
+ * const result = jsonDiff(user1, user2);
225
+ * if (result.success) {
226
+ * console.log(result.value.changes);
227
+ * // [
228
+ * // { path: "profile.hobbies.1", type: "added", newValue: "gaming" },
229
+ * // { path: "settings.theme", type: "modified", oldValue: "dark", newValue: "light" },
230
+ * // { path: "settings.notifications", type: "added", newValue: true }
231
+ * // ]
232
+ * }
233
+ * ```
234
+ *
235
+ * @example Using options for custom behavior
236
+ * ```typescript
237
+ * const options: IJsonDiffOptions = {
238
+ * includeUnchanged: true, // Include unchanged properties
239
+ * pathSeparator: '/', // Use '/' instead of '.' in paths
240
+ * arrayOrderMatters: false // Treat arrays as unordered sets
241
+ * };
242
+ *
243
+ * const result = jsonDiff(obj1, obj2, options);
244
+ * ```
245
+ *
246
+ * @see {@link IDiffResult} for the structure of returned results
247
+ * @see {@link IDiffChange} for details about individual changes
248
+ * @see {@link IJsonDiffOptions} for available configuration options
249
+ * @see {@link jsonThreeWayDiff} for an alternative API focused on actionable results
250
+ * @see {@link jsonEquals} for simple equality checking without change details
251
+ *
252
+ * @public
253
+ */
254
+ function jsonDiff(obj1, obj2, options = {}) {
255
+ const opts = Object.assign({ includeUnchanged: false, pathSeparator: '.', arrayOrderMatters: true }, options);
256
+ const changes = [];
257
+ diffRecursive(obj1, obj2, '', opts, changes);
258
+ const result = {
259
+ changes,
260
+ identical: changes.length === 0 || changes.every((c) => c.type === 'unchanged')
261
+ };
262
+ return (0, ts_utils_1.succeed)(result);
263
+ }
264
+ /**
265
+ * A simpler helper function that returns true if two JSON values are deeply equal.
266
+ *
267
+ * This function provides a fast boolean check for JSON equality without the overhead
268
+ * of tracking individual changes. It performs the same deep comparison logic as
269
+ * {@link Diff.jsonDiff} but returns only a true/false result, making it ideal for
270
+ * conditional logic and validation scenarios.
271
+ *
272
+ * **Key Features:**
273
+ * - Deep recursive comparison of all nested structures
274
+ * - Handles all JSON types: objects, arrays, primitives, null
275
+ * - Object property order independence
276
+ * - Array order significance (index positions matter)
277
+ * - Performance optimized for equality checking
278
+ *
279
+ * **Use Cases:**
280
+ * - Conditional logic based on data equality
281
+ * - Input validation and testing assertions
282
+ * - Caching and memoization keys
283
+ * - Quick checks before expensive diff operations
284
+ *
285
+ * @param obj1 - The first JSON value to compare
286
+ * @param obj2 - The second JSON value to compare
287
+ * @returns True if the values are deeply equal, false otherwise
288
+ *
289
+ * @example Basic equality checking
290
+ * ```typescript
291
+ * // Objects with same structure and values
292
+ * const user1 = { name: "John", hobbies: ["reading", "gaming"] };
293
+ * const user2 = { name: "John", hobbies: ["reading", "gaming"] };
294
+ * console.log(jsonEquals(user1, user2)); // true
295
+ *
296
+ * // Different property order (still equal)
297
+ * const obj1 = { a: 1, b: 2 };
298
+ * const obj2 = { b: 2, a: 1 };
299
+ * console.log(jsonEquals(obj1, obj2)); // true
300
+ *
301
+ * // Different values
302
+ * const before = { status: "pending" };
303
+ * const after = { status: "completed" };
304
+ * console.log(jsonEquals(before, after)); // false
305
+ * ```
306
+ *
307
+ * @example With nested structures
308
+ * ```typescript
309
+ * const config1 = {
310
+ * database: { host: "localhost", port: 5432 },
311
+ * features: ["auth", "cache"]
312
+ * };
313
+ * const config2 = {
314
+ * database: { host: "localhost", port: 5432 },
315
+ * features: ["auth", "cache"]
316
+ * };
317
+ *
318
+ * if (jsonEquals(config1, config2)) {
319
+ * console.log("Configurations are identical");
320
+ * }
321
+ * ```
322
+ *
323
+ * @example Array order sensitivity
324
+ * ```typescript
325
+ * const list1 = [1, 2, 3];
326
+ * const list2 = [3, 2, 1];
327
+ * console.log(jsonEquals(list1, list2)); // false - order matters
328
+ *
329
+ * const list3 = [1, 2, 3];
330
+ * const list4 = [1, 2, 3];
331
+ * console.log(jsonEquals(list3, list4)); // true - same order
332
+ * ```
333
+ *
334
+ * @see {@link Diff.jsonDiff} for detailed change analysis when equality fails
335
+ * @see {@link jsonThreeWayDiff} for actionable difference results
336
+ *
337
+ * @public
338
+ */
339
+ function jsonEquals(obj1, obj2) {
340
+ return (0, utils_1.deepEquals)(obj1, obj2);
341
+ }
342
+ //# sourceMappingURL=detailedDiff.js.map
@@ -0,0 +1,3 @@
1
+ export * from './detailedDiff';
2
+ export * from './threeWayDiff';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2025 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
35
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ __exportStar(require("./detailedDiff"), exports);
39
+ __exportStar(require("./threeWayDiff"), exports);
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,263 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { JsonValue } from '@fgv/ts-json-base';
3
+ /**
4
+ * Metadata about the differences found in a three-way diff.
5
+ *
6
+ * Provides summary statistics about the types and quantities of changes
7
+ * detected between two JSON values, making it easy to understand the
8
+ * overall scope of differences at a glance.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const metadata: IThreeWayDiffMetadata = {
13
+ * removed: 2, // 2 properties only in first object
14
+ * added: 1, // 1 property only in second object
15
+ * modified: 3, // 3 properties changed between objects
16
+ * unchanged: 5 // 5 properties identical in both objects
17
+ * };
18
+ *
19
+ * console.log(`Total changes: ${metadata.added + metadata.removed + metadata.modified}`);
20
+ * console.log(`Stability: ${metadata.unchanged / (metadata.unchanged + metadata.modified) * 100}%`);
21
+ * ```
22
+ *
23
+ * @public
24
+ */
25
+ export interface IThreeWayDiffMetadata {
26
+ /**
27
+ * Number of properties that exist only in the first object.
28
+ *
29
+ * These represent data that was removed when transitioning from
30
+ * the first object to the second object.
31
+ */
32
+ removed: number;
33
+ /**
34
+ * Number of properties that exist only in the second object.
35
+ *
36
+ * These represent new data that was added when transitioning from
37
+ * the first object to the second object.
38
+ */
39
+ added: number;
40
+ /**
41
+ * Number of properties that exist in both objects but have different values.
42
+ *
43
+ * These represent data that was modified during the transition between objects.
44
+ * For arrays, this counts entire array replacements as single modifications.
45
+ */
46
+ modified: number;
47
+ /**
48
+ * Number of properties that exist in both objects with identical values.
49
+ *
50
+ * These represent stable data that remained consistent between the two objects.
51
+ */
52
+ unchanged: number;
53
+ }
54
+ /**
55
+ * Result of a three-way JSON diff operation.
56
+ *
57
+ * This interface provides an actionable representation of differences between
58
+ * two JSON values by separating them into three distinct objects. This approach
59
+ * makes it easy to apply changes, display side-by-side comparisons, perform
60
+ * merges, or programmatically work with the differences.
61
+ *
62
+ * **Key Benefits:**
63
+ * - **Actionable Results**: Objects can be directly used for merging or applying changes
64
+ * - **UI-Friendly**: Perfect for side-by-side diff displays with clear visual separation
65
+ * - **Merge-Ready**: Simplified three-way merge operations
66
+ * - **Structured Data**: Maintains original JSON structure rather than flattened paths
67
+ *
68
+ * @example Basic usage
69
+ * ```typescript
70
+ * const result: IThreeWayDiff = {
71
+ * onlyInA: { name: "John", city: "NYC" }, // Original or removed data
72
+ * unchanged: { age: 30 }, // Stable data
73
+ * onlyInB: { name: "Jane", country: "USA" }, // New or modified data
74
+ * metadata: { added: 1, removed: 1, modified: 1, unchanged: 1 },
75
+ * identical: false
76
+ * };
77
+ *
78
+ * // Apply changes: merge unchanged + onlyInB
79
+ * const updated = { ...result.unchanged, ...result.onlyInB };
80
+ * // Result: { age: 30, name: "Jane", country: "USA" }
81
+ *
82
+ * // Revert changes: merge unchanged + onlyInA
83
+ * const reverted = { ...result.unchanged, ...result.onlyInA };
84
+ * // Result: { age: 30, name: "John", city: "NYC" }
85
+ * ```
86
+ *
87
+ * @see {@link IThreeWayDiffMetadata} for metadata structure details
88
+ * @see {@link jsonThreeWayDiff} for the function that produces this result
89
+ * @see {@link Diff.jsonDiff} for an alternative detailed change-list approach
90
+ *
91
+ * @public
92
+ */
93
+ export interface IThreeWayDiff {
94
+ /**
95
+ * Contains properties that exist only in the first object, plus the first object's
96
+ * version of any properties that exist in both but have different values.
97
+ *
98
+ * This object represents the "old" or "source" state and can be used for:
99
+ * - Reverting changes by merging with `unchanged`
100
+ * - Displaying what was removed or changed from the original
101
+ * - Understanding the baseline state before modifications
102
+ *
103
+ * Will be `null` if no properties are unique to the first object.
104
+ */
105
+ onlyInA: JsonValue;
106
+ /**
107
+ * Contains properties that exist in both objects with identical values.
108
+ *
109
+ * This object represents the stable, consistent data between both inputs
110
+ * and can be used for:
111
+ * - The foundation for merging operations
112
+ * - Identifying what remained constant during changes
113
+ * - Building complete objects by combining with other parts
114
+ *
115
+ * Will be `null` if no properties are shared between the objects.
116
+ */
117
+ unchanged: JsonValue;
118
+ /**
119
+ * Contains properties that exist only in the second object, plus the second object's
120
+ * version of any properties that exist in both but have different values.
121
+ *
122
+ * This object represents the "new" or "target" state and can be used for:
123
+ * - Applying changes by merging with `unchanged`
124
+ * - Displaying what was added or changed in the update
125
+ * - Understanding the desired end state after modifications
126
+ *
127
+ * Will be `null` if no properties are unique to the second object.
128
+ */
129
+ onlyInB: JsonValue;
130
+ /**
131
+ * Summary metadata about the differences found.
132
+ *
133
+ * Provides counts of added, removed, modified, and unchanged properties
134
+ * for quick assessment of the scope and nature of changes.
135
+ */
136
+ metadata: IThreeWayDiffMetadata;
137
+ /**
138
+ * True if the objects are identical, false otherwise.
139
+ *
140
+ * When `true`, both `onlyInA` and `onlyInB` will be `null`, and `unchanged`
141
+ * will contain the complete shared structure. The metadata will show zero
142
+ * added, removed, and modified properties.
143
+ */
144
+ identical: boolean;
145
+ }
146
+ /**
147
+ * Performs a three-way diff comparison between two JSON values, returning separate
148
+ * objects containing the differences and similarities.
149
+ *
150
+ * This function provides an alternative to {@link Diff.jsonDiff} that focuses on actionable
151
+ * results rather than detailed change analysis. Instead of a list of individual changes,
152
+ * it returns three objects that can be directly used for merging, UI display, or
153
+ * programmatic manipulation.
154
+ *
155
+ * **Key Features:**
156
+ * - **Actionable Results**: Returns objects ready for immediate use in merging operations
157
+ * - **Simplified Array Handling**: Arrays are treated as atomic values for cleaner results
158
+ * - **Structural Preservation**: Maintains original JSON structure rather than flattened paths
159
+ * - **UI-Optimized**: Perfect for side-by-side diff displays and change visualization
160
+ * - **Merge-Friendly**: Designed specifically for three-way merge scenarios
161
+ *
162
+ * **Array Handling:**
163
+ * Unlike {@link Diff.jsonDiff}, this function treats arrays as complete units. If arrays differ,
164
+ * the entire array appears in the appropriate result object rather than computing
165
+ * element-by-element deltas. This approach is simpler and more predictable for most
166
+ * use cases involving data updates and synchronization.
167
+ *
168
+ * **Use Cases:**
169
+ * - Applying configuration updates while preserving unchanged settings
170
+ * - Creating side-by-side diff displays in user interfaces
171
+ * - Building three-way merge tools for data synchronization
172
+ * - Implementing undo/redo functionality with granular control
173
+ * - Generating patch objects for API updates
174
+ *
175
+ * @param obj1 - The first JSON value to compare (often the "before" or "source" state)
176
+ * @param obj2 - The second JSON value to compare (often the "after" or "target" state)
177
+ * @returns A Result containing the three-way diff with separate objects and metadata
178
+ *
179
+ * @example Basic usage for applying changes
180
+ * ```typescript
181
+ * const original = { name: "John", age: 30, city: "NYC", active: true };
182
+ * const updated = { name: "Jane", age: 30, country: "USA", active: true };
183
+ *
184
+ * const result = jsonThreeWayDiff(original, updated);
185
+ * if (result.success) {
186
+ * const { onlyInA, unchanged, onlyInB } = result.value;
187
+ *
188
+ * // Apply changes: merge unchanged + onlyInB
189
+ * const applied = { ...unchanged, ...onlyInB };
190
+ * console.log(applied); // { age: 30, active: true, name: "Jane", country: "USA" }
191
+ *
192
+ * // Revert changes: merge unchanged + onlyInA
193
+ * const reverted = { ...unchanged, ...onlyInA };
194
+ * console.log(reverted); // { age: 30, active: true, name: "John", city: "NYC" }
195
+ * }
196
+ * ```
197
+ *
198
+ * @example UI-friendly diff display
199
+ * ```typescript
200
+ * const result = jsonThreeWayDiff(userBefore, userAfter);
201
+ * if (result.success) {
202
+ * const { onlyInA, unchanged, onlyInB, metadata } = result.value;
203
+ *
204
+ * // Display summary
205
+ * console.log(`Changes: ${metadata.added} added, ${metadata.removed} removed, ${metadata.modified} modified`);
206
+ *
207
+ * // Show removed/old values in red
208
+ * if (onlyInA) displayInColor(onlyInA, 'red');
209
+ *
210
+ * // Show unchanged values in gray
211
+ * if (unchanged) displayInColor(unchanged, 'gray');
212
+ *
213
+ * // Show added/new values in green
214
+ * if (onlyInB) displayInColor(onlyInB, 'green');
215
+ * }
216
+ * ```
217
+ *
218
+ * @example Nested objects and array handling
219
+ * ```typescript
220
+ * const config1 = {
221
+ * database: { host: "localhost", port: 5432 },
222
+ * features: ["auth", "logging"],
223
+ * version: "1.0"
224
+ * };
225
+ * const config2 = {
226
+ * database: { host: "production.db", port: 5432 },
227
+ * features: ["auth", "logging", "metrics"], // Array treated as complete unit
228
+ * version: "1.1"
229
+ * };
230
+ *
231
+ * const result = jsonThreeWayDiff(config1, config2);
232
+ * if (result.success) {
233
+ * // result.value.onlyInA = { database: { host: "localhost" }, features: ["auth", "logging"], version: "1.0" }
234
+ * // result.value.unchanged = { database: { port: 5432 } }
235
+ * // result.value.onlyInB = { database: { host: "production.db" }, features: ["auth", "logging", "metrics"], version: "1.1" }
236
+ * }
237
+ * ```
238
+ *
239
+ * @example Conditional updates based on changes
240
+ * ```typescript
241
+ * const result = jsonThreeWayDiff(currentState, newState);
242
+ * if (result.success && !result.value.identical) {
243
+ * const { metadata } = result.value;
244
+ *
245
+ * if (metadata.modified > 0) {
246
+ * console.log("Critical settings changed - requires restart");
247
+ * } else if (metadata.added > 0) {
248
+ * console.log("New features enabled");
249
+ * } else if (metadata.removed > 0) {
250
+ * console.log("Features disabled");
251
+ * }
252
+ * }
253
+ * ```
254
+ *
255
+ * @see {@link IThreeWayDiff} for the structure of returned results
256
+ * @see {@link IThreeWayDiffMetadata} for metadata details
257
+ * @see {@link Diff.jsonDiff} for detailed change-by-change analysis
258
+ * @see {@link jsonEquals} for simple equality checking
259
+ *
260
+ * @public
261
+ */
262
+ export declare function jsonThreeWayDiff(obj1: JsonValue, obj2: JsonValue): Result<IThreeWayDiff>;
263
+ //# sourceMappingURL=threeWayDiff.d.ts.map