@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,154 +1,156 @@
1
1
  import { isSignal } from '@angular/core';
2
+
2
3
  class TrieNode {
3
- value = null;
4
- children = new Map();
4
+ value = null;
5
+ children = new Map();
5
6
  }
6
- export class PathIndex {
7
- root = new TrieNode();
8
- pathCache = new Map();
9
- stats = {
10
- hits: 0,
11
- misses: 0,
12
- sets: 0,
13
- cleanups: 0,
14
- };
15
- set(path, signal) {
16
- const pathStr = this.pathToString(path);
17
- const ref = new WeakRef(signal);
18
- let node = this.root;
19
- for (const segment of path) {
20
- const key = String(segment);
21
- if (!node.children.has(key)) {
22
- node.children.set(key, new TrieNode());
23
- }
24
- const nextNode = node.children.get(key);
25
- if (!nextNode) {
26
- throw new Error(`Failed to get node for key: ${key}`);
27
- }
28
- node = nextNode;
29
- }
30
- node.value = ref;
31
- this.pathCache.set(pathStr, ref);
32
- this.stats.sets++;
7
+ class PathIndex {
8
+ root = new TrieNode();
9
+ pathCache = new Map();
10
+ stats = {
11
+ hits: 0,
12
+ misses: 0,
13
+ sets: 0,
14
+ cleanups: 0
15
+ };
16
+ set(path, signal) {
17
+ const pathStr = this.pathToString(path);
18
+ const ref = new WeakRef(signal);
19
+ let node = this.root;
20
+ for (const segment of path) {
21
+ const key = String(segment);
22
+ if (!node.children.has(key)) {
23
+ node.children.set(key, new TrieNode());
24
+ }
25
+ const nextNode = node.children.get(key);
26
+ if (!nextNode) {
27
+ throw new Error(`Failed to get node for key: ${key}`);
28
+ }
29
+ node = nextNode;
30
+ }
31
+ node.value = ref;
32
+ this.pathCache.set(pathStr, ref);
33
+ this.stats.sets++;
34
+ }
35
+ get(path) {
36
+ const pathStr = this.pathToString(path);
37
+ const cached = this.pathCache.get(pathStr);
38
+ if (cached) {
39
+ const value = cached.deref();
40
+ if (value) {
41
+ this.stats.hits++;
42
+ return value;
43
+ }
44
+ this.pathCache.delete(pathStr);
45
+ this.stats.cleanups++;
33
46
  }
34
- get(path) {
35
- const pathStr = this.pathToString(path);
36
- const cached = this.pathCache.get(pathStr);
37
- if (cached) {
38
- const value = cached.deref();
39
- if (value) {
40
- this.stats.hits++;
41
- return value;
42
- }
43
- this.pathCache.delete(pathStr);
44
- this.stats.cleanups++;
45
- }
46
- let node = this.root;
47
- for (const segment of path) {
48
- const key = String(segment);
49
- node = node.children.get(key);
50
- if (!node) {
51
- this.stats.misses++;
52
- return null;
53
- }
54
- }
55
- if (node.value) {
56
- const value = node.value.deref();
57
- if (value) {
58
- this.pathCache.set(pathStr, node.value);
59
- this.stats.hits++;
60
- return value;
61
- }
62
- node.value = null;
63
- this.stats.cleanups++;
64
- }
47
+ let node = this.root;
48
+ for (const segment of path) {
49
+ const key = String(segment);
50
+ node = node.children.get(key);
51
+ if (!node) {
65
52
  this.stats.misses++;
66
53
  return null;
54
+ }
67
55
  }
68
- has(path) {
69
- return this.get(path) !== null;
56
+ if (node.value) {
57
+ const value = node.value.deref();
58
+ if (value) {
59
+ this.pathCache.set(pathStr, node.value);
60
+ this.stats.hits++;
61
+ return value;
62
+ }
63
+ node.value = null;
64
+ this.stats.cleanups++;
70
65
  }
71
- getByPrefix(prefix) {
72
- const results = new Map();
73
- let node = this.root;
74
- for (const segment of prefix) {
75
- const key = String(segment);
76
- node = node.children.get(key);
77
- if (!node) {
78
- return results;
79
- }
80
- }
81
- this.collectDescendants(node, [], results);
66
+ this.stats.misses++;
67
+ return null;
68
+ }
69
+ has(path) {
70
+ return this.get(path) !== null;
71
+ }
72
+ getByPrefix(prefix) {
73
+ const results = new Map();
74
+ let node = this.root;
75
+ for (const segment of prefix) {
76
+ const key = String(segment);
77
+ node = node.children.get(key);
78
+ if (!node) {
82
79
  return results;
80
+ }
81
+ }
82
+ this.collectDescendants(node, [], results);
83
+ return results;
84
+ }
85
+ delete(path) {
86
+ const pathStr = this.pathToString(path);
87
+ this.pathCache.delete(pathStr);
88
+ let node = this.root;
89
+ const nodes = [node];
90
+ for (const segment of path) {
91
+ const key = String(segment);
92
+ node = node.children.get(key);
93
+ if (!node) {
94
+ return false;
95
+ }
96
+ nodes.push(node);
83
97
  }
84
- delete(path) {
85
- const pathStr = this.pathToString(path);
86
- this.pathCache.delete(pathStr);
87
- let node = this.root;
88
- const nodes = [node];
89
- for (const segment of path) {
90
- const key = String(segment);
91
- node = node.children.get(key);
92
- if (!node) {
93
- return false;
94
- }
95
- nodes.push(node);
96
- }
97
- const hadValue = node.value !== null;
98
- node.value = null;
99
- for (let i = nodes.length - 1; i > 0; i--) {
100
- const current = nodes[i];
101
- if (current.value === null && current.children.size === 0) {
102
- const parent = nodes[i - 1];
103
- const segment = path[i - 1];
104
- parent.children.delete(String(segment));
105
- }
106
- else {
107
- break;
108
- }
109
- }
110
- return hadValue;
98
+ const hadValue = node.value !== null;
99
+ node.value = null;
100
+ for (let i = nodes.length - 1; i > 0; i--) {
101
+ const current = nodes[i];
102
+ if (current.value === null && current.children.size === 0) {
103
+ const parent = nodes[i - 1];
104
+ const segment = path[i - 1];
105
+ parent.children.delete(String(segment));
106
+ } else {
107
+ break;
108
+ }
109
+ }
110
+ return hadValue;
111
+ }
112
+ clear() {
113
+ this.root = new TrieNode();
114
+ this.pathCache.clear();
115
+ }
116
+ getStats() {
117
+ const total = this.stats.hits + this.stats.misses;
118
+ const hitRate = total > 0 ? this.stats.hits / total : 0;
119
+ return {
120
+ ...this.stats,
121
+ hitRate,
122
+ cacheSize: this.pathCache.size
123
+ };
124
+ }
125
+ buildFromTree(tree, path = []) {
126
+ if (!tree) {
127
+ return;
111
128
  }
112
- clear() {
113
- this.root = new TrieNode();
114
- this.pathCache.clear();
129
+ if (isSignal(tree)) {
130
+ this.set(path, tree);
131
+ return;
115
132
  }
116
- getStats() {
117
- const total = this.stats.hits + this.stats.misses;
118
- const hitRate = total > 0 ? this.stats.hits / total : 0;
119
- return {
120
- ...this.stats,
121
- hitRate,
122
- cacheSize: this.pathCache.size,
123
- };
133
+ if (typeof tree !== 'object') {
134
+ return;
124
135
  }
125
- buildFromTree(tree, path = []) {
126
- if (!tree) {
127
- return;
128
- }
129
- if (isSignal(tree)) {
130
- this.set(path, tree);
131
- return;
132
- }
133
- if (typeof tree !== 'object') {
134
- return;
135
- }
136
- for (const [key, value] of Object.entries(tree)) {
137
- this.buildFromTree(value, [...path, key]);
138
- }
136
+ for (const [key, value] of Object.entries(tree)) {
137
+ this.buildFromTree(value, [...path, key]);
139
138
  }
140
- pathToString(path) {
141
- return path.join('.');
139
+ }
140
+ pathToString(path) {
141
+ return path.join('.');
142
+ }
143
+ collectDescendants(node, currentPath, results) {
144
+ if (node.value) {
145
+ const value = node.value.deref();
146
+ if (value) {
147
+ results.set(this.pathToString(currentPath), value);
148
+ }
142
149
  }
143
- collectDescendants(node, currentPath, results) {
144
- if (node.value) {
145
- const value = node.value.deref();
146
- if (value) {
147
- results.set(this.pathToString(currentPath), value);
148
- }
149
- }
150
- for (const [key, child] of node.children) {
151
- this.collectDescendants(child, [...currentPath, key], results);
152
- }
150
+ for (const [key, child] of node.children) {
151
+ this.collectDescendants(child, [...currentPath, key], results);
153
152
  }
153
+ }
154
154
  }
155
+
156
+ export { PathIndex };
@@ -1,193 +1,188 @@
1
1
  import { isSignal } from '@angular/core';
2
- import { ChangeType, DiffEngine } from './diff-engine';
3
- import { PathIndex } from './path-index';
4
- export class OptimizedUpdateEngine {
5
- pathIndex;
6
- diffEngine;
7
- constructor(tree) {
8
- this.pathIndex = new PathIndex();
9
- this.diffEngine = new DiffEngine();
10
- this.pathIndex.buildFromTree(tree);
2
+ import { DiffEngine, ChangeType } from './diff-engine.js';
3
+ import { PathIndex } from './path-index.js';
4
+
5
+ class OptimizedUpdateEngine {
6
+ pathIndex;
7
+ diffEngine;
8
+ constructor(tree) {
9
+ this.pathIndex = new PathIndex();
10
+ this.diffEngine = new DiffEngine();
11
+ this.pathIndex.buildFromTree(tree);
12
+ }
13
+ update(tree, updates, options = {}) {
14
+ const startTime = performance.now();
15
+ const diffOptions = {};
16
+ if (options.maxDepth !== undefined) diffOptions.maxDepth = options.maxDepth;
17
+ if (options.ignoreArrayOrder !== undefined) diffOptions.ignoreArrayOrder = options.ignoreArrayOrder;
18
+ if (options.equalityFn !== undefined) diffOptions.equalityFn = options.equalityFn;
19
+ const diff = this.diffEngine.diff(tree, updates, diffOptions);
20
+ if (diff.changes.length === 0) {
21
+ return {
22
+ changed: false,
23
+ duration: performance.now() - startTime,
24
+ changedPaths: []
25
+ };
11
26
  }
12
- update(tree, updates, options = {}) {
13
- const startTime = performance.now();
14
- const diffOptions = {};
15
- if (options.maxDepth !== undefined)
16
- diffOptions.maxDepth = options.maxDepth;
17
- if (options.ignoreArrayOrder !== undefined)
18
- diffOptions.ignoreArrayOrder = options.ignoreArrayOrder;
19
- if (options.equalityFn !== undefined)
20
- diffOptions.equalityFn = options.equalityFn;
21
- const diff = this.diffEngine.diff(tree, updates, diffOptions);
22
- if (diff.changes.length === 0) {
23
- return {
24
- changed: false,
25
- duration: performance.now() - startTime,
26
- changedPaths: [],
27
- };
27
+ const patches = this.createPatches(diff.changes);
28
+ const sortedPatches = this.sortPatches(patches);
29
+ const result = options.batch ? this.batchApplyPatches(tree, sortedPatches, options.batchSize) : this.applyPatches(tree, sortedPatches);
30
+ const duration = performance.now() - startTime;
31
+ return {
32
+ changed: true,
33
+ duration,
34
+ changedPaths: result.appliedPaths,
35
+ stats: {
36
+ totalPaths: diff.changes.length,
37
+ optimizedPaths: patches.length,
38
+ batchedUpdates: result.batchCount
39
+ }
40
+ };
41
+ }
42
+ rebuildIndex(tree) {
43
+ this.pathIndex.clear();
44
+ this.pathIndex.buildFromTree(tree);
45
+ }
46
+ getIndexStats() {
47
+ return this.pathIndex.getStats();
48
+ }
49
+ createPatches(changes) {
50
+ const patches = [];
51
+ const processedPaths = new Set();
52
+ for (const change of changes) {
53
+ const pathStr = change.path.join('.');
54
+ let skipPath = false;
55
+ for (const processed of processedPaths) {
56
+ if (pathStr.startsWith(processed + '.')) {
57
+ skipPath = true;
58
+ break;
28
59
  }
29
- const patches = this.createPatches(diff.changes);
30
- const sortedPatches = this.sortPatches(patches);
31
- const result = options.batch
32
- ? this.batchApplyPatches(tree, sortedPatches, options.batchSize)
33
- : this.applyPatches(tree, sortedPatches);
34
- const duration = performance.now() - startTime;
35
- return {
36
- changed: true,
37
- duration,
38
- changedPaths: result.appliedPaths,
39
- stats: {
40
- totalPaths: diff.changes.length,
41
- optimizedPaths: patches.length,
42
- batchedUpdates: result.batchCount,
43
- },
44
- };
60
+ }
61
+ if (skipPath) {
62
+ continue;
63
+ }
64
+ const patch = this.createPatch(change);
65
+ patches.push(patch);
66
+ processedPaths.add(pathStr);
67
+ if (change.type === ChangeType.REPLACE && typeof change.value === 'object') {
68
+ processedPaths.add(pathStr);
69
+ }
45
70
  }
46
- rebuildIndex(tree) {
47
- this.pathIndex.clear();
48
- this.pathIndex.buildFromTree(tree);
71
+ return patches;
72
+ }
73
+ createPatch(change) {
74
+ return {
75
+ type: change.type,
76
+ path: change.path,
77
+ value: change.value,
78
+ oldValue: change.oldValue,
79
+ priority: this.calculatePriority(change),
80
+ signal: this.pathIndex.get(change.path)
81
+ };
82
+ }
83
+ calculatePriority(change) {
84
+ let priority = 0;
85
+ priority += (10 - change.path.length) * 10;
86
+ if (change.path.some(p => typeof p === 'number')) {
87
+ priority -= 20;
49
88
  }
50
- getIndexStats() {
51
- return this.pathIndex.getStats();
89
+ if (change.type === ChangeType.REPLACE) {
90
+ priority += 30;
52
91
  }
53
- createPatches(changes) {
54
- const patches = [];
55
- const processedPaths = new Set();
56
- for (const change of changes) {
57
- const pathStr = change.path.join('.');
58
- let skipPath = false;
59
- for (const processed of processedPaths) {
60
- if (pathStr.startsWith(processed + '.')) {
61
- skipPath = true;
62
- break;
63
- }
64
- }
65
- if (skipPath) {
66
- continue;
67
- }
68
- const patch = this.createPatch(change);
69
- patches.push(patch);
70
- processedPaths.add(pathStr);
71
- if (change.type === ChangeType.REPLACE &&
72
- typeof change.value === 'object') {
73
- processedPaths.add(pathStr);
74
- }
75
- }
76
- return patches;
92
+ return priority;
93
+ }
94
+ sortPatches(patches) {
95
+ return patches.sort((a, b) => {
96
+ if (a.priority !== b.priority) {
97
+ return b.priority - a.priority;
98
+ }
99
+ return a.path.length - b.path.length;
100
+ });
101
+ }
102
+ applyPatches(tree, patches) {
103
+ const appliedPaths = [];
104
+ let updateCount = 0;
105
+ for (const patch of patches) {
106
+ if (this.applyPatch(patch, tree)) {
107
+ appliedPaths.push(patch.path.join('.'));
108
+ updateCount++;
109
+ }
77
110
  }
78
- createPatch(change) {
79
- return {
80
- type: change.type,
81
- path: change.path,
82
- value: change.value,
83
- oldValue: change.oldValue,
84
- priority: this.calculatePriority(change),
85
- signal: this.pathIndex.get(change.path),
86
- };
111
+ return {
112
+ appliedPaths,
113
+ updateCount,
114
+ batchCount: 1
115
+ };
116
+ }
117
+ batchApplyPatches(tree, patches, batchSize = 50) {
118
+ const batches = [];
119
+ for (let i = 0; i < patches.length; i += batchSize) {
120
+ batches.push(patches.slice(i, i + batchSize));
87
121
  }
88
- calculatePriority(change) {
89
- let priority = 0;
90
- priority += (10 - change.path.length) * 10;
91
- if (change.path.some((p) => typeof p === 'number')) {
92
- priority -= 20;
93
- }
94
- if (change.type === ChangeType.REPLACE) {
95
- priority += 30;
122
+ const appliedPaths = [];
123
+ let updateCount = 0;
124
+ for (const currentBatch of batches) {
125
+ for (const patch of currentBatch) {
126
+ if (this.applyPatch(patch, tree)) {
127
+ appliedPaths.push(patch.path.join('.'));
128
+ updateCount++;
96
129
  }
97
- return priority;
130
+ }
98
131
  }
99
- sortPatches(patches) {
100
- return patches.sort((a, b) => {
101
- if (a.priority !== b.priority) {
102
- return b.priority - a.priority;
103
- }
104
- return a.path.length - b.path.length;
105
- });
106
- }
107
- applyPatches(tree, patches) {
108
- const appliedPaths = [];
109
- let updateCount = 0;
110
- for (const patch of patches) {
111
- if (this.applyPatch(patch, tree)) {
112
- appliedPaths.push(patch.path.join('.'));
113
- updateCount++;
114
- }
132
+ return {
133
+ appliedPaths,
134
+ updateCount,
135
+ batchCount: batches.length
136
+ };
137
+ }
138
+ applyPatch(patch, tree) {
139
+ try {
140
+ if (patch.signal && isSignal(patch.signal) && 'set' in patch.signal) {
141
+ const currentValue = patch.signal();
142
+ if (this.isEqual(currentValue, patch.value)) {
143
+ return false;
115
144
  }
116
- return {
117
- appliedPaths,
118
- updateCount,
119
- batchCount: 1,
120
- };
121
- }
122
- batchApplyPatches(tree, patches, batchSize = 50) {
123
- const batches = [];
124
- for (let i = 0; i < patches.length; i += batchSize) {
125
- batches.push(patches.slice(i, i + batchSize));
145
+ patch.signal.set(patch.value);
146
+ if (patch.type === ChangeType.ADD && patch.value !== undefined) {
147
+ this.pathIndex.set(patch.path, patch.signal);
126
148
  }
127
- const appliedPaths = [];
128
- let updateCount = 0;
129
- for (const currentBatch of batches) {
130
- for (const patch of currentBatch) {
131
- if (this.applyPatch(patch, tree)) {
132
- appliedPaths.push(patch.path.join('.'));
133
- updateCount++;
134
- }
135
- }
149
+ return true;
150
+ }
151
+ let current = tree;
152
+ for (let i = 0; i < patch.path.length - 1; i++) {
153
+ const key = patch.path[i];
154
+ current = current[key];
155
+ if (!current || typeof current !== 'object') {
156
+ return false;
136
157
  }
137
- return {
138
- appliedPaths,
139
- updateCount,
140
- batchCount: batches.length,
141
- };
158
+ }
159
+ const lastKey = patch.path[patch.path.length - 1];
160
+ if (this.isEqual(current[lastKey], patch.value)) {
161
+ return false;
162
+ }
163
+ current[lastKey] = patch.value;
164
+ return true;
165
+ } catch (error) {
166
+ console.error(`Failed to apply patch at ${patch.path.join('.')}:`, error);
167
+ return false;
142
168
  }
143
- applyPatch(patch, tree) {
144
- try {
145
- if (patch.signal && isSignal(patch.signal) && 'set' in patch.signal) {
146
- const currentValue = patch.signal();
147
- if (this.isEqual(currentValue, patch.value)) {
148
- return false;
149
- }
150
- patch.signal.set(patch.value);
151
- if (patch.type === ChangeType.ADD && patch.value !== undefined) {
152
- this.pathIndex.set(patch.path, patch.signal);
153
- }
154
- return true;
155
- }
156
- let current = tree;
157
- for (let i = 0; i < patch.path.length - 1; i++) {
158
- const key = patch.path[i];
159
- current = current[key];
160
- if (!current || typeof current !== 'object') {
161
- return false;
162
- }
163
- }
164
- const lastKey = patch.path[patch.path.length - 1];
165
- if (this.isEqual(current[lastKey], patch.value)) {
166
- return false;
167
- }
168
- current[lastKey] = patch.value;
169
- return true;
170
- }
171
- catch (error) {
172
- console.error(`Failed to apply patch at ${patch.path.join('.')}:`, error);
173
- return false;
174
- }
169
+ }
170
+ isEqual(a, b) {
171
+ if (a === b) {
172
+ return true;
175
173
  }
176
- isEqual(a, b) {
177
- if (a === b) {
178
- return true;
179
- }
180
- if (typeof a !== typeof b) {
181
- return false;
182
- }
183
- if (typeof a !== 'object' || a === null || b === null) {
184
- return false;
185
- }
186
- try {
187
- return JSON.stringify(a) === JSON.stringify(b);
188
- }
189
- catch {
190
- return false;
191
- }
174
+ if (typeof a !== typeof b) {
175
+ return false;
176
+ }
177
+ if (typeof a !== 'object' || a === null || b === null) {
178
+ return false;
179
+ }
180
+ try {
181
+ return JSON.stringify(a) === JSON.stringify(b);
182
+ } catch {
183
+ return false;
192
184
  }
185
+ }
193
186
  }
187
+
188
+ export { OptimizedUpdateEngine };