@signaltree/enterprise 4.0.16 → 4.1.1
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/index.js +6 -0
- package/dist/lib/diff-engine.js +155 -0
- package/dist/lib/enterprise-enhancer.js +28 -0
- package/dist/lib/path-index.js +159 -0
- package/dist/lib/scheduler.js +22 -0
- package/dist/lib/thread-pools.js +13 -0
- package/dist/lib/update-engine.js +186 -0
- package/package.json +44 -13
- package/src/lib/diff-engine.d.ts +0 -75
- package/src/lib/enterprise-enhancer.d.ts +0 -66
- package/src/lib/path-index.d.ts +0 -94
- package/src/lib/update-engine.d.ts +0 -83
- package/src/index.js +0 -10
- package/src/index.js.map +0 -1
- package/src/lib/diff-engine.js +0 -236
- package/src/lib/diff-engine.js.map +0 -1
- package/src/lib/enterprise-enhancer.js +0 -78
- package/src/lib/enterprise-enhancer.js.map +0 -1
- package/src/lib/enterprise.js +0 -7
- package/src/lib/enterprise.js.map +0 -1
- package/src/lib/path-index.js +0 -265
- package/src/lib/path-index.js.map +0 -1
- package/src/lib/scheduler.js +0 -25
- package/src/lib/scheduler.js.map +0 -1
- package/src/lib/thread-pools.js +0 -14
- package/src/lib/thread-pools.js.map +0 -1
- package/src/lib/update-engine.js +0 -287
- package/src/lib/update-engine.js.map +0 -1
- package/src/test-setup.js +0 -8
- package/src/test-setup.js.map +0 -1
package/src/lib/diff-engine.d.ts
CHANGED
|
@@ -1,108 +1,33 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DiffEngine - Efficient change detection for tree updates
|
|
3
|
-
* @packageDocumentation
|
|
4
|
-
*/
|
|
5
1
|
import type { Path } from './path-index';
|
|
6
|
-
/**
|
|
7
|
-
* Type of change detected
|
|
8
|
-
*/
|
|
9
2
|
export declare enum ChangeType {
|
|
10
3
|
ADD = "add",
|
|
11
4
|
UPDATE = "update",
|
|
12
5
|
DELETE = "delete",
|
|
13
6
|
REPLACE = "replace"
|
|
14
7
|
}
|
|
15
|
-
/**
|
|
16
|
-
* A detected change
|
|
17
|
-
*/
|
|
18
8
|
export interface Change {
|
|
19
|
-
/** Type of change */
|
|
20
9
|
type: ChangeType;
|
|
21
|
-
/** Path to the changed value */
|
|
22
10
|
path: Path;
|
|
23
|
-
/** New value */
|
|
24
11
|
value?: unknown;
|
|
25
|
-
/** Old value (for updates/deletes) */
|
|
26
12
|
oldValue?: unknown;
|
|
27
13
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Diff result
|
|
30
|
-
*/
|
|
31
14
|
export interface Diff {
|
|
32
|
-
/** List of changes */
|
|
33
15
|
changes: Change[];
|
|
34
|
-
/** Whether any changes were detected */
|
|
35
16
|
hasChanges: boolean;
|
|
36
17
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Configuration for diff operation
|
|
39
|
-
*/
|
|
40
18
|
export interface DiffOptions {
|
|
41
|
-
/** Maximum depth to traverse */
|
|
42
19
|
maxDepth?: number;
|
|
43
|
-
/** Whether to detect deletions */
|
|
44
20
|
detectDeletions?: boolean;
|
|
45
|
-
/** Whether to ignore array order */
|
|
46
21
|
ignoreArrayOrder?: boolean;
|
|
47
|
-
/** Custom equality function */
|
|
48
22
|
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
49
|
-
/** Optional key validator for security (e.g., to prevent prototype pollution) */
|
|
50
23
|
keyValidator?: (key: string) => boolean;
|
|
51
24
|
}
|
|
52
|
-
/**
|
|
53
|
-
* DiffEngine
|
|
54
|
-
*
|
|
55
|
-
* Efficiently detects changes between two objects to minimize unnecessary updates.
|
|
56
|
-
*
|
|
57
|
-
* Features:
|
|
58
|
-
* - Deep object comparison
|
|
59
|
-
* - Circular reference detection
|
|
60
|
-
* - Configurable equality checking
|
|
61
|
-
* - Array diffing (ordered and unordered)
|
|
62
|
-
* - Path tracking for precise updates
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```ts
|
|
66
|
-
* const engine = new DiffEngine();
|
|
67
|
-
*
|
|
68
|
-
* const current = { user: { name: 'Alice', age: 30 } };
|
|
69
|
-
* const updates = { user: { name: 'Alice', age: 31 } };
|
|
70
|
-
*
|
|
71
|
-
* const diff = engine.diff(current, updates);
|
|
72
|
-
*
|
|
73
|
-
* console.log(diff.changes);
|
|
74
|
-
* // [{ type: 'update', path: ['user', 'age'], value: 31, oldValue: 30 }]
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
25
|
export declare class DiffEngine {
|
|
78
26
|
private defaultOptions;
|
|
79
|
-
/**
|
|
80
|
-
* Diff two objects and return changes
|
|
81
|
-
*
|
|
82
|
-
* @param current - Current state
|
|
83
|
-
* @param updates - Updated state
|
|
84
|
-
* @param options - Diff options
|
|
85
|
-
* @returns Diff result with all changes
|
|
86
|
-
*/
|
|
87
27
|
diff(current: unknown, updates: unknown, options?: DiffOptions): Diff;
|
|
88
|
-
/**
|
|
89
|
-
* Traverse and compare objects recursively
|
|
90
|
-
*/
|
|
91
28
|
private traverse;
|
|
92
|
-
/**
|
|
93
|
-
* Diff arrays
|
|
94
|
-
*/
|
|
95
29
|
private diffArrays;
|
|
96
|
-
/**
|
|
97
|
-
* Diff arrays in order (index-based)
|
|
98
|
-
*/
|
|
99
30
|
private diffArraysOrdered;
|
|
100
|
-
/**
|
|
101
|
-
* Diff arrays ignoring order (value-based)
|
|
102
|
-
*/
|
|
103
31
|
private diffArraysUnordered;
|
|
104
|
-
/**
|
|
105
|
-
* Stringify value for set comparison
|
|
106
|
-
*/
|
|
107
32
|
private stringify;
|
|
108
33
|
}
|
|
@@ -2,80 +2,14 @@ import { PathIndex } from './path-index';
|
|
|
2
2
|
import { UpdateResult } from './update-engine';
|
|
3
3
|
import type { Signal } from '@angular/core';
|
|
4
4
|
import type { Enhancer } from '@signaltree/core';
|
|
5
|
-
/**
|
|
6
|
-
* Enterprise-grade optimizations for large-scale applications.
|
|
7
|
-
*
|
|
8
|
-
* **Includes:**
|
|
9
|
-
* - Diff-based updates (only update changed signals)
|
|
10
|
-
* - Bulk operation optimization (2-5x faster)
|
|
11
|
-
* - Advanced change tracking
|
|
12
|
-
* - Update statistics and monitoring
|
|
13
|
-
*
|
|
14
|
-
* **Use when:**
|
|
15
|
-
* - 500+ signals in state tree
|
|
16
|
-
* - Bulk updates at high frequency (60Hz+)
|
|
17
|
-
* - Real-time dashboards or data feeds
|
|
18
|
-
* - Enterprise-scale applications
|
|
19
|
-
*
|
|
20
|
-
* **Skip when:**
|
|
21
|
-
* - Small to medium apps (<100 signals)
|
|
22
|
-
* - Infrequent state updates
|
|
23
|
-
* - Startup/prototype projects
|
|
24
|
-
*
|
|
25
|
-
* **Bundle cost:** +2.4KB gzipped
|
|
26
|
-
* **Performance gain:** 2-5x faster bulk updates, detailed monitoring
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```typescript
|
|
30
|
-
* import { signalTree } from '@signaltree/core';
|
|
31
|
-
* import { withEnterprise } from '@signaltree/enterprise';
|
|
32
|
-
*
|
|
33
|
-
* const tree = signalTree(largeState).with(withEnterprise());
|
|
34
|
-
*
|
|
35
|
-
* // Now available: optimized bulk updates
|
|
36
|
-
* const result = tree.updateOptimized(newData, {
|
|
37
|
-
* ignoreArrayOrder: true,
|
|
38
|
-
* maxDepth: 10
|
|
39
|
-
* });
|
|
40
|
-
*
|
|
41
|
-
* console.log(result.stats);
|
|
42
|
-
* // { totalChanges: 45, adds: 10, updates: 30, deletes: 5 }
|
|
43
|
-
* ```
|
|
44
|
-
*
|
|
45
|
-
* @public
|
|
46
|
-
*/
|
|
47
5
|
export declare function withEnterprise<T extends Record<string, unknown>>(): Enhancer<T, T & EnterpriseEnhancedTree<T>>;
|
|
48
|
-
/**
|
|
49
|
-
* Type augmentation for trees enhanced with enterprise features.
|
|
50
|
-
* This is applied when using withEnterprise().
|
|
51
|
-
*
|
|
52
|
-
* @public
|
|
53
|
-
*/
|
|
54
6
|
export interface EnterpriseEnhancedTree<T> {
|
|
55
|
-
/**
|
|
56
|
-
* Optimized bulk update method using diff-based change detection.
|
|
57
|
-
* Only available when using withEnterprise().
|
|
58
|
-
*
|
|
59
|
-
* @param newValue - The new state value
|
|
60
|
-
* @param options - Update options
|
|
61
|
-
* @returns Update result with statistics
|
|
62
|
-
*/
|
|
63
7
|
updateOptimized(newValue: T, options?: {
|
|
64
|
-
/** Maximum depth to traverse (default: 100) */
|
|
65
8
|
maxDepth?: number;
|
|
66
|
-
/** Ignore array element order (default: false) */
|
|
67
9
|
ignoreArrayOrder?: boolean;
|
|
68
|
-
/** Custom equality function */
|
|
69
10
|
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
70
|
-
/** Automatically batch updates (default: true) */
|
|
71
11
|
autoBatch?: boolean;
|
|
72
|
-
/** Number of patches per batch (default: 10) */
|
|
73
12
|
batchSize?: number;
|
|
74
13
|
}): UpdateResult;
|
|
75
|
-
/**
|
|
76
|
-
* Get the PathIndex for debugging/monitoring.
|
|
77
|
-
* Only available when using withEnterprise().
|
|
78
|
-
* Returns null if updateOptimized hasn't been called yet (lazy initialization).
|
|
79
|
-
*/
|
|
80
14
|
getPathIndex(): PathIndex<Signal<unknown>> | null;
|
|
81
15
|
}
|
package/src/lib/path-index.d.ts
CHANGED
|
@@ -1,98 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PathIndex - Fast signal lookup using Trie data structure
|
|
3
|
-
* @packageDocumentation
|
|
4
|
-
*/
|
|
5
1
|
import type { WritableSignal } from '@angular/core';
|
|
6
|
-
/**
|
|
7
|
-
* Path segment (string or number for array indices)
|
|
8
|
-
*/
|
|
9
2
|
export type PathSegment = string | number;
|
|
10
|
-
/**
|
|
11
|
-
* Path as array of segments
|
|
12
|
-
*/
|
|
13
3
|
export type Path = PathSegment[];
|
|
14
|
-
/**
|
|
15
|
-
* PathIndex
|
|
16
|
-
*
|
|
17
|
-
* Fast signal lookup using a Trie (prefix tree) data structure.
|
|
18
|
-
* Provides O(k) lookup time where k is the path length, regardless of total signals.
|
|
19
|
-
*
|
|
20
|
-
* Features:
|
|
21
|
-
* - Trie-based indexing for O(k) lookup
|
|
22
|
-
* - WeakRef caching for memory efficiency
|
|
23
|
-
* - Automatic cleanup of stale references
|
|
24
|
-
* - Prefix matching for batch operations
|
|
25
|
-
* - Path normalization
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* const index = new PathIndex();
|
|
30
|
-
*
|
|
31
|
-
* // Index signals
|
|
32
|
-
* index.set(['user', 'name'], nameSignal);
|
|
33
|
-
* index.set(['user', 'email'], emailSignal);
|
|
34
|
-
*
|
|
35
|
-
* // Fast lookup
|
|
36
|
-
* const signal = index.get(['user', 'name']);
|
|
37
|
-
*
|
|
38
|
-
* // Prefix matching
|
|
39
|
-
* const userSignals = index.getByPrefix(['user']);
|
|
40
|
-
* // Returns: { name: nameSignal, email: emailSignal }
|
|
41
|
-
*
|
|
42
|
-
* // Check if path exists
|
|
43
|
-
* if (index.has(['user', 'name'])) {
|
|
44
|
-
* // ...
|
|
45
|
-
* }
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
4
|
export declare class PathIndex<T extends object = WritableSignal<any>> {
|
|
49
5
|
private root;
|
|
50
6
|
private pathCache;
|
|
51
7
|
private stats;
|
|
52
|
-
/**
|
|
53
|
-
* Set a value at the given path
|
|
54
|
-
*
|
|
55
|
-
* @param path - Path segments
|
|
56
|
-
* @param value - Value to store
|
|
57
|
-
*/
|
|
58
8
|
set(path: Path, signal: T): void;
|
|
59
|
-
/**
|
|
60
|
-
* Get value at the given path
|
|
61
|
-
*
|
|
62
|
-
* @param path - Path segments
|
|
63
|
-
* @returns Value if found and not GC'd, null otherwise
|
|
64
|
-
*/
|
|
65
9
|
get(path: Path): T | null;
|
|
66
|
-
/**
|
|
67
|
-
* Check if path exists in index
|
|
68
|
-
*
|
|
69
|
-
* @param path - Path segments
|
|
70
|
-
* @returns True if path exists and value is not GC'd
|
|
71
|
-
*/
|
|
72
10
|
has(path: Path): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Get all values matching a prefix
|
|
75
|
-
*
|
|
76
|
-
* @param prefix - Path prefix
|
|
77
|
-
* @returns Map of relative paths to values
|
|
78
|
-
*/
|
|
79
11
|
getByPrefix(prefix: Path): Map<string, T>;
|
|
80
|
-
/**
|
|
81
|
-
* Delete value at path
|
|
82
|
-
*
|
|
83
|
-
* @param path - Path segments
|
|
84
|
-
* @returns True if deleted, false if not found
|
|
85
|
-
*/
|
|
86
12
|
delete(path: Path): boolean;
|
|
87
|
-
/**
|
|
88
|
-
* Clear all entries
|
|
89
|
-
*/
|
|
90
13
|
clear(): void;
|
|
91
|
-
/**
|
|
92
|
-
* Get statistics
|
|
93
|
-
*
|
|
94
|
-
* @returns Index statistics
|
|
95
|
-
*/
|
|
96
14
|
getStats(): {
|
|
97
15
|
hits: number;
|
|
98
16
|
misses: number;
|
|
@@ -101,19 +19,7 @@ export declare class PathIndex<T extends object = WritableSignal<any>> {
|
|
|
101
19
|
hitRate: number;
|
|
102
20
|
cacheSize: number;
|
|
103
21
|
};
|
|
104
|
-
/**
|
|
105
|
-
* Build index from a tree structure
|
|
106
|
-
*
|
|
107
|
-
* @param tree - Tree object to index
|
|
108
|
-
* @param path - Current path (for recursion)
|
|
109
|
-
*/
|
|
110
22
|
buildFromTree(tree: unknown, path?: Path): void;
|
|
111
|
-
/**
|
|
112
|
-
* Convert path to string for caching
|
|
113
|
-
*/
|
|
114
23
|
private pathToString;
|
|
115
|
-
/**
|
|
116
|
-
* Collect all descendant values recursively
|
|
117
|
-
*/
|
|
118
24
|
private collectDescendants;
|
|
119
25
|
}
|
|
@@ -1,115 +1,32 @@
|
|
|
1
1
|
import { PathIndex } from './path-index';
|
|
2
2
|
import type { DiffOptions } from './diff-engine';
|
|
3
|
-
/**
|
|
4
|
-
* OptimizedUpdateEngine - High-performance tree updates
|
|
5
|
-
* @packageDocumentation
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Update options
|
|
9
|
-
*/
|
|
10
3
|
export interface UpdateOptions extends DiffOptions {
|
|
11
|
-
/** Whether to batch updates */
|
|
12
4
|
batch?: boolean;
|
|
13
|
-
/** Batch size for chunked updates */
|
|
14
5
|
batchSize?: number;
|
|
15
6
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Update result
|
|
18
|
-
*/
|
|
19
7
|
export interface UpdateResult {
|
|
20
|
-
/** Whether any changes were made */
|
|
21
8
|
changed: boolean;
|
|
22
|
-
/** Update duration in milliseconds */
|
|
23
9
|
duration: number;
|
|
24
|
-
/** List of changed paths */
|
|
25
10
|
changedPaths: string[];
|
|
26
|
-
/** Update statistics */
|
|
27
11
|
stats?: {
|
|
28
12
|
totalPaths: number;
|
|
29
13
|
optimizedPaths: number;
|
|
30
14
|
batchedUpdates: number;
|
|
31
15
|
};
|
|
32
16
|
}
|
|
33
|
-
/**
|
|
34
|
-
* OptimizedUpdateEngine
|
|
35
|
-
*
|
|
36
|
-
* High-performance update engine using path indexing and diffing to minimize
|
|
37
|
-
* unnecessary signal updates.
|
|
38
|
-
*
|
|
39
|
-
* Features:
|
|
40
|
-
* - Diff-based updates (only update what changed)
|
|
41
|
-
* - Path indexing for O(k) lookups
|
|
42
|
-
* - Automatic batching for large updates
|
|
43
|
-
* - Priority-based patch ordering
|
|
44
|
-
* - Skip unchanged values
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```ts
|
|
48
|
-
* const tree = signalTree(data, { useLazySignals: true });
|
|
49
|
-
* const engine = new OptimizedUpdateEngine(tree);
|
|
50
|
-
*
|
|
51
|
-
* // Optimized update - only changes what's different
|
|
52
|
-
* const result = engine.update({
|
|
53
|
-
* user: { name: 'Alice' } // Only updates if name changed
|
|
54
|
-
* });
|
|
55
|
-
*
|
|
56
|
-
* console.log(result.changedPaths); // ['user.name']
|
|
57
|
-
* console.log(result.duration); // ~2ms
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
17
|
export declare class OptimizedUpdateEngine {
|
|
61
18
|
private pathIndex;
|
|
62
19
|
private diffEngine;
|
|
63
20
|
constructor(tree: unknown);
|
|
64
|
-
/**
|
|
65
|
-
* Update tree with optimizations
|
|
66
|
-
*
|
|
67
|
-
* @param tree - Current tree state
|
|
68
|
-
* @param updates - Updates to apply
|
|
69
|
-
* @param options - Update options
|
|
70
|
-
* @returns Update result
|
|
71
|
-
*/
|
|
72
21
|
update(tree: unknown, updates: unknown, options?: UpdateOptions): UpdateResult;
|
|
73
|
-
/**
|
|
74
|
-
* Rebuild path index from current tree state
|
|
75
|
-
*
|
|
76
|
-
* @param tree - Current tree
|
|
77
|
-
*/
|
|
78
22
|
rebuildIndex(tree: unknown): void;
|
|
79
|
-
/**
|
|
80
|
-
* Get path index statistics
|
|
81
|
-
*/
|
|
82
23
|
getIndexStats(): ReturnType<PathIndex['getStats']>;
|
|
83
|
-
/**
|
|
84
|
-
* Creates optimized patches from diff changes
|
|
85
|
-
*/
|
|
86
24
|
private createPatches;
|
|
87
|
-
/**
|
|
88
|
-
* Creates a single patch from a change
|
|
89
|
-
*/
|
|
90
25
|
private createPatch;
|
|
91
|
-
/**
|
|
92
|
-
* Calculates update priority for optimal ordering
|
|
93
|
-
*/
|
|
94
26
|
private calculatePriority;
|
|
95
|
-
/**
|
|
96
|
-
* Sorts patches for optimal application
|
|
97
|
-
*/
|
|
98
27
|
private sortPatches;
|
|
99
|
-
/**
|
|
100
|
-
* Applies patches directly (no batching)
|
|
101
|
-
*/
|
|
102
28
|
private applyPatches;
|
|
103
|
-
/**
|
|
104
|
-
* Applies patches with batching for better performance
|
|
105
|
-
*/
|
|
106
29
|
private batchApplyPatches;
|
|
107
|
-
/**
|
|
108
|
-
* Applies a single patch to the tree object
|
|
109
|
-
*/
|
|
110
30
|
private applyPatch;
|
|
111
|
-
/**
|
|
112
|
-
* Check equality
|
|
113
|
-
*/
|
|
114
31
|
private isEqual;
|
|
115
32
|
}
|
package/src/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./lib/diff-engine"), exports);
|
|
5
|
-
tslib_1.__exportStar(require("./lib/path-index"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./lib/update-engine"), exports);
|
|
7
|
-
tslib_1.__exportStar(require("./lib/enterprise-enhancer"), exports);
|
|
8
|
-
tslib_1.__exportStar(require("./lib/scheduler"), exports);
|
|
9
|
-
tslib_1.__exportStar(require("./lib/thread-pools"), exports);
|
|
10
|
-
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/enterprise/src/index.ts"],"names":[],"mappings":";;;AAAA,4DAAkC;AAClC,2DAAiC;AACjC,8DAAoC;AACpC,oEAA0C;AAC1C,0DAAgC;AAChC,6DAAmC"}
|
package/src/lib/diff-engine.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* DiffEngine - Efficient change detection for tree updates
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.DiffEngine = exports.ChangeType = void 0;
|
|
8
|
-
/**
|
|
9
|
-
* Type of change detected
|
|
10
|
-
*/
|
|
11
|
-
var ChangeType;
|
|
12
|
-
(function (ChangeType) {
|
|
13
|
-
ChangeType["ADD"] = "add";
|
|
14
|
-
ChangeType["UPDATE"] = "update";
|
|
15
|
-
ChangeType["DELETE"] = "delete";
|
|
16
|
-
ChangeType["REPLACE"] = "replace";
|
|
17
|
-
})(ChangeType || (exports.ChangeType = ChangeType = {}));
|
|
18
|
-
/**
|
|
19
|
-
* DiffEngine
|
|
20
|
-
*
|
|
21
|
-
* Efficiently detects changes between two objects to minimize unnecessary updates.
|
|
22
|
-
*
|
|
23
|
-
* Features:
|
|
24
|
-
* - Deep object comparison
|
|
25
|
-
* - Circular reference detection
|
|
26
|
-
* - Configurable equality checking
|
|
27
|
-
* - Array diffing (ordered and unordered)
|
|
28
|
-
* - Path tracking for precise updates
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```ts
|
|
32
|
-
* const engine = new DiffEngine();
|
|
33
|
-
*
|
|
34
|
-
* const current = { user: { name: 'Alice', age: 30 } };
|
|
35
|
-
* const updates = { user: { name: 'Alice', age: 31 } };
|
|
36
|
-
*
|
|
37
|
-
* const diff = engine.diff(current, updates);
|
|
38
|
-
*
|
|
39
|
-
* console.log(diff.changes);
|
|
40
|
-
* // [{ type: 'update', path: ['user', 'age'], value: 31, oldValue: 30 }]
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
class DiffEngine {
|
|
44
|
-
constructor() {
|
|
45
|
-
this.defaultOptions = {
|
|
46
|
-
maxDepth: 100,
|
|
47
|
-
detectDeletions: false,
|
|
48
|
-
ignoreArrayOrder: false,
|
|
49
|
-
equalityFn: (a, b) => a === b,
|
|
50
|
-
keyValidator: undefined,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Diff two objects and return changes
|
|
55
|
-
*
|
|
56
|
-
* @param current - Current state
|
|
57
|
-
* @param updates - Updated state
|
|
58
|
-
* @param options - Diff options
|
|
59
|
-
* @returns Diff result with all changes
|
|
60
|
-
*/
|
|
61
|
-
diff(current, updates, options = {}) {
|
|
62
|
-
const opts = Object.assign(Object.assign({}, this.defaultOptions), options);
|
|
63
|
-
const changes = [];
|
|
64
|
-
const visited = new WeakSet();
|
|
65
|
-
this.traverse(current, updates, [], changes, visited, opts, 0);
|
|
66
|
-
return {
|
|
67
|
-
changes,
|
|
68
|
-
hasChanges: changes.length > 0,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Traverse and compare objects recursively
|
|
73
|
-
*/
|
|
74
|
-
traverse(curr, upd, path, changes, visited, opts, depth) {
|
|
75
|
-
// Depth limit
|
|
76
|
-
if (depth > opts.maxDepth) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// Handle primitives
|
|
80
|
-
if (typeof upd !== 'object' || upd === null) {
|
|
81
|
-
if (!opts.equalityFn(curr, upd)) {
|
|
82
|
-
changes.push({
|
|
83
|
-
type: curr === undefined ? ChangeType.ADD : ChangeType.UPDATE,
|
|
84
|
-
path: [...path],
|
|
85
|
-
value: upd,
|
|
86
|
-
oldValue: curr,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// Circular reference detection
|
|
92
|
-
if (visited.has(upd)) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
visited.add(upd);
|
|
96
|
-
// Handle arrays
|
|
97
|
-
if (Array.isArray(upd)) {
|
|
98
|
-
this.diffArrays(curr, upd, path, changes, visited, opts, depth);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
// Handle objects
|
|
102
|
-
if (!curr || typeof curr !== 'object' || Array.isArray(curr)) {
|
|
103
|
-
// Type mismatch - replace entire subtree
|
|
104
|
-
changes.push({
|
|
105
|
-
type: ChangeType.REPLACE,
|
|
106
|
-
path: [...path],
|
|
107
|
-
value: upd,
|
|
108
|
-
oldValue: curr,
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// Diff object properties
|
|
113
|
-
const currObj = curr;
|
|
114
|
-
const updObj = upd;
|
|
115
|
-
for (const key in updObj) {
|
|
116
|
-
if (Object.prototype.hasOwnProperty.call(updObj, key)) {
|
|
117
|
-
// Validate key for security (e.g., prevent prototype pollution)
|
|
118
|
-
if (opts.keyValidator && !opts.keyValidator(key)) {
|
|
119
|
-
// Skip dangerous keys silently
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
this.traverse(currObj[key], updObj[key], [...path, key], changes, visited, opts, depth + 1);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Check for deletions if enabled
|
|
126
|
-
if (opts.detectDeletions) {
|
|
127
|
-
for (const key in currObj) {
|
|
128
|
-
if (Object.prototype.hasOwnProperty.call(currObj, key) &&
|
|
129
|
-
!(key in updObj)) {
|
|
130
|
-
changes.push({
|
|
131
|
-
type: ChangeType.DELETE,
|
|
132
|
-
path: [...path, key],
|
|
133
|
-
oldValue: currObj[key],
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Diff arrays
|
|
141
|
-
*/
|
|
142
|
-
diffArrays(curr, upd, path, changes, visited, opts, depth) {
|
|
143
|
-
if (!Array.isArray(curr)) {
|
|
144
|
-
// Not an array - replace
|
|
145
|
-
changes.push({
|
|
146
|
-
type: ChangeType.REPLACE,
|
|
147
|
-
path: [...path],
|
|
148
|
-
value: upd,
|
|
149
|
-
oldValue: curr,
|
|
150
|
-
});
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (opts.ignoreArrayOrder) {
|
|
154
|
-
this.diffArraysUnordered(curr, upd, path, changes, opts);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
this.diffArraysOrdered(curr, upd, path, changes, visited, opts, depth);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Diff arrays in order (index-based)
|
|
162
|
-
*/
|
|
163
|
-
diffArraysOrdered(curr, upd, path, changes, visited, opts, depth) {
|
|
164
|
-
// Check each index
|
|
165
|
-
const maxLength = Math.max(curr.length, upd.length);
|
|
166
|
-
for (let i = 0; i < maxLength; i++) {
|
|
167
|
-
if (i >= upd.length) {
|
|
168
|
-
// Deletion (if enabled)
|
|
169
|
-
if (opts.detectDeletions) {
|
|
170
|
-
changes.push({
|
|
171
|
-
type: ChangeType.DELETE,
|
|
172
|
-
path: [...path, i],
|
|
173
|
-
oldValue: curr[i],
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else if (i >= curr.length) {
|
|
178
|
-
// Addition
|
|
179
|
-
changes.push({
|
|
180
|
-
type: ChangeType.ADD,
|
|
181
|
-
path: [...path, i],
|
|
182
|
-
value: upd[i],
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// Potential update
|
|
187
|
-
this.traverse(curr[i], upd[i], [...path, i], changes, visited, opts, depth + 1);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Diff arrays ignoring order (value-based)
|
|
193
|
-
*/
|
|
194
|
-
diffArraysUnordered(curr, upd, path, changes, opts) {
|
|
195
|
-
// Build value sets
|
|
196
|
-
const currSet = new Set(curr.map((v) => this.stringify(v)));
|
|
197
|
-
const updSet = new Set(upd.map((v) => this.stringify(v)));
|
|
198
|
-
// Find additions
|
|
199
|
-
upd.forEach((value, index) => {
|
|
200
|
-
const str = this.stringify(value);
|
|
201
|
-
if (!currSet.has(str)) {
|
|
202
|
-
changes.push({
|
|
203
|
-
type: ChangeType.ADD,
|
|
204
|
-
path: [...path, index],
|
|
205
|
-
value,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
// Find deletions (if enabled)
|
|
210
|
-
if (opts.detectDeletions) {
|
|
211
|
-
curr.forEach((value, index) => {
|
|
212
|
-
const str = this.stringify(value);
|
|
213
|
-
if (!updSet.has(str)) {
|
|
214
|
-
changes.push({
|
|
215
|
-
type: ChangeType.DELETE,
|
|
216
|
-
path: [...path, index],
|
|
217
|
-
oldValue: value,
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Stringify value for set comparison
|
|
225
|
-
*/
|
|
226
|
-
stringify(value) {
|
|
227
|
-
try {
|
|
228
|
-
return JSON.stringify(value);
|
|
229
|
-
}
|
|
230
|
-
catch (_a) {
|
|
231
|
-
return String(value);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
exports.DiffEngine = DiffEngine;
|
|
236
|
-
//# sourceMappingURL=diff-engine.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diff-engine.js","sourceRoot":"","sources":["../../../../../packages/enterprise/src/lib/diff-engine.ts"],"names":[],"mappings":"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"}
|