@fgv/ts-json 5.0.0-5 → 5.0.0-7

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/dist/ts-json.d.ts CHANGED
@@ -230,6 +230,86 @@ export { Converters }
230
230
  */
231
231
  export declare function defaultExtendVars(base: TemplateVars | undefined, values: VariableValue[]): Result<TemplateVars | undefined>;
232
232
 
233
+ declare namespace Diff {
234
+ export {
235
+ jsonDiff,
236
+ jsonEquals,
237
+ DiffChangeType,
238
+ IDiffChange,
239
+ IDiffResult,
240
+ IJsonDiffOptions,
241
+ jsonThreeWayDiff,
242
+ IThreeWayDiffMetadata,
243
+ IThreeWayDiff
244
+ }
245
+ }
246
+ export { Diff }
247
+
248
+ /**
249
+ * JSON Diff Utilities for TypeScript
250
+ *
251
+ * This module provides comprehensive tools for comparing JSON values and identifying
252
+ * differences between them. It offers two complementary approaches:
253
+ *
254
+ * ## **Detailed Diff API** (`jsonDiff`)
255
+ * Best for analysis, debugging, and understanding specific changes:
256
+ * - Returns a list of individual changes with exact paths and values
257
+ * - Ideal for logging, change tracking, and detailed analysis
258
+ * - Configurable options for array handling and path notation
259
+ *
260
+ * ## **Three-Way Diff API** (`jsonThreeWayDiff`)
261
+ * Best for applying changes and programmatic manipulation:
262
+ * - Returns three objects representing removed, unchanged, and added data
263
+ * - Perfect for merging, UI displays, and actionable results
264
+ * - Treats arrays as atomic units for simpler handling
265
+ *
266
+ * ## **Simple Equality** (`jsonEquals`)
267
+ * Fast boolean check for JSON equality without change details.
268
+ *
269
+ * **Key Features:**
270
+ * - Deep recursive comparison of nested structures
271
+ * - Support for all JSON types: objects, arrays, primitives, null
272
+ * - TypeScript-first with comprehensive type safety
273
+ * - Result pattern for consistent error handling
274
+ * - Extensive TSDoc documentation with practical examples
275
+ *
276
+ * @example Quick comparison of the two main APIs
277
+ * ```typescript
278
+ * const before = { name: "John", age: 30, city: "NYC" };
279
+ * const after = { name: "Jane", age: 30, country: "USA" };
280
+ *
281
+ * // Detailed analysis
282
+ * const detailed = jsonDiff(before, after);
283
+ * // Returns: [
284
+ * // { path: "name", type: "modified", oldValue: "John", newValue: "Jane" },
285
+ * // { path: "city", type: "removed", oldValue: "NYC" },
286
+ * // { path: "country", type: "added", newValue: "USA" }
287
+ * // ]
288
+ *
289
+ * // Actionable objects
290
+ * const actionable = jsonThreeWayDiff(before, after);
291
+ * // Returns: {
292
+ * // onlyInA: { name: "John", city: "NYC" },
293
+ * // unchanged: { age: 30 },
294
+ * // onlyInB: { name: "Jane", country: "USA" }
295
+ * // }
296
+ *
297
+ * // Apply changes: { ...unchanged, ...onlyInB }
298
+ * // Revert changes: { ...unchanged, ...onlyInA }
299
+ * ```
300
+ */
301
+ /**
302
+ * Type of change detected in a JSON diff operation.
303
+ *
304
+ * - `'added'` - Property exists only in the second object
305
+ * - `'removed'` - Property exists only in the first object
306
+ * - `'modified'` - Property exists in both objects but with different values
307
+ * - `'unchanged'` - Property exists in both objects with identical values (only included when `includeUnchanged` is true)
308
+ *
309
+ * @public
310
+ */
311
+ declare type DiffChangeType = 'added' | 'removed' | 'modified' | 'unchanged';
312
+
233
313
  declare namespace EditorRules {
234
314
  export {
235
315
  IConditionalJsonKeyResult,
@@ -276,6 +356,98 @@ declare interface IConditionalJsonRuleOptions extends Partial<IJsonEditorOptions
276
356
  flattenUnconditionalValues?: boolean;
277
357
  }
278
358
 
359
+ /**
360
+ * Represents a single change in a JSON diff operation.
361
+ *
362
+ * Each change describes a specific difference between two JSON values, including
363
+ * the location of the change and the old/new values involved.
364
+ *
365
+ * @example
366
+ * ```typescript
367
+ * // Example changes from diffing { name: "John", age: 30 } vs { name: "Jane", city: "NYC" }
368
+ * const changes: IDiffChange[] = [
369
+ * { path: "name", type: "modified", oldValue: "John", newValue: "Jane" },
370
+ * { path: "age", type: "removed", oldValue: 30 },
371
+ * { path: "city", type: "added", newValue: "NYC" }
372
+ * ];
373
+ * ```
374
+ *
375
+ * @public
376
+ */
377
+ declare interface IDiffChange {
378
+ /**
379
+ * The path to the changed value using dot notation.
380
+ *
381
+ * For nested objects, uses dots to separate levels (e.g., "user.profile.name").
382
+ * For arrays, uses numeric indices (e.g., "items.0.id", "tags.2").
383
+ * Empty string indicates the root value itself changed.
384
+ *
385
+ * @example "user.name", "items.0.id", "settings.theme", ""
386
+ */
387
+ path: string;
388
+ /**
389
+ * The type of change that occurred.
390
+ *
391
+ * @see {@link DiffChangeType} for detailed descriptions of each change type.
392
+ */
393
+ type: DiffChangeType;
394
+ /**
395
+ * The value in the first object.
396
+ *
397
+ * - Present for `'removed'` and `'modified'` changes
398
+ * - Present for `'unchanged'` changes when `includeUnchanged` is true
399
+ * - Undefined for `'added'` changes
400
+ */
401
+ oldValue?: JsonValue;
402
+ /**
403
+ * The value in the second object.
404
+ *
405
+ * - Present for `'added'` and `'modified'` changes
406
+ * - Present for `'unchanged'` changes when `includeUnchanged` is true
407
+ * - Undefined for `'removed'` changes
408
+ */
409
+ newValue?: JsonValue;
410
+ }
411
+
412
+ /**
413
+ * Result of a JSON diff operation containing all detected changes.
414
+ *
415
+ * This interface provides detailed information about every difference found
416
+ * between two JSON values, making it ideal for analysis, debugging, and
417
+ * understanding exactly what changed.
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * const result: IDiffResult = {
422
+ * changes: [
423
+ * { path: "name", type: "modified", oldValue: "John", newValue: "Jane" },
424
+ * { path: "hobbies.1", type: "added", newValue: "gaming" }
425
+ * ],
426
+ * identical: false
427
+ * };
428
+ * ```
429
+ *
430
+ * @see {@link IDiffChange} for details about individual change objects
431
+ * @see {@link Diff.jsonDiff} for the function that produces this result
432
+ * @public
433
+ */
434
+ declare interface IDiffResult {
435
+ /**
436
+ * Array of all changes detected between the two JSON objects.
437
+ *
438
+ * Changes are ordered by the path where they occur. For nested structures,
439
+ * parent changes appear before child changes.
440
+ */
441
+ changes: IDiffChange[];
442
+ /**
443
+ * True if the objects are identical, false otherwise.
444
+ *
445
+ * When true, the `changes` array will be empty (unless `includeUnchanged`
446
+ * option was used, in which case it may contain 'unchanged' entries).
447
+ */
448
+ identical: boolean;
449
+ }
450
+
279
451
  /**
280
452
  * A specialized JSON editor which does a deep clone of a supplied `JsonValue`.
281
453
  * @public
@@ -397,6 +569,64 @@ export declare interface IJsonConverterOptions {
397
569
  onUndefinedPropertyValue: 'error' | 'ignore';
398
570
  }
399
571
 
572
+ /**
573
+ * Options for customizing JSON diff behavior.
574
+ *
575
+ * These options allow you to control how the diff algorithm processes
576
+ * different types of JSON structures and what information is included
577
+ * in the results.
578
+ *
579
+ * @example
580
+ * ```typescript
581
+ * // Include unchanged values and use custom path separator
582
+ * const options: IJsonDiffOptions = {
583
+ * includeUnchanged: true,
584
+ * pathSeparator: '/',
585
+ * arrayOrderMatters: false
586
+ * };
587
+ *
588
+ * const result = jsonDiff(obj1, obj2, options);
589
+ * ```
590
+ *
591
+ * @public
592
+ */
593
+ declare interface IJsonDiffOptions {
594
+ /**
595
+ * If true, includes unchanged values in the result.
596
+ *
597
+ * When enabled, the diff result will include entries with `type: 'unchanged'`
598
+ * for properties that exist in both objects with identical values. This can
599
+ * be useful for displaying complete side-by-side comparisons.
600
+ *
601
+ * @defaultValue false
602
+ */
603
+ includeUnchanged?: boolean;
604
+ /**
605
+ * Custom path separator for nested property paths.
606
+ *
607
+ * Controls the character used to separate levels in nested object paths.
608
+ * For example, with separator `'/'`, a nested property would be reported
609
+ * as `"user/profile/name"` instead of `"user.profile.name"`.
610
+ *
611
+ * @defaultValue "."
612
+ * @example "/", "-\>", "::"
613
+ */
614
+ pathSeparator?: string;
615
+ /**
616
+ * If true, treats arrays as ordered lists where position matters.
617
+ * If false, treats arrays as unordered sets.
618
+ *
619
+ * When `true` (default), array changes are reported by index position:
620
+ * `[1,2,3]` vs `[1,3,2]` shows modifications at indices 1 and 2.
621
+ *
622
+ * When `false`, arrays are compared as sets: `[1,2,3]` vs `[1,3,2]`
623
+ * may be considered equivalent (simplified unordered comparison).
624
+ *
625
+ * @defaultValue true
626
+ */
627
+ arrayOrderMatters?: boolean;
628
+ }
629
+
400
630
  /**
401
631
  * Merge options for a {@link JsonEditor | JsonEditor}.
402
632
  * @public
@@ -607,6 +837,151 @@ declare interface ITemplatedJsonRuleOptions extends Partial<IJsonEditorOptions>
607
837
  useValueTemplates?: boolean;
608
838
  }
609
839
 
840
+ /**
841
+ * Result of a three-way JSON diff operation.
842
+ *
843
+ * This interface provides an actionable representation of differences between
844
+ * two JSON values by separating them into three distinct objects. This approach
845
+ * makes it easy to apply changes, display side-by-side comparisons, perform
846
+ * merges, or programmatically work with the differences.
847
+ *
848
+ * **Key Benefits:**
849
+ * - **Actionable Results**: Objects can be directly used for merging or applying changes
850
+ * - **UI-Friendly**: Perfect for side-by-side diff displays with clear visual separation
851
+ * - **Merge-Ready**: Simplified three-way merge operations
852
+ * - **Structured Data**: Maintains original JSON structure rather than flattened paths
853
+ *
854
+ * @example Basic usage
855
+ * ```typescript
856
+ * const result: IThreeWayDiff = {
857
+ * onlyInA: { name: "John", city: "NYC" }, // Original or removed data
858
+ * unchanged: { age: 30 }, // Stable data
859
+ * onlyInB: { name: "Jane", country: "USA" }, // New or modified data
860
+ * metadata: { added: 1, removed: 1, modified: 1, unchanged: 1 },
861
+ * identical: false
862
+ * };
863
+ *
864
+ * // Apply changes: merge unchanged + onlyInB
865
+ * const updated = { ...result.unchanged, ...result.onlyInB };
866
+ * // Result: { age: 30, name: "Jane", country: "USA" }
867
+ *
868
+ * // Revert changes: merge unchanged + onlyInA
869
+ * const reverted = { ...result.unchanged, ...result.onlyInA };
870
+ * // Result: { age: 30, name: "John", city: "NYC" }
871
+ * ```
872
+ *
873
+ * @see {@link IThreeWayDiffMetadata} for metadata structure details
874
+ * @see {@link jsonThreeWayDiff} for the function that produces this result
875
+ * @see {@link Diff.jsonDiff} for an alternative detailed change-list approach
876
+ *
877
+ * @public
878
+ */
879
+ declare interface IThreeWayDiff {
880
+ /**
881
+ * Contains properties that exist only in the first object, plus the first object's
882
+ * version of any properties that exist in both but have different values.
883
+ *
884
+ * This object represents the "old" or "source" state and can be used for:
885
+ * - Reverting changes by merging with `unchanged`
886
+ * - Displaying what was removed or changed from the original
887
+ * - Understanding the baseline state before modifications
888
+ *
889
+ * Will be `null` if no properties are unique to the first object.
890
+ */
891
+ onlyInA: JsonValue;
892
+ /**
893
+ * Contains properties that exist in both objects with identical values.
894
+ *
895
+ * This object represents the stable, consistent data between both inputs
896
+ * and can be used for:
897
+ * - The foundation for merging operations
898
+ * - Identifying what remained constant during changes
899
+ * - Building complete objects by combining with other parts
900
+ *
901
+ * Will be `null` if no properties are shared between the objects.
902
+ */
903
+ unchanged: JsonValue;
904
+ /**
905
+ * Contains properties that exist only in the second object, plus the second object's
906
+ * version of any properties that exist in both but have different values.
907
+ *
908
+ * This object represents the "new" or "target" state and can be used for:
909
+ * - Applying changes by merging with `unchanged`
910
+ * - Displaying what was added or changed in the update
911
+ * - Understanding the desired end state after modifications
912
+ *
913
+ * Will be `null` if no properties are unique to the second object.
914
+ */
915
+ onlyInB: JsonValue;
916
+ /**
917
+ * Summary metadata about the differences found.
918
+ *
919
+ * Provides counts of added, removed, modified, and unchanged properties
920
+ * for quick assessment of the scope and nature of changes.
921
+ */
922
+ metadata: IThreeWayDiffMetadata;
923
+ /**
924
+ * True if the objects are identical, false otherwise.
925
+ *
926
+ * When `true`, both `onlyInA` and `onlyInB` will be `null`, and `unchanged`
927
+ * will contain the complete shared structure. The metadata will show zero
928
+ * added, removed, and modified properties.
929
+ */
930
+ identical: boolean;
931
+ }
932
+
933
+ /**
934
+ * Metadata about the differences found in a three-way diff.
935
+ *
936
+ * Provides summary statistics about the types and quantities of changes
937
+ * detected between two JSON values, making it easy to understand the
938
+ * overall scope of differences at a glance.
939
+ *
940
+ * @example
941
+ * ```typescript
942
+ * const metadata: IThreeWayDiffMetadata = {
943
+ * removed: 2, // 2 properties only in first object
944
+ * added: 1, // 1 property only in second object
945
+ * modified: 3, // 3 properties changed between objects
946
+ * unchanged: 5 // 5 properties identical in both objects
947
+ * };
948
+ *
949
+ * console.log(`Total changes: ${metadata.added + metadata.removed + metadata.modified}`);
950
+ * console.log(`Stability: ${metadata.unchanged / (metadata.unchanged + metadata.modified) * 100}%`);
951
+ * ```
952
+ *
953
+ * @public
954
+ */
955
+ declare interface IThreeWayDiffMetadata {
956
+ /**
957
+ * Number of properties that exist only in the first object.
958
+ *
959
+ * These represent data that was removed when transitioning from
960
+ * the first object to the second object.
961
+ */
962
+ removed: number;
963
+ /**
964
+ * Number of properties that exist only in the second object.
965
+ *
966
+ * These represent new data that was added when transitioning from
967
+ * the first object to the second object.
968
+ */
969
+ added: number;
970
+ /**
971
+ * Number of properties that exist in both objects but have different values.
972
+ *
973
+ * These represent data that was modified during the transition between objects.
974
+ * For arrays, this counts entire array replacements as single modifications.
975
+ */
976
+ modified: number;
977
+ /**
978
+ * Number of properties that exist in both objects with identical values.
979
+ *
980
+ * These represent stable data that remained consistent between the two objects.
981
+ */
982
+ unchanged: number;
983
+ }
984
+
610
985
  /**
611
986
  * A simple validating {@link JsonConverter | JSON converter}. Converts unknown to
612
987
  * JSON or fails if the unknown contains any invalid JSON values.
@@ -759,6 +1134,92 @@ export declare class JsonConverter extends JsonEditorConverter {
759
1134
  static create(options?: Partial<IJsonConverterOptions>): Result<JsonConverter>;
760
1135
  }
761
1136
 
1137
+ /**
1138
+ * Performs a deep diff comparison between two JSON values.
1139
+ *
1140
+ * This function provides detailed change tracking by analyzing every difference
1141
+ * between two JSON structures. It returns a list of specific changes with paths,
1142
+ * making it ideal for debugging, logging, change analysis, and understanding
1143
+ * exactly what has changed between two data states.
1144
+ *
1145
+ * **Key Features:**
1146
+ * - Deep recursive comparison of nested objects and arrays
1147
+ * - Precise path tracking using dot notation (e.g., "user.profile.name")
1148
+ * - Support for all JSON value types: objects, arrays, primitives, null
1149
+ * - Configurable array comparison (ordered vs unordered)
1150
+ * - Optional inclusion of unchanged values for complete comparisons
1151
+ *
1152
+ * **Use Cases:**
1153
+ * - Debugging data changes in applications
1154
+ * - Generating change logs or audit trails
1155
+ * - Validating API responses against expected data
1156
+ * - Creating detailed diff reports for data synchronization
1157
+ *
1158
+ * @param obj1 - The first JSON value to compare (often the "before" state)
1159
+ * @param obj2 - The second JSON value to compare (often the "after" state)
1160
+ * @param options - Optional configuration for customizing diff behavior
1161
+ * @returns A Result containing the diff result with all detected changes
1162
+ *
1163
+ * @example Basic usage with objects
1164
+ * ```typescript
1165
+ * const before = { name: "John", age: 30, city: "NYC" };
1166
+ * const after = { name: "Jane", age: 30, country: "USA" };
1167
+ *
1168
+ * const result = jsonDiff(before, after);
1169
+ * if (result.success) {
1170
+ * result.value.changes.forEach(change => {
1171
+ * console.log(`${change.path}: ${change.type}`);
1172
+ * // Output:
1173
+ * // name: modified
1174
+ * // city: removed
1175
+ * // country: added
1176
+ * });
1177
+ * }
1178
+ * ```
1179
+ *
1180
+ * @example With arrays and nested structures
1181
+ * ```typescript
1182
+ * const user1 = {
1183
+ * profile: { name: "John", hobbies: ["reading"] },
1184
+ * settings: { theme: "dark" }
1185
+ * };
1186
+ * const user2 = {
1187
+ * profile: { name: "John", hobbies: ["reading", "gaming"] },
1188
+ * settings: { theme: "light", notifications: true }
1189
+ * };
1190
+ *
1191
+ * const result = jsonDiff(user1, user2);
1192
+ * if (result.success) {
1193
+ * console.log(result.value.changes);
1194
+ * // [
1195
+ * // { path: "profile.hobbies.1", type: "added", newValue: "gaming" },
1196
+ * // { path: "settings.theme", type: "modified", oldValue: "dark", newValue: "light" },
1197
+ * // { path: "settings.notifications", type: "added", newValue: true }
1198
+ * // ]
1199
+ * }
1200
+ * ```
1201
+ *
1202
+ * @example Using options for custom behavior
1203
+ * ```typescript
1204
+ * const options: IJsonDiffOptions = {
1205
+ * includeUnchanged: true, // Include unchanged properties
1206
+ * pathSeparator: '/', // Use '/' instead of '.' in paths
1207
+ * arrayOrderMatters: false // Treat arrays as unordered sets
1208
+ * };
1209
+ *
1210
+ * const result = jsonDiff(obj1, obj2, options);
1211
+ * ```
1212
+ *
1213
+ * @see {@link IDiffResult} for the structure of returned results
1214
+ * @see {@link IDiffChange} for details about individual changes
1215
+ * @see {@link IJsonDiffOptions} for available configuration options
1216
+ * @see {@link jsonThreeWayDiff} for an alternative API focused on actionable results
1217
+ * @see {@link jsonEquals} for simple equality checking without change details
1218
+ *
1219
+ * @public
1220
+ */
1221
+ declare function jsonDiff(obj1: JsonValue, obj2: JsonValue, options?: IJsonDiffOptions): Result<IDiffResult>;
1222
+
762
1223
  /**
763
1224
  * Possible `DetailedResult` details for various editor operations.
764
1225
  * @public
@@ -1091,6 +1552,83 @@ export declare class JsonEditorState {
1091
1552
  */
1092
1553
  export declare type JsonEditorValidationRules = 'invalidPropertyName' | 'invalidPropertyValue' | 'undefinedPropertyValue';
1093
1554
 
1555
+ /**
1556
+ * A simpler helper function that returns true if two JSON values are deeply equal.
1557
+ *
1558
+ * This function provides a fast boolean check for JSON equality without the overhead
1559
+ * of tracking individual changes. It performs the same deep comparison logic as
1560
+ * {@link Diff.jsonDiff} but returns only a true/false result, making it ideal for
1561
+ * conditional logic and validation scenarios.
1562
+ *
1563
+ * **Key Features:**
1564
+ * - Deep recursive comparison of all nested structures
1565
+ * - Handles all JSON types: objects, arrays, primitives, null
1566
+ * - Object property order independence
1567
+ * - Array order significance (index positions matter)
1568
+ * - Performance optimized for equality checking
1569
+ *
1570
+ * **Use Cases:**
1571
+ * - Conditional logic based on data equality
1572
+ * - Input validation and testing assertions
1573
+ * - Caching and memoization keys
1574
+ * - Quick checks before expensive diff operations
1575
+ *
1576
+ * @param obj1 - The first JSON value to compare
1577
+ * @param obj2 - The second JSON value to compare
1578
+ * @returns True if the values are deeply equal, false otherwise
1579
+ *
1580
+ * @example Basic equality checking
1581
+ * ```typescript
1582
+ * // Objects with same structure and values
1583
+ * const user1 = { name: "John", hobbies: ["reading", "gaming"] };
1584
+ * const user2 = { name: "John", hobbies: ["reading", "gaming"] };
1585
+ * console.log(jsonEquals(user1, user2)); // true
1586
+ *
1587
+ * // Different property order (still equal)
1588
+ * const obj1 = { a: 1, b: 2 };
1589
+ * const obj2 = { b: 2, a: 1 };
1590
+ * console.log(jsonEquals(obj1, obj2)); // true
1591
+ *
1592
+ * // Different values
1593
+ * const before = { status: "pending" };
1594
+ * const after = { status: "completed" };
1595
+ * console.log(jsonEquals(before, after)); // false
1596
+ * ```
1597
+ *
1598
+ * @example With nested structures
1599
+ * ```typescript
1600
+ * const config1 = {
1601
+ * database: { host: "localhost", port: 5432 },
1602
+ * features: ["auth", "cache"]
1603
+ * };
1604
+ * const config2 = {
1605
+ * database: { host: "localhost", port: 5432 },
1606
+ * features: ["auth", "cache"]
1607
+ * };
1608
+ *
1609
+ * if (jsonEquals(config1, config2)) {
1610
+ * console.log("Configurations are identical");
1611
+ * }
1612
+ * ```
1613
+ *
1614
+ * @example Array order sensitivity
1615
+ * ```typescript
1616
+ * const list1 = [1, 2, 3];
1617
+ * const list2 = [3, 2, 1];
1618
+ * console.log(jsonEquals(list1, list2)); // false - order matters
1619
+ *
1620
+ * const list3 = [1, 2, 3];
1621
+ * const list4 = [1, 2, 3];
1622
+ * console.log(jsonEquals(list3, list4)); // true - same order
1623
+ * ```
1624
+ *
1625
+ * @see {@link Diff.jsonDiff} for detailed change analysis when equality fails
1626
+ * @see {@link jsonThreeWayDiff} for actionable difference results
1627
+ *
1628
+ * @public
1629
+ */
1630
+ declare function jsonEquals(obj1: JsonValue, obj2: JsonValue): boolean;
1631
+
1094
1632
  /**
1095
1633
  * A simple validating {@link JsonConverter | JSON converter}. Converts unknown
1096
1634
  * to a `JsonObject`, or fails if the `unknown` contains invalid
@@ -1113,6 +1651,124 @@ export declare type JsonPropertyEditFailureReason = JsonEditFailureReason | 'def
1113
1651
  */
1114
1652
  export declare type JsonReferenceMapFailureReason = 'unknown' | 'error';
1115
1653
 
1654
+ /**
1655
+ * Performs a three-way diff comparison between two JSON values, returning separate
1656
+ * objects containing the differences and similarities.
1657
+ *
1658
+ * This function provides an alternative to {@link Diff.jsonDiff} that focuses on actionable
1659
+ * results rather than detailed change analysis. Instead of a list of individual changes,
1660
+ * it returns three objects that can be directly used for merging, UI display, or
1661
+ * programmatic manipulation.
1662
+ *
1663
+ * **Key Features:**
1664
+ * - **Actionable Results**: Returns objects ready for immediate use in merging operations
1665
+ * - **Simplified Array Handling**: Arrays are treated as atomic values for cleaner results
1666
+ * - **Structural Preservation**: Maintains original JSON structure rather than flattened paths
1667
+ * - **UI-Optimized**: Perfect for side-by-side diff displays and change visualization
1668
+ * - **Merge-Friendly**: Designed specifically for three-way merge scenarios
1669
+ *
1670
+ * **Array Handling:**
1671
+ * Unlike {@link Diff.jsonDiff}, this function treats arrays as complete units. If arrays differ,
1672
+ * the entire array appears in the appropriate result object rather than computing
1673
+ * element-by-element deltas. This approach is simpler and more predictable for most
1674
+ * use cases involving data updates and synchronization.
1675
+ *
1676
+ * **Use Cases:**
1677
+ * - Applying configuration updates while preserving unchanged settings
1678
+ * - Creating side-by-side diff displays in user interfaces
1679
+ * - Building three-way merge tools for data synchronization
1680
+ * - Implementing undo/redo functionality with granular control
1681
+ * - Generating patch objects for API updates
1682
+ *
1683
+ * @param obj1 - The first JSON value to compare (often the "before" or "source" state)
1684
+ * @param obj2 - The second JSON value to compare (often the "after" or "target" state)
1685
+ * @returns A Result containing the three-way diff with separate objects and metadata
1686
+ *
1687
+ * @example Basic usage for applying changes
1688
+ * ```typescript
1689
+ * const original = { name: "John", age: 30, city: "NYC", active: true };
1690
+ * const updated = { name: "Jane", age: 30, country: "USA", active: true };
1691
+ *
1692
+ * const result = jsonThreeWayDiff(original, updated);
1693
+ * if (result.success) {
1694
+ * const { onlyInA, unchanged, onlyInB } = result.value;
1695
+ *
1696
+ * // Apply changes: merge unchanged + onlyInB
1697
+ * const applied = { ...unchanged, ...onlyInB };
1698
+ * console.log(applied); // { age: 30, active: true, name: "Jane", country: "USA" }
1699
+ *
1700
+ * // Revert changes: merge unchanged + onlyInA
1701
+ * const reverted = { ...unchanged, ...onlyInA };
1702
+ * console.log(reverted); // { age: 30, active: true, name: "John", city: "NYC" }
1703
+ * }
1704
+ * ```
1705
+ *
1706
+ * @example UI-friendly diff display
1707
+ * ```typescript
1708
+ * const result = jsonThreeWayDiff(userBefore, userAfter);
1709
+ * if (result.success) {
1710
+ * const { onlyInA, unchanged, onlyInB, metadata } = result.value;
1711
+ *
1712
+ * // Display summary
1713
+ * console.log(`Changes: ${metadata.added} added, ${metadata.removed} removed, ${metadata.modified} modified`);
1714
+ *
1715
+ * // Show removed/old values in red
1716
+ * if (onlyInA) displayInColor(onlyInA, 'red');
1717
+ *
1718
+ * // Show unchanged values in gray
1719
+ * if (unchanged) displayInColor(unchanged, 'gray');
1720
+ *
1721
+ * // Show added/new values in green
1722
+ * if (onlyInB) displayInColor(onlyInB, 'green');
1723
+ * }
1724
+ * ```
1725
+ *
1726
+ * @example Nested objects and array handling
1727
+ * ```typescript
1728
+ * const config1 = {
1729
+ * database: { host: "localhost", port: 5432 },
1730
+ * features: ["auth", "logging"],
1731
+ * version: "1.0"
1732
+ * };
1733
+ * const config2 = {
1734
+ * database: { host: "production.db", port: 5432 },
1735
+ * features: ["auth", "logging", "metrics"], // Array treated as complete unit
1736
+ * version: "1.1"
1737
+ * };
1738
+ *
1739
+ * const result = jsonThreeWayDiff(config1, config2);
1740
+ * if (result.success) {
1741
+ * // result.value.onlyInA = { database: { host: "localhost" }, features: ["auth", "logging"], version: "1.0" }
1742
+ * // result.value.unchanged = { database: { port: 5432 } }
1743
+ * // result.value.onlyInB = { database: { host: "production.db" }, features: ["auth", "logging", "metrics"], version: "1.1" }
1744
+ * }
1745
+ * ```
1746
+ *
1747
+ * @example Conditional updates based on changes
1748
+ * ```typescript
1749
+ * const result = jsonThreeWayDiff(currentState, newState);
1750
+ * if (result.success && !result.value.identical) {
1751
+ * const { metadata } = result.value;
1752
+ *
1753
+ * if (metadata.modified > 0) {
1754
+ * console.log("Critical settings changed - requires restart");
1755
+ * } else if (metadata.added > 0) {
1756
+ * console.log("New features enabled");
1757
+ * } else if (metadata.removed > 0) {
1758
+ * console.log("Features disabled");
1759
+ * }
1760
+ * }
1761
+ * ```
1762
+ *
1763
+ * @see {@link IThreeWayDiff} for the structure of returned results
1764
+ * @see {@link IThreeWayDiffMetadata} for metadata details
1765
+ * @see {@link Diff.jsonDiff} for detailed change-by-change analysis
1766
+ * @see {@link jsonEquals} for simple equality checking
1767
+ *
1768
+ * @public
1769
+ */
1770
+ declare function jsonThreeWayDiff(obj1: JsonValue, obj2: JsonValue): Result<IThreeWayDiff>;
1771
+
1116
1772
  /**
1117
1773
  * Type representing either a `Map\<string, T\>` or a `Record\<string, T\>`.
1118
1774
  * @public