@signaltree/core 1.1.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -86
- package/fesm2022/signaltree-core.mjs +679 -345
- package/fesm2022/signaltree-core.mjs.map +1 -1
- package/index.d.ts +105 -22
- 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,112 +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
|
-
if (v instanceof Int8Array ||
|
|
328
|
-
v instanceof Uint8Array ||
|
|
329
|
-
v instanceof Uint8ClampedArray ||
|
|
330
|
-
v instanceof Int16Array ||
|
|
331
|
-
v instanceof Uint16Array ||
|
|
332
|
-
v instanceof Int32Array ||
|
|
333
|
-
v instanceof Uint32Array ||
|
|
334
|
-
v instanceof Float32Array ||
|
|
335
|
-
v instanceof Float64Array ||
|
|
336
|
-
v instanceof BigInt64Array ||
|
|
337
|
-
v instanceof BigUint64Array) {
|
|
338
|
-
return true;
|
|
339
|
-
}
|
|
340
|
-
if (typeof window !== 'undefined') {
|
|
341
|
-
if (v instanceof URL ||
|
|
342
|
-
v instanceof URLSearchParams ||
|
|
343
|
-
v instanceof FormData ||
|
|
344
|
-
v instanceof Blob ||
|
|
345
|
-
(typeof File !== 'undefined' && v instanceof File) ||
|
|
346
|
-
(typeof FileList !== 'undefined' && v instanceof FileList) ||
|
|
347
|
-
(typeof Headers !== 'undefined' && v instanceof Headers) ||
|
|
348
|
-
(typeof Request !== 'undefined' && v instanceof Request) ||
|
|
349
|
-
(typeof Response !== 'undefined' && v instanceof Response) ||
|
|
350
|
-
(typeof AbortController !== 'undefined' &&
|
|
351
|
-
v instanceof AbortController) ||
|
|
352
|
-
(typeof AbortSignal !== 'undefined' && v instanceof AbortSignal)) {
|
|
353
|
-
return true;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
const NodeBuffer = globalThis?.Buffer;
|
|
358
|
-
if (NodeBuffer && v instanceof NodeBuffer) {
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
}
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
function create(obj, config = {}) {
|
|
367
|
-
if (obj === null || obj === undefined) {
|
|
368
|
-
throw new Error('Cannot create SignalTree from null or undefined');
|
|
369
|
-
}
|
|
370
|
-
const equalityFn = createEqualityFn(config.useShallowComparison ?? false);
|
|
371
|
-
const useLazy = shouldUseLazy(obj, config);
|
|
372
|
-
if (config.debugMode) {
|
|
373
|
-
const estimatedSize = estimateObjectSize(obj);
|
|
374
|
-
console.log(`[SignalTree] Creating tree with ${useLazy ? 'lazy' : 'eager'} strategy (estimated size: ${estimatedSize})`);
|
|
375
|
-
}
|
|
376
|
-
let signalState;
|
|
377
|
-
try {
|
|
378
|
-
if (useLazy && typeof obj === 'object') {
|
|
379
|
-
signalState = createLazySignalTree(obj, equalityFn);
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
signalState = createSignalStore(obj, equalityFn);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
catch (error) {
|
|
386
|
-
if (useLazy) {
|
|
387
|
-
console.warn('[SignalTree] Lazy creation failed, falling back to eager:', error);
|
|
388
|
-
signalState = createSignalStore(obj, equalityFn);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
throw error;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const resultTree = {
|
|
395
|
-
state: signalState,
|
|
396
|
-
$: signalState,
|
|
397
|
-
};
|
|
398
|
-
enhanceTree(resultTree, config);
|
|
399
|
-
return resultTree;
|
|
400
|
-
}
|
|
401
694
|
function createSignalStore(obj, equalityFn) {
|
|
402
695
|
if (obj === null || obj === undefined || typeof obj !== 'object') {
|
|
403
696
|
return signal(obj, { equal: equalityFn });
|
|
@@ -411,7 +704,7 @@ function createSignalStore(obj, equalityFn) {
|
|
|
411
704
|
const store = {};
|
|
412
705
|
const processedObjects = new WeakSet();
|
|
413
706
|
if (processedObjects.has(obj)) {
|
|
414
|
-
console.warn(
|
|
707
|
+
console.warn(SIGNAL_TREE_MESSAGES.CIRCULAR_REF);
|
|
415
708
|
return signal(obj, { equal: equalityFn });
|
|
416
709
|
}
|
|
417
710
|
processedObjects.add(obj);
|
|
@@ -440,11 +733,26 @@ function createSignalStore(obj, equalityFn) {
|
|
|
440
733
|
});
|
|
441
734
|
}
|
|
442
735
|
else {
|
|
443
|
-
|
|
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;
|
|
444
752
|
}
|
|
445
753
|
}
|
|
446
754
|
catch (error) {
|
|
447
|
-
console.warn(
|
|
755
|
+
console.warn(`${SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED} "${key}":`, error);
|
|
448
756
|
store[key] = signal(value, {
|
|
449
757
|
equal: equalityFn,
|
|
450
758
|
});
|
|
@@ -464,7 +772,7 @@ function createSignalStore(obj, equalityFn) {
|
|
|
464
772
|
}
|
|
465
773
|
}
|
|
466
774
|
catch (error) {
|
|
467
|
-
console.warn(
|
|
775
|
+
console.warn(SIGNAL_TREE_MESSAGES.SIGNAL_CREATION_FAILED, error);
|
|
468
776
|
}
|
|
469
777
|
}
|
|
470
778
|
}
|
|
@@ -475,143 +783,81 @@ function createSignalStore(obj, equalityFn) {
|
|
|
475
783
|
}
|
|
476
784
|
function enhanceTree(tree, config = {}) {
|
|
477
785
|
const isLazy = config.useLazySignals ?? shouldUseLazy(tree.state, config);
|
|
478
|
-
tree.
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
786
|
+
tree.with = ((...enhancers) => {
|
|
787
|
+
if (enhancers.length === 0) {
|
|
788
|
+
return tree;
|
|
789
|
+
}
|
|
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);
|
|
491
810
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (!Object.prototype.hasOwnProperty.call(obj, key))
|
|
495
|
-
continue;
|
|
496
|
-
const value = obj[key];
|
|
497
|
-
if (isSignal(value)) {
|
|
498
|
-
result[key] = value();
|
|
499
|
-
}
|
|
500
|
-
else if (typeof value === 'object' &&
|
|
501
|
-
value !== null &&
|
|
502
|
-
!Array.isArray(value) &&
|
|
503
|
-
!isBuiltInObject(value)) {
|
|
504
|
-
result[key] = unwrapObject(value);
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
result[key] = value;
|
|
508
|
-
}
|
|
811
|
+
catch (err) {
|
|
812
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_ORDER_FAILED, err);
|
|
509
813
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
result[sym] = value;
|
|
518
|
-
}
|
|
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)));
|
|
519
821
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const currentValue = tree.unwrap();
|
|
528
|
-
const partialObj = updater(currentValue);
|
|
529
|
-
if (!partialObj || typeof partialObj !== 'object') {
|
|
530
|
-
throw new Error('Updater must return an object');
|
|
531
|
-
}
|
|
532
|
-
const updateObject = (target, updates, path = '') => {
|
|
533
|
-
for (const key in updates) {
|
|
534
|
-
if (!Object.prototype.hasOwnProperty.call(updates, key))
|
|
535
|
-
continue;
|
|
536
|
-
const updateValue = updates[key];
|
|
537
|
-
const currentPath = path ? `${path}.${key}` : key;
|
|
538
|
-
const currentSignalOrState = target[key];
|
|
539
|
-
try {
|
|
540
|
-
if (isSignal(currentSignalOrState)) {
|
|
541
|
-
const originalValue = currentSignalOrState();
|
|
542
|
-
transactionLog.push({
|
|
543
|
-
path: currentPath,
|
|
544
|
-
oldValue: originalValue,
|
|
545
|
-
newValue: updateValue,
|
|
546
|
-
});
|
|
547
|
-
currentSignalOrState.set(updateValue);
|
|
548
|
-
}
|
|
549
|
-
else if (typeof updateValue === 'object' &&
|
|
550
|
-
updateValue !== null &&
|
|
551
|
-
!Array.isArray(updateValue) &&
|
|
552
|
-
!isBuiltInObject(updateValue) &&
|
|
553
|
-
typeof currentSignalOrState === 'object' &&
|
|
554
|
-
currentSignalOrState !== null &&
|
|
555
|
-
!isSignal(currentSignalOrState)) {
|
|
556
|
-
updateObject(currentSignalOrState, updateValue, currentPath);
|
|
557
|
-
}
|
|
558
|
-
else if (currentSignalOrState === undefined) {
|
|
559
|
-
console.warn(`[SignalTree] Cannot update non-existent path: ${currentPath}`);
|
|
560
|
-
}
|
|
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);
|
|
561
829
|
}
|
|
562
|
-
|
|
563
|
-
console.
|
|
564
|
-
throw error;
|
|
830
|
+
else {
|
|
831
|
+
console.warn(msg);
|
|
565
832
|
}
|
|
566
833
|
}
|
|
567
|
-
};
|
|
568
|
-
updateObject(tree.state, partialObj);
|
|
569
|
-
if (config.debugMode && transactionLog.length > 0) {
|
|
570
|
-
console.log('[SignalTree] Update transaction:', transactionLog);
|
|
571
834
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (!
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
if (current) {
|
|
585
|
-
const lastKey = pathParts[pathParts.length - 1];
|
|
586
|
-
const targetSignal = current[lastKey];
|
|
587
|
-
if (isSignal(targetSignal)) {
|
|
588
|
-
targetSignal.set(oldValue);
|
|
835
|
+
try {
|
|
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));
|
|
589
846
|
}
|
|
590
847
|
}
|
|
591
848
|
}
|
|
592
|
-
catch (rollbackError) {
|
|
593
|
-
console.error('[SignalTree] Rollback failed for path:', path, rollbackError);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
throw error;
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
tree.pipe = ((...fns) => {
|
|
600
|
-
if (fns.length === 0) {
|
|
601
|
-
return tree;
|
|
602
|
-
}
|
|
603
|
-
return fns.reduce((acc, fn) => {
|
|
604
|
-
if (typeof fn !== 'function') {
|
|
605
|
-
throw new Error('All pipe arguments must be functions');
|
|
606
|
-
}
|
|
607
|
-
try {
|
|
608
|
-
return fn(acc);
|
|
609
849
|
}
|
|
610
850
|
catch (error) {
|
|
611
|
-
|
|
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
|
+
}
|
|
612
857
|
throw error;
|
|
613
858
|
}
|
|
614
|
-
}
|
|
859
|
+
}
|
|
860
|
+
return currentTree;
|
|
615
861
|
});
|
|
616
862
|
tree.destroy = () => {
|
|
617
863
|
try {
|
|
@@ -625,11 +871,11 @@ function enhanceTree(tree, config = {}) {
|
|
|
625
871
|
}
|
|
626
872
|
}
|
|
627
873
|
if (config.debugMode) {
|
|
628
|
-
console.log(
|
|
874
|
+
console.log(SIGNAL_TREE_MESSAGES.TREE_DESTROYED);
|
|
629
875
|
}
|
|
630
876
|
}
|
|
631
877
|
catch (error) {
|
|
632
|
-
console.error(
|
|
878
|
+
console.error(SIGNAL_TREE_MESSAGES.CLEANUP_ERROR, error);
|
|
633
879
|
}
|
|
634
880
|
};
|
|
635
881
|
addStubMethods(tree, config);
|
|
@@ -637,21 +883,24 @@ function enhanceTree(tree, config = {}) {
|
|
|
637
883
|
}
|
|
638
884
|
function addStubMethods(tree, config) {
|
|
639
885
|
tree.batchUpdate = (updater) => {
|
|
640
|
-
console.warn(
|
|
641
|
-
tree
|
|
886
|
+
console.warn(SIGNAL_TREE_MESSAGES.BATCH_NOT_ENABLED);
|
|
887
|
+
tree((current) => {
|
|
888
|
+
const partial = updater(current);
|
|
889
|
+
return { ...current, ...partial };
|
|
890
|
+
});
|
|
642
891
|
};
|
|
643
892
|
tree.memoize = (fn, cacheKey) => {
|
|
644
|
-
console.warn(
|
|
893
|
+
console.warn(SIGNAL_TREE_MESSAGES.MEMOIZE_NOT_ENABLED);
|
|
645
894
|
void cacheKey;
|
|
646
|
-
return computed(() => fn(tree
|
|
895
|
+
return computed(() => fn(tree()));
|
|
647
896
|
};
|
|
648
897
|
tree.effect = (fn) => {
|
|
649
898
|
try {
|
|
650
|
-
effect(() => fn(tree
|
|
899
|
+
effect(() => fn(tree()));
|
|
651
900
|
}
|
|
652
901
|
catch (error) {
|
|
653
902
|
if (config.debugMode) {
|
|
654
|
-
console.warn(
|
|
903
|
+
console.warn(SIGNAL_TREE_MESSAGES.EFFECT_NO_CONTEXT, error);
|
|
655
904
|
}
|
|
656
905
|
}
|
|
657
906
|
};
|
|
@@ -661,7 +910,7 @@ function addStubMethods(tree, config) {
|
|
|
661
910
|
let isDestroyed = false;
|
|
662
911
|
const effectRef = effect(() => {
|
|
663
912
|
if (!isDestroyed) {
|
|
664
|
-
fn(tree
|
|
913
|
+
fn(tree());
|
|
665
914
|
}
|
|
666
915
|
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : []));
|
|
667
916
|
const unsubscribe = () => {
|
|
@@ -673,26 +922,26 @@ function addStubMethods(tree, config) {
|
|
|
673
922
|
}
|
|
674
923
|
catch (error) {
|
|
675
924
|
if (config.debugMode) {
|
|
676
|
-
console.warn(
|
|
925
|
+
console.warn(SIGNAL_TREE_MESSAGES.SUBSCRIBE_NO_CONTEXT, error);
|
|
677
926
|
}
|
|
678
|
-
fn(tree
|
|
927
|
+
fn(tree());
|
|
679
928
|
return () => {
|
|
680
929
|
};
|
|
681
930
|
}
|
|
682
931
|
};
|
|
683
932
|
tree.optimize = () => {
|
|
684
933
|
if (config.debugMode) {
|
|
685
|
-
console.warn(
|
|
934
|
+
console.warn(SIGNAL_TREE_MESSAGES.OPTIMIZE_NOT_AVAILABLE);
|
|
686
935
|
}
|
|
687
936
|
};
|
|
688
937
|
tree.clearCache = () => {
|
|
689
938
|
if (config.debugMode) {
|
|
690
|
-
console.warn(
|
|
939
|
+
console.warn(SIGNAL_TREE_MESSAGES.CACHE_NOT_AVAILABLE);
|
|
691
940
|
}
|
|
692
941
|
};
|
|
693
942
|
tree.invalidatePattern = () => {
|
|
694
943
|
if (config.debugMode) {
|
|
695
|
-
console.warn(
|
|
944
|
+
console.warn(SIGNAL_TREE_MESSAGES.PERFORMANCE_NOT_ENABLED);
|
|
696
945
|
}
|
|
697
946
|
return 0;
|
|
698
947
|
};
|
|
@@ -707,25 +956,25 @@ function addStubMethods(tree, config) {
|
|
|
707
956
|
};
|
|
708
957
|
tree.addTap = (middleware) => {
|
|
709
958
|
if (config.debugMode) {
|
|
710
|
-
console.warn(
|
|
959
|
+
console.warn(SIGNAL_TREE_MESSAGES.MIDDLEWARE_NOT_AVAILABLE);
|
|
711
960
|
}
|
|
712
961
|
void middleware;
|
|
713
962
|
};
|
|
714
963
|
tree.removeTap = (id) => {
|
|
715
964
|
if (config.debugMode) {
|
|
716
|
-
console.warn(
|
|
965
|
+
console.warn(SIGNAL_TREE_MESSAGES.MIDDLEWARE_NOT_AVAILABLE);
|
|
717
966
|
}
|
|
718
967
|
void id;
|
|
719
968
|
};
|
|
720
969
|
tree.asCrud = () => {
|
|
721
970
|
if (config.debugMode) {
|
|
722
|
-
console.warn(
|
|
971
|
+
console.warn(SIGNAL_TREE_MESSAGES.ENTITY_HELPERS_NOT_AVAILABLE);
|
|
723
972
|
}
|
|
724
973
|
return {};
|
|
725
974
|
};
|
|
726
975
|
tree.asyncAction = (operation, asyncConfig = {}) => {
|
|
727
976
|
if (config.debugMode) {
|
|
728
|
-
console.warn(
|
|
977
|
+
console.warn(SIGNAL_TREE_MESSAGES.ASYNC_ACTIONS_NOT_AVAILABLE);
|
|
729
978
|
}
|
|
730
979
|
void operation;
|
|
731
980
|
void asyncConfig;
|
|
@@ -733,55 +982,137 @@ function addStubMethods(tree, config) {
|
|
|
733
982
|
};
|
|
734
983
|
tree.undo = () => {
|
|
735
984
|
if (config.debugMode) {
|
|
736
|
-
console.warn(
|
|
985
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
737
986
|
}
|
|
738
987
|
};
|
|
739
988
|
tree.redo = () => {
|
|
740
989
|
if (config.debugMode) {
|
|
741
|
-
console.warn(
|
|
990
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
742
991
|
}
|
|
743
992
|
};
|
|
744
993
|
tree.getHistory = () => {
|
|
745
994
|
if (config.debugMode) {
|
|
746
|
-
console.warn(
|
|
995
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
747
996
|
}
|
|
748
997
|
return [];
|
|
749
998
|
};
|
|
750
999
|
tree.resetHistory = () => {
|
|
751
1000
|
if (config.debugMode) {
|
|
752
|
-
console.warn(
|
|
1001
|
+
console.warn(SIGNAL_TREE_MESSAGES.TIME_TRAVEL_NOT_AVAILABLE);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
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);
|
|
753
1058
|
}
|
|
754
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;
|
|
755
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
|
+
};
|
|
756
1111
|
function signalTree(obj, configOrPreset) {
|
|
757
1112
|
if (typeof configOrPreset === 'string') {
|
|
758
|
-
const presetConfigs = {
|
|
759
|
-
basic: {
|
|
760
|
-
useLazySignals: false,
|
|
761
|
-
debugMode: false,
|
|
762
|
-
},
|
|
763
|
-
performance: {
|
|
764
|
-
useLazySignals: true,
|
|
765
|
-
batchUpdates: true,
|
|
766
|
-
useMemoization: true,
|
|
767
|
-
useShallowComparison: true,
|
|
768
|
-
},
|
|
769
|
-
development: {
|
|
770
|
-
useLazySignals: false,
|
|
771
|
-
debugMode: true,
|
|
772
|
-
enableDevTools: true,
|
|
773
|
-
trackPerformance: true,
|
|
774
|
-
},
|
|
775
|
-
production: {
|
|
776
|
-
useLazySignals: true,
|
|
777
|
-
batchUpdates: true,
|
|
778
|
-
useMemoization: true,
|
|
779
|
-
debugMode: false,
|
|
780
|
-
},
|
|
781
|
-
};
|
|
782
1113
|
const config = presetConfigs[configOrPreset];
|
|
783
1114
|
if (!config) {
|
|
784
|
-
console.warn(
|
|
1115
|
+
console.warn(SIGNAL_TREE_MESSAGES.PRESET_UNKNOWN.replace('%s', configOrPreset));
|
|
785
1116
|
return create(obj, {});
|
|
786
1117
|
}
|
|
787
1118
|
return create(obj, config);
|
|
@@ -789,6 +1120,9 @@ function signalTree(obj, configOrPreset) {
|
|
|
789
1120
|
const config = configOrPreset || {};
|
|
790
1121
|
return create(obj, config);
|
|
791
1122
|
}
|
|
1123
|
+
function applyEnhancer(tree, enhancer) {
|
|
1124
|
+
return enhancer(tree);
|
|
1125
|
+
}
|
|
792
1126
|
|
|
793
|
-
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 };
|
|
794
1128
|
//# sourceMappingURL=signaltree-core.mjs.map
|