@fgv/ts-json 5.0.0-0 → 5.0.0-11

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
@@ -408,6 +638,12 @@ export declare interface IJsonEditorMergeOptions {
408
638
  * - `'replace'`: Existing array is completely replaced with the new array
409
639
  */
410
640
  arrayMergeBehavior: ArrayMergeBehavior;
641
+ /**
642
+ * Controls whether null values should be treated as property deletion during merge operations.
643
+ * - `false` (default): Null values are merged normally, setting the property to null
644
+ * - `true`: Null values delete the property from the target object during merge
645
+ */
646
+ nullAsDelete?: boolean;
411
647
  }
412
648
 
413
649
  /**
@@ -607,6 +843,151 @@ declare interface ITemplatedJsonRuleOptions extends Partial<IJsonEditorOptions>
607
843
  useValueTemplates?: boolean;
608
844
  }
609
845
 
846
+ /**
847
+ * Result of a three-way JSON diff operation.
848
+ *
849
+ * This interface provides an actionable representation of differences between
850
+ * two JSON values by separating them into three distinct objects. This approach
851
+ * makes it easy to apply changes, display side-by-side comparisons, perform
852
+ * merges, or programmatically work with the differences.
853
+ *
854
+ * **Key Benefits:**
855
+ * - **Actionable Results**: Objects can be directly used for merging or applying changes
856
+ * - **UI-Friendly**: Perfect for side-by-side diff displays with clear visual separation
857
+ * - **Merge-Ready**: Simplified three-way merge operations
858
+ * - **Structured Data**: Maintains original JSON structure rather than flattened paths
859
+ *
860
+ * @example Basic usage
861
+ * ```typescript
862
+ * const result: IThreeWayDiff = {
863
+ * onlyInA: { name: "John", city: "NYC" }, // Original or removed data
864
+ * unchanged: { age: 30 }, // Stable data
865
+ * onlyInB: { name: "Jane", country: "USA" }, // New or modified data
866
+ * metadata: { added: 1, removed: 1, modified: 1, unchanged: 1 },
867
+ * identical: false
868
+ * };
869
+ *
870
+ * // Apply changes: merge unchanged + onlyInB
871
+ * const updated = { ...result.unchanged, ...result.onlyInB };
872
+ * // Result: { age: 30, name: "Jane", country: "USA" }
873
+ *
874
+ * // Revert changes: merge unchanged + onlyInA
875
+ * const reverted = { ...result.unchanged, ...result.onlyInA };
876
+ * // Result: { age: 30, name: "John", city: "NYC" }
877
+ * ```
878
+ *
879
+ * @see {@link IThreeWayDiffMetadata} for metadata structure details
880
+ * @see {@link jsonThreeWayDiff} for the function that produces this result
881
+ * @see {@link Diff.jsonDiff} for an alternative detailed change-list approach
882
+ *
883
+ * @public
884
+ */
885
+ declare interface IThreeWayDiff {
886
+ /**
887
+ * Contains properties that exist only in the first object, plus the first object's
888
+ * version of any properties that exist in both but have different values.
889
+ *
890
+ * This object represents the "old" or "source" state and can be used for:
891
+ * - Reverting changes by merging with `unchanged`
892
+ * - Displaying what was removed or changed from the original
893
+ * - Understanding the baseline state before modifications
894
+ *
895
+ * Will be `null` if no properties are unique to the first object.
896
+ */
897
+ onlyInA: JsonValue;
898
+ /**
899
+ * Contains properties that exist in both objects with identical values.
900
+ *
901
+ * This object represents the stable, consistent data between both inputs
902
+ * and can be used for:
903
+ * - The foundation for merging operations
904
+ * - Identifying what remained constant during changes
905
+ * - Building complete objects by combining with other parts
906
+ *
907
+ * Will be `null` if no properties are shared between the objects.
908
+ */
909
+ unchanged: JsonValue;
910
+ /**
911
+ * Contains properties that exist only in the second object, plus the second object's
912
+ * version of any properties that exist in both but have different values.
913
+ *
914
+ * This object represents the "new" or "target" state and can be used for:
915
+ * - Applying changes by merging with `unchanged`
916
+ * - Displaying what was added or changed in the update
917
+ * - Understanding the desired end state after modifications
918
+ *
919
+ * Will be `null` if no properties are unique to the second object.
920
+ */
921
+ onlyInB: JsonValue;
922
+ /**
923
+ * Summary metadata about the differences found.
924
+ *
925
+ * Provides counts of added, removed, modified, and unchanged properties
926
+ * for quick assessment of the scope and nature of changes.
927
+ */
928
+ metadata: IThreeWayDiffMetadata;
929
+ /**
930
+ * True if the objects are identical, false otherwise.
931
+ *
932
+ * When `true`, both `onlyInA` and `onlyInB` will be `null`, and `unchanged`
933
+ * will contain the complete shared structure. The metadata will show zero
934
+ * added, removed, and modified properties.
935
+ */
936
+ identical: boolean;
937
+ }
938
+
939
+ /**
940
+ * Metadata about the differences found in a three-way diff.
941
+ *
942
+ * Provides summary statistics about the types and quantities of changes
943
+ * detected between two JSON values, making it easy to understand the
944
+ * overall scope of differences at a glance.
945
+ *
946
+ * @example
947
+ * ```typescript
948
+ * const metadata: IThreeWayDiffMetadata = {
949
+ * removed: 2, // 2 properties only in first object
950
+ * added: 1, // 1 property only in second object
951
+ * modified: 3, // 3 properties changed between objects
952
+ * unchanged: 5 // 5 properties identical in both objects
953
+ * };
954
+ *
955
+ * console.log(`Total changes: ${metadata.added + metadata.removed + metadata.modified}`);
956
+ * console.log(`Stability: ${metadata.unchanged / (metadata.unchanged + metadata.modified) * 100}%`);
957
+ * ```
958
+ *
959
+ * @public
960
+ */
961
+ declare interface IThreeWayDiffMetadata {
962
+ /**
963
+ * Number of properties that exist only in the first object.
964
+ *
965
+ * These represent data that was removed when transitioning from
966
+ * the first object to the second object.
967
+ */
968
+ removed: number;
969
+ /**
970
+ * Number of properties that exist only in the second object.
971
+ *
972
+ * These represent new data that was added when transitioning from
973
+ * the first object to the second object.
974
+ */
975
+ added: number;
976
+ /**
977
+ * Number of properties that exist in both objects but have different values.
978
+ *
979
+ * These represent data that was modified during the transition between objects.
980
+ * For arrays, this counts entire array replacements as single modifications.
981
+ */
982
+ modified: number;
983
+ /**
984
+ * Number of properties that exist in both objects with identical values.
985
+ *
986
+ * These represent stable data that remained consistent between the two objects.
987
+ */
988
+ unchanged: number;
989
+ }
990
+
610
991
  /**
611
992
  * A simple validating {@link JsonConverter | JSON converter}. Converts unknown to
612
993
  * JSON or fails if the unknown contains any invalid JSON values.
@@ -759,6 +1140,92 @@ export declare class JsonConverter extends JsonEditorConverter {
759
1140
  static create(options?: Partial<IJsonConverterOptions>): Result<JsonConverter>;
760
1141
  }
761
1142
 
1143
+ /**
1144
+ * Performs a deep diff comparison between two JSON values.
1145
+ *
1146
+ * This function provides detailed change tracking by analyzing every difference
1147
+ * between two JSON structures. It returns a list of specific changes with paths,
1148
+ * making it ideal for debugging, logging, change analysis, and understanding
1149
+ * exactly what has changed between two data states.
1150
+ *
1151
+ * **Key Features:**
1152
+ * - Deep recursive comparison of nested objects and arrays
1153
+ * - Precise path tracking using dot notation (e.g., "user.profile.name")
1154
+ * - Support for all JSON value types: objects, arrays, primitives, null
1155
+ * - Configurable array comparison (ordered vs unordered)
1156
+ * - Optional inclusion of unchanged values for complete comparisons
1157
+ *
1158
+ * **Use Cases:**
1159
+ * - Debugging data changes in applications
1160
+ * - Generating change logs or audit trails
1161
+ * - Validating API responses against expected data
1162
+ * - Creating detailed diff reports for data synchronization
1163
+ *
1164
+ * @param obj1 - The first JSON value to compare (often the "before" state)
1165
+ * @param obj2 - The second JSON value to compare (often the "after" state)
1166
+ * @param options - Optional configuration for customizing diff behavior
1167
+ * @returns A Result containing the diff result with all detected changes
1168
+ *
1169
+ * @example Basic usage with objects
1170
+ * ```typescript
1171
+ * const before = { name: "John", age: 30, city: "NYC" };
1172
+ * const after = { name: "Jane", age: 30, country: "USA" };
1173
+ *
1174
+ * const result = jsonDiff(before, after);
1175
+ * if (result.success) {
1176
+ * result.value.changes.forEach(change => {
1177
+ * console.log(`${change.path}: ${change.type}`);
1178
+ * // Output:
1179
+ * // name: modified
1180
+ * // city: removed
1181
+ * // country: added
1182
+ * });
1183
+ * }
1184
+ * ```
1185
+ *
1186
+ * @example With arrays and nested structures
1187
+ * ```typescript
1188
+ * const user1 = {
1189
+ * profile: { name: "John", hobbies: ["reading"] },
1190
+ * settings: { theme: "dark" }
1191
+ * };
1192
+ * const user2 = {
1193
+ * profile: { name: "John", hobbies: ["reading", "gaming"] },
1194
+ * settings: { theme: "light", notifications: true }
1195
+ * };
1196
+ *
1197
+ * const result = jsonDiff(user1, user2);
1198
+ * if (result.success) {
1199
+ * console.log(result.value.changes);
1200
+ * // [
1201
+ * // { path: "profile.hobbies.1", type: "added", newValue: "gaming" },
1202
+ * // { path: "settings.theme", type: "modified", oldValue: "dark", newValue: "light" },
1203
+ * // { path: "settings.notifications", type: "added", newValue: true }
1204
+ * // ]
1205
+ * }
1206
+ * ```
1207
+ *
1208
+ * @example Using options for custom behavior
1209
+ * ```typescript
1210
+ * const options: IJsonDiffOptions = {
1211
+ * includeUnchanged: true, // Include unchanged properties
1212
+ * pathSeparator: '/', // Use '/' instead of '.' in paths
1213
+ * arrayOrderMatters: false // Treat arrays as unordered sets
1214
+ * };
1215
+ *
1216
+ * const result = jsonDiff(obj1, obj2, options);
1217
+ * ```
1218
+ *
1219
+ * @see {@link IDiffResult} for the structure of returned results
1220
+ * @see {@link IDiffChange} for details about individual changes
1221
+ * @see {@link IJsonDiffOptions} for available configuration options
1222
+ * @see {@link jsonThreeWayDiff} for an alternative API focused on actionable results
1223
+ * @see {@link jsonEquals} for simple equality checking without change details
1224
+ *
1225
+ * @public
1226
+ */
1227
+ declare function jsonDiff(obj1: JsonValue, obj2: JsonValue, options?: IJsonDiffOptions): Result<IDiffResult>;
1228
+
762
1229
  /**
763
1230
  * Possible `DetailedResult` details for various editor operations.
764
1231
  * @public
@@ -819,6 +1286,11 @@ export declare class JsonEditor implements IJsonCloneEditor {
819
1286
  */
