@signaltree/core 5.1.6 → 6.0.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/README.md +76 -76
- package/dist/enhancers/batching/{lib/batching.js → batching.js} +45 -17
- package/dist/enhancers/devtools/{lib/devtools.js → devtools.js} +83 -98
- package/dist/enhancers/effects/effects.js +66 -0
- package/dist/enhancers/entities/entities.js +51 -0
- package/dist/enhancers/memoization/{lib/memoization.js → memoization.js} +33 -23
- package/dist/enhancers/presets/lib/presets.js +5 -65
- package/dist/enhancers/serialization/{lib/serialization.js → serialization.js} +12 -18
- package/dist/enhancers/time-travel/{lib/time-travel.js → time-travel.js} +62 -24
- package/dist/enhancers/time-travel/utils.js +11 -0
- package/dist/index.js +8 -8
- package/dist/lib/async-helpers.js +6 -6
- package/dist/lib/presets.js +21 -0
- package/dist/lib/signal-tree.js +156 -496
- package/dist/lib/types.js +1 -1
- package/dist/lib/utils.js +40 -2
- package/package.json +1 -6
- package/src/enhancers/batching/batching.d.ts +11 -0
- package/src/enhancers/batching/index.d.ts +1 -1
- package/src/enhancers/devtools/{lib/devtools.d.ts → devtools.d.ts} +10 -19
- package/src/enhancers/devtools/devtools.types.d.ts +1 -0
- package/src/enhancers/devtools/index.d.ts +1 -1
- package/src/enhancers/effects/effects.d.ts +9 -0
- package/src/enhancers/effects/effects.types.d.ts +1 -0
- package/src/enhancers/effects/index.d.ts +1 -0
- package/src/enhancers/entities/entities.d.ts +11 -0
- package/src/enhancers/entities/entities.types.d.ts +1 -0
- package/src/enhancers/entities/index.d.ts +1 -1
- package/src/enhancers/index.d.ts +3 -3
- package/src/enhancers/memoization/index.d.ts +1 -1
- package/src/enhancers/memoization/memoization.d.ts +54 -0
- package/src/enhancers/memoization/memoization.types.d.ts +1 -0
- package/src/enhancers/presets/lib/presets.d.ts +3 -6
- package/src/enhancers/serialization/index.d.ts +1 -1
- package/src/{serialization.d.ts → enhancers/serialization/serialization.d.ts} +17 -8
- package/src/enhancers/test-helpers/types-equals.d.ts +2 -0
- package/src/enhancers/time-travel/index.d.ts +1 -1
- package/src/enhancers/time-travel/time-travel.d.ts +10 -0
- package/src/enhancers/time-travel/time-travel.types.d.ts +1 -0
- package/src/enhancers/time-travel/utils.d.ts +2 -0
- package/src/enhancers/types.d.ts +1 -74
- package/src/enhancers/typing/helpers-types.d.ts +2 -0
- package/src/index.d.ts +7 -8
- package/src/lib/async-helpers.d.ts +2 -2
- package/src/lib/dev-proxy.d.ts +3 -0
- package/src/lib/presets.d.ts +34 -0
- package/src/lib/signal-tree.d.ts +2 -7
- package/src/lib/types.d.ts +121 -90
- package/src/lib/utils.d.ts +4 -0
- package/dist/deep-clone.js +0 -80
- package/dist/enhancers/computed/lib/computed.js +0 -21
- package/dist/enhancers/entities/lib/entities.js +0 -66
- package/dist/lib/performance/diff-engine.js +0 -156
- package/dist/lib/performance/path-index.js +0 -156
- package/dist/lib/performance/update-engine.js +0 -188
- package/src/async-helpers.d.ts +0 -8
- package/src/batching.d.ts +0 -16
- package/src/computed.d.ts +0 -12
- package/src/constants.d.ts +0 -14
- package/src/devtools.d.ts +0 -77
- package/src/diff-engine.d.ts +0 -33
- package/src/enhancers/batching/lib/batching.d.ts +0 -16
- package/src/enhancers/computed/index.d.ts +0 -1
- package/src/enhancers/computed/lib/computed.d.ts +0 -12
- package/src/enhancers/entities/lib/entities.d.ts +0 -17
- package/src/enhancers/memoization/lib/memoization.d.ts +0 -65
- package/src/enhancers/presets/test-setup.d.ts +0 -3
- package/src/enhancers/serialization/lib/serialization.d.ts +0 -59
- package/src/enhancers/time-travel/lib/time-travel.d.ts +0 -36
- package/src/enhancers/time-travel/lib/utils.d.ts +0 -1
- package/src/entities.d.ts +0 -28
- package/src/memoization.d.ts +0 -65
- package/src/memory-manager.d.ts +0 -30
- package/src/path-index.d.ts +0 -25
- package/src/path-notifier.d.ts +0 -12
- package/src/presets.d.ts +0 -11
- package/src/security-validator.d.ts +0 -33
- package/src/signal-tree.d.ts +0 -8
- package/src/test-setup.d.ts +0 -3
- package/src/time-travel.d.ts +0 -36
- package/src/types.d.ts +0 -436
- package/src/update-engine.d.ts +0 -32
- package/src/utils.d.ts +0 -1
- /package/src/{entity-signal.d.ts → enhancers/batching/batching.types.d.ts} +0 -0
package/dist/lib/signal-tree.js
CHANGED
|
@@ -1,80 +1,20 @@
|
|
|
1
|
-
import { signal, isSignal
|
|
2
|
-
import { resolveEnhancerOrder } from '../enhancers/index.js';
|
|
1
|
+
import { signal, isSignal } from '@angular/core';
|
|
3
2
|
import { SIGNAL_TREE_MESSAGES, SIGNAL_TREE_CONSTANTS } from './constants.js';
|
|
4
3
|
import { SignalMemoryManager } from './memory/memory-manager.js';
|
|
5
|
-
import { OptimizedUpdateEngine } from './performance/update-engine.js';
|
|
6
4
|
import { SecurityValidator } from './security/security-validator.js';
|
|
7
5
|
import { createLazySignalTree, unwrap } from './utils.js';
|
|
8
6
|
import { deepEqual } from '../deep-equal.js';
|
|
9
7
|
import { isBuiltInObject } from '../is-built-in-object.js';
|
|
10
8
|
|
|
11
|
-
const NODE_ACCESSOR_SYMBOL = Symbol.for('NodeAccessor');
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
if (arguments.length === 0) {
|
|
15
|
-
return unwrap(accessor);
|
|
16
|
-
}
|
|
17
|
-
if (typeof arg === 'function') {
|
|
18
|
-
const updater = arg;
|
|
19
|
-
const currentValue = unwrap(accessor);
|
|
20
|
-
const newValue = updater(currentValue);
|
|
21
|
-
recursiveUpdate(accessor, newValue);
|
|
22
|
-
} else {
|
|
23
|
-
recursiveUpdate(accessor, arg);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
accessor[NODE_ACCESSOR_SYMBOL] = true;
|
|
27
|
-
return accessor;
|
|
28
|
-
}
|
|
29
|
-
function makeRootNodeAccessor(readSignal, writeSignal) {
|
|
30
|
-
const accessor = function (arg) {
|
|
31
|
-
if (arguments.length === 0) {
|
|
32
|
-
return readSignal();
|
|
33
|
-
}
|
|
34
|
-
if (typeof arg === 'function') {
|
|
35
|
-
const updater = arg;
|
|
36
|
-
writeSignal.set(updater(readSignal()));
|
|
37
|
-
} else {
|
|
38
|
-
writeSignal.set(arg);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
accessor[NODE_ACCESSOR_SYMBOL] = true;
|
|
42
|
-
return accessor;
|
|
9
|
+
const NODE_ACCESSOR_SYMBOL = Symbol.for('SignalTree:NodeAccessor');
|
|
10
|
+
function isNodeAccessor(value) {
|
|
11
|
+
return typeof value === 'function' && value[NODE_ACCESSOR_SYMBOL] === true;
|
|
43
12
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
let targetObj;
|
|
49
|
-
if (isNodeAccessor(target)) {
|
|
50
|
-
targetObj = target;
|
|
51
|
-
} else if (target && typeof target === 'object') {
|
|
52
|
-
targetObj = target;
|
|
53
|
-
} else {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const updatesObj = updates;
|
|
57
|
-
for (const key in updatesObj) {
|
|
58
|
-
if (!(key in targetObj)) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const targetProp = targetObj[key];
|
|
62
|
-
const updateValue = updatesObj[key];
|
|
63
|
-
if (isSignal(targetProp)) {
|
|
64
|
-
if ('set' in targetProp && typeof targetProp.set === 'function') {
|
|
65
|
-
targetProp.set(updateValue);
|
|
66
|
-
}
|
|
67
|
-
} else if (isNodeAccessor(targetProp)) {
|
|
68
|
-
if (updateValue && typeof updateValue === 'object') {
|
|
69
|
-
recursiveUpdate(targetProp, updateValue);
|
|
70
|
-
} else {
|
|
71
|
-
targetProp(updateValue);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
13
|
+
function isEntityMapMarker(value) {
|
|
14
|
+
return Boolean(value && typeof value === 'object' && value['__isEntityMap'] === true);
|
|
75
15
|
}
|
|
76
|
-
function
|
|
77
|
-
return
|
|
16
|
+
function createEqualityFn(useShallowComparison) {
|
|
17
|
+
return useShallowComparison ? Object.is : deepEqual;
|
|
78
18
|
}
|
|
79
19
|
function estimateObjectSize(obj, maxDepth = SIGNAL_TREE_CONSTANTS.ESTIMATE_MAX_DEPTH, currentDepth = 0) {
|
|
80
20
|
if (currentDepth >= maxDepth) return 1;
|
|
@@ -105,56 +45,79 @@ function estimateObjectSize(obj, maxDepth = SIGNAL_TREE_CONSTANTS.ESTIMATE_MAX_D
|
|
|
105
45
|
function shouldUseLazy(obj, config, precomputedSize) {
|
|
106
46
|
if (config.useLazySignals !== undefined) return config.useLazySignals;
|
|
107
47
|
if (config.debugMode || config.enableDevTools) return false;
|
|
108
|
-
if (config.batchUpdates && config.useMemoization) return true;
|
|
109
48
|
const estimatedSize = precomputedSize ?? estimateObjectSize(obj);
|
|
110
49
|
return estimatedSize > SIGNAL_TREE_CONSTANTS.LAZY_THRESHOLD;
|
|
111
50
|
}
|
|
112
|
-
function validateTree(obj, config
|
|
113
|
-
if (!config.security)
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
51
|
+
function validateTree(obj, config) {
|
|
52
|
+
if (!config.security) return;
|
|
116
53
|
const validator = new SecurityValidator(config.security);
|
|
117
|
-
function validate(value,
|
|
118
|
-
if (value === null || value === undefined)
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
54
|
+
function validate(value, path) {
|
|
55
|
+
if (value === null || value === undefined) return;
|
|
121
56
|
if (typeof value !== 'object') {
|
|
122
57
|
validator.validateValue(value);
|
|
123
58
|
return;
|
|
124
59
|
}
|
|
125
|
-
if (isBuiltInObject(value))
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
60
|
+
if (isBuiltInObject(value)) return;
|
|
128
61
|
if (Array.isArray(value)) {
|
|
129
|
-
value.forEach((item,
|
|
130
|
-
validate(item, [...currentPath, String(index)]);
|
|
131
|
-
});
|
|
62
|
+
value.forEach((item, i) => validate(item, [...path, String(i)]));
|
|
132
63
|
return;
|
|
133
64
|
}
|
|
134
|
-
const
|
|
135
|
-
const uniqueKeys = [...new Set(keys)];
|
|
136
|
-
for (const key of uniqueKeys) {
|
|
137
|
-
if (typeof key === 'symbol') continue;
|
|
65
|
+
for (const key of Object.keys(value)) {
|
|
138
66
|
try {
|
|
139
67
|
validator.validateKey(key);
|
|
140
68
|
} catch (error) {
|
|
141
|
-
|
|
142
|
-
throw new Error(`${err.message}\nPath: ${[...currentPath, key].join('.')}`);
|
|
69
|
+
throw new Error(`${error.message}\nPath: ${[...path, key].join('.')}`);
|
|
143
70
|
}
|
|
144
71
|
const val = value[key];
|
|
145
72
|
try {
|
|
146
73
|
validator.validateValue(val);
|
|
147
74
|
} catch (error) {
|
|
148
|
-
|
|
149
|
-
throw new Error(`${err.message}\nPath: ${[...currentPath, key].join('.')}`);
|
|
75
|
+
throw new Error(`${error.message}\nPath: ${[...path, key].join('.')}`);
|
|
150
76
|
}
|
|
151
|
-
validate(val, [...
|
|
77
|
+
validate(val, [...path, key]);
|
|
152
78
|
}
|
|
153
79
|
}
|
|
154
|
-
validate(obj,
|
|
80
|
+
validate(obj, []);
|
|
155
81
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
82
|
+
function makeNodeAccessor(store) {
|
|
83
|
+
const accessor = function (arg) {
|
|
84
|
+
if (arguments.length === 0) {
|
|
85
|
+
return unwrap(store);
|
|
86
|
+
}
|
|
87
|
+
if (typeof arg === 'function') {
|
|
88
|
+
const updater = arg;
|
|
89
|
+
const current = unwrap(store);
|
|
90
|
+
recursiveUpdate(store, updater(current));
|
|
91
|
+
} else {
|
|
92
|
+
recursiveUpdate(store, arg);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
accessor[NODE_ACCESSOR_SYMBOL] = true;
|
|
96
|
+
for (const key of Object.keys(store)) {
|
|
97
|
+
Object.defineProperty(accessor, key, {
|
|
98
|
+
value: store[key],
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: true
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return accessor;
|
|
104
|
+
}
|
|
105
|
+
function recursiveUpdate(target, updates) {
|
|
106
|
+
if (!updates || typeof updates !== 'object') return;
|
|
107
|
+
const targetObj = isNodeAccessor(target) ? target : target;
|
|
108
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
109
|
+
const prop = targetObj[key];
|
|
110
|
+
if (prop === undefined) continue;
|
|
111
|
+
if (isSignal(prop) && 'set' in prop) {
|
|
112
|
+
prop.set(value);
|
|
113
|
+
} else if (isNodeAccessor(prop)) {
|
|
114
|
+
if (value && typeof value === 'object') {
|
|
115
|
+
recursiveUpdate(prop, value);
|
|
116
|
+
} else {
|
|
117
|
+
prop(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
158
121
|
}
|
|
159
122
|
function createSignalStore(obj, equalityFn) {
|
|
160
123
|
if (obj === null || obj === undefined || typeof obj !== 'object') {
|
|
@@ -173,355 +136,53 @@ function createSignalStore(obj, equalityFn) {
|
|
|
173
136
|
});
|
|
174
137
|
}
|
|
175
138
|
const store = {};
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
equal: equalityFn
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
processedObjects.add(obj);
|
|
184
|
-
try {
|
|
185
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
186
|
-
try {
|
|
187
|
-
if (typeof key === 'symbol') continue;
|
|
188
|
-
if (isEntityMapMarker(value)) {
|
|
189
|
-
store[key] = value;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (isSignal(value)) {
|
|
193
|
-
store[key] = value;
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (value === null || value === undefined) {
|
|
197
|
-
store[key] = signal(value, {
|
|
198
|
-
equal: equalityFn
|
|
199
|
-
});
|
|
200
|
-
} else if (typeof value !== 'object') {
|
|
201
|
-
store[key] = signal(value, {
|
|
202
|
-
equal: equalityFn
|
|
203
|
-
});
|
|
204
|
-
} else if (Array.isArray(value) || isBuiltInObject(value)) {
|
|
205
|
-
store[key] = signal(value, {
|
|
206
|
-
equal: equalityFn
|
|
207
|
-
});
|
|
208
|
-
} else {
|
|
209
|
-
const branch = createSignalStore(value, equalityFn);
|
|
210
|
-
const callableBranch = makeNodeAccessor();
|
|
211
|
-
for (const branchKey in branch) {
|
|
212
|
-
if (Object.prototype.hasOwnProperty.call(branch, branchKey)) {
|
|
213
|
-
try {
|
|
214
|
-
Object.defineProperty(callableBranch, branchKey, {
|
|
215
|
-
value: branch[branchKey],
|
|
216
|
-
enumerable: true,
|
|
217
|
-
configurable: true
|
|
218
|
-
});
|
|
219
|
-
} catch {}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
store[key] = callableBranch;
|
|
223
|
-
}
|
|
224
|
-
} catch (error) {
|
|
225
|
-
console.warn(`${SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED} "${key}":`, error);
|
|
226
|
-
store[key] = signal(value, {
|
|
227
|
-
equal: equalityFn
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const symbols = Object.getOwnPropertySymbols(obj);
|
|
232
|
-
for (const sym of symbols) {
|
|
233
|
-
const value = obj[sym];
|
|
234
|
-
try {
|
|
235
|
-
if (isEntityMapMarker(value)) {
|
|
236
|
-
store[sym] = value;
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (isSignal(value)) {
|
|
240
|
-
store[sym] = value;
|
|
241
|
-
} else {
|
|
242
|
-
store[sym] = signal(value, {
|
|
243
|
-
equal: equalityFn
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
} catch (error) {
|
|
247
|
-
console.warn(SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED, error);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
} catch (error) {
|
|
251
|
-
throw new Error(`Failed to create signal store: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
252
|
-
}
|
|
253
|
-
return store;
|
|
254
|
-
}
|
|
255
|
-
function isEntityMapMarker(value) {
|
|
256
|
-
return Boolean(value && typeof value === 'object' && value['__isEntityMap'] === true);
|
|
257
|
-
}
|
|
258
|
-
function enhanceTree(tree, config = {}) {
|
|
259
|
-
const isLazy = config.useLazySignals ?? shouldUseLazy(tree.state, config);
|
|
260
|
-
tree.with = (...enhancers) => {
|
|
261
|
-
if (enhancers.length === 0) {
|
|
262
|
-
return tree;
|
|
263
|
-
}
|
|
264
|
-
const coreCapabilities = new Set();
|
|
265
|
-
if (config.batchUpdates) coreCapabilities.add('batchUpdate');
|
|
266
|
-
if (config.useMemoization) coreCapabilities.add('memoize');
|
|
267
|
-
if (config.enableTimeTravel) coreCapabilities.add('undo');
|
|
268
|
-
if (config.enableDevTools) coreCapabilities.add('connectDevTools');
|
|
269
|
-
try {
|
|
270
|
-
for (const key of Object.keys(tree)) coreCapabilities.add(String(key));
|
|
271
|
-
} catch {}
|
|
272
|
-
const hasMetadata = enhancers.some(e => Boolean(e.metadata && (e.metadata.requires || e.metadata.provides)));
|
|
273
|
-
let orderedEnhancers = enhancers;
|
|
274
|
-
if (hasMetadata) {
|
|
275
|
-
try {
|
|
276
|
-
orderedEnhancers = resolveEnhancerOrder(enhancers, coreCapabilities, config.debugMode);
|
|
277
|
-
} catch (err) {
|
|
278
|
-
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_ORDER_FAILED, err);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const provided = new Set(coreCapabilities);
|
|
282
|
-
let currentTree = tree;
|
|
283
|
-
for (let i = 0; i < orderedEnhancers.length; i++) {
|
|
284
|
-
const enhancer = orderedEnhancers[i];
|
|
285
|
-
if (typeof enhancer !== 'function') {
|
|
286
|
-
throw new Error(SIGNAL_TREE_MESSAGES.ENHANCER_NOT_FUNCTION.replace('%d', String(i)));
|
|
287
|
-
}
|
|
288
|
-
const reqs = enhancer.metadata?.requires ?? [];
|
|
289
|
-
for (const r of reqs) {
|
|
290
|
-
if (!(r in currentTree) && !provided.has(r)) {
|
|
291
|
-
const name = enhancer.metadata?.name ?? `enhancer#${i}`;
|
|
292
|
-
const msg = SIGNAL_TREE_MESSAGES.ENHANCER_REQUIREMENT_MISSING.replace('%s', name).replace('%s', r);
|
|
293
|
-
if (config.debugMode) {
|
|
294
|
-
throw new Error(msg);
|
|
295
|
-
} else {
|
|
296
|
-
console.warn(msg);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
try {
|
|
301
|
-
const result = enhancer(currentTree);
|
|
302
|
-
if (result !== currentTree) currentTree = result;
|
|
303
|
-
const provs = enhancer.metadata?.provides ?? [];
|
|
304
|
-
for (const p of provs) provided.add(p);
|
|
305
|
-
if (config.debugMode && provs.length > 0) {
|
|
306
|
-
for (const p of provs) {
|
|
307
|
-
if (!(p in currentTree)) {
|
|
308
|
-
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_PROVIDES_MISSING.replace('%s', enhancer.metadata?.name ?? String(i)).replace('%s', p));
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
const name = enhancer.metadata?.name || `enhancer at index ${i}`;
|
|
314
|
-
console.error(SIGNAL_TREE_MESSAGES.ENHANCER_FAILED.replace('%s', name), error);
|
|
315
|
-
if (config.debugMode) {
|
|
316
|
-
console.error('[SignalTree] Enhancer stack trace:', enhancer);
|
|
317
|
-
console.error('[SignalTree] Tree state at failure:', currentTree);
|
|
318
|
-
}
|
|
319
|
-
throw error;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return currentTree;
|
|
323
|
-
};
|
|
324
|
-
tree.destroy = () => {
|
|
325
|
-
try {
|
|
326
|
-
if (isLazy) {
|
|
327
|
-
const state = tree.state;
|
|
328
|
-
if (state && typeof state === 'object' && '__cleanup__' in state) {
|
|
329
|
-
const cleanup = state.__cleanup__;
|
|
330
|
-
if (typeof cleanup === 'function') {
|
|
331
|
-
cleanup();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (config.debugMode) {
|
|
336
|
-
console.log(SIGNAL_TREE_MESSAGES.TREE_DESTROYED);
|
|
337
|
-
}
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.error(SIGNAL_TREE_MESSAGES.CLEANUP_ERROR, error);
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
addStubMethods(tree, config);
|
|
343
|
-
return tree;
|
|
344
|
-
}
|
|
345
|
-
function addStubMethods(tree, config) {
|
|
346
|
-
tree.batchUpdate = updater => {
|
|
347
|
-
console.warn(SIGNAL_TREE_MESSAGES.BATCH_NOT_ENABLED);
|
|
348
|
-
tree(current => {
|
|
349
|
-
const partial = updater(current);
|
|
350
|
-
return {
|
|
351
|
-
...current,
|
|
352
|
-
...partial
|
|
353
|
-
};
|
|
354
|
-
});
|
|
355
|
-
};
|
|
356
|
-
tree.memoize = (fn, cacheKey) => {
|
|
357
|
-
console.warn(SIGNAL_TREE_MESSAGES.MEMOIZE_NOT_ENABLED);
|
|
358
|
-
return computed(() => fn(tree()));
|
|
359
|
-
};
|
|
360
|
-
tree.memoizedUpdate = updater => {
|
|
361
|
-
if (config.debugMode) {
|
|
362
|
-
console.warn(SIGNAL_TREE_MESSAGES.MEMOIZE_NOT_ENABLED);
|
|
363
|
-
}
|
|
364
|
-
tree(current => ({
|
|
365
|
-
...current,
|
|
366
|
-
...updater(current)
|
|
367
|
-
}));
|
|
368
|
-
};
|
|
369
|
-
tree.clearMemoCache = () => {
|
|
370
|
-
if (config.debugMode) {
|
|
371
|
-
console.warn(SIGNAL_TREE_MESSAGES.MEMOIZE_NOT_ENABLED);
|
|
139
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
140
|
+
if (isEntityMapMarker(value)) {
|
|
141
|
+
store[key] = value;
|
|
142
|
+
continue;
|
|
372
143
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
hitRate: 0,
|
|
377
|
-
totalHits: 0,
|
|
378
|
-
totalMisses: 0,
|
|
379
|
-
keys: []
|
|
380
|
-
});
|
|
381
|
-
tree.effect = fn => {
|
|
382
|
-
try {
|
|
383
|
-
effect(() => fn(tree()));
|
|
384
|
-
} catch (error) {
|
|
385
|
-
if (config.debugMode) {
|
|
386
|
-
console.warn(SIGNAL_TREE_MESSAGES.EFFECT_NO_CONTEXT, error);
|
|
387
|
-
}
|
|
144
|
+
if (isSignal(value)) {
|
|
145
|
+
store[key] = value;
|
|
146
|
+
continue;
|
|
388
147
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const destroyRef = inject(DestroyRef);
|
|
393
|
-
let isDestroyed = false;
|
|
394
|
-
const effectRef = effect(() => {
|
|
395
|
-
if (!isDestroyed) {
|
|
396
|
-
fn(tree());
|
|
397
|
-
}
|
|
148
|
+
if (value === null || value === undefined || typeof value !== 'object') {
|
|
149
|
+
store[key] = signal(value, {
|
|
150
|
+
equal: equalityFn
|
|
398
151
|
});
|
|
399
|
-
|
|
400
|
-
isDestroyed = true;
|
|
401
|
-
effectRef.destroy();
|
|
402
|
-
};
|
|
403
|
-
destroyRef.onDestroy(unsubscribe);
|
|
404
|
-
return unsubscribe;
|
|
405
|
-
} catch (error) {
|
|
406
|
-
if (config.debugMode) {
|
|
407
|
-
console.warn(SIGNAL_TREE_MESSAGES.SUBSCRIBE_NO_CONTEXT, error);
|
|
408
|
-
}
|
|
409
|
-
throw error;
|
|
410
|
-
}
|
|
411
|
-
};
|
|
412
|
-
tree.optimize = () => {
|
|
413
|
-
if (config.debugMode) {
|
|
414
|
-
console.warn(SIGNAL_TREE_MESSAGES.OPTIMIZE_NOT_AVAILABLE);
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
tree.clearCache = () => {
|
|
418
|
-
if (config.debugMode) {
|
|
419
|
-
console.warn(SIGNAL_TREE_MESSAGES.CACHE_NOT_AVAILABLE);
|
|
152
|
+
continue;
|
|
420
153
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
154
|
+
if (Array.isArray(value) || isBuiltInObject(value)) {
|
|
155
|
+
store[key] = signal(value, {
|
|
156
|
+
equal: equalityFn
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
425
159
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const treeWithEngine = tree;
|
|
429
|
-
if (!treeWithEngine.updateEngine) {
|
|
430
|
-
treeWithEngine.updateEngine = new OptimizedUpdateEngine(tree);
|
|
160
|
+
const nested = createSignalStore(value, equalityFn);
|
|
161
|
+
store[key] = makeNodeAccessor(nested);
|
|
431
162
|
}
|
|
432
|
-
|
|
433
|
-
const treeWithEngine = tree;
|
|
434
|
-
const engine = treeWithEngine.updateEngine;
|
|
435
|
-
if (!engine) {
|
|
436
|
-
if (config.debugMode) {
|
|
437
|
-
console.warn(SIGNAL_TREE_MESSAGES.UPDATE_OPTIMIZED_NOT_AVAILABLE);
|
|
438
|
-
}
|
|
439
|
-
tree(current => ({
|
|
440
|
-
...current,
|
|
441
|
-
...updates
|
|
442
|
-
}));
|
|
443
|
-
return {
|
|
444
|
-
changed: true,
|
|
445
|
-
duration: 0,
|
|
446
|
-
changedPaths: Object.keys(updates),
|
|
447
|
-
stats: undefined
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
return engine.update(tree(), updates, options);
|
|
451
|
-
};
|
|
452
|
-
tree.entities = () => {
|
|
453
|
-
console.warn('[@signaltree/core] tree.entities() is deprecated and will be removed in v6.0. ' + 'Use entityMap<E>() + withEntities() + tree.$.collectionName instead. ' + 'See https://signaltree.dev/docs/migration for migration guide.');
|
|
454
|
-
if (config.debugMode) {
|
|
455
|
-
console.warn(SIGNAL_TREE_MESSAGES.ENTITY_HELPERS_NOT_AVAILABLE);
|
|
456
|
-
}
|
|
457
|
-
return {};
|
|
458
|
-
};
|
|
459
|
-
tree.undo = () => {
|
|
460
|
-
if (config.debugMode) {
|
|
461
|
-
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
tree.redo = () => {
|
|
465
|
-
if (config.debugMode) {
|
|
466
|
-
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
tree.getHistory = () => {
|
|
470
|
-
if (config.debugMode) {
|
|
471
|
-
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
472
|
-
}
|
|
473
|
-
return [];
|
|
474
|
-
};
|
|
475
|
-
tree.resetHistory = () => {
|
|
476
|
-
if (config.debugMode) {
|
|
477
|
-
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
478
|
-
}
|
|
479
|
-
};
|
|
163
|
+
return store;
|
|
480
164
|
}
|
|
481
|
-
function create(
|
|
482
|
-
if (
|
|
165
|
+
function create(initialState, config) {
|
|
166
|
+
if (initialState === null || initialState === undefined) {
|
|
483
167
|
throw new Error(SIGNAL_TREE_MESSAGES.NULL_OR_UNDEFINED);
|
|
484
168
|
}
|
|
485
|
-
validateTree(
|
|
486
|
-
const estimatedSize = estimateObjectSize(obj);
|
|
169
|
+
validateTree(initialState, config);
|
|
487
170
|
const equalityFn = createEqualityFn(config.useShallowComparison ?? false);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
equal: equalityFn
|
|
491
|
-
});
|
|
492
|
-
const tree = makeRootNodeAccessor(signalState, signalState);
|
|
493
|
-
Object.defineProperty(tree, 'state', {
|
|
494
|
-
value: signalState,
|
|
495
|
-
enumerable: false
|
|
496
|
-
});
|
|
497
|
-
Object.defineProperty(tree, '$', {
|
|
498
|
-
value: signalState,
|
|
499
|
-
enumerable: false
|
|
500
|
-
});
|
|
501
|
-
enhanceTree(tree, config);
|
|
502
|
-
return tree;
|
|
503
|
-
}
|
|
504
|
-
const useLazy = shouldUseLazy(obj, config, estimatedSize);
|
|
505
|
-
if (config.debugMode) {
|
|
506
|
-
console.log(SIGNAL_TREE_MESSAGES.STRATEGY_SELECTION.replace('%s', useLazy ? 'lazy' : 'eager').replace('%d', String(estimatedSize)));
|
|
507
|
-
}
|
|
171
|
+
const estimatedSize = estimateObjectSize(initialState);
|
|
172
|
+
const useLazy = shouldUseLazy(initialState, config, estimatedSize);
|
|
508
173
|
let signalState;
|
|
509
174
|
let memoryManager;
|
|
510
|
-
|
|
511
|
-
|
|
175
|
+
if (useLazy && typeof initialState === 'object') {
|
|
176
|
+
try {
|
|
512
177
|
memoryManager = new SignalMemoryManager();
|
|
513
|
-
signalState = createLazySignalTree(
|
|
514
|
-
}
|
|
515
|
-
signalState = createSignalStore(obj, equalityFn);
|
|
516
|
-
}
|
|
517
|
-
} catch (error) {
|
|
518
|
-
if (useLazy) {
|
|
178
|
+
signalState = createLazySignalTree(initialState, equalityFn, '', memoryManager);
|
|
179
|
+
} catch (error) {
|
|
519
180
|
console.warn(SIGNAL_TREE_MESSAGES.LAZY_FALLBACK, error);
|
|
520
|
-
signalState = createSignalStore(
|
|
181
|
+
signalState = createSignalStore(initialState, equalityFn);
|
|
521
182
|
memoryManager = undefined;
|
|
522
|
-
} else {
|
|
523
|
-
throw error;
|
|
524
183
|
}
|
|
184
|
+
} else {
|
|
185
|
+
signalState = createSignalStore(initialState, equalityFn);
|
|
525
186
|
}
|
|
526
187
|
const tree = function (arg) {
|
|
527
188
|
if (arguments.length === 0) {
|
|
@@ -529,89 +190,88 @@ function create(obj, config = {}) {
|
|
|
529
190
|
}
|
|
530
191
|
if (typeof arg === 'function') {
|
|
531
192
|
const updater = arg;
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
recursiveUpdate(signalState, newValue);
|
|
193
|
+
const current = unwrap(signalState);
|
|
194
|
+
recursiveUpdate(signalState, updater(current));
|
|
535
195
|
} else {
|
|
536
196
|
recursiveUpdate(signalState, arg);
|
|
537
197
|
}
|
|
538
198
|
};
|
|
539
|
-
|
|
540
|
-
value: true,
|
|
541
|
-
enumerable: false
|
|
542
|
-
});
|
|
199
|
+
tree[NODE_ACCESSOR_SYMBOL] = true;
|
|
543
200
|
Object.defineProperty(tree, 'state', {
|
|
544
201
|
value: signalState,
|
|
545
|
-
enumerable: false
|
|
202
|
+
enumerable: false,
|
|
203
|
+
writable: false
|
|
546
204
|
});
|
|
547
205
|
Object.defineProperty(tree, '$', {
|
|
548
206
|
value: signalState,
|
|
549
|
-
enumerable: false
|
|
207
|
+
enumerable: false,
|
|
208
|
+
writable: false
|
|
550
209
|
});
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
210
|
+
Object.defineProperty(tree, 'with', {
|
|
211
|
+
value: function (enhancer) {
|
|
212
|
+
if (typeof enhancer !== 'function') {
|
|
213
|
+
throw new Error('Enhancer must be a function');
|
|
214
|
+
}
|
|
215
|
+
return enhancer(tree);
|
|
216
|
+
},
|
|
217
|
+
enumerable: false,
|
|
218
|
+
writable: false
|
|
219
|
+
});
|
|
220
|
+
Object.defineProperty(tree, 'bind', {
|
|
221
|
+
value: function (thisArg) {
|
|
222
|
+
return Function.prototype.bind.call(tree, thisArg);
|
|
223
|
+
},
|
|
224
|
+
enumerable: false,
|
|
225
|
+
writable: true,
|
|
226
|
+
configurable: true
|
|
227
|
+
});
|
|
228
|
+
Object.defineProperty(tree, 'destroy', {
|
|
229
|
+
value: function () {
|
|
230
|
+
if (memoryManager) {
|
|
231
|
+
memoryManager.dispose();
|
|
232
|
+
}
|
|
233
|
+
if (config.debugMode) {
|
|
234
|
+
console.log(SIGNAL_TREE_MESSAGES.TREE_DESTROYED);
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
enumerable: false,
|
|
238
|
+
writable: true,
|
|
239
|
+
configurable: true
|
|
240
|
+
});
|
|
241
|
+
Object.defineProperty(tree, 'clearCache', {
|
|
242
|
+
value: () => {},
|
|
243
|
+
enumerable: false,
|
|
244
|
+
writable: true,
|
|
245
|
+
configurable: true
|
|
246
|
+
});
|
|
247
|
+
Object.defineProperty(tree, 'batchUpdate', {
|
|
248
|
+
value: function (arg) {
|
|
249
|
+
if (arguments.length === 0) return;
|
|
250
|
+
if (typeof arg === 'function') {
|
|
251
|
+
const updater = arg;
|
|
252
|
+
const current = unwrap(signalState);
|
|
253
|
+
recursiveUpdate(signalState, updater(current));
|
|
254
|
+
} else {
|
|
255
|
+
recursiveUpdate(signalState, arg);
|
|
575
256
|
}
|
|
257
|
+
},
|
|
258
|
+
enumerable: false,
|
|
259
|
+
writable: true,
|
|
260
|
+
configurable: true
|
|
261
|
+
});
|
|
262
|
+
for (const key of Object.keys(signalState)) {
|
|
263
|
+
if (!(key in tree)) {
|
|
264
|
+
Object.defineProperty(tree, key, {
|
|
265
|
+
value: signalState[key],
|
|
266
|
+
enumerable: true,
|
|
267
|
+
configurable: true
|
|
268
|
+
});
|
|
576
269
|
}
|
|
577
270
|
}
|
|
578
271
|
return tree;
|
|
579
272
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
useLazySignals: false,
|
|
583
|
-
debugMode: false
|
|
584
|
-
},
|
|
585
|
-
performance: {
|
|
586
|
-
useLazySignals: true,
|
|
587
|
-
batchUpdates: true,
|
|
588
|
-
useMemoization: true,
|
|
589
|
-
useShallowComparison: true
|
|
590
|
-
},
|
|
591
|
-
development: {
|
|
592
|
-
useLazySignals: false,
|
|
593
|
-
debugMode: true,
|
|
594
|
-
enableDevTools: true,
|
|
595
|
-
trackPerformance: true
|
|
596
|
-
},
|
|
597
|
-
production: {
|
|
598
|
-
useLazySignals: true,
|
|
599
|
-
batchUpdates: true,
|
|
600
|
-
useMemoization: true,
|
|
601
|
-
debugMode: false
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
function signalTree(obj, configOrPreset) {
|
|
605
|
-
if (typeof configOrPreset === 'string') {
|
|
606
|
-
const config = presetConfigs[configOrPreset];
|
|
607
|
-
if (!config) {
|
|
608
|
-
console.warn(SIGNAL_TREE_MESSAGES.PRESET_UNKNOWN.replace('%s', configOrPreset));
|
|
609
|
-
return create(obj, {});
|
|
610
|
-
}
|
|
611
|
-
return create(obj, config);
|
|
612
|
-
}
|
|
613
|
-
const config = configOrPreset || {};
|
|
614
|
-
return create(obj, config);
|
|
273
|
+
function signalTree(initialState, config = {}) {
|
|
274
|
+
return create(initialState, config);
|
|
615
275
|
}
|
|
616
276
|
|
|
617
277
|
export { isNodeAccessor, signalTree };
|