@signaltree/core 1.1.9 → 2.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 +138 -106
- package/fesm2022/signaltree-core.mjs +688 -326
- package/fesm2022/signaltree-core.mjs.map +1 -1
- package/index.d.ts +102 -46
- package/package.json +1 -1
|
@@ -1,5 +1,159 @@
|
|
|
1
1
|
import { signal, isSignal, computed, effect, inject, DestroyRef } from '@angular/core';
|
|
2
2
|
|
|
3
|
+
const SIGNAL_TREE_CONSTANTS = {
|
|
4
|
+
MAX_PATH_CACHE_SIZE: 1000,
|
|
5
|
+
LAZY_THRESHOLD: 50,
|
|
6
|
+
ESTIMATE_MAX_DEPTH: 3,
|
|
7
|
+
ESTIMATE_SAMPLE_SIZE_ARRAY: 3,
|
|
8
|
+
ESTIMATE_SAMPLE_SIZE_OBJECT: 5,
|
|
9
|
+
DEFAULT_CACHE_SIZE: 100,
|
|
10
|
+
DEFAULT_BATCH_SIZE: 10,
|
|
11
|
+
};
|
|
12
|
+
const DEV_MESSAGES = {
|
|
13
|
+
NULL_OR_UNDEFINED: 'null/undefined',
|
|
14
|
+
CIRCULAR_REF: 'circular ref',
|
|
15
|
+
UPDATER_INVALID: 'updater invalid',
|
|
16
|
+
LAZY_FALLBACK: 'lazy fallback',
|
|
17
|
+
SIGNAL_CREATION_FAILED: 'signal creation failed',
|
|
18
|
+
UPDATE_PATH_NOT_FOUND: 'update path not found',
|
|
19
|
+
UPDATE_FAILED: 'update failed',
|
|
20
|
+
ROLLBACK_FAILED: 'rollback failed',
|
|
21
|
+
CLEANUP_ERROR: 'cleanup error',
|
|
22
|
+
PRESET_UNKNOWN: 'unknown preset',
|
|
23
|
+
STRATEGY_SELECTION: 'strategy select',
|
|
24
|
+
TREE_DESTROYED: 'destroyed',
|
|
25
|
+
UPDATE_TRANSACTION: 'update tx',
|
|
26
|
+
BATCH_NOT_ENABLED: 'batching disabled',
|
|
27
|
+
MEMOIZE_NOT_ENABLED: 'memoize disabled',
|
|
28
|
+
MIDDLEWARE_NOT_AVAILABLE: 'middleware missing',
|
|
29
|
+
ENTITY_HELPERS_NOT_AVAILABLE: 'entity helpers missing',
|
|
30
|
+
ASYNC_ACTIONS_NOT_AVAILABLE: 'async actions missing',
|
|
31
|
+
TIME_TRAVEL_NOT_AVAILABLE: 'time travel missing',
|
|
32
|
+
OPTIMIZE_NOT_AVAILABLE: 'optimize missing',
|
|
33
|
+
CACHE_NOT_AVAILABLE: 'cache missing',
|
|
34
|
+
PERFORMANCE_NOT_ENABLED: 'performance disabled',
|
|
35
|
+
ENHANCER_ORDER_FAILED: 'enhancer order failed',
|
|
36
|
+
ENHANCER_CYCLE_DETECTED: 'enhancer cycle',
|
|
37
|
+
ENHANCER_REQUIREMENT_MISSING: 'enhancer req missing',
|
|
38
|
+
ENHANCER_PROVIDES_MISSING: 'enhancer provides missing',
|
|
39
|
+
ENHANCER_FAILED: 'enhancer failed',
|
|
40
|
+
ENHANCER_NOT_FUNCTION: 'enhancer not function',
|
|
41
|
+
EFFECT_NO_CONTEXT: 'no angular context',
|
|
42
|
+
SUBSCRIBE_NO_CONTEXT: 'no angular context',
|
|
43
|
+
};
|
|
44
|
+
const PROD_MESSAGES = (() => {
|
|
45
|
+
const out = {};
|
|
46
|
+
let i = 0;
|
|
47
|
+
for (const k of Object.keys(DEV_MESSAGES)) {
|
|
48
|
+
out[k] = String(i++);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
})();
|
|
52
|
+
const _isProdByEnv = Boolean(typeof globalThis === 'object' &&
|
|
53
|
+
globalThis !== null &&
|
|
54
|
+
'process' in globalThis &&
|
|
55
|
+
typeof globalThis.process === 'object' &&
|
|
56
|
+
'env' in globalThis.process &&
|
|
57
|
+
globalThis.process.env.NODE_ENV === 'production');
|
|
58
|
+
const _isDev = typeof ngDevMode !== 'undefined' ? Boolean(ngDevMode) : !_isProdByEnv;
|
|
59
|
+
const SIGNAL_TREE_MESSAGES = Object.freeze(_isDev
|
|
60
|
+
? DEV_MESSAGES
|
|
61
|
+
: PROD_MESSAGES);
|
|
62
|
+
|
|
63
|
+
const ENHANCER_META = Symbol('signaltree:enhancer:meta');
|
|
64
|
+
function isSignalTree(value) {
|
|
65
|
+
return (value !== null &&
|
|
66
|
+
typeof value === 'function' &&
|
|
67
|
+
'state' in value &&
|
|
68
|
+
'$' in value &&
|
|
69
|
+
'with' in value &&
|
|
70
|
+
'destroy' in value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createEnhancer(meta, enhancerFn) {
|
|
74
|
+
const fn = enhancerFn;
|
|
75
|
+
try {
|
|
76
|
+
fn.metadata = meta;
|
|
77
|
+
try {
|
|
78
|
+
fn[ENHANCER_META] = meta;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
}
|
|
85
|
+
return fn;
|
|
86
|
+
}
|
|
87
|
+
function resolveEnhancerOrder(enhancers, availableCapabilities = new Set(), debugMode = false) {
|
|
88
|
+
const nodes = enhancers.map((e, idx) => ({
|
|
89
|
+
fn: e,
|
|
90
|
+
name: e.metadata && e.metadata.name
|
|
91
|
+
? String(e.metadata.name)
|
|
92
|
+
: `enhancer#${idx}`,
|
|
93
|
+
requires: new Set(e.metadata?.requires ?? []),
|
|
94
|
+
provides: new Set(e.metadata?.provides ?? []),
|
|
95
|
+
}));
|
|
96
|
+
const adj = new Map();
|
|
97
|
+
const nameToNode = new Map();
|
|
98
|
+
for (const n of nodes) {
|
|
99
|
+
nameToNode.set(n.name, n);
|
|
100
|
+
adj.set(n.name, new Set());
|
|
101
|
+
}
|
|
102
|
+
for (const a of nodes) {
|
|
103
|
+
for (const b of nodes) {
|
|
104
|
+
if (a === b)
|
|
105
|
+
continue;
|
|
106
|
+
for (const req of b.requires) {
|
|
107
|
+
if (availableCapabilities.has(req))
|
|
108
|
+
continue;
|
|
109
|
+
if (a.provides.has(req)) {
|
|
110
|
+
const set = adj.get(a.name);
|
|
111
|
+
if (set)
|
|
112
|
+
set.add(b.name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const inDegree = new Map();
|
|
118
|
+
for (const entry of adj.keys())
|
|
119
|
+
inDegree.set(entry, 0);
|
|
120
|
+
for (const [, outs] of adj) {
|
|
121
|
+
for (const to of outs)
|
|
122
|
+
inDegree.set(to, (inDegree.get(to) || 0) + 1);
|
|
123
|
+
}
|
|
124
|
+
const queue = [];
|
|
125
|
+
for (const [name, deg] of inDegree.entries()) {
|
|
126
|
+
if (deg === 0)
|
|
127
|
+
queue.push(name);
|
|
128
|
+
}
|
|
129
|
+
const ordered = [];
|
|
130
|
+
while (queue.length > 0) {
|
|
131
|
+
const n = queue.shift();
|
|
132
|
+
if (!n)
|
|
133
|
+
break;
|
|
134
|
+
ordered.push(n);
|
|
135
|
+
const outs = adj.get(n);
|
|
136
|
+
if (!outs)
|
|
137
|
+
continue;
|
|
138
|
+
for (const m of outs) {
|
|
139
|
+
inDegree.set(m, (inDegree.get(m) || 0) - 1);
|
|
140
|
+
if (inDegree.get(m) === 0)
|
|
141
|
+
queue.push(m);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (ordered.length !== nodes.length) {
|
|
145
|
+
if (debugMode) {
|
|
146
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_CYCLE_DETECTED);
|
|
147
|
+
}
|
|
148
|
+
return enhancers;
|
|
149
|
+
}
|
|
150
|
+
return ordered.map((name) => {
|
|
151
|
+
const n = nameToNode.get(name);
|
|
152
|
+
return (n ? n.fn : enhancers[0]);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const CALLABLE_SIGNAL_SYMBOL = Symbol.for('NodeAccessor');
|
|
3
157
|
function equal(a, b) {
|
|
4
158
|
if (a === b)
|
|
5
159
|
return true;
|
|
@@ -11,12 +165,10 @@ function equal(a, b) {
|
|
|
11
165
|
return false;
|
|
12
166
|
if (typeA !== 'object')
|
|
13
167
|
return false;
|
|
14
|
-
if (a instanceof Date && b instanceof Date)
|
|
168
|
+
if (a instanceof Date && b instanceof Date)
|
|
15
169
|
return a.getTime() === b.getTime();
|
|
16
|
-
|
|
17
|
-
if (a instanceof RegExp && b instanceof RegExp) {
|
|
170
|
+
if (a instanceof RegExp && b instanceof RegExp)
|
|
18
171
|
return a.source === b.source && a.flags === b.flags;
|
|
19
|
-
}
|
|
20
172
|
if (a instanceof Map && b instanceof Map) {
|
|
21
173
|
if (a.size !== b.size)
|
|
22
174
|
return false;
|
|
@@ -38,7 +190,7 @@ function equal(a, b) {
|
|
|
38
190
|
if (Array.isArray(a)) {
|
|
39
191
|
if (!Array.isArray(b) || a.length !== b.length)
|
|
40
192
|
return false;
|
|
41
|
-
return a.every((item,
|
|
193
|
+
return a.every((item, i) => equal(item, b[i]));
|
|
42
194
|
}
|
|
43
195
|
if (Array.isArray(b))
|
|
44
196
|
return false;
|
|
@@ -48,13 +200,75 @@ function equal(a, b) {
|
|
|
48
200
|
const keysB = Object.keys(objB);
|
|
49
201
|
if (keysA.length !== keysB.length)
|
|
50
202
|
return false;
|
|
51
|
-
return keysA.every((
|
|
203
|
+
return keysA.every((k) => k in objB && equal(objA[k], objB[k]));
|
|
52
204
|
}
|
|
205
|
+
const deepEqual = equal;
|
|
53
206
|
function terminalSignal(value, customEqual) {
|
|
54
207
|
return signal(value, {
|
|
55
208
|
equal: customEqual || equal,
|
|
56
209
|
});
|
|
57
210
|
}
|
|
211
|
+
function isBuiltInObject(v) {
|
|
212
|
+
if (v === null || v === undefined)
|
|
213
|
+
return false;
|
|
214
|
+
if (v instanceof Date ||
|
|
215
|
+
v instanceof RegExp ||
|
|
216
|
+
typeof v === 'function' ||
|
|
217
|
+
v instanceof Map ||
|
|
218
|
+
v instanceof Set ||
|
|
219
|
+
v instanceof WeakMap ||
|
|
220
|
+
v instanceof WeakSet ||
|
|
221
|
+
v instanceof ArrayBuffer ||
|
|
222
|
+
v instanceof DataView ||
|
|
223
|
+
v instanceof Error ||
|
|
224
|
+
v instanceof Promise) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
if (v instanceof Int8Array ||
|
|
228
|
+
v instanceof Uint8Array ||
|
|
229
|
+
v instanceof Uint8ClampedArray ||
|
|
230
|
+
v instanceof Int16Array ||
|
|
231
|
+
v instanceof Uint16Array ||
|
|
232
|
+
v instanceof Int32Array ||
|
|
233
|
+
v instanceof Uint32Array ||
|
|
234
|
+
v instanceof Float32Array ||
|
|
235
|
+
v instanceof Float64Array ||
|
|
236
|
+
v instanceof BigInt64Array ||
|
|
237
|
+
v instanceof BigUint64Array) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
if (typeof window !== 'undefined') {
|
|
241
|
+
if (v instanceof URL ||
|
|
242
|
+
v instanceof URLSearchParams ||
|
|
243
|
+
v instanceof FormData ||
|
|
244
|
+
v instanceof Blob ||
|
|
245
|
+
(typeof File !== 'undefined' && v instanceof File) ||
|
|
246
|
+
(typeof FileList !== 'undefined' && v instanceof FileList) ||
|
|
247
|
+
(typeof Headers !== 'undefined' && v instanceof Headers) ||
|
|
248
|
+
(typeof Request !== 'undefined' && v instanceof Request) ||
|
|
249
|
+
(typeof Response !== 'undefined' && v instanceof Response) ||
|
|
250
|
+
(typeof AbortController !== 'undefined' &&
|
|
251
|
+
v instanceof AbortController) ||
|
|
252
|
+
(typeof AbortSignal !== 'undefined' && v instanceof AbortSignal)) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const NodeBuffer = globalThis?.Buffer;
|
|
258
|
+
if (NodeBuffer &&
|
|
259
|
+
v instanceof NodeBuffer)
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
function isNodeAccessor$1(value) {
|
|
267
|
+
return (typeof value === 'function' && value && CALLABLE_SIGNAL_SYMBOL in value);
|
|
268
|
+
}
|
|
269
|
+
function isAnySignal(value) {
|
|
270
|
+
return isSignal(value) || isNodeAccessor$1(value);
|
|
271
|
+
}
|
|
58
272
|
class LRUCache {
|
|
59
273
|
maxSize;
|
|
60
274
|
cache = new Map();
|
|
@@ -64,9 +278,8 @@ class LRUCache {
|
|
|
64
278
|
set(key, value) {
|
|
65
279
|
if (this.cache.size >= this.maxSize) {
|
|
66
280
|
const firstKey = this.cache.keys().next().value;
|
|
67
|
-
if (firstKey !== undefined)
|
|
281
|
+
if (firstKey !== undefined)
|
|
68
282
|
this.cache.delete(firstKey);
|
|
69
|
-
}
|
|
70
283
|
}
|
|
71
284
|
this.cache.delete(key);
|
|
72
285
|
this.cache.set(key, value);
|
|
@@ -86,25 +299,26 @@ class LRUCache {
|
|
|
86
299
|
return this.cache.size;
|
|
87
300
|
}
|
|
88
301
|
}
|
|
89
|
-
const
|
|
90
|
-
const pathCache = new LRUCache(MAX_CACHE_SIZE);
|
|
302
|
+
const pathCache = new LRUCache(SIGNAL_TREE_CONSTANTS.MAX_PATH_CACHE_SIZE);
|
|
91
303
|
function parsePath(path) {
|
|
92
304
|
const cached = pathCache.get(path);
|
|
93
|
-
if (cached)
|
|
305
|
+
if (cached)
|
|
94
306
|
return cached;
|
|
95
|
-
}
|
|
96
307
|
const parts = path.split('.');
|
|
97
308
|
pathCache.set(path, parts);
|
|
98
309
|
return parts;
|
|
99
310
|
}
|
|
311
|
+
function composeEnhancers(...enhancers) {
|
|
312
|
+
return (tree) => enhancers.reduce((t, e) => e(t), tree);
|
|
313
|
+
}
|
|
100
314
|
function createLazySignalTree(obj, equalityFn, basePath = '') {
|
|
101
315
|
const signalCache = new Map();
|
|
102
316
|
const nestedProxies = new Map();
|
|
103
317
|
const nestedCleanups = new Map();
|
|
104
318
|
const cleanup = () => {
|
|
105
|
-
nestedCleanups.forEach((
|
|
319
|
+
nestedCleanups.forEach((fn) => {
|
|
106
320
|
try {
|
|
107
|
-
|
|
321
|
+
fn();
|
|
108
322
|
}
|
|
109
323
|
catch (error) {
|
|
110
324
|
console.warn('Error during nested cleanup:', error);
|
|
@@ -114,31 +328,10 @@ function createLazySignalTree(obj, equalityFn, basePath = '') {
|
|
|
114
328
|
signalCache.clear();
|
|
115
329
|
nestedProxies.clear();
|
|
116
330
|
};
|
|
117
|
-
const isBuiltInObject = (v) => {
|
|
118
|
-
if (v === null || v === undefined)
|
|
119
|
-
return false;
|
|
120
|
-
return (v instanceof Date ||
|
|
121
|
-
v instanceof RegExp ||
|
|
122
|
-
typeof v === 'function' ||
|
|
123
|
-
v instanceof Map ||
|
|
124
|
-
v instanceof Set ||
|
|
125
|
-
v instanceof WeakMap ||
|
|
126
|
-
v instanceof WeakSet ||
|
|
127
|
-
v instanceof ArrayBuffer ||
|
|
128
|
-
v instanceof DataView ||
|
|
129
|
-
v instanceof Error ||
|
|
130
|
-
v instanceof Promise ||
|
|
131
|
-
v instanceof URL ||
|
|
132
|
-
v instanceof URLSearchParams ||
|
|
133
|
-
v instanceof FormData ||
|
|
134
|
-
v instanceof Blob ||
|
|
135
|
-
(typeof File !== 'undefined' && v instanceof File));
|
|
136
|
-
};
|
|
137
331
|
const proxy = new Proxy(obj, {
|
|
138
332
|
get(target, prop) {
|
|
139
|
-
if (prop === '__cleanup__')
|
|
333
|
+
if (prop === '__cleanup__')
|
|
140
334
|
return cleanup;
|
|
141
|
-
}
|
|
142
335
|
if (typeof prop === 'symbol') {
|
|
143
336
|
return target[prop];
|
|
144
337
|
}
|
|
@@ -147,19 +340,15 @@ function createLazySignalTree(obj, equalityFn, basePath = '') {
|
|
|
147
340
|
}
|
|
148
341
|
const key = prop;
|
|
149
342
|
const path = basePath ? `${basePath}.${key}` : key;
|
|
150
|
-
if (!(key in target))
|
|
343
|
+
if (!(key in target))
|
|
151
344
|
return undefined;
|
|
152
|
-
}
|
|
153
345
|
const value = target[key];
|
|
154
|
-
if (isSignal(value))
|
|
346
|
+
if (isSignal(value))
|
|
155
347
|
return value;
|
|
156
|
-
|
|
157
|
-
if (signalCache.has(path)) {
|
|
348
|
+
if (signalCache.has(path))
|
|
158
349
|
return signalCache.get(path);
|
|
159
|
-
|
|
160
|
-
if (nestedProxies.has(path)) {
|
|
350
|
+
if (nestedProxies.has(path))
|
|
161
351
|
return nestedProxies.get(path);
|
|
162
|
-
}
|
|
163
352
|
if (value &&
|
|
164
353
|
typeof value === 'object' &&
|
|
165
354
|
!Array.isArray(value) &&
|
|
@@ -231,37 +420,234 @@ function createLazySignalTree(obj, equalityFn, basePath = '') {
|
|
|
231
420
|
});
|
|
232
421
|
return proxy;
|
|
233
422
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
423
|
+
function unwrap(node) {
|
|
424
|
+
if (node === null || node === undefined) {
|
|
425
|
+
return node;
|
|
426
|
+
}
|
|
427
|
+
if (isNodeAccessor$1(node)) {
|
|
428
|
+
const result = {};
|
|
429
|
+
for (const key in node) {
|
|
430
|
+
if (!Object.prototype.hasOwnProperty.call(node, key))
|
|
431
|
+
continue;
|
|
432
|
+
if (key === 'length' || key === 'prototype')
|
|
433
|
+
continue;
|
|
434
|
+
if (key === 'name') {
|
|
435
|
+
const value = node[key];
|
|
436
|
+
if (!isSignal(value) && !isNodeAccessor$1(value)) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const value = node[key];
|
|
441
|
+
if (isNodeAccessor$1(value)) {
|
|
442
|
+
result[key] = unwrap(value);
|
|
443
|
+
}
|
|
444
|
+
else if (isSignal(value)) {
|
|
445
|
+
const unwrappedValue = value();
|
|
446
|
+
if (typeof unwrappedValue === 'object' &&
|
|
447
|
+
unwrappedValue !== null &&
|
|
448
|
+
!Array.isArray(unwrappedValue) &&
|
|
449
|
+
!isBuiltInObject(unwrappedValue)) {
|
|
450
|
+
result[key] = unwrap(unwrappedValue);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
result[key] = unwrappedValue;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else if (typeof value === 'object' &&
|
|
457
|
+
value !== null &&
|
|
458
|
+
!Array.isArray(value) &&
|
|
459
|
+
!isBuiltInObject(value)) {
|
|
460
|
+
result[key] = unwrap(value);
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
result[key] = value;
|
|
464
|
+
}
|
|
246
465
|
}
|
|
247
|
-
return
|
|
466
|
+
return result;
|
|
248
467
|
}
|
|
249
|
-
if (
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
b[key])
|
|
257
|
-
return false;
|
|
468
|
+
if (isSignal(node)) {
|
|
469
|
+
const value = node();
|
|
470
|
+
if (typeof value === 'object' &&
|
|
471
|
+
value !== null &&
|
|
472
|
+
!Array.isArray(value) &&
|
|
473
|
+
!isBuiltInObject(value)) {
|
|
474
|
+
return unwrap(value);
|
|
258
475
|
}
|
|
259
|
-
return
|
|
476
|
+
return value;
|
|
260
477
|
}
|
|
261
|
-
|
|
478
|
+
if (typeof node !== 'object') {
|
|
479
|
+
return node;
|
|
480
|
+
}
|
|
481
|
+
if (Array.isArray(node)) {
|
|
482
|
+
return node;
|
|
483
|
+
}
|
|
484
|
+
if (isBuiltInObject(node)) {
|
|
485
|
+
return node;
|
|
486
|
+
}
|
|
487
|
+
const result = {};
|
|
488
|
+
for (const key in node) {
|
|
489
|
+
if (!Object.prototype.hasOwnProperty.call(node, key))
|
|
490
|
+
continue;
|
|
491
|
+
if (key === 'set' || key === 'update') {
|
|
492
|
+
const v = node[key];
|
|
493
|
+
if (typeof v === 'function')
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const value = node[key];
|
|
497
|
+
if (isNodeAccessor$1(value)) {
|
|
498
|
+
const unwrappedValue = value();
|
|
499
|
+
if (typeof unwrappedValue === 'object' &&
|
|
500
|
+
unwrappedValue !== null &&
|
|
501
|
+
!Array.isArray(unwrappedValue) &&
|
|
502
|
+
!isBuiltInObject(unwrappedValue)) {
|
|
503
|
+
result[key] = unwrap(unwrappedValue);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
result[key] = unwrappedValue;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else if (isSignal(value)) {
|
|
510
|
+
const unwrappedValue = value();
|
|
511
|
+
if (typeof unwrappedValue === 'object' &&
|
|
512
|
+
unwrappedValue !== null &&
|
|
513
|
+
!Array.isArray(unwrappedValue) &&
|
|
514
|
+
!isBuiltInObject(unwrappedValue)) {
|
|
515
|
+
result[key] = unwrap(unwrappedValue);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
result[key] = unwrappedValue;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else if (typeof value === 'object' &&
|
|
522
|
+
value !== null &&
|
|
523
|
+
!Array.isArray(value) &&
|
|
524
|
+
!isBuiltInObject(value)) {
|
|
525
|
+
result[key] = unwrap(value);
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
result[key] = value;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
const symbols = Object.getOwnPropertySymbols(node);
|
|
532
|
+
for (const sym of symbols) {
|
|
533
|
+
const value = node[sym];
|
|
534
|
+
if (isNodeAccessor$1(value)) {
|
|
535
|
+
const unwrappedValue = value();
|
|
536
|
+
if (typeof unwrappedValue === 'object' &&
|
|
537
|
+
unwrappedValue !== null &&
|
|
538
|
+
!Array.isArray(unwrappedValue) &&
|
|
539
|
+
!isBuiltInObject(unwrappedValue)) {
|
|
540
|
+
result[sym] = unwrap(unwrappedValue);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
result[sym] = unwrappedValue;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
else if (isSignal(value)) {
|
|
547
|
+
const unwrappedValue = value();
|
|
548
|
+
if (typeof unwrappedValue === 'object' &&
|
|
549
|
+
unwrappedValue !== null &&
|
|
550
|
+
!Array.isArray(unwrappedValue) &&
|
|
551
|
+
!isBuiltInObject(unwrappedValue)) {
|
|
552
|
+
result[sym] = unwrap(unwrappedValue);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
result[sym] = unwrappedValue;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (typeof value === 'object' &&
|
|
559
|
+
value !== null &&
|
|
560
|
+
!Array.isArray(value) &&
|
|
561
|
+
!isBuiltInObject(value)) {
|
|
562
|
+
result[sym] = unwrap(value);
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
result[sym] = value;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
function cleanUnwrap(node) {
|
|
571
|
+
return unwrap(node);
|
|
262
572
|
}
|
|
263
573
|
|
|
264
|
-
|
|
574
|
+
const NODE_ACCESSOR_SYMBOL = Symbol.for('NodeAccessor');
|
|
575
|
+
function makeNodeAccessor() {
|
|
576
|
+
const accessor = function (arg) {
|
|
577
|
+
if (arguments.length === 0) {
|
|
578
|
+
return unwrap(accessor);
|
|
579
|
+
}
|
|
580
|
+
if (typeof arg === 'function') {
|
|
581
|
+
const updater = arg;
|
|
582
|
+
const currentValue = unwrap(accessor);
|
|
583
|
+
const newValue = updater(currentValue);
|
|
584
|
+
recursiveUpdate(accessor, newValue);
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
recursiveUpdate(accessor, arg);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
accessor[NODE_ACCESSOR_SYMBOL] = true;
|
|
591
|
+
return accessor;
|
|
592
|
+
}
|
|
593
|
+
function makeRootNodeAccessor(readSignal, writeSignal) {
|
|
594
|
+
const accessor = function (arg) {
|
|
595
|
+
if (arguments.length === 0) {
|
|
596
|
+
return readSignal();
|
|
597
|
+
}
|
|
598
|
+
if (typeof arg === 'function') {
|
|
599
|
+
const updater = arg;
|
|
600
|
+
writeSignal.set(updater(readSignal()));
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
writeSignal.set(arg);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
accessor[NODE_ACCESSOR_SYMBOL] = true;
|
|
607
|
+
return accessor;
|
|
608
|
+
}
|
|
609
|
+
function recursiveUpdate(target, updates) {
|
|
610
|
+
if (!updates || typeof updates !== 'object') {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
let targetObj;
|
|
614
|
+
if (isNodeAccessor(target)) {
|
|
615
|
+
targetObj = target;
|
|
616
|
+
}
|
|
617
|
+
else if (target && typeof target === 'object') {
|
|
618
|
+
targetObj = target;
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const updatesObj = updates;
|
|
624
|
+
for (const key in updatesObj) {
|
|
625
|
+
if (!(key in targetObj)) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const targetProp = targetObj[key];
|
|
629
|
+
const updateValue = updatesObj[key];
|
|
630
|
+
if (isSignal(targetProp)) {
|
|
631
|
+
if ('set' in targetProp && typeof targetProp.set === 'function') {
|
|
632
|
+
targetProp.set(updateValue);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else if (isNodeAccessor(targetProp)) {
|
|
636
|
+
if (updateValue && typeof updateValue === 'object') {
|
|
637
|
+
recursiveUpdate(targetProp, updateValue);
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
targetProp(updateValue);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function isNodeAccessor(value) {
|
|
646
|
+
return (typeof value === 'function' &&
|
|
647
|
+
value &&
|
|
648
|
+
value[NODE_ACCESSOR_SYMBOL] === true);
|
|
649
|
+
}
|
|
650
|
+
function estimateObjectSize(obj, maxDepth = SIGNAL_TREE_CONSTANTS.ESTIMATE_MAX_DEPTH, currentDepth = 0) {
|
|
265
651
|
if (currentDepth >= maxDepth)
|
|
266
652
|
return 1;
|
|
267
653
|
if (obj === null || obj === undefined)
|
|
@@ -272,7 +658,7 @@ function estimateObjectSize(obj, maxDepth = 3, currentDepth = 0) {
|
|
|
272
658
|
try {
|
|
273
659
|
if (Array.isArray(obj)) {
|
|
274
660
|
size = obj.length;
|
|
275
|
-
const sampleSize = Math.min(
|
|
661
|
+
const sampleSize = Math.min(SIGNAL_TREE_CONSTANTS.ESTIMATE_SAMPLE_SIZE_ARRAY, obj.length);
|
|
276
662
|
for (let i = 0; i < sampleSize; i++) {
|
|
277
663
|
size += estimateObjectSize(obj[i], maxDepth, currentDepth + 1) * 0.1;
|
|
278
664
|
}
|
|
@@ -280,7 +666,7 @@ function estimateObjectSize(obj, maxDepth = 3, currentDepth = 0) {
|
|
|
280
666
|
else {
|
|
281
667
|
const keys = Object.keys(obj);
|
|
282
668
|
size = keys.length;
|
|
283
|
-
const sampleSize = Math.min(
|
|
669
|
+
const sampleSize = Math.min(SIGNAL_TREE_CONSTANTS.ESTIMATE_SAMPLE_SIZE_OBJECT, keys.length);
|
|
284
670
|
for (let i = 0; i < sampleSize; i++) {
|
|
285
671
|
const value = obj[keys[i]];
|
|
286
672
|
size += estimateObjectSize(value, maxDepth, currentDepth + 1) * 0.5;
|
|
@@ -292,173 +678,19 @@ function estimateObjectSize(obj, maxDepth = 3, currentDepth = 0) {
|
|
|
292
678
|
}
|
|
293
679
|
return Math.floor(size);
|
|
294
680
|
}
|
|
295
|
-
function shouldUseLazy(obj, config) {
|
|
296
|
-
if (config.useLazySignals !== undefined)
|
|
681
|
+
function shouldUseLazy(obj, config, precomputedSize) {
|
|
682
|
+
if (config.useLazySignals !== undefined)
|
|
297
683
|
return config.useLazySignals;
|
|
298
|
-
|
|
299
|
-
if (config.debugMode || config.enableDevTools) {
|
|
684
|
+
if (config.debugMode || config.enableDevTools)
|
|
300
685
|
return false;
|
|
301
|
-
|
|
302
|
-
if (config.batchUpdates && config.useMemoization) {
|
|
686
|
+
if (config.batchUpdates && config.useMemoization)
|
|
303
687
|
return true;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return estimatedSize > 50;
|
|
688
|
+
const estimatedSize = precomputedSize ?? estimateObjectSize(obj);
|
|
689
|
+
return estimatedSize > SIGNAL_TREE_CONSTANTS.LAZY_THRESHOLD;
|
|
307
690
|
}
|
|
308
691
|
function createEqualityFn(useShallowComparison) {
|
|
309
692
|
return useShallowComparison ? Object.is : equal;
|
|
310
693
|
}
|
|
311
|
-
function isBuiltInObject(v) {
|
|
312
|
-
if (v === null || v === undefined)
|
|
313
|
-
return false;
|
|
314
|
-
if (v instanceof Date ||
|
|
315
|
-
v instanceof RegExp ||
|
|
316
|
-
typeof v === 'function' ||
|
|
317
|
-
v instanceof Map ||
|
|
318
|
-
v instanceof Set ||
|
|
319
|
-
v instanceof WeakMap ||
|
|
320
|
-
v instanceof WeakSet ||
|
|
321
|
-
v instanceof ArrayBuffer ||
|
|
322
|
-
v instanceof DataView ||
|
|
323
|
-
v instanceof Error ||
|
|
324
|
-
v instanceof Promise) {
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
function createCallableProxy(nestedStore, isBuiltInObjectFn) {
|
|
330
|
-
const unwrapFunction = () => {
|
|
331
|
-
const unwrapObject = (obj) => {
|
|
332
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
333
|
-
return obj;
|
|
334
|
-
}
|
|
335
|
-
if (isSignal(obj)) {
|
|
336
|
-
return obj();
|
|
337
|
-
}
|
|
338
|
-
if (typeof obj === 'function' &&
|
|
339
|
-
obj
|
|
340
|
-
.__isCallableProxy__ === true) {
|
|
341
|
-
return obj();
|
|
342
|
-
}
|
|
343
|
-
const result = {};
|
|
344
|
-
for (const key in obj) {
|
|
345
|
-
if (key === 'update' || key === '__isCallableProxy__')
|
|
346
|
-
continue;
|
|
347
|
-
const value = obj[key];
|
|
348
|
-
if (isSignal(value)) {
|
|
349
|
-
result[key] = value();
|
|
350
|
-
}
|
|
351
|
-
else if (typeof value === 'function' &&
|
|
352
|
-
value
|
|
353
|
-
.__isCallableProxy__ === true) {
|
|
354
|
-
result[key] = value();
|
|
355
|
-
}
|
|
356
|
-
else if (typeof value === 'object' &&
|
|
357
|
-
value !== null &&
|
|
358
|
-
!Array.isArray(value) &&
|
|
359
|
-
!isBuiltInObjectFn(value)) {
|
|
360
|
-
result[key] = unwrapObject(value);
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
result[key] = value;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return result;
|
|
367
|
-
};
|
|
368
|
-
return unwrapObject(nestedStore);
|
|
369
|
-
};
|
|
370
|
-
const callableProxy = new Proxy(unwrapFunction, {
|
|
371
|
-
apply(target) {
|
|
372
|
-
return target();
|
|
373
|
-
},
|
|
374
|
-
get(target, prop) {
|
|
375
|
-
if (prop === Symbol.toPrimitive ||
|
|
376
|
-
prop === 'valueOf' ||
|
|
377
|
-
prop === 'toString') {
|
|
378
|
-
return () => unwrapFunction();
|
|
379
|
-
}
|
|
380
|
-
if (prop === '__isCallableProxy__') {
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
if (prop === 'update') {
|
|
384
|
-
return (updaterOrPartial) => {
|
|
385
|
-
let partialObj;
|
|
386
|
-
if (typeof updaterOrPartial === 'function') {
|
|
387
|
-
const currentValue = unwrapFunction();
|
|
388
|
-
partialObj = updaterOrPartial(currentValue);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
partialObj = updaterOrPartial;
|
|
392
|
-
}
|
|
393
|
-
if (!partialObj || typeof partialObj !== 'object') {
|
|
394
|
-
throw new Error('Update must return/receive an object');
|
|
395
|
-
}
|
|
396
|
-
const updateObject = (target, updates) => {
|
|
397
|
-
for (const key in updates) {
|
|
398
|
-
if (!Object.prototype.hasOwnProperty.call(updates, key))
|
|
399
|
-
continue;
|
|
400
|
-
const updateValue = updates[key];
|
|
401
|
-
const currentSignalOrState = target[key];
|
|
402
|
-
if (isSignal(currentSignalOrState)) {
|
|
403
|
-
currentSignalOrState.set(updateValue);
|
|
404
|
-
}
|
|
405
|
-
else if (typeof currentSignalOrState === 'function' &&
|
|
406
|
-
currentSignalOrState.__isCallableProxy__ === true &&
|
|
407
|
-
updateValue !== null &&
|
|
408
|
-
typeof updateValue === 'object' &&
|
|
409
|
-
!Array.isArray(updateValue) &&
|
|
410
|
-
!isBuiltInObjectFn(updateValue)) {
|
|
411
|
-
updateObject(currentSignalOrState, updateValue);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
updateObject(nestedStore, partialObj);
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
if (prop === 'set') {
|
|
419
|
-
return (partialObj) => {
|
|
420
|
-
if (!partialObj || typeof partialObj !== 'object') {
|
|
421
|
-
throw new Error('Set must be called with an object');
|
|
422
|
-
}
|
|
423
|
-
const updateObject = (target, updates) => {
|
|
424
|
-
for (const key in updates) {
|
|
425
|
-
if (!Object.prototype.hasOwnProperty.call(updates, key))
|
|
426
|
-
continue;
|
|
427
|
-
const updateValue = updates[key];
|
|
428
|
-
const currentSignalOrState = target[key];
|
|
429
|
-
if (isSignal(currentSignalOrState)) {
|
|
430
|
-
currentSignalOrState.set(updateValue);
|
|
431
|
-
}
|
|
432
|
-
else if (typeof currentSignalOrState === 'function' &&
|
|
433
|
-
currentSignalOrState.__isCallableProxy__ === true &&
|
|
434
|
-
updateValue !== null &&
|
|
435
|
-
typeof updateValue === 'object' &&
|
|
436
|
-
!Array.isArray(updateValue) &&
|
|
437
|
-
!isBuiltInObjectFn(updateValue)) {
|
|
438
|
-
updateObject(currentSignalOrState, updateValue);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
updateObject(nestedStore, partialObj);
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
return nestedStore[prop];
|
|
446
|
-
},
|
|
447
|
-
has(target, prop) {
|
|
448
|
-
return prop in nestedStore;
|
|
449
|
-
},
|
|
450
|
-
ownKeys() {
|
|
451
|
-
return Reflect.ownKeys(nestedStore);
|
|
452
|
-
},
|
|
453
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
454
|
-
return Reflect.getOwnPropertyDescriptor(nestedStore, prop);
|
|
455
|
-
},
|
|
456
|
-
});
|
|
457
|
-
return callableProxy;
|
|
458
|
-
}
|
|
459
|
-
function enhanceNestedObject(nestedStore, isBuiltInObjectFn) {
|
|
460
|
-
return createCallableProxy(nestedStore, isBuiltInObjectFn);
|
|
461
|
-
}
|
|
462
694
|
function createSignalStore(obj, equalityFn) {
|
|
463
695
|
if (obj === null || obj === undefined || typeof obj !== 'object') {
|
|
464
696
|
return signal(obj, { equal: equalityFn });
|
|
@@ -472,7 +704,7 @@ function createSignalStore(obj, equalityFn) {
|
|
|
472
704
|
const store = {};
|
|
473
705
|
const processedObjects = new WeakSet();
|
|
474
706
|
if (processedObjects.has(obj)) {
|
|
475
|
-
console.warn(
|
|
707
|
+
console.warn(SIGNAL_TREE_MESSAGES.CIRCULAR_REF);
|
|
476
708
|
return signal(obj, { equal: equalityFn });
|
|
477
709
|
}
|
|
478
710
|
processedObjects.add(obj);
|
|
@@ -501,12 +733,26 @@ function createSignalStore(obj, equalityFn) {
|
|
|
501
733
|
});
|
|
502
734
|
}
|
|
503
735
|
else {
|
|
504
|
-
const
|
|
505
|
-
|
|
736
|
+
const branch = createSignalStore(value, equalityFn);
|
|
737
|
+
const callableBranch = makeNodeAccessor();
|
|
738
|
+
for (const branchKey in branch) {
|
|
739
|
+
if (Object.prototype.hasOwnProperty.call(branch, branchKey)) {
|
|
740
|
+
try {
|
|
741
|
+
Object.defineProperty(callableBranch, branchKey, {
|
|
742
|
+
value: branch[branchKey],
|
|
743
|
+
enumerable: true,
|
|
744
|
+
configurable: true,
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
store[key] = callableBranch;
|
|
506
752
|
}
|
|
507
753
|
}
|
|
508
754
|
catch (error) {
|
|
509
|
-
console.warn(
|
|
755
|
+
console.warn(`${SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED} "${key}":`, error);
|
|
510
756
|
store[key] = signal(value, {
|
|
511
757
|
equal: equalityFn,
|
|
512
758
|
});
|
|
@@ -526,68 +772,92 @@ function createSignalStore(obj, equalityFn) {
|
|
|
526
772
|
}
|
|
527
773
|
}
|
|
528
774
|
catch (error) {
|
|
529
|
-
console.warn(
|
|
775
|
+
console.warn(SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED, error);
|
|
530
776
|
}
|
|
531
777
|
}
|
|
532
778
|
}
|
|
533
779
|
catch (error) {
|
|
534
780
|
throw new Error(`Failed to create signal store: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
535
781
|
}
|
|
536
|
-
return
|
|
537
|
-
}
|
|
538
|
-
function create(obj, config = {}) {
|
|
539
|
-
if (obj === null || obj === undefined) {
|
|
540
|
-
throw new Error('Cannot create SignalTree from null or undefined');
|
|
541
|
-
}
|
|
542
|
-
const equalityFn = createEqualityFn(config.useShallowComparison ?? false);
|
|
543
|
-
const useLazy = shouldUseLazy(obj, config);
|
|
544
|
-
if (config.debugMode) {
|
|
545
|
-
const estimatedSize = estimateObjectSize(obj);
|
|
546
|
-
console.log(`[SignalTree] Creating tree with ${useLazy ? 'lazy' : 'eager'} strategy (estimated size: ${estimatedSize})`);
|
|
547
|
-
}
|
|
548
|
-
let signalState;
|
|
549
|
-
try {
|
|
550
|
-
if (useLazy && typeof obj === 'object') {
|
|
551
|
-
signalState = createLazySignalTree(obj, equalityFn);
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
signalState = createSignalStore(obj, equalityFn);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
catch (error) {
|
|
558
|
-
if (useLazy) {
|
|
559
|
-
console.warn('[SignalTree] Lazy creation failed, falling back to eager:', error);
|
|
560
|
-
signalState = createSignalStore(obj, equalityFn);
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
throw error;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
const resultTree = {
|
|
567
|
-
state: signalState,
|
|
568
|
-
$: signalState,
|
|
569
|
-
};
|
|
570
|
-
enhanceTree(resultTree, config);
|
|
571
|
-
return resultTree;
|
|
782
|
+
return store;
|
|
572
783
|
}
|
|
573
784
|
function enhanceTree(tree, config = {}) {
|
|
574
785
|
const isLazy = config.useLazySignals ?? shouldUseLazy(tree.state, config);
|
|
575
|
-
tree.
|
|
576
|
-
if (
|
|
786
|
+
tree.with = ((...enhancers) => {
|
|
787
|
+
if (enhancers.length === 0) {
|
|
577
788
|
return tree;
|
|
578
789
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
790
|
+
const coreCapabilities = new Set();
|
|
791
|
+
if (config.batchUpdates)
|
|
792
|
+
coreCapabilities.add('batchUpdate');
|
|
793
|
+
if (config.useMemoization)
|
|
794
|
+
coreCapabilities.add('memoize');
|
|
795
|
+
if (config.enableTimeTravel)
|
|
796
|
+
coreCapabilities.add('undo');
|
|
797
|
+
if (config.enableDevTools)
|
|
798
|
+
coreCapabilities.add('connectDevTools');
|
|
799
|
+
try {
|
|
800
|
+
for (const key of Object.keys(tree))
|
|
801
|
+
coreCapabilities.add(String(key));
|
|
802
|
+
}
|
|
803
|
+
catch {
|
|
804
|
+
}
|
|
805
|
+
const hasMetadata = enhancers.some((e) => Boolean(e.metadata && (e.metadata.requires || e.metadata.provides)));
|
|
806
|
+
let orderedEnhancers = enhancers;
|
|
807
|
+
if (hasMetadata) {
|
|
808
|
+
try {
|
|
809
|
+
orderedEnhancers = resolveEnhancerOrder(enhancers, coreCapabilities, config.debugMode);
|
|
810
|
+
}
|
|
811
|
+
catch (err) {
|
|
812
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_ORDER_FAILED, err);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const provided = new Set(coreCapabilities);
|
|
816
|
+
let currentTree = tree;
|
|
817
|
+
for (let i = 0; i < orderedEnhancers.length; i++) {
|
|
818
|
+
const enhancer = orderedEnhancers[i];
|
|
819
|
+
if (typeof enhancer !== 'function') {
|
|
820
|
+
throw new Error(SIGNAL_TREE_MESSAGES.ENHANCER_NOT_FUNCTION.replace('%d', String(i)));
|
|
821
|
+
}
|
|
822
|
+
const reqs = enhancer.metadata?.requires ?? [];
|
|
823
|
+
for (const r of reqs) {
|
|
824
|
+
if (!(r in currentTree) && !provided.has(r)) {
|
|
825
|
+
const name = enhancer.metadata?.name ?? `enhancer#${i}`;
|
|
826
|
+
const msg = SIGNAL_TREE_MESSAGES.ENHANCER_REQUIREMENT_MISSING.replace('%s', name).replace('%s', r);
|
|
827
|
+
if (config.debugMode) {
|
|
828
|
+
throw new Error(msg);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
console.warn(msg);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
582
834
|
}
|
|
583
835
|
try {
|
|
584
|
-
|
|
836
|
+
const result = enhancer(currentTree);
|
|
837
|
+
if (result !== currentTree)
|
|
838
|
+
currentTree = result;
|
|
839
|
+
const provs = enhancer.metadata?.provides ?? [];
|
|
840
|
+
for (const p of provs)
|
|
841
|
+
provided.add(p);
|
|
842
|
+
if (config.debugMode && provs.length > 0) {
|
|
843
|
+
for (const p of provs) {
|
|
844
|
+
if (!(p in currentTree)) {
|
|
845
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_PROVIDES_MISSING.replace('%s', enhancer.metadata?.name ?? String(i)).replace('%s', p));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
585
849
|
}
|
|
586
850
|
catch (error) {
|
|
587
|
-
|
|
851
|
+
const name = enhancer.metadata?.name || `enhancer at index ${i}`;
|
|
852
|
+
console.error(SIGNAL_TREE_MESSAGES.ENHANCER_FAILED.replace('%s', name), error);
|
|
853
|
+
if (config.debugMode) {
|
|
854
|
+
console.error('[SignalTree] Enhancer stack trace:', enhancer);
|
|
855
|
+
console.error('[SignalTree] Tree state at failure:', currentTree);
|
|
856
|
+
}
|
|
588
857
|
throw error;
|
|
589
858
|
}
|
|
590
|
-
}
|
|
859
|
+
}
|
|
860
|
+
return currentTree;
|
|
591
861
|
});
|
|
592
862
|
tree.destroy = () => {
|
|
593
863
|
try {
|
|
@@ -601,29 +871,36 @@ function enhanceTree(tree, config = {}) {
|
|
|
601
871
|
}
|
|
602
872
|
}
|
|
603
873
|
if (config.debugMode) {
|
|
604
|
-
console.log(
|
|
874
|
+
console.log(SIGNAL_TREE_MESSAGES.TREE_DESTROYED);
|
|
605
875
|
}
|
|
606
876
|
}
|
|
607
877
|
catch (error) {
|
|
608
|
-
console.error(
|
|
878
|
+
console.error(SIGNAL_TREE_MESSAGES.CLEANUP_ERROR, error);
|
|
609
879
|
}
|
|
610
880
|
};
|
|
611
881
|
addStubMethods(tree, config);
|
|
612
882
|
return tree;
|
|
613
883
|
}
|
|
614
884
|
function addStubMethods(tree, config) {
|
|
885
|
+
tree.batchUpdate = (updater) => {
|
|
886
|
+
console.warn(SIGNAL_TREE_MESSAGES.BATCH_NOT_ENABLED);
|
|
887
|
+
tree((current) => {
|
|
888
|
+
const partial = updater(current);
|
|
889
|
+
return { ...current, ...partial };
|
|
890
|
+
});
|
|
891
|
+
};
|
|
615
892
|
tree.memoize = (fn, cacheKey) => {
|
|
616
|
-
console.warn(
|
|
893
|
+
console.warn(SIGNAL_TREE_MESSAGES.MEMOIZE_NOT_ENABLED);
|
|
617
894
|
void cacheKey;
|
|
618
|
-
return computed(() => fn(tree
|
|
895
|
+
return computed(() => fn(tree()));
|
|
619
896
|
};
|
|
620
897
|
tree.effect = (fn) => {
|
|
621
898
|
try {
|
|
622
|
-
effect(() => fn(tree
|
|
899
|
+
effect(() => fn(tree()));
|
|
623
900
|
}
|
|
624
901
|
catch (error) {
|
|
625
902
|
if (config.debugMode) {
|
|
626
|
-
console.warn(
|
|
903
|
+
console.warn(SIGNAL_TREE_MESSAGES.EFFECT_NO_CONTEXT, error);
|
|
627
904
|
}
|
|
628
905
|
}
|
|
629
906
|
};
|
|
@@ -633,7 +910,7 @@ function addStubMethods(tree, config) {
|
|
|
633
910
|
let isDestroyed = false;
|
|
634
911
|
const effectRef = effect(() => {
|
|
635
912
|
if (!isDestroyed) {
|
|
636
|
-
fn(tree
|
|
913
|
+
fn(tree());
|
|
637
914
|
}
|
|
638
915
|
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : []));
|
|
639
916
|
const unsubscribe = () => {
|
|
@@ -645,26 +922,26 @@ function addStubMethods(tree, config) {
|
|
|
645
922
|
}
|
|
646
923
|
catch (error) {
|
|
647
924
|
if (config.debugMode) {
|
|
648
|
-
console.warn(
|
|
925
|
+
console.warn(SIGNAL_TREE_MESSAGES.SUBSCRIBE_NO_CONTEXT, error);
|
|
649
926
|
}
|
|
650
|
-
fn(tree
|
|
927
|
+
fn(tree());
|
|
651
928
|
return () => {
|
|
652
929
|
};
|
|
653
930
|
}
|
|
654
931
|
};
|
|
655
932
|
tree.optimize = () => {
|
|
656
933
|
if (config.debugMode) {
|
|
657
|
-
console.warn(
|
|
934
|
+
console.warn(SIGNAL_TREE_MESSAGES.OPTIMIZE_NOT_AVAILABLE);
|
|
658
935
|
}
|
|
659
936
|
};
|
|
660
937
|
tree.clearCache = () => {
|
|
661
938
|
if (config.debugMode) {
|
|
662
|
-
console.warn(
|
|
939
|
+
console.warn(SIGNAL_TREE_MESSAGES.CACHE_NOT_AVAILABLE);
|
|
663
940
|
}
|
|
664
941
|
};
|
|
665
942
|
tree.invalidatePattern = () => {
|
|
666
943
|
if (config.debugMode) {
|
|
667
|
-
console.warn(
|
|
944
|
+
console.warn(SIGNAL_TREE_MESSAGES.PERFORMANCE_NOT_ENABLED);
|
|
668
945
|
}
|
|
669
946
|
return 0;
|
|
670
947
|
};
|
|
@@ -679,25 +956,25 @@ function addStubMethods(tree, config) {
|
|
|
679
956
|
};
|
|
680
957
|
tree.addTap = (middleware) => {
|
|
681
958
|
if (config.debugMode) {
|
|
682
|
-
console.warn(
|
|
959
|
+
console.warn(SIGNAL_TREE_MESSAGES.MIDDLEWARE_NOT_AVAILABLE);
|
|
683
960
|
}
|
|
684
961
|
void middleware;
|
|
685
962
|
};
|
|
686
963
|
tree.removeTap = (id) => {
|
|
687
964
|
if (config.debugMode) {
|
|
688
|
-
console.warn(
|
|
965
|
+
console.warn(SIGNAL_TREE_MESSAGES.MIDDLEWARE_NOT_AVAILABLE);
|
|
689
966
|
}
|
|
690
967
|
void id;
|
|
691
968
|
};
|
|
692
969
|
tree.asCrud = () => {
|
|
693
970
|
if (config.debugMode) {
|
|
694
|
-
console.warn(
|
|
971
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENTITY_HELPERS_NOT_AVAILABLE);
|
|
695
972
|
}
|
|
696
973
|
return {};
|
|
697
974
|
};
|
|
698
975
|
tree.asyncAction = (operation, asyncConfig = {}) => {
|
|
699
976
|
if (config.debugMode) {
|
|
700
|
-
console.warn(
|
|
977
|
+
console.warn(SIGNAL_TREE_MESSAGES.ASYNC_ACTIONS_NOT_AVAILABLE);
|
|
701
978
|
}
|
|
702
979
|
void operation;
|
|
703
980
|
void asyncConfig;
|
|
@@ -705,55 +982,137 @@ function addStubMethods(tree, config) {
|
|
|
705
982
|
};
|
|
706
983
|
tree.undo = () => {
|
|
707
984
|
if (config.debugMode) {
|
|
708
|
-
console.warn(
|
|
985
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
709
986
|
}
|
|
710
987
|
};
|
|
711
988
|
tree.redo = () => {
|
|
712
989
|
if (config.debugMode) {
|
|
713
|
-
console.warn(
|
|
990
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
714
991
|
}
|
|
715
992
|
};
|
|
716
993
|
tree.getHistory = () => {
|
|
717
994
|
if (config.debugMode) {
|
|
718
|
-
console.warn(
|
|
995
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
719
996
|
}
|
|
720
997
|
return [];
|
|
721
998
|
};
|
|
722
999
|
tree.resetHistory = () => {
|
|
723
1000
|
if (config.debugMode) {
|
|
724
|
-
console.warn(
|
|
1001
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
725
1002
|
}
|
|
726
1003
|
};
|
|
727
1004
|
}
|
|
1005
|
+
function create(obj, config = {}) {
|
|
1006
|
+
if (obj === null || obj === undefined) {
|
|
1007
|
+
throw new Error(SIGNAL_TREE_MESSAGES.NULL_OR_UNDEFINED);
|
|
1008
|
+
}
|
|
1009
|
+
const estimatedSize = estimateObjectSize(obj);
|
|
1010
|
+
const equalityFn = createEqualityFn(config.useShallowComparison ?? false);
|
|
1011
|
+
if (Array.isArray(obj)) {
|
|
1012
|
+
const signalState = signal(obj, {
|
|
1013
|
+
equal: equalityFn,
|
|
1014
|
+
});
|
|
1015
|
+
const tree = makeRootNodeAccessor(signalState, signalState);
|
|
1016
|
+
Object.defineProperty(tree, 'state', {
|
|
1017
|
+
value: signalState,
|
|
1018
|
+
enumerable: false,
|
|
1019
|
+
});
|
|
1020
|
+
Object.defineProperty(tree, '$', { value: signalState, enumerable: false });
|
|
1021
|
+
enhanceTree(tree, config);
|
|
1022
|
+
return tree;
|
|
1023
|
+
}
|
|
1024
|
+
const useLazy = shouldUseLazy(obj, config, estimatedSize);
|
|
1025
|
+
if (config.debugMode) {
|
|
1026
|
+
console.log(SIGNAL_TREE_MESSAGES.STRATEGY_SELECTION.replace('%s', useLazy ? 'lazy' : 'eager').replace('%d', String(estimatedSize)));
|
|
1027
|
+
}
|
|
1028
|
+
let signalState;
|
|
1029
|
+
try {
|
|
1030
|
+
if (useLazy && typeof obj === 'object') {
|
|
1031
|
+
signalState = createLazySignalTree(obj, equalityFn);
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
signalState = createSignalStore(obj, equalityFn);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
catch (error) {
|
|
1038
|
+
if (useLazy) {
|
|
1039
|
+
console.warn(SIGNAL_TREE_MESSAGES.LAZY_FALLBACK, error);
|
|
1040
|
+
signalState = createSignalStore(obj, equalityFn);
|
|
1041
|
+
}
|
|
1042
|
+
else {
|
|
1043
|
+
throw error;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
const tree = function (arg) {
|
|
1047
|
+
if (arguments.length === 0) {
|
|
1048
|
+
return unwrap(signalState);
|
|
1049
|
+
}
|
|
1050
|
+
if (typeof arg === 'function') {
|
|
1051
|
+
const updater = arg;
|
|
1052
|
+
const currentValue = unwrap(signalState);
|
|
1053
|
+
const newValue = updater(currentValue);
|
|
1054
|
+
recursiveUpdate(signalState, newValue);
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
recursiveUpdate(signalState, arg);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
Object.defineProperty(tree, NODE_ACCESSOR_SYMBOL, {
|
|
1061
|
+
value: true,
|
|
1062
|
+
enumerable: false,
|
|
1063
|
+
});
|
|
1064
|
+
Object.defineProperty(tree, 'state', {
|
|
1065
|
+
value: signalState,
|
|
1066
|
+
enumerable: false,
|
|
1067
|
+
});
|
|
1068
|
+
Object.defineProperty(tree, '$', { value: signalState, enumerable: false });
|
|
1069
|
+
enhanceTree(tree, config);
|
|
1070
|
+
for (const key in signalState) {
|
|
1071
|
+
if (Object.prototype.hasOwnProperty.call(signalState, key)) {
|
|
1072
|
+
if (!(key in tree)) {
|
|
1073
|
+
try {
|
|
1074
|
+
Object.defineProperty(tree, key, {
|
|
1075
|
+
value: signalState[key],
|
|
1076
|
+
enumerable: true,
|
|
1077
|
+
configurable: true,
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
catch {
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return tree;
|
|
1086
|
+
}
|
|
1087
|
+
const presetConfigs = {
|
|
1088
|
+
basic: {
|
|
1089
|
+
useLazySignals: false,
|
|
1090
|
+
debugMode: false,
|
|
1091
|
+
},
|
|
1092
|
+
performance: {
|
|
1093
|
+
useLazySignals: true,
|
|
1094
|
+
batchUpdates: true,
|
|
1095
|
+
useMemoization: true,
|
|
1096
|
+
useShallowComparison: true,
|
|
1097
|
+
},
|
|
1098
|
+
development: {
|
|
1099
|
+
useLazySignals: false,
|
|
1100
|
+
debugMode: true,
|
|
1101
|
+
enableDevTools: true,
|
|
1102
|
+
trackPerformance: true,
|
|
1103
|
+
},
|
|
1104
|
+
production: {
|
|
1105
|
+
useLazySignals: true,
|
|
1106
|
+
batchUpdates: true,
|
|
1107
|
+
useMemoization: true,
|
|
1108
|
+
debugMode: false,
|
|
1109
|
+
},
|
|
1110
|
+
};
|
|
728
1111
|
function signalTree(obj, configOrPreset) {
|
|
729
1112
|
if (typeof configOrPreset === 'string') {
|
|
730
|
-
const presetConfigs = {
|
|
731
|
-
basic: {
|
|
732
|
-
useLazySignals: false,
|
|
733
|
-
debugMode: false,
|
|
734
|
-
},
|
|
735
|
-
performance: {
|
|
736
|
-
useLazySignals: true,
|
|
737
|
-
batchUpdates: true,
|
|
738
|
-
useMemoization: true,
|
|
739
|
-
useShallowComparison: true,
|
|
740
|
-
},
|
|
741
|
-
development: {
|
|
742
|
-
useLazySignals: false,
|
|
743
|
-
debugMode: true,
|
|
744
|
-
enableDevTools: true,
|
|
745
|
-
trackPerformance: true,
|
|
746
|
-
},
|
|
747
|
-
production: {
|
|
748
|
-
useLazySignals: true,
|
|
749
|
-
batchUpdates: true,
|
|
750
|
-
useMemoization: true,
|
|
751
|
-
debugMode: false,
|
|
752
|
-
},
|
|
753
|
-
};
|
|
754
1113
|
const config = presetConfigs[configOrPreset];
|
|
755
1114
|
if (!config) {
|
|
756
|
-
console.warn(
|
|
1115
|
+
console.warn(SIGNAL_TREE_MESSAGES.PRESET_UNKNOWN.replace('%s', configOrPreset));
|
|
757
1116
|
return create(obj, {});
|
|
758
1117
|
}
|
|
759
1118
|
return create(obj, config);
|
|
@@ -761,6 +1120,9 @@ function signalTree(obj, configOrPreset) {
|
|
|
761
1120
|
const config = configOrPreset || {};
|
|
762
1121
|
return create(obj, config);
|
|
763
1122
|
}
|
|
1123
|
+
function applyEnhancer(tree, enhancer) {
|
|
1124
|
+
return enhancer(tree);
|
|
1125
|
+
}
|
|
764
1126
|
|
|
765
|
-
export { createLazySignalTree, deepEqual, equal, parsePath,
|
|
1127
|
+
export { ENHANCER_META, SIGNAL_TREE_CONSTANTS, SIGNAL_TREE_MESSAGES, composeEnhancers, createEnhancer, createLazySignalTree, deepEqual, equal, isAnySignal, isBuiltInObject, isNodeAccessor$1 as isNodeAccessor, parsePath, resolveEnhancerOrder, signalTree, terminalSignal, unwrap };
|
|
766
1128
|
//# sourceMappingURL=signaltree-core.mjs.map
|