@signaltree/core 5.1.4 → 6.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/package.json +1 -6
- package/dist/constants.js +0 -6
- package/dist/deep-clone.js +0 -80
- package/dist/deep-equal.js +0 -41
- package/dist/enhancers/batching/lib/batching.js +0 -161
- package/dist/enhancers/computed/lib/computed.js +0 -21
- package/dist/enhancers/devtools/lib/devtools.js +0 -321
- package/dist/enhancers/entities/lib/entities.js +0 -93
- package/dist/enhancers/index.js +0 -72
- package/dist/enhancers/memoization/lib/memoization.js +0 -410
- package/dist/enhancers/presets/lib/presets.js +0 -87
- package/dist/enhancers/serialization/constants.js +0 -15
- package/dist/enhancers/serialization/lib/serialization.js +0 -662
- package/dist/enhancers/time-travel/lib/time-travel.js +0 -193
- package/dist/index.js +0 -19
- package/dist/is-built-in-object.js +0 -23
- package/dist/lib/async-helpers.js +0 -77
- package/dist/lib/constants.js +0 -56
- package/dist/lib/entity-signal.js +0 -283
- package/dist/lib/memory/memory-manager.js +0 -164
- package/dist/lib/path-notifier.js +0 -106
- package/dist/lib/performance/diff-engine.js +0 -156
- package/dist/lib/performance/path-index.js +0 -156
- package/dist/lib/performance/update-engine.js +0 -188
- package/dist/lib/security/security-validator.js +0 -121
- package/dist/lib/signal-tree.js +0 -626
- package/dist/lib/types.js +0 -9
- package/dist/lib/utils.js +0 -261
- package/dist/lru-cache.js +0 -64
- package/dist/parse-path.js +0 -13
- package/src/async-helpers.d.ts +0 -8
- package/src/batching.d.ts +0 -16
- package/src/computed.d.ts +0 -12
- package/src/constants.d.ts +0 -14
- package/src/devtools.d.ts +0 -77
- package/src/diff-engine.d.ts +0 -33
- package/src/enhancers/batching/index.d.ts +0 -1
- package/src/enhancers/batching/lib/batching.d.ts +0 -16
- package/src/enhancers/batching/test-setup.d.ts +0 -3
- package/src/enhancers/computed/index.d.ts +0 -1
- package/src/enhancers/computed/lib/computed.d.ts +0 -12
- package/src/enhancers/devtools/index.d.ts +0 -1
- package/src/enhancers/devtools/lib/devtools.d.ts +0 -77
- package/src/enhancers/devtools/test-setup.d.ts +0 -3
- package/src/enhancers/entities/index.d.ts +0 -1
- package/src/enhancers/entities/lib/entities.d.ts +0 -20
- package/src/enhancers/entities/test-setup.d.ts +0 -3
- package/src/enhancers/index.d.ts +0 -3
- package/src/enhancers/memoization/index.d.ts +0 -1
- package/src/enhancers/memoization/lib/memoization.d.ts +0 -65
- package/src/enhancers/memoization/test-setup.d.ts +0 -3
- package/src/enhancers/presets/index.d.ts +0 -1
- package/src/enhancers/presets/lib/presets.d.ts +0 -11
- package/src/enhancers/presets/test-setup.d.ts +0 -3
- package/src/enhancers/serialization/constants.d.ts +0 -14
- package/src/enhancers/serialization/index.d.ts +0 -2
- package/src/enhancers/serialization/lib/serialization.d.ts +0 -59
- package/src/enhancers/serialization/test-setup.d.ts +0 -3
- package/src/enhancers/time-travel/index.d.ts +0 -1
- package/src/enhancers/time-travel/lib/time-travel.d.ts +0 -36
- package/src/enhancers/time-travel/lib/utils.d.ts +0 -1
- package/src/enhancers/time-travel/test-setup.d.ts +0 -3
- package/src/enhancers/types.d.ts +0 -74
- package/src/entities.d.ts +0 -20
- package/src/entity-signal.d.ts +0 -1
- package/src/index.d.ts +0 -18
- package/src/lib/async-helpers.d.ts +0 -8
- package/src/lib/constants.d.ts +0 -41
- package/src/lib/entity-signal.d.ts +0 -1
- package/src/lib/memory/memory-manager.d.ts +0 -30
- package/src/lib/path-notifier.d.ts +0 -4
- package/src/lib/performance/diff-engine.d.ts +0 -33
- package/src/lib/performance/path-index.d.ts +0 -25
- package/src/lib/performance/update-engine.d.ts +0 -32
- package/src/lib/security/security-validator.d.ts +0 -33
- package/src/lib/signal-tree.d.ts +0 -8
- package/src/lib/types.d.ts +0 -278
- package/src/lib/utils.d.ts +0 -28
- package/src/memoization.d.ts +0 -65
- package/src/memory-manager.d.ts +0 -30
- package/src/path-index.d.ts +0 -25
- package/src/path-notifier.d.ts +0 -12
- package/src/presets.d.ts +0 -11
- package/src/security-validator.d.ts +0 -33
- package/src/serialization.d.ts +0 -59
- package/src/signal-tree.d.ts +0 -8
- package/src/test-setup.d.ts +0 -3
- package/src/time-travel.d.ts +0 -36
- package/src/types.d.ts +0 -444
- package/src/update-engine.d.ts +0 -32
- package/src/utils.d.ts +0 -1
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { createEntitySignal } from '../../../lib/entity-signal.js';
|
|
2
|
-
import { getPathNotifier } from '../../../lib/path-notifier.js';
|
|
3
|
-
import { isNodeAccessor } from '../../../lib/utils.js';
|
|
4
|
-
|
|
5
|
-
function isEntityMapMarker(value) {
|
|
6
|
-
return Boolean(value && typeof value === 'object' && value['__isEntityMap'] === true);
|
|
7
|
-
}
|
|
8
|
-
function isEntitySignal(value) {
|
|
9
|
-
return !!value && typeof value === 'object' && typeof value['addOne'] === 'function' && typeof value['all'] !== 'undefined';
|
|
10
|
-
}
|
|
11
|
-
function materializeEntities(tree, notifier = getPathNotifier()) {
|
|
12
|
-
const registry = new Map();
|
|
13
|
-
const state = tree.state;
|
|
14
|
-
const visit = (parent, key, value, path) => {
|
|
15
|
-
const nextPath = [...path, key];
|
|
16
|
-
if (isEntityMapMarker(value)) {
|
|
17
|
-
const basePath = nextPath.join('.');
|
|
18
|
-
const config = value.__entityMapConfig ?? {};
|
|
19
|
-
const entitySignal = createEntitySignal(config, notifier, basePath);
|
|
20
|
-
if (parent) {
|
|
21
|
-
try {
|
|
22
|
-
parent[key] = entitySignal;
|
|
23
|
-
} catch {}
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
tree[key] = entitySignal;
|
|
27
|
-
} catch {}
|
|
28
|
-
registry.set(basePath, entitySignal);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (isNodeAccessor(value)) {
|
|
32
|
-
const nodeAsAny = value;
|
|
33
|
-
for (const childKey of Object.keys(nodeAsAny)) {
|
|
34
|
-
visit(nodeAsAny, childKey, nodeAsAny[childKey], nextPath);
|
|
35
|
-
}
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (value && typeof value === 'object') {
|
|
39
|
-
for (const childKey of Object.keys(value)) {
|
|
40
|
-
visit(value, childKey, value[childKey], nextPath);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
for (const key of Object.keys(state)) {
|
|
45
|
-
visit(state, key, state[key], []);
|
|
46
|
-
}
|
|
47
|
-
return registry;
|
|
48
|
-
}
|
|
49
|
-
function resolveEntitySignal(tree, registry, path) {
|
|
50
|
-
const pathStr = String(path);
|
|
51
|
-
const existing = registry.get(pathStr);
|
|
52
|
-
if (existing) return existing;
|
|
53
|
-
const segments = pathStr.split('.');
|
|
54
|
-
let current = tree.state;
|
|
55
|
-
for (const segment of segments) {
|
|
56
|
-
if (!current) break;
|
|
57
|
-
current = current[segment];
|
|
58
|
-
}
|
|
59
|
-
if (isEntitySignal(current)) {
|
|
60
|
-
registry.set(pathStr, current);
|
|
61
|
-
return current;
|
|
62
|
-
}
|
|
63
|
-
throw new Error(`Entity path '${pathStr}' is not configured. Define it with entityMap() in your initial state.`);
|
|
64
|
-
}
|
|
65
|
-
function withEntities(config = {}) {
|
|
66
|
-
const {
|
|
67
|
-
enabled = true
|
|
68
|
-
} = config;
|
|
69
|
-
return function enhanceWithEntities(tree) {
|
|
70
|
-
if (!enabled) {
|
|
71
|
-
return tree;
|
|
72
|
-
}
|
|
73
|
-
const registry = materializeEntities(tree);
|
|
74
|
-
const enhancedTree = Object.assign(tree, {
|
|
75
|
-
entities(path) {
|
|
76
|
-
return resolveEntitySignal(tree, registry, path);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
return enhancedTree;
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function enableEntities() {
|
|
83
|
-
return withEntities({
|
|
84
|
-
enabled: true
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
function withHighPerformanceEntities() {
|
|
88
|
-
return withEntities({
|
|
89
|
-
enabled: true
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export { enableEntities, withEntities, withHighPerformanceEntities };
|
package/dist/enhancers/index.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { SIGNAL_TREE_MESSAGES } from '../lib/constants.js';
|
|
2
|
-
import { ENHANCER_META } from '../lib/types.js';
|
|
3
|
-
|
|
4
|
-
function createEnhancer(meta, enhancerFn) {
|
|
5
|
-
const fn = enhancerFn;
|
|
6
|
-
try {
|
|
7
|
-
fn.metadata = meta;
|
|
8
|
-
try {
|
|
9
|
-
fn[ENHANCER_META] = meta;
|
|
10
|
-
} catch {}
|
|
11
|
-
} catch {}
|
|
12
|
-
return fn;
|
|
13
|
-
}
|
|
14
|
-
function resolveEnhancerOrder(enhancers, availableCapabilities = new Set(), debugMode = false) {
|
|
15
|
-
const nodes = enhancers.map((e, idx) => ({
|
|
16
|
-
fn: e,
|
|
17
|
-
name: e.metadata && e.metadata.name ? String(e.metadata.name) : `enhancer#${idx}`,
|
|
18
|
-
requires: new Set(e.metadata?.requires ?? []),
|
|
19
|
-
provides: new Set(e.metadata?.provides ?? [])
|
|
20
|
-
}));
|
|
21
|
-
const adj = new Map();
|
|
22
|
-
const nameToNode = new Map();
|
|
23
|
-
for (const n of nodes) {
|
|
24
|
-
nameToNode.set(n.name, n);
|
|
25
|
-
adj.set(n.name, new Set());
|
|
26
|
-
}
|
|
27
|
-
for (const a of nodes) {
|
|
28
|
-
for (const b of nodes) {
|
|
29
|
-
if (a === b) continue;
|
|
30
|
-
for (const req of b.requires) {
|
|
31
|
-
if (availableCapabilities.has(req)) continue;
|
|
32
|
-
if (a.provides.has(req)) {
|
|
33
|
-
const set = adj.get(a.name);
|
|
34
|
-
if (set) set.add(b.name);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const inDegree = new Map();
|
|
40
|
-
for (const entry of adj.keys()) inDegree.set(entry, 0);
|
|
41
|
-
for (const [, outs] of adj) {
|
|
42
|
-
for (const to of outs) inDegree.set(to, (inDegree.get(to) || 0) + 1);
|
|
43
|
-
}
|
|
44
|
-
const queue = [];
|
|
45
|
-
for (const [name, deg] of inDegree.entries()) {
|
|
46
|
-
if (deg === 0) queue.push(name);
|
|
47
|
-
}
|
|
48
|
-
const ordered = [];
|
|
49
|
-
while (queue.length > 0) {
|
|
50
|
-
const n = queue.shift();
|
|
51
|
-
if (!n) break;
|
|
52
|
-
ordered.push(n);
|
|
53
|
-
const outs = adj.get(n);
|
|
54
|
-
if (!outs) continue;
|
|
55
|
-
for (const m of outs) {
|
|
56
|
-
inDegree.set(m, (inDegree.get(m) || 0) - 1);
|
|
57
|
-
if (inDegree.get(m) === 0) queue.push(m);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (ordered.length !== nodes.length) {
|
|
61
|
-
if (debugMode) {
|
|
62
|
-
console.warn(SIGNAL_TREE_MESSAGES.ENHANCER_CYCLE_DETECTED);
|
|
63
|
-
}
|
|
64
|
-
return enhancers;
|
|
65
|
-
}
|
|
66
|
-
return ordered.map(name => {
|
|
67
|
-
const n = nameToNode.get(name);
|
|
68
|
-
return n ? n.fn : enhancers[0];
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export { createEnhancer, resolveEnhancerOrder };
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
import { computed } from '@angular/core';
|
|
2
|
-
import { isNodeAccessor } from '../../../lib/utils.js';
|
|
3
|
-
import { LRUCache } from '../../../lru-cache.js';
|
|
4
|
-
import { deepEqual } from '../../../deep-equal.js';
|
|
5
|
-
|
|
6
|
-
function isDevMode() {
|
|
7
|
-
if (typeof __DEV__ !== 'undefined') {
|
|
8
|
-
return __DEV__;
|
|
9
|
-
}
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
const MAX_CACHE_SIZE = 1000;
|
|
13
|
-
const DEFAULT_TTL = 5 * 60 * 1000;
|
|
14
|
-
const memoizationCache = new Map();
|
|
15
|
-
function createMemoCacheStore(maxSize, enableLRU) {
|
|
16
|
-
if (enableLRU) {
|
|
17
|
-
const cache = new LRUCache(maxSize);
|
|
18
|
-
const shadow = new Map();
|
|
19
|
-
const pruneShadow = () => {
|
|
20
|
-
while (shadow.size > cache.size()) {
|
|
21
|
-
const oldestKey = shadow.keys().next().value;
|
|
22
|
-
if (oldestKey === undefined) {
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
shadow.delete(oldestKey);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
return {
|
|
29
|
-
get: key => {
|
|
30
|
-
const value = cache.get(key);
|
|
31
|
-
if (value !== undefined) {
|
|
32
|
-
shadow.set(key, value);
|
|
33
|
-
} else if (shadow.has(key)) {
|
|
34
|
-
shadow.delete(key);
|
|
35
|
-
}
|
|
36
|
-
return value;
|
|
37
|
-
},
|
|
38
|
-
set: (key, value) => {
|
|
39
|
-
cache.set(key, value);
|
|
40
|
-
shadow.set(key, value);
|
|
41
|
-
pruneShadow();
|
|
42
|
-
},
|
|
43
|
-
delete: key => {
|
|
44
|
-
cache.delete(key);
|
|
45
|
-
shadow.delete(key);
|
|
46
|
-
},
|
|
47
|
-
clear: () => {
|
|
48
|
-
cache.clear();
|
|
49
|
-
shadow.clear();
|
|
50
|
-
},
|
|
51
|
-
size: () => shadow.size,
|
|
52
|
-
forEach: callback => {
|
|
53
|
-
shadow.forEach((value, key) => callback(value, key));
|
|
54
|
-
},
|
|
55
|
-
keys: () => shadow.keys()
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
const store = new Map();
|
|
59
|
-
return {
|
|
60
|
-
get: key => store.get(key),
|
|
61
|
-
set: (key, value) => {
|
|
62
|
-
store.set(key, value);
|
|
63
|
-
},
|
|
64
|
-
delete: key => store.delete(key),
|
|
65
|
-
clear: () => store.clear(),
|
|
66
|
-
size: () => store.size,
|
|
67
|
-
forEach: callback => {
|
|
68
|
-
store.forEach((value, key) => callback(value, key));
|
|
69
|
-
},
|
|
70
|
-
keys: () => store.keys()
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function getCleanupInterval(tree) {
|
|
74
|
-
return tree._memoCleanupInterval;
|
|
75
|
-
}
|
|
76
|
-
function setCleanupInterval(tree, interval) {
|
|
77
|
-
if (interval === undefined) {
|
|
78
|
-
delete tree._memoCleanupInterval;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
tree._memoCleanupInterval = interval;
|
|
82
|
-
}
|
|
83
|
-
function clearCleanupInterval(tree) {
|
|
84
|
-
const interval = getCleanupInterval(tree);
|
|
85
|
-
if (!interval) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
clearInterval(interval);
|
|
90
|
-
} catch {}
|
|
91
|
-
setCleanupInterval(tree);
|
|
92
|
-
}
|
|
93
|
-
function resetMemoizationCaches() {
|
|
94
|
-
memoizationCache.forEach((cache, tree) => {
|
|
95
|
-
cache.clear();
|
|
96
|
-
clearCleanupInterval(tree);
|
|
97
|
-
});
|
|
98
|
-
memoizationCache.clear();
|
|
99
|
-
}
|
|
100
|
-
function cleanupMemoizationCache() {
|
|
101
|
-
resetMemoizationCaches();
|
|
102
|
-
}
|
|
103
|
-
function shallowEqual(a, b) {
|
|
104
|
-
if (a === b) return true;
|
|
105
|
-
if (a == null || b == null) return false;
|
|
106
|
-
if (typeof a !== typeof b) return false;
|
|
107
|
-
if (typeof a === 'object' && typeof b === 'object') {
|
|
108
|
-
const objA = a;
|
|
109
|
-
const objB = b;
|
|
110
|
-
let countA = 0;
|
|
111
|
-
for (const key in objA) {
|
|
112
|
-
if (!Object.prototype.hasOwnProperty.call(objA, key)) continue;
|
|
113
|
-
countA++;
|
|
114
|
-
if (!(key in objB) || objA[key] !== objB[key]) return false;
|
|
115
|
-
}
|
|
116
|
-
let countB = 0;
|
|
117
|
-
for (const key in objB) {
|
|
118
|
-
if (Object.prototype.hasOwnProperty.call(objB, key)) countB++;
|
|
119
|
-
}
|
|
120
|
-
return countA === countB;
|
|
121
|
-
}
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
function generateCacheKey(fn, args) {
|
|
125
|
-
try {
|
|
126
|
-
return `${fn.name || 'anonymous'}_${JSON.stringify(args)}`;
|
|
127
|
-
} catch {
|
|
128
|
-
return `${fn.name || 'anonymous'}_${args.length}`;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function getEqualityFn(strategy) {
|
|
132
|
-
switch (strategy) {
|
|
133
|
-
case 'shallow':
|
|
134
|
-
return shallowEqual;
|
|
135
|
-
case 'reference':
|
|
136
|
-
return (a, b) => a === b;
|
|
137
|
-
case 'deep':
|
|
138
|
-
default:
|
|
139
|
-
return deepEqual;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function memoize(fn, keyFn, config = {}) {
|
|
143
|
-
const maxSize = config.maxCacheSize ?? MAX_CACHE_SIZE;
|
|
144
|
-
const ttl = config.ttl ?? DEFAULT_TTL;
|
|
145
|
-
const equality = getEqualityFn(config.equality ?? 'shallow');
|
|
146
|
-
const enableLRU = config.enableLRU ?? false;
|
|
147
|
-
const cache = createMemoCacheStore(maxSize, enableLRU);
|
|
148
|
-
const cleanExpiredEntries = () => {
|
|
149
|
-
if (!ttl) return;
|
|
150
|
-
const now = Date.now();
|
|
151
|
-
cache.forEach((entry, key) => {
|
|
152
|
-
if (entry.timestamp && now - entry.timestamp > ttl) {
|
|
153
|
-
cache.delete(key);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
|
-
return (...args) => {
|
|
158
|
-
if (ttl && Math.random() < 0.01) {
|
|
159
|
-
cleanExpiredEntries();
|
|
160
|
-
}
|
|
161
|
-
const key = keyFn ? keyFn(...args) : generateCacheKey(fn, args);
|
|
162
|
-
const cached = cache.get(key);
|
|
163
|
-
if (cached && (keyFn || equality(cached.deps, args))) {
|
|
164
|
-
if (enableLRU) {
|
|
165
|
-
cached.hitCount += 1;
|
|
166
|
-
}
|
|
167
|
-
return cached.value;
|
|
168
|
-
}
|
|
169
|
-
const result = fn(...args);
|
|
170
|
-
cache.set(key, {
|
|
171
|
-
value: result,
|
|
172
|
-
deps: args,
|
|
173
|
-
timestamp: Date.now(),
|
|
174
|
-
hitCount: 1
|
|
175
|
-
});
|
|
176
|
-
return result;
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function memoizeShallow(fn, keyFn) {
|
|
180
|
-
return memoize(fn, keyFn, {
|
|
181
|
-
equality: 'shallow',
|
|
182
|
-
enableLRU: false,
|
|
183
|
-
ttl: undefined,
|
|
184
|
-
maxCacheSize: 100
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
function memoizeReference(fn, keyFn) {
|
|
188
|
-
return memoize(fn, keyFn, {
|
|
189
|
-
equality: 'reference',
|
|
190
|
-
enableLRU: false,
|
|
191
|
-
ttl: undefined,
|
|
192
|
-
maxCacheSize: 50
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
const MEMOIZATION_PRESETS = {
|
|
196
|
-
selector: {
|
|
197
|
-
equality: 'reference',
|
|
198
|
-
maxCacheSize: 10,
|
|
199
|
-
enableLRU: false,
|
|
200
|
-
ttl: undefined
|
|
201
|
-
},
|
|
202
|
-
computed: {
|
|
203
|
-
equality: 'shallow',
|
|
204
|
-
maxCacheSize: 100,
|
|
205
|
-
enableLRU: false,
|
|
206
|
-
ttl: undefined
|
|
207
|
-
},
|
|
208
|
-
deepState: {
|
|
209
|
-
equality: 'deep',
|
|
210
|
-
maxCacheSize: 1000,
|
|
211
|
-
enableLRU: true,
|
|
212
|
-
ttl: 5 * 60 * 1000
|
|
213
|
-
},
|
|
214
|
-
highFrequency: {
|
|
215
|
-
equality: 'reference',
|
|
216
|
-
maxCacheSize: 5,
|
|
217
|
-
enableLRU: false,
|
|
218
|
-
ttl: undefined
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
function withSelectorMemoization() {
|
|
222
|
-
return withMemoization(MEMOIZATION_PRESETS.selector);
|
|
223
|
-
}
|
|
224
|
-
function withComputedMemoization() {
|
|
225
|
-
return withMemoization(MEMOIZATION_PRESETS.computed);
|
|
226
|
-
}
|
|
227
|
-
function withDeepStateMemoization() {
|
|
228
|
-
return withMemoization(MEMOIZATION_PRESETS.deepState);
|
|
229
|
-
}
|
|
230
|
-
function withHighFrequencyMemoization() {
|
|
231
|
-
return withMemoization(MEMOIZATION_PRESETS.highFrequency);
|
|
232
|
-
}
|
|
233
|
-
function withMemoization(config = {}) {
|
|
234
|
-
const {
|
|
235
|
-
enabled = true,
|
|
236
|
-
maxCacheSize = 1000,
|
|
237
|
-
ttl,
|
|
238
|
-
equality = 'deep',
|
|
239
|
-
enableLRU = true
|
|
240
|
-
} = config;
|
|
241
|
-
return tree => {
|
|
242
|
-
const originalTreeCall = tree.bind(tree);
|
|
243
|
-
const applyUpdateResult = result => {
|
|
244
|
-
Object.entries(result).forEach(([propKey, value]) => {
|
|
245
|
-
const property = tree.state[propKey];
|
|
246
|
-
if (property && 'set' in property) {
|
|
247
|
-
property.set(value);
|
|
248
|
-
} else if (isNodeAccessor(property)) {
|
|
249
|
-
property(value);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
};
|
|
253
|
-
if (!enabled) {
|
|
254
|
-
const memoTree = tree;
|
|
255
|
-
memoTree.memoizedUpdate = updater => {
|
|
256
|
-
const currentState = originalTreeCall();
|
|
257
|
-
const result = updater(currentState);
|
|
258
|
-
applyUpdateResult(result);
|
|
259
|
-
};
|
|
260
|
-
memoTree.clearMemoCache = () => {};
|
|
261
|
-
memoTree.getCacheStats = () => ({
|
|
262
|
-
size: 0,
|
|
263
|
-
hitRate: 0,
|
|
264
|
-
totalHits: 0,
|
|
265
|
-
totalMisses: 0,
|
|
266
|
-
keys: []
|
|
267
|
-
});
|
|
268
|
-
return memoTree;
|
|
269
|
-
}
|
|
270
|
-
const cache = createMemoCacheStore(maxCacheSize, enableLRU);
|
|
271
|
-
memoizationCache.set(tree, cache);
|
|
272
|
-
const equalityFn = getEqualityFn(equality);
|
|
273
|
-
tree.memoizedUpdate = (updater, cacheKey) => {
|
|
274
|
-
const currentState = originalTreeCall();
|
|
275
|
-
const key = cacheKey || generateCacheKey(updater, [currentState]);
|
|
276
|
-
const cached = cache.get(key);
|
|
277
|
-
if (cached && equalityFn(cached.deps, [currentState])) {
|
|
278
|
-
const cachedUpdate = cached.value;
|
|
279
|
-
applyUpdateResult(cachedUpdate);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const result = updater(currentState);
|
|
283
|
-
cache.set(key, {
|
|
284
|
-
value: result,
|
|
285
|
-
deps: [currentState],
|
|
286
|
-
timestamp: Date.now(),
|
|
287
|
-
hitCount: 1
|
|
288
|
-
});
|
|
289
|
-
applyUpdateResult(result);
|
|
290
|
-
};
|
|
291
|
-
const memoizeResultCache = createMemoCacheStore(MAX_CACHE_SIZE, true);
|
|
292
|
-
tree.memoize = (fn, cacheKey) => {
|
|
293
|
-
return computed(() => {
|
|
294
|
-
const currentState = originalTreeCall();
|
|
295
|
-
const key = cacheKey || generateCacheKey(fn, [currentState]);
|
|
296
|
-
const cached = memoizeResultCache.get(key);
|
|
297
|
-
if (cached && equalityFn(cached.deps, [currentState])) {
|
|
298
|
-
return cached.value;
|
|
299
|
-
}
|
|
300
|
-
if (isDevMode() && tree.__devHooks?.onRecompute) {
|
|
301
|
-
try {
|
|
302
|
-
tree.__devHooks.onRecompute(key, 1);
|
|
303
|
-
} catch {}
|
|
304
|
-
}
|
|
305
|
-
const result = fn(currentState);
|
|
306
|
-
memoizeResultCache.set(key, {
|
|
307
|
-
value: result,
|
|
308
|
-
deps: [currentState],
|
|
309
|
-
timestamp: Date.now(),
|
|
310
|
-
hitCount: 1
|
|
311
|
-
});
|
|
312
|
-
return result;
|
|
313
|
-
});
|
|
314
|
-
};
|
|
315
|
-
tree.clearMemoCache = key => {
|
|
316
|
-
if (key) {
|
|
317
|
-
cache.delete(key);
|
|
318
|
-
} else {
|
|
319
|
-
cache.clear();
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
tree.getCacheStats = () => {
|
|
323
|
-
let totalHits = 0;
|
|
324
|
-
let totalMisses = 0;
|
|
325
|
-
cache.forEach(entry => {
|
|
326
|
-
totalHits += entry.hitCount || 0;
|
|
327
|
-
totalMisses += Math.floor((entry.hitCount || 0) / 2);
|
|
328
|
-
});
|
|
329
|
-
const hitRate = totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0;
|
|
330
|
-
return {
|
|
331
|
-
size: cache.size(),
|
|
332
|
-
hitRate,
|
|
333
|
-
totalHits,
|
|
334
|
-
totalMisses,
|
|
335
|
-
keys: Array.from(cache.keys())
|
|
336
|
-
};
|
|
337
|
-
};
|
|
338
|
-
const maybeInterval = getCleanupInterval(tree);
|
|
339
|
-
if (maybeInterval) {
|
|
340
|
-
const origClear = tree.clearMemoCache.bind(tree);
|
|
341
|
-
tree.clearMemoCache = key => {
|
|
342
|
-
origClear(key);
|
|
343
|
-
clearCleanupInterval(tree);
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
if (ttl) {
|
|
347
|
-
const cleanup = () => {
|
|
348
|
-
const now = Date.now();
|
|
349
|
-
cache.forEach((entry, key) => {
|
|
350
|
-
if (entry.timestamp && now - entry.timestamp > ttl) {
|
|
351
|
-
cache.delete(key);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
};
|
|
355
|
-
const intervalId = setInterval(cleanup, ttl);
|
|
356
|
-
setCleanupInterval(tree, intervalId);
|
|
357
|
-
}
|
|
358
|
-
return tree;
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
function withHighPerformanceMemoization() {
|
|
362
|
-
return withMemoization({
|
|
363
|
-
enabled: true,
|
|
364
|
-
maxCacheSize: 10000,
|
|
365
|
-
ttl: 300000,
|
|
366
|
-
equality: 'shallow',
|
|
367
|
-
enableLRU: true
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
function withLightweightMemoization() {
|
|
371
|
-
return withMemoization({
|
|
372
|
-
enabled: true,
|
|
373
|
-
maxCacheSize: 100,
|
|
374
|
-
ttl: undefined,
|
|
375
|
-
equality: 'reference',
|
|
376
|
-
enableLRU: false
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
function withShallowMemoization() {
|
|
380
|
-
return withMemoization({
|
|
381
|
-
enabled: true,
|
|
382
|
-
maxCacheSize: 1000,
|
|
383
|
-
ttl: 60000,
|
|
384
|
-
equality: 'shallow',
|
|
385
|
-
enableLRU: true
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
function clearAllCaches() {
|
|
389
|
-
resetMemoizationCaches();
|
|
390
|
-
}
|
|
391
|
-
function getGlobalCacheStats() {
|
|
392
|
-
let totalSize = 0;
|
|
393
|
-
let totalHits = 0;
|
|
394
|
-
let treeCount = 0;
|
|
395
|
-
memoizationCache.forEach(cache => {
|
|
396
|
-
treeCount++;
|
|
397
|
-
totalSize += cache.size();
|
|
398
|
-
cache.forEach(entry => {
|
|
399
|
-
totalHits += entry.hitCount || 0;
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
return {
|
|
403
|
-
treeCount,
|
|
404
|
-
totalSize,
|
|
405
|
-
totalHits,
|
|
406
|
-
averageCacheSize: treeCount > 0 ? totalSize / treeCount : 0
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
export { MEMOIZATION_PRESETS, cleanupMemoizationCache, clearAllCaches, getGlobalCacheStats, memoize, memoizeReference, memoizeShallow, withComputedMemoization, withDeepStateMemoization, withHighFrequencyMemoization, withHighPerformanceMemoization, withLightweightMemoization, withMemoization, withSelectorMemoization, withShallowMemoization };
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { composeEnhancers } from '../../../lib/utils.js';
|
|
2
|
-
import { withHighPerformanceBatching, withBatching } from '../../batching/lib/batching.js';
|
|
3
|
-
import { withDevTools } from '../../devtools/lib/devtools.js';
|
|
4
|
-
import { withHighPerformanceMemoization, withMemoization } from '../../memoization/lib/memoization.js';
|
|
5
|
-
import { withTimeTravel } from '../../time-travel/lib/time-travel.js';
|
|
6
|
-
|
|
7
|
-
const TREE_PRESETS = {
|
|
8
|
-
basic: {
|
|
9
|
-
batchUpdates: false,
|
|
10
|
-
useMemoization: false,
|
|
11
|
-
trackPerformance: false,
|
|
12
|
-
enableTimeTravel: false,
|
|
13
|
-
enableDevTools: false,
|
|
14
|
-
debugMode: false
|
|
15
|
-
},
|
|
16
|
-
performance: {
|
|
17
|
-
batchUpdates: true,
|
|
18
|
-
useMemoization: true,
|
|
19
|
-
trackPerformance: false,
|
|
20
|
-
enableTimeTravel: false,
|
|
21
|
-
enableDevTools: false,
|
|
22
|
-
debugMode: false,
|
|
23
|
-
useShallowComparison: true,
|
|
24
|
-
maxCacheSize: 200
|
|
25
|
-
},
|
|
26
|
-
development: {
|
|
27
|
-
batchUpdates: true,
|
|
28
|
-
useMemoization: true,
|
|
29
|
-
trackPerformance: true,
|
|
30
|
-
enableTimeTravel: true,
|
|
31
|
-
enableDevTools: true,
|
|
32
|
-
debugMode: true,
|
|
33
|
-
maxCacheSize: 100
|
|
34
|
-
},
|
|
35
|
-
production: {
|
|
36
|
-
batchUpdates: true,
|
|
37
|
-
useMemoization: true,
|
|
38
|
-
trackPerformance: false,
|
|
39
|
-
enableTimeTravel: false,
|
|
40
|
-
enableDevTools: false,
|
|
41
|
-
debugMode: false,
|
|
42
|
-
useShallowComparison: true,
|
|
43
|
-
maxCacheSize: 200
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
function createPresetConfig(preset, overrides = {}) {
|
|
47
|
-
const baseConfig = TREE_PRESETS[preset];
|
|
48
|
-
return {
|
|
49
|
-
...baseConfig,
|
|
50
|
-
...overrides
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function validatePreset(preset) {
|
|
54
|
-
if (!TREE_PRESETS[preset]) {
|
|
55
|
-
throw new Error(`Invalid preset: ${preset}. Valid presets are: ${Object.keys(TREE_PRESETS).join(', ')}`);
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
function getAvailablePresets() {
|
|
60
|
-
return Object.keys(TREE_PRESETS);
|
|
61
|
-
}
|
|
62
|
-
function combinePresets(presets, overrides = {}) {
|
|
63
|
-
let combined = {};
|
|
64
|
-
for (const preset of presets) {
|
|
65
|
-
validatePreset(preset);
|
|
66
|
-
combined = {
|
|
67
|
-
...combined,
|
|
68
|
-
...TREE_PRESETS[preset]
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
...combined,
|
|
73
|
-
...overrides
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function createDevTree(overrides = {}) {
|
|
77
|
-
const config = createPresetConfig('development', overrides);
|
|
78
|
-
const composed = composeEnhancers(withBatching(), withHighPerformanceBatching(), withMemoization(), withHighPerformanceMemoization(), withTimeTravel(), withDevTools({
|
|
79
|
-
treeName: config.treeName ?? 'SignalTree Dev'
|
|
80
|
-
}));
|
|
81
|
-
return {
|
|
82
|
-
config,
|
|
83
|
-
enhancer: composed
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export { TREE_PRESETS, combinePresets, createDevTree, createPresetConfig, getAvailablePresets, validatePreset };
|