@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.
Files changed (84) hide show
  1. package/README.md +76 -76
  2. package/dist/enhancers/batching/{lib/batching.js → batching.js} +45 -17
  3. package/dist/enhancers/devtools/{lib/devtools.js → devtools.js} +83 -98
  4. package/dist/enhancers/effects/effects.js +66 -0
  5. package/dist/enhancers/entities/entities.js +51 -0
  6. package/dist/enhancers/memoization/{lib/memoization.js → memoization.js} +33 -23
  7. package/dist/enhancers/presets/lib/presets.js +5 -65
  8. package/dist/enhancers/serialization/{lib/serialization.js → serialization.js} +12 -18
  9. package/dist/enhancers/time-travel/{lib/time-travel.js → time-travel.js} +62 -24
  10. package/dist/enhancers/time-travel/utils.js +11 -0
  11. package/dist/index.js +8 -8
  12. package/dist/lib/async-helpers.js +6 -6
  13. package/dist/lib/presets.js +21 -0
  14. package/dist/lib/signal-tree.js +156 -496
  15. package/dist/lib/types.js +1 -1
  16. package/dist/lib/utils.js +40 -2
  17. package/package.json +1 -6
  18. package/src/enhancers/batching/batching.d.ts +11 -0
  19. package/src/enhancers/batching/index.d.ts +1 -1
  20. package/src/enhancers/devtools/{lib/devtools.d.ts → devtools.d.ts} +10 -19
  21. package/src/enhancers/devtools/devtools.types.d.ts +1 -0
  22. package/src/enhancers/devtools/index.d.ts +1 -1
  23. package/src/enhancers/effects/effects.d.ts +9 -0
  24. package/src/enhancers/effects/effects.types.d.ts +1 -0
  25. package/src/enhancers/effects/index.d.ts +1 -0
  26. package/src/enhancers/entities/entities.d.ts +11 -0
  27. package/src/enhancers/entities/entities.types.d.ts +1 -0
  28. package/src/enhancers/entities/index.d.ts +1 -1
  29. package/src/enhancers/index.d.ts +3 -3
  30. package/src/enhancers/memoization/index.d.ts +1 -1
  31. package/src/enhancers/memoization/memoization.d.ts +54 -0
  32. package/src/enhancers/memoization/memoization.types.d.ts +1 -0
  33. package/src/enhancers/presets/lib/presets.d.ts +3 -6
  34. package/src/enhancers/serialization/index.d.ts +1 -1
  35. package/src/{serialization.d.ts → enhancers/serialization/serialization.d.ts} +17 -8
  36. package/src/enhancers/test-helpers/types-equals.d.ts +2 -0
  37. package/src/enhancers/time-travel/index.d.ts +1 -1
  38. package/src/enhancers/time-travel/time-travel.d.ts +10 -0
  39. package/src/enhancers/time-travel/time-travel.types.d.ts +1 -0
  40. package/src/enhancers/time-travel/utils.d.ts +2 -0
  41. package/src/enhancers/types.d.ts +1 -74
  42. package/src/enhancers/typing/helpers-types.d.ts +2 -0
  43. package/src/index.d.ts +7 -8
  44. package/src/lib/async-helpers.d.ts +2 -2
  45. package/src/lib/dev-proxy.d.ts +3 -0
  46. package/src/lib/presets.d.ts +34 -0
  47. package/src/lib/signal-tree.d.ts +2 -7
  48. package/src/lib/types.d.ts +121 -90
  49. package/src/lib/utils.d.ts +4 -0
  50. package/dist/deep-clone.js +0 -80
  51. package/dist/enhancers/computed/lib/computed.js +0 -21
  52. package/dist/enhancers/entities/lib/entities.js +0 -66
  53. package/dist/lib/performance/diff-engine.js +0 -156
  54. package/dist/lib/performance/path-index.js +0 -156
  55. package/dist/lib/performance/update-engine.js +0 -188
  56. package/src/async-helpers.d.ts +0 -8
  57. package/src/batching.d.ts +0 -16
  58. package/src/computed.d.ts +0 -12
  59. package/src/constants.d.ts +0 -14
  60. package/src/devtools.d.ts +0 -77
  61. package/src/diff-engine.d.ts +0 -33
  62. package/src/enhancers/batching/lib/batching.d.ts +0 -16
  63. package/src/enhancers/computed/index.d.ts +0 -1
  64. package/src/enhancers/computed/lib/computed.d.ts +0 -12
  65. package/src/enhancers/entities/lib/entities.d.ts +0 -17
  66. package/src/enhancers/memoization/lib/memoization.d.ts +0 -65
  67. package/src/enhancers/presets/test-setup.d.ts +0 -3
  68. package/src/enhancers/serialization/lib/serialization.d.ts +0 -59
  69. package/src/enhancers/time-travel/lib/time-travel.d.ts +0 -36
  70. package/src/enhancers/time-travel/lib/utils.d.ts +0 -1
  71. package/src/entities.d.ts +0 -28
  72. package/src/memoization.d.ts +0 -65
  73. package/src/memory-manager.d.ts +0 -30
  74. package/src/path-index.d.ts +0 -25
  75. package/src/path-notifier.d.ts +0 -12
  76. package/src/presets.d.ts +0 -11
  77. package/src/security-validator.d.ts +0 -33
  78. package/src/signal-tree.d.ts +0 -8
  79. package/src/test-setup.d.ts +0 -3
  80. package/src/time-travel.d.ts +0 -36
  81. package/src/types.d.ts +0 -436
  82. package/src/update-engine.d.ts +0 -32
  83. package/src/utils.d.ts +0 -1
  84. /package/src/{entity-signal.d.ts → enhancers/batching/batching.types.d.ts} +0 -0
