@signaltree/core 4.0.15 → 4.1.0

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 (109) hide show
  1. package/README.md +106 -38
  2. package/dist/constants.js +6 -0
  3. package/dist/deep-clone.js +80 -0
  4. package/dist/deep-equal.js +41 -0
  5. package/dist/enhancers/batching/lib/batching.js +141 -135
  6. package/dist/enhancers/computed/lib/computed.js +18 -16
  7. package/dist/enhancers/devtools/lib/devtools.js +303 -260
  8. package/dist/enhancers/entities/lib/entities.js +109 -104
  9. package/dist/enhancers/index.js +65 -77
  10. package/dist/enhancers/memoization/lib/memoization.js +339 -351
  11. package/dist/enhancers/middleware/lib/async-helpers.js +71 -79
  12. package/dist/enhancers/middleware/lib/middleware.js +126 -169
  13. package/dist/enhancers/presets/lib/presets.js +82 -71
  14. package/dist/enhancers/serialization/constants.js +14 -13
  15. package/dist/enhancers/serialization/lib/serialization.js +615 -623
  16. package/dist/enhancers/time-travel/lib/time-travel.js +178 -177
  17. package/dist/index.d.ts +1 -17
  18. package/dist/index.js +19 -16
  19. package/dist/is-built-in-object.js +23 -0
  20. package/dist/lib/constants.js +51 -55
  21. package/dist/lib/memory/memory-manager.js +152 -154
  22. package/dist/lib/performance/diff-engine.js +141 -141
  23. package/dist/lib/performance/path-index.js +139 -137
  24. package/dist/lib/performance/update-engine.js +171 -176
  25. package/dist/lib/security/security-validator.js +110 -128
  26. package/dist/lib/signal-tree.js +577 -611
  27. package/dist/lib/types.js +3 -9
  28. package/dist/lib/utils.js +236 -268
  29. package/dist/lru-cache.js +64 -0
  30. package/dist/parse-path.js +13 -0
  31. package/package.json +30 -16
  32. package/src/index.d.ts +17 -0
  33. package/{dist → src}/lib/utils.d.ts +1 -0
  34. package/dist/enhancers/batching/index.js +0 -1
  35. package/dist/enhancers/batching/jest.config.js +0 -21
  36. package/dist/enhancers/batching/test-setup.js +0 -5
  37. package/dist/enhancers/computed/index.js +0 -1
  38. package/dist/enhancers/computed/jest.config.js +0 -21
  39. package/dist/enhancers/devtools/index.js +0 -1
  40. package/dist/enhancers/devtools/jest.config.js +0 -21
  41. package/dist/enhancers/devtools/test-setup.js +0 -5
  42. package/dist/enhancers/entities/index.js +0 -1
  43. package/dist/enhancers/entities/jest.config.js +0 -21
  44. package/dist/enhancers/entities/test-setup.js +0 -5
  45. package/dist/enhancers/memoization/index.js +0 -1
  46. package/dist/enhancers/memoization/jest.config.js +0 -21
  47. package/dist/enhancers/memoization/test-setup.js +0 -5
  48. package/dist/enhancers/middleware/index.js +0 -2
  49. package/dist/enhancers/middleware/jest.config.js +0 -21
  50. package/dist/enhancers/middleware/test-setup.js +0 -5
  51. package/dist/enhancers/presets/index.js +0 -1
  52. package/dist/enhancers/presets/jest.config.js +0 -21
  53. package/dist/enhancers/presets/test-setup.js +0 -5
  54. package/dist/enhancers/serialization/index.js +0 -2
  55. package/dist/enhancers/serialization/jest.config.js +0 -21
  56. package/dist/enhancers/serialization/test-setup.js +0 -5
  57. package/dist/enhancers/time-travel/index.js +0 -1
  58. package/dist/enhancers/time-travel/jest.config.js +0 -21
  59. package/dist/enhancers/time-travel/lib/utils.js +0 -1
  60. package/dist/enhancers/time-travel/test-setup.js +0 -5
  61. package/dist/enhancers/types.js +0 -0
  62. /package/{dist → src}/enhancers/batching/index.d.ts +0 -0
  63. /package/{dist → src}/enhancers/batching/jest.config.d.ts +0 -0
  64. /package/{dist → src}/enhancers/batching/lib/batching.d.ts +0 -0
  65. /package/{dist → src}/enhancers/batching/test-setup.d.ts +0 -0
  66. /package/{dist → src}/enhancers/computed/index.d.ts +0 -0
  67. /package/{dist → src}/enhancers/computed/jest.config.d.ts +0 -0
  68. /package/{dist → src}/enhancers/computed/lib/computed.d.ts +0 -0
  69. /package/{dist → src}/enhancers/devtools/index.d.ts +0 -0
  70. /package/{dist → src}/enhancers/devtools/jest.config.d.ts +0 -0
  71. /package/{dist → src}/enhancers/devtools/lib/devtools.d.ts +0 -0
  72. /package/{dist → src}/enhancers/devtools/test-setup.d.ts +0 -0
  73. /package/{dist → src}/enhancers/entities/index.d.ts +0 -0
  74. /package/{dist → src}/enhancers/entities/jest.config.d.ts +0 -0
  75. /package/{dist → src}/enhancers/entities/lib/entities.d.ts +0 -0
  76. /package/{dist → src}/enhancers/entities/test-setup.d.ts +0 -0
  77. /package/{dist → src}/enhancers/index.d.ts +0 -0
  78. /package/{dist → src}/enhancers/memoization/index.d.ts +0 -0
  79. /package/{dist → src}/enhancers/memoization/jest.config.d.ts +0 -0
  80. /package/{dist → src}/enhancers/memoization/lib/memoization.d.ts +0 -0
  81. /package/{dist → src}/enhancers/memoization/test-setup.d.ts +0 -0
  82. /package/{dist → src}/enhancers/middleware/index.d.ts +0 -0
  83. /package/{dist → src}/enhancers/middleware/jest.config.d.ts +0 -0
  84. /package/{dist → src}/enhancers/middleware/lib/async-helpers.d.ts +0 -0
  85. /package/{dist → src}/enhancers/middleware/lib/middleware.d.ts +0 -0
  86. /package/{dist → src}/enhancers/middleware/test-setup.d.ts +0 -0
  87. /package/{dist → src}/enhancers/presets/index.d.ts +0 -0
  88. /package/{dist → src}/enhancers/presets/jest.config.d.ts +0 -0
  89. /package/{dist → src}/enhancers/presets/lib/presets.d.ts +0 -0
  90. /package/{dist → src}/enhancers/presets/test-setup.d.ts +0 -0
  91. /package/{dist → src}/enhancers/serialization/constants.d.ts +0 -0
  92. /package/{dist → src}/enhancers/serialization/index.d.ts +0 -0
  93. /package/{dist → src}/enhancers/serialization/jest.config.d.ts +0 -0
  94. /package/{dist → src}/enhancers/serialization/lib/serialization.d.ts +0 -0
  95. /package/{dist → src}/enhancers/serialization/test-setup.d.ts +0 -0
  96. /package/{dist → src}/enhancers/time-travel/index.d.ts +0 -0
  97. /package/{dist → src}/enhancers/time-travel/jest.config.d.ts +0 -0
  98. /package/{dist → src}/enhancers/time-travel/lib/time-travel.d.ts +0 -0
  99. /package/{dist → src}/enhancers/time-travel/lib/utils.d.ts +0 -0
  100. /package/{dist → src}/enhancers/time-travel/test-setup.d.ts +0 -0
  101. /package/{dist → src}/enhancers/types.d.ts +0 -0
  102. /package/{dist → src}/lib/constants.d.ts +0 -0
  103. /package/{dist → src}/lib/memory/memory-manager.d.ts +0 -0
  104. /package/{dist → src}/lib/performance/diff-engine.d.ts +0 -0
  105. /package/{dist → src}/lib/performance/path-index.d.ts +0 -0
  106. /package/{dist → src}/lib/performance/update-engine.d.ts +0 -0
  107. /package/{dist → src}/lib/security/security-validator.d.ts +0 -0
  108. /package/{dist → src}/lib/signal-tree.d.ts +0 -0
  109. /package/{dist → src}/lib/types.d.ts +0 -0