820
1287
  static getDefaultRules(options?: IJsonEditorOptions): Result<IJsonEditorRule[]>;
821
1288
  /**
1289
+ * Creates a complete IJsonEditorOptions object from partial options, filling in
1290
+ * default values for any missing properties. This ensures all editor instances
1291
+ * have consistent, complete configuration including validation rules and merge behavior.
1292
+ * @param options - Optional partial editor options to merge with defaults
1293
+ * @returns Success with complete editor options, or Failure if validation fails
822
1294
  * @internal
823
1295
  */
824
1296
  protected static _getDefaultOptions(options?: Partial<IJsonEditorOptions>): Result<IJsonEditorOptions>;
@@ -861,54 +1333,78 @@ export declare class JsonEditor implements IJsonCloneEditor {
861
1333
  */
862
1334
  clone(src: JsonValue, context?: IJsonContext): DetailedResult<JsonValue, JsonEditFailureReason>;
863
1335
  /**
864
- *
865
- * @param target -
866
- * @param src -
867
- * @param state -
868
- * @returns
1336
+ * Merges properties from a source object into a target object, applying editor rules and
1337
+ * null-as-delete logic. This is the core merge implementation that handles property-by-property
1338
+ * merging with rule processing and deferred property handling.
1339
+ * @param target - The target object to merge properties into
1340
+ * @param src - The source object containing properties to merge
1341
+ * @param state - The editor state containing options and context
1342
+ * @returns Success with the modified target object, or Failure with error details
869
1343
  * @internal
870
1344
  */
871
1345
  protected _mergeObjectInPlace(target: JsonObject, src: JsonObject, state: JsonEditorState): Result<JsonObject>;
872
1346
  /**
873
- *
874
- * @param src -
875
- * @param context -
876
- * @returns
1347
+ * Creates a deep clone of a JSON array by recursively cloning each element.
1348
+ * Each array element is cloned using the main clone method, preserving the
1349
+ * editor's rules and validation settings.
1350
+ * @param src - The source JSON array to clone
1351
+ * @param context - Optional JSON context for cloning operations
1352
+ * @returns Success with the cloned array, or Failure with error details
877
1353
  * @internal
878
1354
  */
879
1355
  protected _cloneArray(src: JsonArray, context?: IJsonContext): DetailedResult<JsonArray, JsonEditFailureReason>;
880
1356
  /**
881
- *
882
- * @param target -
883
- * @param key -
884
- * @param newValue -
885
- * @param state -
886
- * @returns
1357
+ * Merges a single cloned property value into a target object. This method handles
1358
+ * the core merge logic including null-as-delete behavior, array merging, and
1359
+ * recursive object merging. The null-as-delete check occurs before primitive
1360
+ * handling to ensure null values can signal property deletion.
1361
+ * @param target - The target object to merge the property into
1362
+ * @param key - The property key being merged
1363
+ * @param newValue - The cloned value to merge (from source object)
1364
+ * @param state - The editor state containing merge options and context
1365
+ * @returns Success with the merged value, or Failure with error details
887
1366
  * @internal
888
1367
  */
889
1368
  protected _mergeClonedProperty(target: JsonObject, key: string, newValue: JsonValue, state: JsonEditorState): DetailedResult<JsonValue, JsonEditFailureReason>;
890
1369
  /**
891
- *
892
- * @param key -
893
- * @param value -
894
- * @param state -
895
- * @returns
1370
+ * Applies editor rules to a single property during merge operations. This method
1371
+ * iterates through all configured editor rules to process the property, handling
1372
+ * templates, conditionals, multi-value properties, and references.
1373
+ * @param key - The property key to edit
1374
+ * @param value - The property value to edit
1375
+ * @param state - The editor state containing rules and context
1376
+ * @returns Success with transformed property object, or Failure if rules cannot process
896
1377
  * @internal
897
1378
  */
898
1379
  protected _editProperty(key: string, value: JsonValue, state: JsonEditorState): DetailedResult<JsonObject, JsonPropertyEditFailureReason>;
899
1380
  /**
900
- *
901
- * @param value -
902
- * @param state -
903
- * @returns
1381
+ * Applies editor rules to a single JSON value during clone operations. This method
1382
+ * iterates through all configured editor rules to process the value, handling
1383
+ * templates, conditionals, multi-value expressions, and references.
1384
+ * @param value - The JSON value to edit and transform
1385
+ * @param state - The editor state containing rules and context
1386
+ * @returns Success with transformed value, or Failure if rules cannot process
904
1387
  * @internal
905
1388
  */
906
1389
  protected _editValue(value: JsonValue, state: JsonEditorState): DetailedResult<JsonValue, JsonEditFailureReason>;
907
1390
  /**
908
- *
909
- * @param target -
910
- * @param state -
911
- * @returns
1391
+ * Clone an object without applying null-as-delete behavior.
1392
+ * This preserves null values during cloning so they can be used for deletion signaling during merge.
1393
+ * @param target - The target object to clone into
1394
+ * @param src - The source object to clone
1395
+ * @param state - The editor state
1396
+ * @returns The cloned object
1397
+ * @internal
1398
+ */
1399
+ protected _cloneObjectWithoutNullAsDelete(target: JsonObject, src: JsonObject, state: JsonEditorState): DetailedResult<JsonObject, JsonEditFailureReason>;
1400
+ /**
1401
+ * Finalizes the merge operation by processing any deferred properties and merging
1402
+ * them into the target object. Deferred properties are those that require special
1403
+ * processing after the initial merge phase, such as references that depend on
1404
+ * other properties being resolved first.
1405
+ * @param target - The target object that has been merged
1406
+ * @param state - The editor state containing deferred properties and rules
1407
+ * @returns Success with the finalized target object, or Failure with error details
912
1408
  * @internal
913
1409
  */
914
1410
  protected _finalizeAndMerge(target: JsonObject, state: JsonEditorState): DetailedResult<JsonObject, JsonEditFailureReason>;
@@ -1091,6 +1587,83 @@ export declare class JsonEditorState {
1091
1587
  */
1092
1588
  export declare type JsonEditorValidationRules = 'invalidPropertyName' | 'invalidPropertyValue' | 'undefinedPropertyValue';
1093
1589
 
1590
+ /**
1591
+ * A simpler helper function that returns true if two JSON values are deeply equal.
1592
+ *
1593
+ * This function provides a fast boolean check for JSON equality without the overhead
1594
+ * of tracking individual changes. It performs the same deep comparison logic as
1595
+ * {@link Diff.jsonDiff} but returns only a true/false result, making it ideal for
1596
+ * conditional logic and validation scenarios.
1597
+ *
1598
+ * **Key Features:**
1599
+ * - Deep recursive comparison of all nested structures
1600
+ * - Handles all JSON types: objects, arrays, primitives, null
1601
+ * - Object property order independence
1602
+ * - Array order significance (index positions matter)
1603
+ * - Performance optimized for equality checking
1604
+ *
1605
+ * **Use Cases:**
1606
+ * - Conditional logic based on data equality
1607
+ * - Input validation and testing assertions
1608
+ * - Caching and memoization keys
1609
+ * - Quick checks before expensive diff operations
1610
+ *
1611
+ * @param obj1 - The first JSON value to compare
1612
+ * @param obj2 - The second JSON value to compare
1613
+ * @returns True if the values are deeply equal, false otherwise
1614
+ *
1615
+ * @example Basic equality checking
1616
+ * ```typescript
1617
+ * // Objects with same structure and values
1618
+ * const user1 = { name: "John", hobbies: ["reading", "gaming"] };
1619
+ * const user2 = { name: "John", hobbies: ["reading", "gaming"] };
1620
+ * console.log(jsonEquals(user1, user2)); // true
1621
+ *
1622
+ * // Different property order (still equal)
1623
+ * const obj1 = { a: 1, b: 2 };
1624
+ * const obj2 = { b: 2, a: 1 };
1625
+ * console.log(jsonEquals(obj1, obj2)); // true
1626
+ *
1627
+ * // Different values
1628
+ * const before = { status: "pending" };
1629
+ * const after = { status: "completed" };
1630
+ * console.log(jsonEquals(before, after)); // false
1631
+ * ```
1632
+ *
1633
+ * @example With nested structures
1634
+ * ```typescript
1635
+ * const config1 = {
1636
+ * database: { host: "localhost", port: 5432 },
1637
+ * features: ["auth", "cache"]
1638
+ * };
1639
+ * const config2 = {
1640
+ * database: { host: "localhost", port: 5432 },
1641
+ * features: ["auth", "cache"]
1642
+ * };
1643
+ *
1644
+ * if (jsonEquals(config1, config2)) {
1645
+ * console.log("Configurations are identical");
1646
+ * }
1647
+ * ```
1648
+ *
1649
+ * @example Array order sensitivity
1650
+ * ```typescript
1651
+ * const list1 = [1, 2, 3];
1652
+ * const list2 = [3, 2, 1];
1653
+ * console.log(jsonEquals(list1, list2)); // false - order matters
1654
+ *
1655
+ * const list3 = [1, 2, 3];
1656
+ * const list4 = [1, 2, 3];
1657
+ * console.log(jsonEquals(list3, list4)); // true - same order
1658
+ * ```
1659
+ *
1660
+ * @see {@link Diff.jsonDiff} for detailed change analysis when equality fails
1661
+ * @see {@link jsonThreeWayDiff} for actionable difference results
1662
+ *
1663
+ * @public
1664
+ */
1665
+ declare function jsonEquals(obj1: JsonValue, obj2: JsonValue): boolean;
1666
+
1094
1667
  /**
1095
1668
  * A simple validating {@link JsonConverter | JSON converter}. Converts unknown
1096
1669
  * to a `JsonObject`, or fails if the `unknown` contains invalid
@@ -1113,6 +1686,124 @@ export declare type JsonPropertyEditFailureReason = JsonEditFailureReason | 'def
1113
1686
  */
1114
1687
  export declare type JsonReferenceMapFailureReason = 'unknown' | 'error';
1115
1688
 
1689
+ /**
1690
+ * Performs a three-way diff comparison between two JSON values, returning separate
1691
+ * objects containing the differences and similarities.
1692
+ *
1693
+ * This function provides an alternative to {@link Diff.jsonDiff} that focuses on actionable
1694
+ * results rather than detailed change analysis. Instead of a list of individual changes,
1695
+ * it returns three objects that can be directly used for merging, UI display, or
1696
+ * programmatic manipulation.
1697
+ *
1698
+ * **Key Features:**
1699
+ * - **Actionable Results**: Returns objects ready for immediate use in merging operations
1700
+ * - **Simplified Array Handling**: Arrays are treated as atomic values for cleaner results
1701
+ * - **Structural Preservation**: Maintains original JSON structure rather than flattened paths
1702
+ * - **UI-Optimized**: Perfect for side-by-side diff displays and change visualization
1703
+ * - **Merge-Friendly**: Designed specifically for three-way merge scenarios
1704
+ *
1705
+ * **Array Handling:**
1706
+ * Unlike {@link Diff.jsonDiff}, this function treats arrays as complete units. If arrays differ,
1707
+ * the entire array appears in the appropriate result object rather than computing
1708
+ * element-by-element deltas. This approach is simpler and more predictable for most
1709
+ * use cases involving data updates and synchronization.
1710
+ *
1711
+ * **Use Cases:**
1712
+ * - Applying configuration updates while preserving unchanged settings
1713
+ * - Creating side-by-side diff displays in user interfaces
1714
+ * - Building three-way merge tools for data synchronization
1715
+ * - Implementing undo/redo functionality with granular control
1716
+ * - Generating patch objects for API updates
1717
+ *
1718
+ * @param obj1 - The first JSON value to compare (often the "before" or "source" state)
1719
+ * @param obj2 - The second JSON value to compare (often the "after" or "target" state)
1720
+ * @returns A Result containing the three-way diff with separate objects and metadata
1721
+ *
1722
+ * @example Basic usage for applying changes
1723
+ * ```typescript
1724
+ * const original = { name: "John", age: 30, city: "NYC", active: true };
1725
+ * const updated = { name: "Jane", age: 30, country: "USA", active: true };
1726
+ *
1727
+ * const result = jsonThreeWayDiff(original, updated);
1728
+ * if (result.success) {
1729
+ * const { onlyInA, unchanged, onlyInB } = result.value;
1730
+ *
1731
+ * // Apply changes: merge unchanged + onlyInB
1732
+ * const applied = { ...unchanged, ...onlyInB };
1733
+ * console.log(applied); // { age: 30, active: true, name: "Jane", country: "USA" }
1734
+ *
1735
+ * // Revert changes: merge unchanged + onlyInA
1736
+ * const reverted = { ...unchanged, ...onlyInA };
1737
+ * console.log(reverted); // { age: 30, active: true, name: "John", city: "NYC" }
1738
+ * }
1739
+ * ```
1740
+ *
1741
+ * @example UI-friendly diff display
1742
+ * ```typescript
1743
+ * const result = jsonThreeWayDiff(userBefore, userAfter);
1744
+ * if (result.success) {
1745
+ * const { onlyInA, unchanged, onlyInB, metadata } = result.value;
1746
+ *
1747
+ * // Display summary
1748
+ * console.log(`Changes: ${metadata.added} added, ${metadata.removed} removed, ${metadata.modified} modified`);
1749
+ *
1750
+ * // Show removed/old values in red
1751
+ * if (onlyInA) displayInColor(onlyInA, 'red');
1752
+ *
1753
+ * // Show unchanged values in gray
1754
+ * if (unchanged) displayInColor(unchanged, 'gray');
1755
+ *
1756
+ * // Show added/new values in green
1757
+ * if (onlyInB) displayInColor(onlyInB, 'green');
1758
+ * }
1759
+ * ```
1760
+ *
1761
+ * @example Nested objects and array handling
1762
+ * ```typescript
1763
+ * const config1 = {
1764
+ * database: { host: "localhost", port: 5432 },
1765
+ * features: ["auth", "logging"],
1766
+ * version: "1.0"
1767
+ * };
1768
+ * const config2 = {
1769
+ * database: { host: "production.db", port: 5432 },
1770
+ * features: ["auth", "logging", "metrics"], // Array treated as complete unit
1771
+ * version: "1.1"
1772
+ * };
1773
+ *
1774
+ * const result = jsonThreeWayDiff(config1, config2);
1775
+ * if (result.success) {
1776
+ * // result.value.onlyInA = { database: { host: "localhost" }, features: ["auth", "logging"], version: "1.0" }
1777
+ * // result.value.unchanged = { database: { port: 5432 } }
1778
+ * // result.value.onlyInB = { database: { host: "production.db" }, features: ["auth", "logging", "metrics"], version: "1.1" }
1779
+ * }
1780
+ * ```
1781
+ *
1782
+ * @example Conditional updates based on changes
1783
+ * ```typescript
1784
+ * const result = jsonThreeWayDiff(currentState, newState);
1785
+ * if (result.success && !result.value.identical) {
1786
+ * const { metadata } = result.value;
1787
+ *
1788
+ * if (metadata.modified > 0) {
1789
+ * console.log("Critical settings changed - requires restart");
1790
+ * } else if (metadata.added > 0) {
1791
+ * console.log("New features enabled");
1792
+ * } else if (metadata.removed > 0) {
1793
+ * console.log("Features disabled");
1794
+ * }
1795
+ * }
1796
+ * ```
1797
+ *
1798
+ * @see {@link IThreeWayDiff} for the structure of returned results
1799
+ * @see {@link IThreeWayDiffMetadata} for metadata details
1800
+ * @see {@link Diff.jsonDiff} for detailed change-by-change analysis
1801
+ * @see {@link jsonEquals} for simple equality checking
1802
+ *
1803
+ * @public
1804
+ */
1805
+ declare function jsonThreeWayDiff(obj1: JsonValue, obj2: JsonValue): Result<IThreeWayDiff>;
1806
+
1116
1807
  /**
1117
1808
  * Type representing either a `Map\<string, T\>` or a `Record\<string, T\>`.
1118
1809
  * @public