@@ -1,80 +1,20 @@
1
- import { signal, isSignal, computed, effect, inject, DestroyRef } from '@angular/core';
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 makeNodeAccessor() {
13
- const accessor = function (arg) {
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 recursiveUpdate(target, updates) {
45
- if (!updates || typeof updates !== 'object') {
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 isNodeAccessor(value) {
77
- return typeof value === 'function' && value && value[NODE_ACCESSOR_SYMBOL] === true;
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, path = []) {
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, currentPath) {
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, index) => {
130
- validate(item, [...currentPath, String(index)]);
131
- });
62
+ value.forEach((item, i) => validate(item, [...path, String(i)]));
132
63
  return;
133
64
  }
134
- const keys = [...Object.keys(value), ...Object.getOwnPropertyNames(value)];
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
- const err = error;
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
- const err = error;
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, [...currentPath, key]);
77
+ validate(val, [...path, key]);
152
78
  }
153
79
  }
154
- validate(obj, path);
80
+ validate(obj, []);
155
81
  }
156
- function createEqualityFn(useShallowComparison) {
157
- return useShallowComparison ? Object.is : deepEqual;
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 processedObjects = new WeakSet();
177
- if (processedObjects.has(obj)) {
178
- console.warn(SIGNAL_TREE_MESSAGES.CIRCULAR_REF);
179
- return signal(obj, {
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
- tree.getCacheStats = () => ({
375
- size: 0,
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
- tree.subscribe = fn => {
391
- try {
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
- const unsubscribe = () => {
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
- tree.invalidatePattern = () => {
423
- if (config.debugMode) {
424
- console.warn(SIGNAL_TREE_MESSAGES.PERFORMANCE_NOT_ENABLED);
154
+ if (Array.isArray(value) || isBuiltInObject(value)) {
155
+ store[key] = signal(value, {
156
+ equal: equalityFn
157
+ });
158
+ continue;
425
159
  }
426
- return 0;
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
- tree.updateOptimized = (updates, options = {}) => {
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(obj, config = {}) {
482
- if (obj === null || obj === undefined) {
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(obj, config);
486
- const estimatedSize = estimateObjectSize(obj);
169
+ validateTree(initialState, config);
487
170
  const equalityFn = createEqualityFn(config.useShallowComparison ?? false);
488
- if (Array.isArray(obj)) {
489
- const signalState = signal(obj, {
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
- try {
511
- if (useLazy && typeof obj === 'object') {
175
+ if (useLazy && typeof initialState === 'object') {
176
+ try {
512
177
  memoryManager = new SignalMemoryManager();
513
- signalState = createLazySignalTree(obj, equalityFn, '', memoryManager);
514
- } else {
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(obj, equalityFn);
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 currentValue = unwrap(signalState);
533
- const newValue = updater(currentValue);
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
- Object.defineProperty(tree, NODE_ACCESSOR_SYMBOL, {
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
- if (memoryManager) {
552
- Object.defineProperty(tree, 'dispose', {
553
- value: () => {
554
- memoryManager?.dispose();
555
- const cleanup = signalState.__cleanup__;
556
- if (typeof cleanup === 'function') {
557
- cleanup();
558
- }
559
- },
560
- enumerable: false,
561
- writable: false
562
- });
563
- }
564
- enhanceTree(tree, config);
565
- for (const key in signalState) {
566
- if (Object.prototype.hasOwnProperty.call(signalState, key)) {
567
- if (!(key in tree)) {
568
- try {
569
- Object.defineProperty(tree, key, {
570
- value: signalState[key],
571
- enumerable: true,
572
- configurable: true
573
- });
574
- } catch {}
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
- const presetConfigs = {
581
- basic: {
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 };