@signaltree/core 8.0.2 → 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -66
- package/dist/edit-session.js +1 -0
- package/dist/enhancers/batching/batching.js +19 -16
- package/dist/enhancers/devtools/devtools.js +14 -1
- package/dist/enhancers/serialization/serialization.js +38 -17
- package/dist/enhancers/time-travel/time-travel.js +15 -7
- package/dist/index.js +6 -13
- package/dist/lib/constants.js +2 -1
- package/dist/lib/signal-tree.js +59 -1
- package/dist/security.js +1 -0
- package/dist/storage.js +1 -0
- package/package.json +16 -1
- package/src/edit-session.d.ts +1 -0
- package/src/enhancers/types.d.ts +1 -1
- package/src/enhancers/typing/helpers-types.d.ts +1 -1
- package/src/index.d.ts +7 -13
- package/src/lib/types.d.ts +3 -26
- package/src/security.d.ts +1 -0
- package/src/storage.d.ts +2 -0
- package/dist/enhancers/effects/effects.js +0 -66
- package/dist/enhancers/entities/entities.js +0 -7
- package/dist/enhancers/memoization/memoization.js +0 -420
- package/dist/enhancers/presets/lib/presets.js +0 -27
- package/dist/lib/async-helpers.js +0 -77
- package/dist/lib/presets.js +0 -20
- package/src/enhancers/memoization/index.d.ts +0 -1
- package/src/enhancers/memoization/memoization.d.ts +0 -54
- package/src/enhancers/memoization/memoization.types.d.ts +0 -1
- 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 -8
- package/src/lib/presets.d.ts +0 -34
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ Performance and bundle size vary by app shape, build tooling, device, and runtim
|
|
|
118
118
|
|
|
119
119
|
## Best Practices (SignalTree-First)
|
|
120
120
|
|
|
121
|
-
> 📖 **
|
|
121
|
+
> 📖 **Production app structure**: For anything beyond a single-component prototype, follow the [Recommended Default Architecture](https://github.com/JBorgia/signaltree/blob/main/docs/architecture/signaltree-architecture-guide.md#recommended-default-architecture) — it wraps the patterns below in an `AppStore` facade with a `$ + ops` split, derived tiers, and an ESLint guard against direct tree mutation from components/services. The snippets in this README are deliberately minimal and use the low-level core API directly so they stay self-contained; in real apps the mutations shown here belong inside `*Ops` classes.
|
|
122
122
|
|
|
123
123
|
Follow these principles for idiomatic SignalTree code:
|
|
124
124
|
|
|
@@ -416,17 +416,18 @@ effect(() => {
|
|
|
416
416
|
// Best Practices:
|
|
417
417
|
// 1. Use computed() for derived state that depends on signals
|
|
418
418
|
// 2. Keep computations pure - no side effects
|
|
419
|
-
// 3.
|
|
419
|
+
// 3. Angular's computed() automatically caches results
|
|
420
420
|
// 4. Chain computed values for complex transformations
|
|
421
421
|
// 5. Use factory functions for parameterized computations
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
-
### Performance optimization with
|
|
424
|
+
### Performance optimization with computed()
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
Angular's built-in `computed()` provides automatic memoization — a result is cached until one of the signals it reads from changes. No additional enhancer is required:
|
|
427
427
|
|
|
428
428
|
```typescript
|
|
429
|
-
import {
|
|
429
|
+
import { computed } from '@angular/core';
|
|
430
|
+
import { signalTree } from '@signaltree/core';
|
|
430
431
|
|
|
431
432
|
const tree = signalTree({
|
|
432
433
|
items: Array.from({ length: 10000 }, (_, i) => ({
|
|
@@ -434,9 +435,9 @@ const tree = signalTree({
|
|
|
434
435
|
value: Math.random(),
|
|
435
436
|
category: `cat-${i % 10}`,
|
|
436
437
|
})),
|
|
437
|
-
})
|
|
438
|
+
});
|
|
438
439
|
|
|
439
|
-
// Expensive computation - automatically cached by
|
|
440
|
+
// Expensive computation - automatically cached by Angular's computed()
|
|
440
441
|
const expensiveComputation = computed(() => {
|
|
441
442
|
return tree.$.items()
|
|
442
443
|
.filter((item) => item.value > 0.5)
|
|
@@ -444,9 +445,11 @@ const expensiveComputation = computed(() => {
|
|
|
444
445
|
});
|
|
445
446
|
|
|
446
447
|
// The computation only runs when tree.$.items() actually changes
|
|
447
|
-
// Subsequent calls return cached result
|
|
448
|
+
// Subsequent calls return the cached result
|
|
448
449
|
```
|
|
449
450
|
|
|
451
|
+
> **9.0.1 note:** The `memoization()` enhancer was removed. Angular's `computed()` already memoizes; the enhancer added no value on top of it.
|
|
452
|
+
|
|
450
453
|
### Advanced usage (full state tree)
|
|
451
454
|
|
|
452
455
|
```typescript
|
|
@@ -644,9 +647,8 @@ All enhancers are exported directly from `@signaltree/core`:
|
|
|
644
647
|
**Performance Enhancers:**
|
|
645
648
|
|
|
646
649
|
- `batching()` - Batch updates to reduce recomputation and rendering
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
- `withHighPerformanceMemoization()` - Optimized memoization for large state trees
|
|
650
|
+
|
|
651
|
+
> **9.0.1 note:** The `memoization()` enhancer and all preset variants were removed. Use Angular's built-in `computed()` — it provides equivalent memoization with zero additional runtime cost.
|
|
650
652
|
|
|
651
653
|
**Data Management:**
|
|
652
654
|
|
|
@@ -661,11 +663,6 @@ All enhancers are exported directly from `@signaltree/core`:
|
|
|
661
663
|
- `devTools()` - Redux DevTools auto-connect, path actions, and time-travel dispatch
|
|
662
664
|
- `withTimeTravel()` - Undo/redo functionality
|
|
663
665
|
|
|
664
|
-
**Presets:**
|
|
665
|
-
|
|
666
|
-
- `createDevTree()` - Pre-configured development setup
|
|
667
|
-
- `TREE_PRESETS` - Common configuration patterns
|
|
668
|
-
|
|
669
666
|
#### Additional Packages
|
|
670
667
|
|
|
671
668
|
These are the **only** separate packages in the SignalTree ecosystem:
|
|
@@ -691,7 +688,7 @@ const tree = signalTree({ count: 0 }).with(
|
|
|
691
688
|
**Performance-Focused Stack:**
|
|
692
689
|
|
|
693
690
|
```typescript
|
|
694
|
-
import { signalTree, batching,
|
|
691
|
+
import { signalTree, batching, entities } from '@signaltree/core';
|
|
695
692
|
|
|
696
693
|
const tree = signalTree({
|
|
697
694
|
products: entityMap<Product>(),
|
|
@@ -756,27 +753,9 @@ Enhancers can declare metadata for automatic dependency resolution:
|
|
|
756
753
|
// Enhancers are automatically ordered based on requirements
|
|
757
754
|
const tree = signalTree(state).with(
|
|
758
755
|
devTools(), // Requires: core, provides: debugging
|
|
759
|
-
batching()
|
|
760
|
-
memoization() // Requires: batching, provides: caching
|
|
756
|
+
batching() // Requires: core, provides: batching
|
|
761
757
|
);
|
|
762
|
-
// Automatically ordered: batching ->
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
#### Quick Start with Presets
|
|
766
|
-
|
|
767
|
-
For common patterns, use presets that combine multiple enhancers:
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
import { createDevTree, TREE_PRESETS } from '@signaltree/core';
|
|
771
|
-
|
|
772
|
-
// Development preset includes: batching, memoization, devtools, time-travel
|
|
773
|
-
const devTree = createDevTree({
|
|
774
|
-
products: [] as Product[],
|
|
775
|
-
cart: { items: [], total: 0 },
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
// Or use preset configurations
|
|
779
|
-
const customTree = signalTree(state, TREE_PRESETS.DASHBOARD);
|
|
758
|
+
// Automatically ordered: batching -> devtools
|
|
780
759
|
```
|
|
781
760
|
|
|
782
761
|
#### Core Stubs
|
|
@@ -1548,15 +1527,14 @@ tree.effect(() => console.log('State changed'));
|
|
|
1548
1527
|
### Performance-Enhanced Composition
|
|
1549
1528
|
|
|
1550
1529
|
```typescript
|
|
1551
|
-
import { signalTree, batching
|
|
1530
|
+
import { signalTree, batching } from '@signaltree/core';
|
|
1552
1531
|
|
|
1553
1532
|
// Add performance optimizations
|
|
1554
1533
|
const tree = signalTree({
|
|
1555
1534
|
products: [] as Product[],
|
|
1556
1535
|
filters: { category: '', search: '' },
|
|
1557
1536
|
}).with(
|
|
1558
|
-
batching()
|
|
1559
|
-
memoization() // Cache expensive computations
|
|
1537
|
+
batching() // Batch updates for optimal rendering
|
|
1560
1538
|
);
|
|
1561
1539
|
|
|
1562
1540
|
// Now supports batched updates
|
|
@@ -1565,7 +1543,7 @@ tree.batchUpdate((state) => ({
|
|
|
1565
1543
|
filters: { category: 'electronics', search: '' },
|
|
1566
1544
|
}));
|
|
1567
1545
|
|
|
1568
|
-
//
|
|
1546
|
+
// Angular's computed() automatically caches derived values
|
|
1569
1547
|
const filteredProducts = computed(() => {
|
|
1570
1548
|
return tree.$.products()
|
|
1571
1549
|
.filter((p) => p.category.includes(tree.$.filters.category()))
|
|
@@ -1671,7 +1649,8 @@ tree.save(); // Persistence
|
|
|
1671
1649
|
```
|
|
1672
1650
|
|
|
1673
1651
|
### Aggregated Redux DevTools Instance
|
|
1674
|
-
|
|
1652
|
+
|
|
1653
|
+
````
|
|
1675
1654
|
When using multiple independent trees (e.g. per lazy-loaded feature module), each `devTools()` call creates a separate Redux DevTools instance by default. Use `aggregatedReduxInstance` to group multiple trees under a **single Redux DevTools instance**:
|
|
1676
1655
|
|
|
1677
1656
|
```typescript
|
|
@@ -1770,21 +1749,21 @@ const tree = signalTree(state).with(
|
|
|
1770
1749
|
### Preset-Based Composition
|
|
1771
1750
|
|
|
1772
1751
|
```typescript
|
|
1773
|
-
import {
|
|
1752
|
+
import { signalTree, batching, devTools, withTimeTravel } from '@signaltree/core';
|
|
1774
1753
|
|
|
1775
|
-
//
|
|
1776
|
-
const devTree =
|
|
1754
|
+
// Compose the enhancers you actually need
|
|
1755
|
+
const devTree = signalTree({
|
|
1777
1756
|
products: [],
|
|
1778
1757
|
cart: { items: [], total: 0 },
|
|
1779
1758
|
user: null,
|
|
1780
|
-
})
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
const customTree = signalTree(state, TREE_PRESETS.PERFORMANCE);
|
|
1785
|
-
// Includes: batching, memoization optimizations
|
|
1759
|
+
})
|
|
1760
|
+
.with(batching())
|
|
1761
|
+
.with(devTools())
|
|
1762
|
+
.with(withTimeTravel());
|
|
1786
1763
|
```
|
|
1787
1764
|
|
|
1765
|
+
> **9.0.1 note:** Preset factories (`createDevTree`, `TREE_PRESETS`, etc.) were removed. Compose enhancers directly with `.with()`.
|
|
1766
|
+
|
|
1788
1767
|
### Measuring bundle size
|
|
1789
1768
|
|
|
1790
1769
|
Bundle sizes depend on your build, tree-shaking, and which enhancers you include. Use the scripts in `scripts/` to analyze min+gz for your configuration.
|
|
@@ -1818,7 +1797,7 @@ if (isDevelopment) {
|
|
|
1818
1797
|
}
|
|
1819
1798
|
|
|
1820
1799
|
if (needsPerformance) {
|
|
1821
|
-
tree = tree.with(batching()
|
|
1800
|
+
tree = tree.with(batching());
|
|
1822
1801
|
}
|
|
1823
1802
|
|
|
1824
1803
|
if (needsTimeTravel) {
|
|
@@ -2101,10 +2080,10 @@ tree.destroy(); // Cleanup resources
|
|
|
2101
2080
|
SignalTree Core includes all enhancers built-in:
|
|
2102
2081
|
|
|
2103
2082
|
```typescript
|
|
2104
|
-
import { signalTree, batching,
|
|
2083
|
+
import { signalTree, batching, withTimeTravel } from '@signaltree/core';
|
|
2105
2084
|
|
|
2106
2085
|
// All enhancers available from @signaltree/core
|
|
2107
|
-
const tree = signalTree(initialState).with(batching(),
|
|
2086
|
+
const tree = signalTree(initialState).with(batching(), withTimeTravel());
|
|
2108
2087
|
```
|
|
2109
2088
|
|
|
2110
2089
|
### Available enhancers
|
|
@@ -2112,13 +2091,10 @@ const tree = signalTree(initialState).with(batching(), memoization(), withTimeTr
|
|
|
2112
2091
|
All enhancers are included in `@signaltree/core`:
|
|
2113
2092
|
|
|
2114
2093
|
- **batching()** - Batch multiple updates for better performance
|
|
2115
|
-
- **memoization()** - Intelligent caching & performance optimization
|
|
2116
2094
|
- **entities()** - Advanced entity management & CRUD operations
|
|
2117
2095
|
- **devTools()** - Redux DevTools integration for debugging
|
|
2118
2096
|
- **withTimeTravel()** - Undo/redo functionality & state history
|
|
2119
2097
|
- **serialization()** - State persistence & SSR support
|
|
2120
|
-
- **createDevTree()** - Pre-configured development setup
|
|
2121
|
-
- **TREE_PRESETS** - Common configuration patterns (PERFORMANCE, DASHBOARD, etc.)
|
|
2122
2098
|
|
|
2123
2099
|
## When to use core only
|
|
2124
2100
|
|
|
@@ -2132,7 +2108,7 @@ Perfect for:
|
|
|
2132
2108
|
|
|
2133
2109
|
Consider enhancers when you need:
|
|
2134
2110
|
|
|
2135
|
-
- ⚡ Performance optimization (batching
|
|
2111
|
+
- ⚡ Performance optimization (batching)
|
|
2136
2112
|
- 🐛 Advanced debugging (devTools, withTimeTravel)
|
|
2137
2113
|
- 📦 Entity management (entities)
|
|
2138
2114
|
|
|
@@ -2144,6 +2120,21 @@ Consider separate packages when you need:
|
|
|
2144
2120
|
|
|
2145
2121
|
## Migration from NgRx
|
|
2146
2122
|
|
|
2123
|
+
### Case Study
|
|
2124
|
+
|
|
2125
|
+
Measured from a production Angular mobile application migrating from NgRx Signal Store to SignalTree. Results reflect one team's experience; your mileage will vary depending on app complexity and existing architecture.
|
|
2126
|
+
|
|
2127
|
+
| Metric | NgRx | SignalTree | Change |
|
|
2128
|
+
| --- | --- | --- | --- |
|
|
2129
|
+
| **App state code** | 11,735 lines / 45 files | 2,825 lines / 23 files | **-76%** |
|
|
2130
|
+
| **npm packages** | 4 (@ngrx/\*) | 1 (@signaltree/core) | **-75%** |
|
|
2131
|
+
| **State bundle (gzip)** | ~50KB | ~27KB | **-46%** |
|
|
2132
|
+
| **Boilerplate files** | 17 custom `withX` helpers | 0 (built-in) | **Eliminated** |
|
|
2133
|
+
|
|
2134
|
+
13 separate stores → 1 unified tree. `entityMap()` replaced a 222-line `withEntityCrud` wrapper. Derived tiers replaced scattered `withComputed` blocks.
|
|
2135
|
+
|
|
2136
|
+
### Migration Steps
|
|
2137
|
+
|
|
2147
2138
|
```typescript
|
|
2148
2139
|
// Step 1: Create parallel tree
|
|
2149
2140
|
const tree = signalTree(initialState);
|
|
@@ -2265,7 +2256,6 @@ All enhancers are now consolidated in the core package. The following features a
|
|
|
2265
2256
|
### Performance & Optimization
|
|
2266
2257
|
|
|
2267
2258
|
- **batching()** (+1.27KB gzipped) - Batch multiple updates for better performance
|
|
2268
|
-
- **memoization()** (+2.33KB gzipped) - Intelligent caching & performance optimization
|
|
2269
2259
|
|
|
2270
2260
|
### Advanced Features
|
|
2271
2261
|
|
|
@@ -2279,8 +2269,6 @@ All enhancers are now consolidated in the core package. The following features a
|
|
|
2279
2269
|
### Integration & Convenience
|
|
2280
2270
|
|
|
2281
2271
|
- **serialization()** (+0.84KB gzipped) - State persistence & SSR support
|
|
2282
|
-
- **ecommercePreset()** - Pre-configured setups for e-commerce applications
|
|
2283
|
-
- **dashboardPreset()** - Pre-configured setups for dashboard applications
|
|
2284
2272
|
|
|
2285
2273
|
### Quick Start with Extensions
|
|
2286
2274
|
|
|
@@ -2294,13 +2282,10 @@ npm install @signaltree/core
|
|
|
2294
2282
|
import {
|
|
2295
2283
|
signalTree,
|
|
2296
2284
|
batching,
|
|
2297
|
-
memoization,
|
|
2298
2285
|
entities,
|
|
2299
2286
|
devTools,
|
|
2300
2287
|
withTimeTravel,
|
|
2301
|
-
serialization
|
|
2302
|
-
ecommercePreset,
|
|
2303
|
-
dashboardPreset
|
|
2288
|
+
serialization
|
|
2304
2289
|
} from '@signaltree/core';
|
|
2305
2290
|
```
|
|
2306
2291
|
|
|
@@ -2626,7 +2611,7 @@ export default {
|
|
|
2626
2611
|
|
|
2627
2612
|
**Start with just `@signaltree/core`** - it includes comprehensive enhancers for most applications:
|
|
2628
2613
|
|
|
2629
|
-
- Performance optimization (batching
|
|
2614
|
+
- Performance optimization (batching)
|
|
2630
2615
|
- Data management (entities, async operations)
|
|
2631
2616
|
- Development tools (devtools, time-travel)
|
|
2632
2617
|
- State persistence (serialization)
|
|
@@ -2671,3 +2656,4 @@ MIT License with AI Training Restriction - see the [LICENSE](../../LICENSE) file
|
|
|
2671
2656
|
---
|
|
2672
2657
|
|
|
2673
2658
|
**Ready to get started?** This core package provides everything you need for most applications. Add extensions only when you need them! 🚀
|
|
2659
|
+
````
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createEditSession } from './lib/edit-session.js';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { copyTreeProperties } from '../utils/copy-tree-properties.js';
|
|
2
|
+
import { ENHANCER_META } from '../../lib/types.js';
|
|
2
3
|
|
|
3
4
|
function batching(config = {}) {
|
|
4
5
|
const enabled = config.enabled ?? true;
|
|
5
6
|
const notificationDelayMs = config.notificationDelayMs ?? 0;
|
|
6
|
-
|
|
7
|
+
const enhancerFn = tree => {
|
|
7
8
|
if (!enabled) {
|
|
8
9
|
const passthrough = {
|
|
9
10
|
batch: fn => fn(),
|
|
@@ -200,8 +201,24 @@ function batching(config = {}) {
|
|
|
200
201
|
});
|
|
201
202
|
});
|
|
202
203
|
};
|
|
204
|
+
if (typeof tree.registerCleanup === 'function') {
|
|
205
|
+
tree.registerCleanup(() => {
|
|
206
|
+
if (notificationTimeoutId !== undefined) {
|
|
207
|
+
clearTimeout(notificationTimeoutId);
|
|
208
|
+
notificationTimeoutId = undefined;
|
|
209
|
+
}
|
|
210
|
+
coalescedUpdates.clear();
|
|
211
|
+
});
|
|
212
|
+
}
|
|
203
213
|
return enhancedTree;
|
|
204
214
|
};
|
|
215
|
+
const meta = {
|
|
216
|
+
name: 'batching',
|
|
217
|
+
provides: ['batching']
|
|
218
|
+
};
|
|
219
|
+
enhancerFn.metadata = meta;
|
|
220
|
+
enhancerFn[ENHANCER_META] = meta;
|
|
221
|
+
return enhancerFn;
|
|
205
222
|
}
|
|
206
223
|
function highPerformanceBatching() {
|
|
207
224
|
return batching({
|
|
@@ -209,22 +226,8 @@ function highPerformanceBatching() {
|
|
|
209
226
|
notificationDelayMs: 0
|
|
210
227
|
});
|
|
211
228
|
}
|
|
212
|
-
function batchingWithConfig(config = {}) {
|
|
213
|
-
return batching(config);
|
|
214
|
-
}
|
|
215
|
-
function flushBatchedUpdates() {
|
|
216
|
-
console.warn('[SignalTree] flushBatchedUpdates() is deprecated. Use tree.flushNotifications() instead.');
|
|
217
|
-
}
|
|
218
|
-
function hasPendingUpdates() {
|
|
219
|
-
console.warn('[SignalTree] hasPendingUpdates() is deprecated. Use tree.hasPendingNotifications() instead.');
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
function getBatchQueueSize() {
|
|
223
|
-
console.warn('[SignalTree] getBatchQueueSize() is deprecated. Signal writes are now synchronous.');
|
|
224
|
-
return 0;
|
|
225
|
-
}
|
|
226
229
|
Object.assign((config = {}) => batching(config), {
|
|
227
230
|
highPerformance: highPerformanceBatching
|
|
228
231
|
});
|
|
229
232
|
|
|
230
|
-
export { batching,
|
|
233
|
+
export { batching, highPerformanceBatching };
|
|
@@ -2,6 +2,7 @@ import { signal } from '@angular/core';
|
|
|
2
2
|
import { copyTreeProperties } from '../utils/copy-tree-properties.js';
|
|
3
3
|
import { applyState, snapshotState } from '../../lib/utils.js';
|
|
4
4
|
import { getPathNotifier } from '../../lib/path-notifier.js';
|
|
5
|
+
import { ENHANCER_META } from '../../lib/types.js';
|
|
5
6
|
|
|
6
7
|
function createActivityTracker(options) {
|
|
7
8
|
const modules = new Map();
|
|
@@ -698,7 +699,7 @@ function devTools(config = {}) {
|
|
|
698
699
|
const pathExclude = toArray(excludePaths);
|
|
699
700
|
const sendRateLimitMs = maxSendsPerSecond && maxSendsPerSecond > 0 ? Math.ceil(1000 / maxSendsPerSecond) : rateLimitMs ?? 0;
|
|
700
701
|
const formatPathFn = formatPath ?? defaultFormatPath;
|
|
701
|
-
|
|
702
|
+
const enhancerFn = tree => {
|
|
702
703
|
if (!enabled) {
|
|
703
704
|
const noopMethods = {
|
|
704
705
|
connectDevTools() {},
|
|
@@ -1306,8 +1307,20 @@ function devTools(config = {}) {
|
|
|
1306
1307
|
if (enableBrowserDevTools) {
|
|
1307
1308
|
initBrowserDevTools();
|
|
1308
1309
|
}
|
|
1310
|
+
if (typeof tree.registerCleanup === 'function') {
|
|
1311
|
+
tree.registerCleanup(() => {
|
|
1312
|
+
result.disconnectDevTools();
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1309
1315
|
return result;
|
|
1310
1316
|
};
|
|
1317
|
+
const meta = {
|
|
1318
|
+
name: 'devTools',
|
|
1319
|
+
provides: ['devTools']
|
|
1320
|
+
};
|
|
1321
|
+
enhancerFn.metadata = meta;
|
|
1322
|
+
enhancerFn[ENHANCER_META] = meta;
|
|
1323
|
+
return enhancerFn;
|
|
1311
1324
|
}
|
|
1312
1325
|
function enableDevTools(treeName = 'SignalTree') {
|
|
1313
1326
|
return devTools({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isSignal } from '@angular/core';
|
|
2
|
+
import { ENHANCER_META } from '../../lib/types.js';
|
|
2
3
|
import { TYPE_MARKERS } from './constants.js';
|
|
3
4
|
|
|
4
5
|
const DEFAULT_CONFIG = {
|
|
@@ -181,7 +182,7 @@ function resolveCircularReferences(obj, circularPaths) {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
function serialization(defaultConfig = {}) {
|
|
184
|
-
|
|
185
|
+
const enhancerFn = tree => {
|
|
185
186
|
const enhanced = tree;
|
|
186
187
|
enhanced.toJSON = () => {
|
|
187
188
|
return tree();
|
|
@@ -455,15 +456,15 @@ function serialization(defaultConfig = {}) {
|
|
|
455
456
|
};
|
|
456
457
|
return enhanced;
|
|
457
458
|
};
|
|
459
|
+
const meta = {
|
|
460
|
+
name: 'serialization',
|
|
461
|
+
provides: ['serialization']
|
|
462
|
+
};
|
|
463
|
+
enhancerFn.metadata = meta;
|
|
464
|
+
enhancerFn[ENHANCER_META] = meta;
|
|
465
|
+
return enhancerFn;
|
|
458
466
|
}
|
|
459
467
|
Object.assign((defaultConfig = {}) => serialization(defaultConfig), {});
|
|
460
|
-
function enableSerialization() {
|
|
461
|
-
return serialization({
|
|
462
|
-
includeMetadata: true,
|
|
463
|
-
preserveTypes: true,
|
|
464
|
-
handleCircular: true
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
468
|
function persistence(config) {
|
|
468
469
|
const {
|
|
469
470
|
key,
|
|
@@ -477,7 +478,7 @@ function persistence(config) {
|
|
|
477
478
|
throw new Error('No storage adapter available. Provide a storage adapter in the config.');
|
|
478
479
|
}
|
|
479
480
|
const storageAdapter = storage;
|
|
480
|
-
|
|
481
|
+
const persistenceFn = tree => {
|
|
481
482
|
const serializable = serialization(serializationConfig)(tree);
|
|
482
483
|
const enhanced = serializable;
|
|
483
484
|
let lastCacheKey = null;
|
|
@@ -553,8 +554,9 @@ function persistence(config) {
|
|
|
553
554
|
});
|
|
554
555
|
}, debounceMs);
|
|
555
556
|
};
|
|
557
|
+
let unsubscribeAutoSave = null;
|
|
556
558
|
try {
|
|
557
|
-
tree.subscribe(() => {
|
|
559
|
+
unsubscribeAutoSave = tree.subscribe(() => {
|
|
558
560
|
const currentState = JSON.stringify(tree());
|
|
559
561
|
if (currentState !== previousState) {
|
|
560
562
|
previousState = currentState;
|
|
@@ -575,6 +577,10 @@ function persistence(config) {
|
|
|
575
577
|
}
|
|
576
578
|
enhanced.__flushAutoSave = () => {
|
|
577
579
|
pollingActive = false;
|
|
580
|
+
if (unsubscribeAutoSave) {
|
|
581
|
+
unsubscribeAutoSave();
|
|
582
|
+
unsubscribeAutoSave = null;
|
|
583
|
+
}
|
|
578
584
|
if (saveTimeout) {
|
|
579
585
|
clearTimeout(saveTimeout);
|
|
580
586
|
saveTimeout = undefined;
|
|
@@ -582,9 +588,30 @@ function persistence(config) {
|
|
|
582
588
|
}
|
|
583
589
|
return Promise.resolve();
|
|
584
590
|
};
|
|
591
|
+
if (typeof tree.registerCleanup === 'function') {
|
|
592
|
+
tree.registerCleanup(() => {
|
|
593
|
+
pollingActive = false;
|
|
594
|
+
if (unsubscribeAutoSave) {
|
|
595
|
+
unsubscribeAutoSave();
|
|
596
|
+
unsubscribeAutoSave = null;
|
|
597
|
+
}
|
|
598
|
+
if (saveTimeout) {
|
|
599
|
+
clearTimeout(saveTimeout);
|
|
600
|
+
saveTimeout = undefined;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
585
604
|
}
|
|
586
605
|
return enhanced;
|
|
587
606
|
};
|
|
607
|
+
const meta = {
|
|
608
|
+
name: 'persistence',
|
|
609
|
+
provides: ['persistence', 'serialization'],
|
|
610
|
+
requires: []
|
|
611
|
+
};
|
|
612
|
+
persistenceFn.metadata = meta;
|
|
613
|
+
persistenceFn[ENHANCER_META] = meta;
|
|
614
|
+
return persistenceFn;
|
|
588
615
|
}
|
|
589
616
|
Object.assign(cfg => persistence(cfg), {});
|
|
590
617
|
function createStorageAdapter(getItem, setItem, removeItem) {
|
|
@@ -646,11 +673,5 @@ function createIndexedDBAdapter(dbName = 'SignalTreeDB', storeName = 'states') {
|
|
|
646
673
|
}
|
|
647
674
|
};
|
|
648
675
|
}
|
|
649
|
-
function applySerialization(tree) {
|
|
650
|
-
return serialization()(tree);
|
|
651
|
-
}
|
|
652
|
-
function applyPersistence(tree, cfg) {
|
|
653
|
-
return persistence(cfg)(tree);
|
|
654
|
-
}
|
|
655
676
|
|
|
656
|
-
export {
|
|
677
|
+
export { createIndexedDBAdapter, createStorageAdapter, persistence, serialization };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { snapshotState } from '../../lib/utils.js';
|
|
2
2
|
import { deepEqual, deepClone } from './utils.js';
|
|
3
|
+
import { ENHANCER_META } from '../../lib/types.js';
|
|
3
4
|
|
|
4
5
|
class TimeTravelManager {
|
|
5
6
|
tree;
|
|
@@ -130,7 +131,7 @@ function timeTravel(config = {}) {
|
|
|
130
131
|
const {
|
|
131
132
|
enabled = true
|
|
132
133
|
} = config;
|
|
133
|
-
|
|
134
|
+
const enhancerFn = tree => {
|
|
134
135
|
if (!enabled) {
|
|
135
136
|
const noopMethods = {
|
|
136
137
|
undo() {},
|
|
@@ -255,13 +256,20 @@ function timeTravel(config = {}) {
|
|
|
255
256
|
enhancedTree['canRedo'] = () => timeTravelManager.canRedo();
|
|
256
257
|
enhancedTree['getCurrentIndex'] = () => timeTravelManager.getCurrentIndex();
|
|
257
258
|
enhancedTree['__timeTravel'] = timeTravelManager;
|
|
259
|
+
if (typeof tree.registerCleanup === 'function') {
|
|
260
|
+
tree.registerCleanup(() => {
|
|
261
|
+
timeTravelManager.resetHistory();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
258
264
|
return enhancedTree;
|
|
259
265
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
266
|
+
const meta = {
|
|
267
|
+
name: 'timeTravel',
|
|
268
|
+
provides: ['timeTravel']
|
|
269
|
+
};
|
|
270
|
+
enhancerFn.metadata = meta;
|
|
271
|
+
enhancerFn[ENHANCER_META] = meta;
|
|
272
|
+
return enhancerFn;
|
|
265
273
|
}
|
|
266
274
|
function timeTravelHistory(maxHistorySize) {
|
|
267
275
|
return timeTravel({
|
|
@@ -280,4 +288,4 @@ Object.assign((config = {}) => timeTravel(config), {
|
|
|
280
288
|
history: timeTravelHistory
|
|
281
289
|
});
|
|
282
290
|
|
|
283
|
-
export {
|
|
291
|
+
export { timeTravel, timeTravelHistory };
|
package/dist/index.js
CHANGED
|
@@ -6,22 +6,15 @@ export { LoadingState, isStatusMarker, status } from './lib/markers/status.js';
|
|
|
6
6
|
export { clearStoragePrefix, createStorageKeys, isStoredMarker, stored } from './lib/markers/stored.js';
|
|
7
7
|
export { FORM_MARKER, createFormSignal, form, isFormMarker, validators } from './lib/markers/form.js';
|
|
8
8
|
export { registerMarkerProcessor } from './lib/internals/materialize-markers.js';
|
|
9
|
-
export { composeEnhancers,
|
|
10
|
-
export { createEditSession } from './lib/edit-session.js';
|
|
9
|
+
export { composeEnhancers, isAnySignal, isNodeAccessor, toWritableSignal } from './lib/utils.js';
|
|
11
10
|
export { getPathNotifier } from './lib/path-notifier.js';
|
|
12
|
-
export { SecurityPresets, SecurityValidator } from './lib/security/security-validator.js';
|
|
13
11
|
export { createEnhancer, resolveEnhancerOrder } from './enhancers/index.js';
|
|
14
|
-
export { batching
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
19
|
-
export { devTools, enableDevTools, fullDevTools, productionDevTools } from './enhancers/devtools/devtools.js';
|
|
20
|
-
export { createAsyncOperation, trackAsync } from './lib/async-helpers.js';
|
|
21
|
-
export { TREE_PRESETS, combinePresets, createPresetConfig, getAvailablePresets, validatePreset } from './enhancers/presets/lib/presets.js';
|
|
22
|
-
export { SIGNAL_TREE_CONSTANTS, SIGNAL_TREE_MESSAGES } from './lib/constants.js';
|
|
12
|
+
export { batching } from './enhancers/batching/batching.js';
|
|
13
|
+
export { timeTravel } from './enhancers/time-travel/time-travel.js';
|
|
14
|
+
export { persistence, serialization } from './enhancers/serialization/serialization.js';
|
|
15
|
+
export { devTools } from './enhancers/devtools/devtools.js';
|
|
16
|
+
export { SIGNAL_TREE_CONSTANTS, SIGNAL_TREE_MESSAGES, isDev } from './lib/constants.js';
|
|
23
17
|
export { entityMap } from './lib/markers/entity-map.js';
|
|
24
18
|
export { deepEqual, deepEqual as equal } from './shared/lib/deep-equal.js';
|
|
25
19
|
export { parsePath } from './shared/lib/parse-path.js';
|
|
26
20
|
export { isBuiltInObject } from './shared/lib/is-built-in-object.js';
|
|
27
|
-
export { createDevTree } from './lib/presets.js';
|
package/dist/lib/constants.js
CHANGED
|
@@ -51,6 +51,7 @@ const PROD_MESSAGES = (() => {
|
|
|
51
51
|
})();
|
|
52
52
|
const _isProdByEnv = Boolean(typeof globalThis === 'object' && globalThis !== null && 'process' in globalThis && typeof globalThis.process === 'object' && 'env' in globalThis.process && globalThis.process.env.NODE_ENV === 'production');
|
|
53
53
|
const _isDev = typeof ngDevMode !== 'undefined' ? Boolean(ngDevMode) : !_isProdByEnv;
|
|
54
|
+
const isDev = _isDev;
|
|
54
55
|
const SIGNAL_TREE_MESSAGES = Object.freeze(_isDev ? DEV_MESSAGES : PROD_MESSAGES);
|
|
55
56
|
|
|
56
|
-
export { SIGNAL_TREE_CONSTANTS, SIGNAL_TREE_MESSAGES };
|
|
57
|
+
export { SIGNAL_TREE_CONSTANTS, SIGNAL_TREE_MESSAGES, isDev };
|