@@ -1,166 +1,164 @@
1
- export class SignalMemoryManager {
2
- cache = new Map();
3
- registry = null;
4
- config;
5
- stats = {
6
- cleanedUpSignals: 0,
7
- peakCachedSignals: 0,
8
- manualDisposes: 0,
1
+ class SignalMemoryManager {
2
+ cache = new Map();
3
+ registry = null;
4
+ config;
5
+ stats = {
6
+ cleanedUpSignals: 0,
7
+ peakCachedSignals: 0,
8
+ manualDisposes: 0
9
+ };
10
+ constructor(config = {}) {
11
+ this.config = {
12
+ enableAutoCleanup: config.enableAutoCleanup ?? true,
13
+ debugMode: config.debugMode ?? false,
14
+ onCleanup: config.onCleanup ?? (() => {})
9
15
  };
10
- constructor(config = {}) {
11
- this.config = {
12
- enableAutoCleanup: config.enableAutoCleanup ?? true,
13
- debugMode: config.debugMode ?? false,
14
- onCleanup: config.onCleanup ??
15
- (() => {
16
- }),
17
- };
18
- if (this.config.enableAutoCleanup &&
19
- typeof FinalizationRegistry !== 'undefined') {
20
- this.registry = new FinalizationRegistry((path) => {
21
- this.handleCleanup(path);
22
- });
23
- }
24
- if (this.config.debugMode) {
25
- console.log('[SignalMemoryManager] Initialized', {
26
- autoCleanup: this.config.enableAutoCleanup,
27
- hasRegistry: !!this.registry,
28
- });
29
- }
16
+ if (this.config.enableAutoCleanup && typeof FinalizationRegistry !== 'undefined') {
17
+ this.registry = new FinalizationRegistry(path => {
18
+ this.handleCleanup(path);
19
+ });
30
20
  }
31
- cacheSignal(path, signal) {
32
- const ref = new WeakRef(signal);
33
- const entry = {
34
- ref,
35
- path,
36
- cachedAt: Date.now(),
37
- };
38
- this.cache.set(path, entry);
39
- if (this.registry) {
40
- this.registry.register(signal, path, signal);
41
- }
42
- const currentSize = this.cache.size;
43
- if (currentSize > this.stats.peakCachedSignals) {
44
- this.stats.peakCachedSignals = currentSize;
45
- }
46
- if (this.config.debugMode) {
47
- console.log(`[SignalMemoryManager] Cached signal: ${path}`, {
48
- cacheSize: currentSize,
49
- peak: this.stats.peakCachedSignals,
50
- });
51
- }
21
+ if (this.config.debugMode) {
22
+ console.log('[SignalMemoryManager] Initialized', {
23
+ autoCleanup: this.config.enableAutoCleanup,
24
+ hasRegistry: !!this.registry
25
+ });
52
26
  }
53
- getSignal(path) {
54
- const entry = this.cache.get(path);
55
- if (!entry) {
56
- return undefined;
57
- }
58
- const signal = entry.ref.deref();
59
- if (!signal) {
60
- this.cache.delete(path);
61
- if (this.config.debugMode) {
62
- console.log(`[SignalMemoryManager] Signal GC'd: ${path}`);
63
- }
64
- return undefined;
65
- }
66
- return signal;
27
+ }
28
+ cacheSignal(path, signal) {
29
+ const ref = new WeakRef(signal);
30
+ const entry = {
31
+ ref,
32
+ path,
33
+ cachedAt: Date.now()
34
+ };
35
+ this.cache.set(path, entry);
36
+ if (this.registry) {
37
+ this.registry.register(signal, path, signal);
67
38
  }
68
- hasSignal(path) {
69
- return this.cache.has(path);
39
+ const currentSize = this.cache.size;
40
+ if (currentSize > this.stats.peakCachedSignals) {
41
+ this.stats.peakCachedSignals = currentSize;
70
42
  }
71
- removeSignal(path) {
72
- const entry = this.cache.get(path);
73
- if (!entry) {
74
- return false;
75
- }
76
- const signal = entry.ref.deref();
77
- if (signal && this.registry) {
78
- this.registry.unregister(signal);
79
- }
43
+ if (this.config.debugMode) {
44
+ console.log(`[SignalMemoryManager] Cached signal: ${path}`, {
45
+ cacheSize: currentSize,
46
+ peak: this.stats.peakCachedSignals
47
+ });
48
+ }
49
+ }
50
+ getSignal(path) {
51
+ const entry = this.cache.get(path);
52
+ if (!entry) {
53
+ return undefined;
54
+ }
55
+ const signal = entry.ref.deref();
56
+ if (!signal) {
57
+ this.cache.delete(path);
58
+ if (this.config.debugMode) {
59
+ console.log(`[SignalMemoryManager] Signal GC'd: ${path}`);
60
+ }
61
+ return undefined;
62
+ }
63
+ return signal;
64
+ }
65
+ hasSignal(path) {
66
+ return this.cache.has(path);
67
+ }
68
+ removeSignal(path) {
69
+ const entry = this.cache.get(path);
70
+ if (!entry) {
71
+ return false;
72
+ }
73
+ const signal = entry.ref.deref();
74
+ if (signal && this.registry) {
75
+ this.registry.unregister(signal);
76
+ }
77
+ this.cache.delete(path);
78
+ if (this.config.debugMode) {
79
+ console.log(`[SignalMemoryManager] Removed signal: ${path}`);
80
+ }
81
+ return true;
82
+ }
83
+ handleCleanup(path) {
84
+ this.cache.delete(path);
85
+ this.stats.cleanedUpSignals++;
86
+ const currentStats = this.getStats();
87
+ if (this.config.debugMode) {
88
+ console.log(`[SignalMemoryManager] Auto cleanup: ${path}`, currentStats);
89
+ }
90
+ this.config.onCleanup(path, currentStats);
91
+ }
92
+ getStats() {
93
+ let validSignals = 0;
94
+ for (const [path, entry] of this.cache.entries()) {
95
+ if (entry.ref.deref()) {
96
+ validSignals++;
97
+ } else {
80
98
  this.cache.delete(path);
81
- if (this.config.debugMode) {
82
- console.log(`[SignalMemoryManager] Removed signal: ${path}`);
99
+ }
100
+ }
101
+ const estimatedMemoryBytes = validSignals * 100;
102
+ return {
103
+ cachedSignals: validSignals,
104
+ cleanedUpSignals: this.stats.cleanedUpSignals,
105
+ peakCachedSignals: this.stats.peakCachedSignals,
106
+ manualDisposes: this.stats.manualDisposes,
107
+ estimatedMemoryBytes
108
+ };
109
+ }
110
+ dispose() {
111
+ if (this.config.debugMode) {
112
+ console.log('[SignalMemoryManager] Disposing', {
113
+ cachedSignals: this.cache.size
114
+ });
115
+ }
116
+ if (this.registry) {
117
+ for (const entry of this.cache.values()) {
118
+ const signal = entry.ref.deref();
119
+ if (signal) {
120
+ this.registry.unregister(signal);
83
121
  }
84
- return true;
122
+ }
85
123
  }
86
- handleCleanup(path) {
124
+ this.cache.clear();
125
+ this.stats.manualDisposes++;
126
+ if (this.config.debugMode) {
127
+ console.log('[SignalMemoryManager] Disposed', this.getStats());
128
+ }
129
+ }
130
+ getCachedPaths() {
131
+ const paths = [];
132
+ for (const [path, entry] of this.cache.entries()) {
133
+ if (entry.ref.deref()) {
134
+ paths.push(path);
135
+ }
136
+ }
137
+ return paths;
138
+ }
139
+ clearStale() {
140
+ let removed = 0;
141
+ for (const [path, entry] of this.cache.entries()) {
142
+ if (!entry.ref.deref()) {
87
143
  this.cache.delete(path);
88
- this.stats.cleanedUpSignals++;
89
- const currentStats = this.getStats();
90
- if (this.config.debugMode) {
91
- console.log(`[SignalMemoryManager] Auto cleanup: ${path}`, currentStats);
92
- }
93
- this.config.onCleanup(path, currentStats);
94
- }
95
- getStats() {
96
- let validSignals = 0;
97
- for (const [path, entry] of this.cache.entries()) {
98
- if (entry.ref.deref()) {
99
- validSignals++;
100
- }
101
- else {
102
- this.cache.delete(path);
103
- }
104
- }
105
- const estimatedMemoryBytes = validSignals * 100;
106
- return {
107
- cachedSignals: validSignals,
108
- cleanedUpSignals: this.stats.cleanedUpSignals,
109
- peakCachedSignals: this.stats.peakCachedSignals,
110
- manualDisposes: this.stats.manualDisposes,
111
- estimatedMemoryBytes,
112
- };
113
- }
114
- dispose() {
115
- if (this.config.debugMode) {
116
- console.log('[SignalMemoryManager] Disposing', {
117
- cachedSignals: this.cache.size,
118
- });
119
- }
120
- if (this.registry) {
121
- for (const entry of this.cache.values()) {
122
- const signal = entry.ref.deref();
123
- if (signal) {
124
- this.registry.unregister(signal);
125
- }
126
- }
127
- }
128
- this.cache.clear();
129
- this.stats.manualDisposes++;
130
- if (this.config.debugMode) {
131
- console.log('[SignalMemoryManager] Disposed', this.getStats());
132
- }
144
+ removed++;
145
+ }
133
146
  }
134
- getCachedPaths() {
135
- const paths = [];
136
- for (const [path, entry] of this.cache.entries()) {
137
- if (entry.ref.deref()) {
138
- paths.push(path);
139
- }
140
- }
141
- return paths;
142
- }
143
- clearStale() {
144
- let removed = 0;
145
- for (const [path, entry] of this.cache.entries()) {
146
- if (!entry.ref.deref()) {
147
- this.cache.delete(path);
148
- removed++;
149
- }
150
- }
151
- if (this.config.debugMode && removed > 0) {
152
- console.log(`[SignalMemoryManager] Cleared ${removed} stale entries`);
153
- }
154
- return removed;
155
- }
156
- resetStats() {
157
- this.stats = {
158
- cleanedUpSignals: 0,
159
- peakCachedSignals: 0,
160
- manualDisposes: 0,
161
- };
162
- if (this.config.debugMode) {
163
- console.log('[SignalMemoryManager] Stats reset');
164
- }
147
+ if (this.config.debugMode && removed > 0) {
148
+ console.log(`[SignalMemoryManager] Cleared ${removed} stale entries`);
149
+ }
150
+ return removed;
151
+ }
152
+ resetStats() {
153
+ this.stats = {
154
+ cleanedUpSignals: 0,
155
+ peakCachedSignals: 0,
156
+ manualDisposes: 0
157
+ };
158
+ if (this.config.debugMode) {
159
+ console.log('[SignalMemoryManager] Stats reset');
165
160
  }
161
+ }
166
162
  }
163
+
164
+ export { SignalMemoryManager };
@@ -1,156 +1,156 @@
1
- export var ChangeType;
1
+ var ChangeType;
2
2
  (function (ChangeType) {
3
- ChangeType["ADD"] = "add";
4
- ChangeType["UPDATE"] = "update";
5
- ChangeType["DELETE"] = "delete";
6
- ChangeType["REPLACE"] = "replace";
3
+ ChangeType["ADD"] = "add";
4
+ ChangeType["UPDATE"] = "update";
5
+ ChangeType["DELETE"] = "delete";
6
+ ChangeType["REPLACE"] = "replace";
7
7
  })(ChangeType || (ChangeType = {}));
8
- export class DiffEngine {
9
- defaultOptions = {
10
- maxDepth: 100,
11
- detectDeletions: false,
12
- ignoreArrayOrder: false,
13
- equalityFn: (a, b) => a === b,
14
- keyValidator: undefined,
8
+ class DiffEngine {
9
+ defaultOptions = {
10
+ maxDepth: 100,
11
+ detectDeletions: false,
12
+ ignoreArrayOrder: false,
13
+ equalityFn: (a, b) => a === b,
14
+ keyValidator: undefined
15
+ };
16
+ diff(current, updates, options = {}) {
17
+ const opts = {
18
+ ...this.defaultOptions,
19
+ ...options
15
20
  };
16
- diff(current, updates, options = {}) {
17
- const opts = { ...this.defaultOptions, ...options };
18
- const changes = [];
19
- const visited = new WeakSet();
20
- this.traverse(current, updates, [], changes, visited, opts, 0);
21
- return {
22
- changes,
23
- hasChanges: changes.length > 0,
24
- };
21
+ const changes = [];
22
+ const visited = new WeakSet();
23
+ this.traverse(current, updates, [], changes, visited, opts, 0);
24
+ return {
25
+ changes,
26
+ hasChanges: changes.length > 0
27
+ };
28
+ }
29
+ traverse(curr, upd, path, changes, visited, opts, depth) {
30
+ if (depth > opts.maxDepth) {
31
+ return;
25
32
  }
26
- traverse(curr, upd, path, changes, visited, opts, depth) {
27
- if (depth > opts.maxDepth) {
28
- return;
29
- }
30
- if (typeof upd !== 'object' || upd === null) {
31
- if (!opts.equalityFn(curr, upd)) {
32
- changes.push({
33
- type: curr === undefined ? ChangeType.ADD : ChangeType.UPDATE,
34
- path: [...path],
35
- value: upd,
36
- oldValue: curr,
37
- });
38
- }
39
- return;
40
- }
41
- if (visited.has(upd)) {
42
- return;
43
- }
44
- visited.add(upd);
45
- if (Array.isArray(upd)) {
46
- this.diffArrays(curr, upd, path, changes, visited, opts, depth);
47
- return;
48
- }
49
- if (!curr || typeof curr !== 'object' || Array.isArray(curr)) {
50
- changes.push({
51
- type: ChangeType.REPLACE,
52
- path: [...path],
53
- value: upd,
54
- oldValue: curr,
55
- });
56
- return;
57
- }
58
- const currObj = curr;
59
- const updObj = upd;
60
- for (const key in updObj) {
61
- if (Object.prototype.hasOwnProperty.call(updObj, key)) {
62
- if (opts.keyValidator && !opts.keyValidator(key)) {
63
- continue;
64
- }
65
- this.traverse(currObj[key], updObj[key], [...path, key], changes, visited, opts, depth + 1);
66
- }
67
- }
68
- if (opts.detectDeletions) {
69
- for (const key in currObj) {
70
- if (Object.prototype.hasOwnProperty.call(currObj, key) &&
71
- !(key in updObj)) {
72
- changes.push({
73
- type: ChangeType.DELETE,
74
- path: [...path, key],
75
- oldValue: currObj[key],
76
- });
77
- }
78
- }
79
- }
33
+ if (typeof upd !== 'object' || upd === null) {
34
+ if (!opts.equalityFn(curr, upd)) {
35
+ changes.push({
36
+ type: curr === undefined ? ChangeType.ADD : ChangeType.UPDATE,
37
+ path: [...path],
38
+ value: upd,
39
+ oldValue: curr
40
+ });
41
+ }
42
+ return;
80
43
  }
81
- diffArrays(curr, upd, path, changes, visited, opts, depth) {
82
- if (!Array.isArray(curr)) {
83
- changes.push({
84
- type: ChangeType.REPLACE,
85
- path: [...path],
86
- value: upd,
87
- oldValue: curr,
88
- });
89
- return;
90
- }
91
- if (opts.ignoreArrayOrder) {
92
- this.diffArraysUnordered(curr, upd, path, changes, opts);
93
- }
94
- else {
95
- this.diffArraysOrdered(curr, upd, path, changes, visited, opts, depth);
44
+ if (visited.has(upd)) {
45
+ return;
46
+ }
47
+ visited.add(upd);
48
+ if (Array.isArray(upd)) {
49
+ this.diffArrays(curr, upd, path, changes, visited, opts, depth);
50
+ return;
51
+ }
52
+ if (!curr || typeof curr !== 'object' || Array.isArray(curr)) {
53
+ changes.push({
54
+ type: ChangeType.REPLACE,
55
+ path: [...path],
56
+ value: upd,
57
+ oldValue: curr
58
+ });
59
+ return;
60
+ }
61
+ const currObj = curr;
62
+ const updObj = upd;
63
+ for (const key in updObj) {
64
+ if (Object.prototype.hasOwnProperty.call(updObj, key)) {
65
+ if (opts.keyValidator && !opts.keyValidator(key)) {
66
+ continue;
96
67
  }
68
+ this.traverse(currObj[key], updObj[key], [...path, key], changes, visited, opts, depth + 1);
69
+ }
97
70
  }
98
- diffArraysOrdered(curr, upd, path, changes, visited, opts, depth) {
99
- const maxLength = Math.max(curr.length, upd.length);
100
- for (let i = 0; i < maxLength; i++) {
101
- if (i >= upd.length) {
102
- if (opts.detectDeletions) {
103
- changes.push({
104
- type: ChangeType.DELETE,
105
- path: [...path, i],
106
- oldValue: curr[i],
107
- });
108
- }
109
- }
110
- else if (i >= curr.length) {
111
- changes.push({
112
- type: ChangeType.ADD,
113
- path: [...path, i],
114
- value: upd[i],
115
- });
116
- }
117
- else {
118
- this.traverse(curr[i], upd[i], [...path, i], changes, visited, opts, depth + 1);
119
- }
71
+ if (opts.detectDeletions) {
72
+ for (const key in currObj) {
73
+ if (Object.prototype.hasOwnProperty.call(currObj, key) && !(key in updObj)) {
74
+ changes.push({
75
+ type: ChangeType.DELETE,
76
+ path: [...path, key],
77
+ oldValue: currObj[key]
78
+ });
120
79
  }
80
+ }
121
81
  }
122
- diffArraysUnordered(curr, upd, path, changes, opts) {
123
- const currSet = new Set(curr.map((v) => this.stringify(v)));
124
- const updSet = new Set(upd.map((v) => this.stringify(v)));
125
- upd.forEach((value, index) => {
126
- const str = this.stringify(value);
127
- if (!currSet.has(str)) {
128
- changes.push({
129
- type: ChangeType.ADD,
130
- path: [...path, index],
131
- value,
132
- });
133
- }
134
- });
82
+ }
83
+ diffArrays(curr, upd, path, changes, visited, opts, depth) {
84
+ if (!Array.isArray(curr)) {
85
+ changes.push({
86
+ type: ChangeType.REPLACE,
87
+ path: [...path],
88
+ value: upd,
89
+ oldValue: curr
90
+ });
91
+ return;
92
+ }
93
+ if (opts.ignoreArrayOrder) {
94
+ this.diffArraysUnordered(curr, upd, path, changes, opts);
95
+ } else {
96
+ this.diffArraysOrdered(curr, upd, path, changes, visited, opts, depth);
97
+ }
98
+ }
99
+ diffArraysOrdered(curr, upd, path, changes, visited, opts, depth) {
100
+ const maxLength = Math.max(curr.length, upd.length);
101
+ for (let i = 0; i < maxLength; i++) {
102
+ if (i >= upd.length) {
135
103
  if (opts.detectDeletions) {
136
- curr.forEach((value, index) => {
137
- const str = this.stringify(value);
138
- if (!updSet.has(str)) {
139
- changes.push({
140
- type: ChangeType.DELETE,
141
- path: [...path, index],
142
- oldValue: value,
143
- });
144
- }
145
- });
104
+ changes.push({
105
+ type: ChangeType.DELETE,
106
+ path: [...path, i],
107
+ oldValue: curr[i]
108
+ });
146
109
  }
110
+ } else if (i >= curr.length) {
111
+ changes.push({
112
+ type: ChangeType.ADD,
113
+ path: [...path, i],
114
+ value: upd[i]
115
+ });
116
+ } else {
117
+ this.traverse(curr[i], upd[i], [...path, i], changes, visited, opts, depth + 1);
118
+ }
147
119
  }
148
- stringify(value) {
149
- try {
150
- return JSON.stringify(value);
151
- }
152
- catch {
153
- return String(value);
120
+ }
121
+ diffArraysUnordered(curr, upd, path, changes, opts) {
122
+ const currSet = new Set(curr.map(v => this.stringify(v)));
123
+ const updSet = new Set(upd.map(v => this.stringify(v)));
124
+ upd.forEach((value, index) => {
125
+ const str = this.stringify(value);
126
+ if (!currSet.has(str)) {
127
+ changes.push({
128
+ type: ChangeType.ADD,
129
+ path: [...path, index],
130
+ value
131
+ });
132
+ }
133
+ });
134
+ if (opts.detectDeletions) {
135
+ curr.forEach((value, index) => {
136
+ const str = this.stringify(value);
137
+ if (!updSet.has(str)) {
138
+ changes.push({
139
+ type: ChangeType.DELETE,
140
+ path: [...path, index],
141
+ oldValue: value
142
+ });
154
143
  }
144
+ });
145
+ }
146
+ }
147
+ stringify(value) {
148
+ try {
149
+ return JSON.stringify(value);
150
+ } catch {
151
+ return String(value);
155
152
  }
153
+ }
156
154
  }
155
+
156
+ export { ChangeType, DiffEngine };