@signaltree/core 4.0.16 → 4.1.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/LICENSE +54 -0
- package/README.md +27 -0
- package/dist/constants.js +6 -0
- package/dist/deep-clone.js +80 -0
- package/dist/deep-equal.js +41 -0
- package/dist/enhancers/batching/lib/batching.js +161 -0
- package/dist/enhancers/computed/lib/computed.js +21 -0
- package/dist/enhancers/devtools/lib/devtools.js +321 -0
- package/dist/enhancers/entities/lib/entities.js +115 -0
- package/dist/enhancers/index.js +72 -0
- package/dist/enhancers/memoization/lib/memoization.js +379 -0
- package/dist/enhancers/middleware/lib/async-helpers.js +77 -0
- package/dist/enhancers/middleware/lib/middleware.js +136 -0
- package/dist/enhancers/presets/lib/presets.js +88 -0
- package/dist/enhancers/serialization/constants.js +15 -0
- package/dist/enhancers/serialization/lib/serialization.js +660 -0
- package/dist/enhancers/time-travel/lib/time-travel.js +193 -0
- package/dist/index.js +19 -0
- package/dist/is-built-in-object.js +23 -0
- package/dist/lib/constants.js +57 -0
- package/dist/lib/memory/memory-manager.js +164 -0
- package/dist/lib/performance/diff-engine.js +156 -0
- package/dist/lib/performance/path-index.js +156 -0
- package/dist/lib/performance/update-engine.js +188 -0
- package/dist/lib/security/security-validator.js +121 -0
- package/dist/lib/signal-tree.js +631 -0
- package/dist/lib/types.js +3 -0
- package/dist/lib/utils.js +254 -0
- package/dist/lru-cache.js +64 -0
- package/dist/parse-path.js +13 -0
- package/package.json +31 -18
- package/src/enhancers/batching/index.d.ts +1 -0
- package/src/enhancers/batching/jest.config.d.ts +15 -0
- package/src/enhancers/batching/lib/batching.d.ts +16 -0
- package/src/enhancers/batching/test-setup.d.ts +1 -0
- package/src/enhancers/computed/index.d.ts +1 -0
- package/src/enhancers/computed/jest.config.d.ts +15 -0
- package/src/enhancers/computed/lib/computed.d.ts +12 -0
- package/src/enhancers/devtools/index.d.ts +1 -0
- package/src/enhancers/devtools/jest.config.d.ts +15 -0
- package/src/enhancers/devtools/lib/devtools.d.ts +77 -0
- package/src/enhancers/devtools/test-setup.d.ts +1 -0
- package/src/enhancers/entities/index.d.ts +1 -0
- package/src/enhancers/entities/jest.config.d.ts +15 -0
- package/src/enhancers/entities/lib/entities.d.ts +22 -0
- package/src/enhancers/entities/test-setup.d.ts +1 -0
- package/src/enhancers/index.d.ts +3 -0
- package/src/enhancers/memoization/index.d.ts +1 -0
- package/src/enhancers/memoization/jest.config.d.ts +15 -0
- package/src/enhancers/memoization/lib/memoization.d.ts +65 -0
- package/src/enhancers/memoization/test-setup.d.ts +1 -0
- package/src/enhancers/middleware/index.d.ts +2 -0
- package/src/enhancers/middleware/jest.config.d.ts +15 -0
- package/src/enhancers/middleware/lib/async-helpers.d.ts +8 -0
- package/src/enhancers/middleware/lib/middleware.d.ts +11 -0
- package/src/enhancers/middleware/test-setup.d.ts +1 -0
- package/src/enhancers/presets/index.d.ts +1 -0
- package/src/enhancers/presets/jest.config.d.ts +15 -0
- package/src/enhancers/presets/lib/presets.d.ts +11 -0
- package/src/enhancers/presets/test-setup.d.ts +1 -0
- package/src/enhancers/serialization/constants.d.ts +14 -0
- package/src/enhancers/serialization/index.d.ts +2 -0
- package/src/enhancers/serialization/jest.config.d.ts +15 -0
- package/src/enhancers/serialization/lib/serialization.d.ts +59 -0
- package/src/enhancers/serialization/test-setup.d.ts +1 -0
- package/src/enhancers/time-travel/index.d.ts +1 -0
- package/src/enhancers/time-travel/jest.config.d.ts +15 -0
- package/src/enhancers/time-travel/lib/time-travel.d.ts +36 -0
- package/src/enhancers/time-travel/lib/utils.d.ts +1 -0
- package/src/enhancers/time-travel/test-setup.d.ts +1 -0
- package/src/enhancers/types.d.ts +105 -0
- package/src/index.d.ts +17 -0
- package/src/lib/constants.d.ts +42 -0
- package/src/lib/memory/memory-manager.d.ts +30 -0
- package/src/lib/performance/diff-engine.d.ts +33 -0
- package/src/lib/performance/path-index.d.ts +25 -0
- package/src/lib/performance/update-engine.d.ts +32 -0
- package/src/lib/security/security-validator.d.ts +33 -0
- package/src/lib/signal-tree.d.ts +8 -0
- package/src/lib/types.d.ts +164 -0
- package/src/lib/utils.d.ts +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
BUSINESS SOURCE LICENSE 1.1
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jonathan D Borgia
|
|
4
|
+
|
|
5
|
+
This Business Source License 1.1 ("License") governs the use of the software and associated documentation files (the "Software"). You are granted a limited license to use the Software under the terms of this License.
|
|
6
|
+
|
|
7
|
+
1. Definitions
|
|
8
|
+
|
|
9
|
+
"Change Date" means the date on which the Change License set out in section 6 will apply to the Software. The Change Date for this release is 2028-09-05.
|
|
10
|
+
|
|
11
|
+
"Change License" means the open source license that will apply to the Software on and after the Change Date. The Change License for this release is the MIT License.
|
|
12
|
+
|
|
13
|
+
"Licensor" means the copyright owner granting rights under this License (Jonathan D Borgia).
|
|
14
|
+
|
|
15
|
+
"You" ("Licensee") means an individual or legal entity exercising rights under this License who has not violated the terms of this License or had their rights terminated.
|
|
16
|
+
|
|
17
|
+
2. License Grant
|
|
18
|
+
|
|
19
|
+
Subject to the terms and conditions of this License, Licensor hereby grants You a non-exclusive, non-transferable, worldwide license to use, reproduce, display, perform, and distribute the Software, and to make modifications and derivative works for internal use, until the Change Date.
|
|
20
|
+
|
|
21
|
+
3. Commercial Use
|
|
22
|
+
|
|
23
|
+
You may use the Software in commercial applications, including for providing services, selling products that include the Software, or otherwise exploiting the Software commercially, subject to the other terms of this License.
|
|
24
|
+
|
|
25
|
+
4. Limitations and Conditions
|
|
26
|
+
|
|
27
|
+
a. You may not remove or alter this License, the copyright notice, or notices of the Change Date.
|
|
28
|
+
|
|
29
|
+
b. You may not publicly offer a modified version of the Software that would directly compete with Licensor's public offering of the Software if doing so would circumvent the intent of this License.
|
|
30
|
+
|
|
31
|
+
c. Except as expressly provided in this License, no rights are granted to You under any patent or trademark of Licensor.
|
|
32
|
+
|
|
33
|
+
5. Disclaimer and Limitation of Liability
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. TO THE FULLEST EXTENT PERMITTED BY LAW, LICENSOR WILL NOT BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM OR RELATING TO THE SOFTWARE.
|
|
36
|
+
|
|
37
|
+
6. Change License
|
|
38
|
+
|
|
39
|
+
On and after the Change Date specified above, the Software will be licensed under the Change License (MIT License) on the same terms and conditions as set forth by that Change License.
|
|
40
|
+
|
|
41
|
+
7. Governing Law
|
|
42
|
+
|
|
43
|
+
This License will be governed by and construed in accordance with the laws of the State of New York, USA, without regard to conflict of law principles.
|
|
44
|
+
|
|
45
|
+
8. Accepting this License
|
|
46
|
+
|
|
47
|
+
You accept this License by copying, modifying, or distributing the Software or any portion thereof.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
LICENSE NOTE
|
|
52
|
+
|
|
53
|
+
- Original license file replaced on 2025-09-05 to Business Source License 1.1. Change Date: 2028-09-05. Change License: MIT.
|
|
54
|
+
or standard modifications for your own applications.
|
package/README.md
CHANGED
|
@@ -17,6 +17,33 @@ SignalTree Core is a lightweight package that provides:
|
|
|
17
17
|
- Small API surface with zero-cost abstractions
|
|
18
18
|
- Compact bundle size suited for production
|
|
19
19
|
|
|
20
|
+
## Import guidance (tree-shaking)
|
|
21
|
+
|
|
22
|
+
Modern bundlers (webpack 5+, esbuild, Rollup, Vite) **automatically tree-shake barrel imports** from `@signaltree/core`. Both import styles produce identical bundle sizes:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// ✅ Recommended: Simple and clean
|
|
26
|
+
import { signalTree, withBatching } from '@signaltree/core';
|
|
27
|
+
|
|
28
|
+
// ✅ Also fine: Explicit subpath (same bundle size)
|
|
29
|
+
import { signalTree } from '@signaltree/core';
|
|
30
|
+
import { withBatching } from '@signaltree/core/enhancers/batching';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Measured impact** (with modern bundlers):
|
|
34
|
+
|
|
35
|
+
- Core only: ~8.5 KB gzipped
|
|
36
|
+
- Core + batching: ~9.3 KB gzipped (barrel vs subpath: identical)
|
|
37
|
+
- Unused enhancers: **automatically excluded** by tree-shaking
|
|
38
|
+
|
|
39
|
+
**When to use subpath imports:**
|
|
40
|
+
|
|
41
|
+
- Older bundlers (webpack <5) with poor tree-shaking
|
|
42
|
+
- Explicit control over what gets included
|
|
43
|
+
- Personal/team preference for clarity
|
|
44
|
+
|
|
45
|
+
This repo's ESLint rule is **disabled by default** since testing confirms effective tree-shaking with barrel imports.
|
|
46
|
+
|
|
20
47
|
### Callable leaf signals (DX sugar only)
|
|
21
48
|
|
|
22
49
|
SignalTree provides TypeScript support for callable syntax on leaf signals as developer experience sugar:
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const globalStructuredClone = typeof globalThis === 'object' && globalThis !== null ? globalThis.structuredClone : undefined;
|
|
2
|
+
function deepClone(value) {
|
|
3
|
+
if (globalStructuredClone) {
|
|
4
|
+
try {
|
|
5
|
+
return globalStructuredClone(value);
|
|
6
|
+
} catch (_a) {}
|
|
7
|
+
}
|
|
8
|
+
return cloneValue(value, new WeakMap());
|
|
9
|
+
}
|
|
10
|
+
function cloneValue(value, seen) {
|
|
11
|
+
if (value === null || typeof value !== 'object') {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === 'function') {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
const existing = seen.get(value);
|
|
18
|
+
if (existing) {
|
|
19
|
+
return existing;
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Date) {
|
|
22
|
+
return new Date(value.getTime());
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof RegExp) {
|
|
25
|
+
return new RegExp(value.source, value.flags);
|
|
26
|
+
}
|
|
27
|
+
if (value instanceof Map) {
|
|
28
|
+
const result = new Map();
|
|
29
|
+
seen.set(value, result);
|
|
30
|
+
for (const [key, entryValue] of value) {
|
|
31
|
+
result.set(cloneValue(key, seen), cloneValue(entryValue, seen));
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
if (value instanceof Set) {
|
|
36
|
+
const result = new Set();
|
|
37
|
+
seen.set(value, result);
|
|
38
|
+
for (const entry of value) {
|
|
39
|
+
result.add(cloneValue(entry, seen));
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
const result = new Array(value.length);
|
|
45
|
+
seen.set(value, result);
|
|
46
|
+
for (let i = 0; i < value.length; i++) {
|
|
47
|
+
result[i] = cloneValue(value[i], seen);
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
if (ArrayBuffer.isView(value)) {
|
|
52
|
+
if (value instanceof DataView) {
|
|
53
|
+
const bufferClone = cloneValue(value.buffer, seen);
|
|
54
|
+
return new DataView(bufferClone, value.byteOffset, value.byteLength);
|
|
55
|
+
}
|
|
56
|
+
const viewWithSlice = value;
|
|
57
|
+
if (typeof viewWithSlice.slice === 'function') {
|
|
58
|
+
return viewWithSlice.slice();
|
|
59
|
+
}
|
|
60
|
+
const bufferClone = cloneValue(value.buffer, seen);
|
|
61
|
+
return new value.constructor(bufferClone, value.byteOffset, value.length);
|
|
62
|
+
}
|
|
63
|
+
if (value instanceof ArrayBuffer) {
|
|
64
|
+
return value.slice(0);
|
|
65
|
+
}
|
|
66
|
+
const proto = Object.getPrototypeOf(value);
|
|
67
|
+
const result = proto ? Object.create(proto) : {};
|
|
68
|
+
seen.set(value, result);
|
|
69
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
70
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
71
|
+
if (!descriptor) continue;
|
|
72
|
+
if ('value' in descriptor) {
|
|
73
|
+
descriptor.value = cloneValue(descriptor.value, seen);
|
|
74
|
+
}
|
|
75
|
+
Object.defineProperty(result, key, descriptor);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { deepClone };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
function deepEqual(a, b) {
|
|
2
|
+
if (a === b) return true;
|
|
3
|
+
if (a == null || b == null) return a === b;
|
|
4
|
+
const typeA = typeof a;
|
|
5
|
+
const typeB = typeof b;
|
|
6
|
+
if (typeA !== typeB) return false;
|
|
7
|
+
if (typeA !== 'object') return false;
|
|
8
|
+
if (a instanceof Date && b instanceof Date) {
|
|
9
|
+
return a.getTime() === b.getTime();
|
|
10
|
+
}
|
|
11
|
+
if (a instanceof RegExp && b instanceof RegExp) {
|
|
12
|
+
return a.source === b.source && a.flags === b.flags;
|
|
13
|
+
}
|
|
14
|
+
if (a instanceof Map && b instanceof Map) {
|
|
15
|
+
if (a.size !== b.size) return false;
|
|
16
|
+
for (const [key, value] of a) {
|
|
17
|
+
if (!b.has(key) || !deepEqual(value, b.get(key))) return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (a instanceof Set && b instanceof Set) {
|
|
22
|
+
if (a.size !== b.size) return false;
|
|
23
|
+
for (const value of a) {
|
|
24
|
+
if (!b.has(value)) return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(a)) {
|
|
29
|
+
if (!Array.isArray(b) || a.length !== b.length) return false;
|
|
30
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(b)) return false;
|
|
33
|
+
const objA = a;
|
|
34
|
+
const objB = b;
|
|
35
|
+
const keysA = Object.keys(objA);
|
|
36
|
+
const keysB = Object.keys(objB);
|
|
37
|
+
if (keysA.length !== keysB.length) return false;
|
|
38
|
+
return keysA.every(key => key in objB && deepEqual(objA[key], objB[key]));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { deepEqual };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { isNodeAccessor } from '../../../lib/utils.js';
|
|
2
|
+
|
|
3
|
+
let updateQueue = [];
|
|
4
|
+
let isUpdating = false;
|
|
5
|
+
let flushTimeoutId;
|
|
6
|
+
let currentBatchingConfig = {};
|
|
7
|
+
function addToQueue(update, config = currentBatchingConfig) {
|
|
8
|
+
const maxSize = config.maxBatchSize ?? 100;
|
|
9
|
+
if (update.path) {
|
|
10
|
+
updateQueue = updateQueue.filter(existing => existing.path !== update.path);
|
|
11
|
+
}
|
|
12
|
+
updateQueue.push(update);
|
|
13
|
+
if (updateQueue.length > maxSize) {
|
|
14
|
+
flushUpdates();
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
scheduleFlush(config);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
function scheduleFlush(config) {
|
|
21
|
+
if (flushTimeoutId !== undefined) {
|
|
22
|
+
clearTimeout(flushTimeoutId);
|
|
23
|
+
}
|
|
24
|
+
const delay = config.autoFlushDelay ?? 16;
|
|
25
|
+
flushTimeoutId = setTimeout(() => {
|
|
26
|
+
flushUpdates();
|
|
27
|
+
}, delay);
|
|
28
|
+
}
|
|
29
|
+
function flushUpdates() {
|
|
30
|
+
if (isUpdating) return;
|
|
31
|
+
let queue;
|
|
32
|
+
do {
|
|
33
|
+
if (updateQueue.length === 0) return;
|
|
34
|
+
isUpdating = true;
|
|
35
|
+
queue = updateQueue;
|
|
36
|
+
updateQueue = [];
|
|
37
|
+
if (flushTimeoutId !== undefined) {
|
|
38
|
+
clearTimeout(flushTimeoutId);
|
|
39
|
+
flushTimeoutId = undefined;
|
|
40
|
+
}
|
|
41
|
+
queue.sort((a, b) => (b.depth ?? 0) - (a.depth ?? 0));
|
|
42
|
+
try {
|
|
43
|
+
queue.forEach(({
|
|
44
|
+
fn
|
|
45
|
+
}) => fn());
|
|
46
|
+
} finally {
|
|
47
|
+
isUpdating = false;
|
|
48
|
+
}
|
|
49
|
+
} while (updateQueue.length > 0);
|
|
50
|
+
}
|
|
51
|
+
function batchUpdates(fn, path) {
|
|
52
|
+
const startTime = performance.now();
|
|
53
|
+
const depth = 0;
|
|
54
|
+
const update = {
|
|
55
|
+
fn,
|
|
56
|
+
startTime,
|
|
57
|
+
depth,
|
|
58
|
+
path
|
|
59
|
+
};
|
|
60
|
+
const wasFlushed = addToQueue(update, currentBatchingConfig);
|
|
61
|
+
if (!wasFlushed) {
|
|
62
|
+
const isTimedOut = currentBatchingConfig.batchTimeoutMs && updateQueue.length > 0 && startTime - updateQueue[0].startTime >= currentBatchingConfig.batchTimeoutMs;
|
|
63
|
+
if (isTimedOut) {
|
|
64
|
+
flushUpdates();
|
|
65
|
+
} else if (!isUpdating && updateQueue.length > 0) {
|
|
66
|
+
queueMicrotask(() => {
|
|
67
|
+
flushUpdates();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function withBatching(config = {}) {
|
|
73
|
+
const {
|
|
74
|
+
enabled = true
|
|
75
|
+
} = config;
|
|
76
|
+
currentBatchingConfig = {
|
|
77
|
+
...currentBatchingConfig,
|
|
78
|
+
...config
|
|
79
|
+
};
|
|
80
|
+
return tree => {
|
|
81
|
+
if (!enabled) {
|
|
82
|
+
return tree;
|
|
83
|
+
}
|
|
84
|
+
const originalTreeCall = tree.bind(tree);
|
|
85
|
+
const enhancedTree = function (...args) {
|
|
86
|
+
if (args.length === 0) {
|
|
87
|
+
return originalTreeCall();
|
|
88
|
+
} else {
|
|
89
|
+
batchUpdates(() => {
|
|
90
|
+
if (args.length === 1) {
|
|
91
|
+
const arg = args[0];
|
|
92
|
+
if (typeof arg === 'function') {
|
|
93
|
+
originalTreeCall(arg);
|
|
94
|
+
} else {
|
|
95
|
+
originalTreeCall(arg);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
Object.setPrototypeOf(enhancedTree, Object.getPrototypeOf(tree));
|
|
102
|
+
Object.assign(enhancedTree, tree);
|
|
103
|
+
if ('state' in tree) {
|
|
104
|
+
Object.defineProperty(enhancedTree, 'state', {
|
|
105
|
+
value: tree.state,
|
|
106
|
+
enumerable: false,
|
|
107
|
+
configurable: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if ('$' in tree) {
|
|
111
|
+
Object.defineProperty(enhancedTree, '$', {
|
|
112
|
+
value: tree['$'],
|
|
113
|
+
enumerable: false,
|
|
114
|
+
configurable: true
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
enhancedTree.batchUpdate = updater => {
|
|
118
|
+
batchUpdates(() => {
|
|
119
|
+
const current = originalTreeCall();
|
|
120
|
+
const updates = updater(current);
|
|
121
|
+
Object.entries(updates).forEach(([key, value]) => {
|
|
122
|
+
const property = enhancedTree.state[key];
|
|
123
|
+
if (property && 'set' in property) {
|
|
124
|
+
property.set(value);
|
|
125
|
+
} else if (isNodeAccessor(property)) {
|
|
126
|
+
property(value);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
return enhancedTree;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function withHighPerformanceBatching() {
|
|
135
|
+
return withBatching({
|
|
136
|
+
enabled: true,
|
|
137
|
+
maxBatchSize: 200,
|
|
138
|
+
batchTimeoutMs: 0
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function flushBatchedUpdates() {
|
|
142
|
+
if (updateQueue.length > 0) {
|
|
143
|
+
const queue = updateQueue.slice();
|
|
144
|
+
updateQueue = [];
|
|
145
|
+
isUpdating = false;
|
|
146
|
+
queue.sort((a, b) => (b.depth ?? 0) - (a.depth ?? 0));
|
|
147
|
+
queue.forEach(({
|
|
148
|
+
fn
|
|
149
|
+
}) => {
|
|
150
|
+
fn();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function hasPendingUpdates() {
|
|
155
|
+
return updateQueue.length > 0;
|
|
156
|
+
}
|
|
157
|
+
function getBatchQueueSize() {
|
|
158
|
+
return updateQueue.length;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { flushBatchedUpdates, getBatchQueueSize, hasPendingUpdates, withBatching, withHighPerformanceBatching };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { computed } from '@angular/core';
|
|
2
|
+
import { createEnhancer } from '../../index.js';
|
|
3
|
+
|
|
4
|
+
function computedEnhancer(_config = {}) {
|
|
5
|
+
return createEnhancer({
|
|
6
|
+
name: 'computed',
|
|
7
|
+
provides: ['computed'],
|
|
8
|
+
requires: []
|
|
9
|
+
}, tree => {
|
|
10
|
+
const computedTree = tree;
|
|
11
|
+
computedTree.computed = function (computeFn) {
|
|
12
|
+
return computed(() => computeFn(tree.state));
|
|
13
|
+
};
|
|
14
|
+
return computedTree;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function createComputed(dependencies, computeFn) {
|
|
18
|
+
return computed(computeFn);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { computedEnhancer, createComputed };
|