@signaltree/core 4.0.12 → 4.0.15

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.
Files changed (102) hide show
  1. package/README.md +312 -0
  2. package/dist/enhancers/batching/index.d.ts +1 -0
  3. package/dist/enhancers/batching/index.js +1 -0
  4. package/dist/enhancers/batching/jest.config.d.ts +15 -0
  5. package/dist/enhancers/batching/jest.config.js +21 -0
  6. package/dist/enhancers/batching/lib/batching.d.ts +16 -0
  7. package/dist/enhancers/batching/lib/batching.js +155 -0
  8. package/dist/enhancers/batching/test-setup.d.ts +1 -0
  9. package/dist/enhancers/batching/test-setup.js +5 -0
  10. package/dist/enhancers/computed/index.d.ts +1 -0
  11. package/dist/enhancers/computed/index.js +1 -0
  12. package/dist/enhancers/computed/jest.config.d.ts +15 -0
  13. package/dist/enhancers/computed/jest.config.js +21 -0
  14. package/dist/enhancers/computed/lib/computed.d.ts +12 -0
  15. package/dist/enhancers/computed/lib/computed.js +19 -0
  16. package/dist/enhancers/devtools/index.d.ts +1 -0
  17. package/dist/enhancers/devtools/index.js +1 -0
  18. package/dist/enhancers/devtools/jest.config.d.ts +15 -0
  19. package/dist/enhancers/devtools/jest.config.js +21 -0
  20. package/dist/enhancers/devtools/lib/devtools.d.ts +77 -0
  21. package/dist/enhancers/devtools/lib/devtools.js +278 -0
  22. package/dist/enhancers/devtools/test-setup.d.ts +1 -0
  23. package/dist/enhancers/devtools/test-setup.js +5 -0
  24. package/dist/enhancers/entities/index.d.ts +1 -0
  25. package/dist/enhancers/entities/index.js +1 -0
  26. package/dist/enhancers/entities/jest.config.d.ts +15 -0
  27. package/dist/enhancers/entities/jest.config.js +21 -0
  28. package/dist/enhancers/entities/lib/entities.d.ts +22 -0
  29. package/dist/enhancers/entities/lib/entities.js +110 -0
  30. package/dist/enhancers/entities/test-setup.d.ts +1 -0
  31. package/dist/enhancers/entities/test-setup.js +5 -0
  32. package/dist/enhancers/index.d.ts +3 -0
  33. package/dist/enhancers/index.js +84 -0
  34. package/dist/enhancers/memoization/index.d.ts +1 -0
  35. package/dist/enhancers/memoization/index.js +1 -0
  36. package/dist/enhancers/memoization/jest.config.d.ts +15 -0
  37. package/dist/enhancers/memoization/jest.config.js +21 -0
  38. package/dist/enhancers/memoization/lib/memoization.d.ts +65 -0
  39. package/dist/enhancers/memoization/lib/memoization.js +391 -0
  40. package/dist/enhancers/memoization/test-setup.d.ts +1 -0
  41. package/dist/enhancers/memoization/test-setup.js +5 -0
  42. package/dist/enhancers/middleware/index.d.ts +2 -0
  43. package/dist/enhancers/middleware/index.js +2 -0
  44. package/dist/enhancers/middleware/jest.config.d.ts +15 -0
  45. package/dist/enhancers/middleware/jest.config.js +21 -0
  46. package/dist/enhancers/middleware/lib/async-helpers.d.ts +8 -0
  47. package/dist/enhancers/middleware/lib/async-helpers.js +85 -0
  48. package/dist/enhancers/middleware/lib/middleware.d.ts +11 -0
  49. package/dist/enhancers/middleware/lib/middleware.js +179 -0
  50. package/dist/enhancers/middleware/test-setup.d.ts +1 -0
  51. package/dist/enhancers/middleware/test-setup.js +5 -0
  52. package/dist/enhancers/presets/index.d.ts +1 -0
  53. package/dist/enhancers/presets/index.js +1 -0
  54. package/dist/enhancers/presets/jest.config.d.ts +15 -0
  55. package/dist/enhancers/presets/jest.config.js +21 -0
  56. package/dist/enhancers/presets/lib/presets.d.ts +11 -0
  57. package/dist/enhancers/presets/lib/presets.js +77 -0
  58. package/dist/enhancers/presets/test-setup.d.ts +1 -0
  59. package/dist/enhancers/presets/test-setup.js +5 -0
  60. package/dist/enhancers/serialization/constants.d.ts +14 -0
  61. package/dist/enhancers/serialization/constants.js +14 -0
  62. package/dist/enhancers/serialization/index.d.ts +2 -0
  63. package/dist/enhancers/serialization/index.js +2 -0
  64. package/dist/enhancers/serialization/jest.config.d.ts +15 -0
  65. package/dist/enhancers/serialization/jest.config.js +21 -0
  66. package/dist/enhancers/serialization/lib/serialization.d.ts +59 -0
  67. package/dist/enhancers/serialization/lib/serialization.js +668 -0
  68. package/dist/enhancers/serialization/test-setup.d.ts +1 -0
  69. package/dist/enhancers/serialization/test-setup.js +5 -0
  70. package/dist/enhancers/time-travel/index.d.ts +1 -0
  71. package/dist/enhancers/time-travel/index.js +1 -0
  72. package/dist/enhancers/time-travel/jest.config.d.ts +15 -0
  73. package/dist/enhancers/time-travel/jest.config.js +21 -0
  74. package/dist/enhancers/time-travel/lib/time-travel.d.ts +36 -0
  75. package/dist/enhancers/time-travel/lib/time-travel.js +192 -0
  76. package/dist/enhancers/time-travel/lib/utils.d.ts +1 -0
  77. package/dist/enhancers/time-travel/lib/utils.js +1 -0
  78. package/dist/enhancers/time-travel/test-setup.d.ts +1 -0
  79. package/dist/enhancers/time-travel/test-setup.js +5 -0
  80. package/dist/enhancers/types.d.ts +105 -0
  81. package/dist/enhancers/types.js +0 -0
  82. package/dist/index.d.ts +17 -0
  83. package/dist/index.js +16 -0
  84. package/dist/lib/constants.d.ts +42 -0
  85. package/dist/lib/constants.js +61 -0
  86. package/dist/lib/memory/memory-manager.d.ts +30 -0
  87. package/dist/lib/memory/memory-manager.js +166 -0
  88. package/dist/lib/performance/diff-engine.d.ts +33 -0
  89. package/dist/lib/performance/diff-engine.js +156 -0
  90. package/dist/lib/performance/path-index.d.ts +25 -0
  91. package/dist/lib/performance/path-index.js +154 -0
  92. package/dist/lib/performance/update-engine.d.ts +32 -0
  93. package/dist/lib/performance/update-engine.js +193 -0
  94. package/dist/lib/security/security-validator.d.ts +33 -0
  95. package/dist/lib/security/security-validator.js +139 -0
  96. package/dist/lib/signal-tree.d.ts +8 -0
  97. package/dist/lib/signal-tree.js +665 -0
  98. package/dist/lib/types.d.ts +164 -0
  99. package/dist/lib/types.js +9 -0
  100. package/dist/lib/utils.d.ts +27 -0
  101. package/dist/lib/utils.js +286 -0
  102. package/package.json +2 -11
package/README.md CHANGED
@@ -1451,6 +1451,318 @@ import {
1451
1451
  } from '@signaltree/core';
1452
1452
  ```
1453
1453
 
1454
+ ## Companion Packages
1455
+
1456
+ While `@signaltree/core` includes comprehensive built-in enhancers for most use cases, the SignalTree ecosystem also provides specialized companion packages for specific needs:
1457
+
1458
+ ### 📝 @signaltree/ng-forms
1459
+
1460
+ **Angular Reactive Forms integration for SignalTree**
1461
+
1462
+ Seamlessly connect Angular Reactive Forms with your SignalTree state for two-way data binding, validation, and form control.
1463
+
1464
+ ```bash
1465
+ npm install @signaltree/ng-forms
1466
+ ```
1467
+
1468
+ **Features:**
1469
+ - 🔗 Two-way binding between forms and SignalTree state
1470
+ - ✅ Built-in validation integration
1471
+ - 🎯 Type-safe form controls
1472
+ - 🔄 Automatic sync between form state and tree state
1473
+ - 📊 Form status tracking (valid, pristine, touched, etc.)
1474
+
1475
+ **Quick Example:**
1476
+
1477
+ ```typescript
1478
+ import { signalTree } from '@signaltree/core';
1479
+ import { bindFormToTree } from '@signaltree/ng-forms';
1480
+
1481
+ const tree = signalTree({
1482
+ user: { name: '', email: '', age: 0 }
1483
+ });
1484
+
1485
+ @Component({
1486
+ template: `
1487
+ <form [formGroup]="form">
1488
+ <input formControlName="name" />
1489
+ <input formControlName="email" type="email" />
1490
+ <input formControlName="age" type="number" />
1491
+ </form>
1492
+ `
1493
+ })
1494
+ class UserFormComponent {
1495
+ form = new FormGroup({
1496
+ name: new FormControl(''),
1497
+ email: new FormControl(''),
1498
+ age: new FormControl(0)
1499
+ });
1500
+
1501
+ constructor() {
1502
+ // Automatically sync form with tree state
1503
+ bindFormToTree(this.form, tree.$.user);
1504
+ }
1505
+ }
1506
+ ```
1507
+
1508
+ **When to use:**
1509
+ - Building forms with Angular Reactive Forms
1510
+ - Need validation integration
1511
+ - Two-way data binding between forms and state
1512
+ - Complex form scenarios with nested form groups
1513
+
1514
+ **Learn more:** [npm package](https://www.npmjs.com/package/@signaltree/ng-forms)
1515
+
1516
+ ---
1517
+
1518
+ ### 🏢 @signaltree/enterprise
1519
+
1520
+ **Enterprise-scale optimizations for large applications**
1521
+
1522
+ Advanced performance optimizations designed for applications with 500+ signals and complex state trees.
1523
+
1524
+ ```bash
1525
+ npm install @signaltree/enterprise
1526
+ ```
1527
+
1528
+ **Features:**
1529
+ - ⚡ PathIndex for O(k) lookup time regardless of tree size
1530
+ - 🗜️ Advanced memory optimization algorithms
1531
+ - 📊 Performance profiling and monitoring
1532
+ - 🔍 Efficient path-based signal resolution
1533
+ - 🎯 Optimized for large-scale applications
1534
+
1535
+ **Quick Example:**
1536
+
1537
+ ```typescript
1538
+ import { signalTree } from '@signaltree/core';
1539
+ import { withEnterpriseOptimizations } from '@signaltree/enterprise';
1540
+
1541
+ const tree = signalTree({
1542
+ // Large application state with hundreds of signals
1543
+ modules: {
1544
+ auth: { /* ... */ },
1545
+ data: { /* ... */ },
1546
+ ui: { /* ... */ },
1547
+ // ... many more modules
1548
+ }
1549
+ }).with(withEnterpriseOptimizations({
1550
+ enablePathIndex: true,
1551
+ enableMemoryOptimizations: true,
1552
+ enablePerformanceMonitoring: true
1553
+ }));
1554
+ ```
1555
+
1556
+ **Performance Benefits:**
1557
+ - **Constant-time lookups:** O(k) lookup where k is path depth, not total signal count
1558
+ - **Memory efficiency:** Up to 40% reduction in memory usage for large trees
1559
+ - **Faster updates:** Optimized update batching for high-frequency scenarios
1560
+
1561
+ **When to use:**
1562
+ - Applications with 500+ signals
1563
+ - Complex nested state structures (10+ levels deep)
1564
+ - High-frequency state updates
1565
+ - Enterprise-scale applications with performance requirements
1566
+ - Need detailed performance profiling
1567
+
1568
+ **Learn more:** [npm package](https://www.npmjs.com/package/@signaltree/enterprise)
1569
+
1570
+ ---
1571
+
1572
+ ### 🛡️ @signaltree/guardrails
1573
+
1574
+ **Development-only performance monitoring and debugging**
1575
+
1576
+ Comprehensive development tools for detecting performance issues, memory leaks, and anti-patterns during development. **Zero production overhead** via conditional exports.
1577
+
1578
+ ```bash
1579
+ npm install --save-dev @signaltree/guardrails
1580
+ ```
1581
+
1582
+ **Features:**
1583
+ - 🔥 Hot-path detection - identifies frequently accessed signals
1584
+ - 💾 Memory leak detection - tracks signal cleanup issues
1585
+ - 📊 Performance budgets - enforces performance thresholds
1586
+ - ⚠️ Anti-pattern warnings - detects common mistakes
1587
+ - 📈 Real-time performance metrics
1588
+ - 🎯 Zero production overhead (no-op in production builds)
1589
+
1590
+ **Quick Example:**
1591
+
1592
+ ```typescript
1593
+ import { signalTree } from '@signaltree/core';
1594
+ import { withGuardrails } from '@signaltree/guardrails';
1595
+
1596
+ const tree = signalTree({
1597
+ users: [] as User[],
1598
+ posts: [] as Post[]
1599
+ }).with(withGuardrails({
1600
+ hotPathThreshold: 100, // Warn if signal accessed >100 times/sec
1601
+ memoryLeakThreshold: 50, // Warn if >50 uncleaned signals
1602
+ budgets: {
1603
+ updateTime: 16, // Warn if updates take >16ms
1604
+ signalCount: 1000 // Warn if >1000 signals created
1605
+ }
1606
+ }));
1607
+
1608
+ // Development warnings will appear in console
1609
+ // Production builds get no-op functions (0 overhead)
1610
+ ```
1611
+
1612
+ **Development Features:**
1613
+
1614
+ ```typescript
1615
+ import { getPerformanceMetrics, getHotPaths, checkMemoryLeaks } from '@signaltree/guardrails';
1616
+
1617
+ // Get detailed performance metrics
1618
+ const metrics = getPerformanceMetrics();
1619
+ console.log('Update time:', metrics.avgUpdateTime);
1620
+ console.log('Signal count:', metrics.totalSignals);
1621
+
1622
+ // Identify hot paths
1623
+ const hotPaths = getHotPaths();
1624
+ console.log('Most accessed signals:', hotPaths);
1625
+
1626
+ // Check for memory leaks
1627
+ const leaks = checkMemoryLeaks();
1628
+ if (leaks.length > 0) {
1629
+ console.warn('Potential memory leaks:', leaks);
1630
+ }
1631
+ ```
1632
+
1633
+ **Conditional Exports (Zero Production Overhead):**
1634
+
1635
+ ```json
1636
+ {
1637
+ "exports": {
1638
+ ".": {
1639
+ "development": "./dist/index.js",
1640
+ "production": "./dist/noop.js"
1641
+ }
1642
+ }
1643
+ }
1644
+ ```
1645
+
1646
+ In production builds, all guardrails functions become no-ops with zero runtime cost.
1647
+
1648
+ **When to use:**
1649
+ - During active development
1650
+ - Performance optimization phase
1651
+ - Debugging state management issues
1652
+ - Team onboarding and code reviews
1653
+ - CI/CD performance regression detection
1654
+
1655
+ **Learn more:** [npm package](https://www.npmjs.com/package/@signaltree/guardrails)
1656
+
1657
+ ---
1658
+
1659
+ ### 🎯 @signaltree/callable-syntax
1660
+
1661
+ **Build-time transform for callable signal syntax**
1662
+
1663
+ A TypeScript transformer that enables callable syntax sugar for setting signal values. This is **purely a developer experience enhancement** with zero runtime overhead.
1664
+
1665
+ ```bash
1666
+ npm install --save-dev @signaltree/callable-syntax
1667
+ ```
1668
+
1669
+ **Features:**
1670
+ - 🍬 Syntactic sugar for signal updates
1671
+ - ⚡ Zero runtime overhead (build-time transform)
1672
+ - ✅ Full TypeScript type safety
1673
+ - 🔧 Works with any build tool (Rollup, Webpack, esbuild, etc.)
1674
+ - 📝 Optional - use direct `.set/.update` if preferred
1675
+
1676
+ **Syntax Transformation:**
1677
+
1678
+ ```typescript
1679
+ // With callable-syntax transform
1680
+ tree.$.name('Jane'); // Transformed to: tree.$.name.set('Jane')
1681
+ tree.$.count((n) => n + 1); // Transformed to: tree.$.count.update((n) => n + 1)
1682
+
1683
+ // Reading always works directly (no transform needed)
1684
+ const name = tree.$.name(); // Direct Angular signal API
1685
+ ```
1686
+
1687
+ **Setup (tsconfig.json):**
1688
+
1689
+ ```json
1690
+ {
1691
+ "compilerOptions": {
1692
+ "plugins": [
1693
+ { "transform": "@signaltree/callable-syntax" }
1694
+ ]
1695
+ }
1696
+ }
1697
+ ```
1698
+
1699
+ **Setup (Rollup):**
1700
+
1701
+ ```javascript
1702
+ import { callableSyntaxTransform } from '@signaltree/callable-syntax/rollup';
1703
+
1704
+ export default {
1705
+ plugins: [
1706
+ callableSyntaxTransform()
1707
+ ]
1708
+ };
1709
+ ```
1710
+
1711
+ **Important Notes:**
1712
+ - **Optional:** You can always use direct `.set(value)` or `.update(fn)` syntax
1713
+ - **Build-time only:** No runtime code is added to your bundle
1714
+ - **Function-valued leaves:** When storing functions, use `.set(fn)` directly
1715
+ - **Type-safe:** Full TypeScript support via module augmentation
1716
+
1717
+ **When to use:**
1718
+ - Prefer shorter, more concise syntax
1719
+ - Team convention favors callable style
1720
+ - Migrating from other signal libraries with similar syntax
1721
+ - Want familiar DX without runtime overhead
1722
+
1723
+ **When to skip:**
1724
+ - Team prefers explicit `.set/.update` syntax
1725
+ - Build pipeline doesn't support transformers
1726
+ - Storing functions as signal values (use direct `.set`)
1727
+
1728
+ **Learn more:** [npm package](https://www.npmjs.com/package/@signaltree/callable-syntax)
1729
+
1730
+ ---
1731
+
1732
+ ## Package Selection Guide
1733
+
1734
+ **Start with just `@signaltree/core`** - it includes comprehensive enhancers for most applications:
1735
+ - Performance optimization (batching, memoization)
1736
+ - Data management (entities, async operations)
1737
+ - Development tools (devtools, time-travel)
1738
+ - State persistence (serialization)
1739
+
1740
+ **Add companion packages when you need:**
1741
+
1742
+ | Package | When to Add | Bundle Impact |
1743
+ |---------|------------|---------------|
1744
+ | `@signaltree/ng-forms` | Angular Reactive Forms integration | ~10KB gzipped |
1745
+ | `@signaltree/enterprise` | 500+ signals, large-scale apps | ~8KB gzipped |
1746
+ | `@signaltree/guardrails` | Development performance monitoring | 0KB (dev-only) |
1747
+ | `@signaltree/callable-syntax` | Prefer callable syntax sugar | 0KB (build-time) |
1748
+
1749
+ **Typical Installation Patterns:**
1750
+
1751
+ ```bash
1752
+ # Basic application
1753
+ npm install @signaltree/core
1754
+
1755
+ # Application with forms
1756
+ npm install @signaltree/core @signaltree/ng-forms
1757
+
1758
+ # Large enterprise application
1759
+ npm install @signaltree/core @signaltree/enterprise
1760
+
1761
+ # Development with all tools
1762
+ npm install @signaltree/core @signaltree/enterprise @signaltree/ng-forms
1763
+ npm install --save-dev @signaltree/guardrails @signaltree/callable-syntax
1764
+ ```
1765
+
1454
1766
  ## Links
1455
1767
 
1456
1768
  - [SignalTree Documentation](https://signaltree.io)
@@ -0,0 +1 @@
1
+ export * from './lib/batching';
@@ -0,0 +1 @@
1
+ export * from './lib/batching';
@@ -0,0 +1,15 @@
1
+ declare const _default: {
2
+ displayName: string;
3
+ preset: string;
4
+ setupFilesAfterEnv: string[];
5
+ coverageDirectory: string;
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': (string | {
8
+ tsconfig: string;
9
+ stringifyContentPathRegex: string;
10
+ })[];
11
+ };
12
+ transformIgnorePatterns: string[];
13
+ snapshotSerializers: string[];
14
+ };
15
+ export default _default;
@@ -0,0 +1,21 @@
1
+ export default {
2
+ displayName: 'batching',
3
+ preset: '../../../../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/test-setup.ts'],
5
+ coverageDirectory: '../../../../../coverage/packages/core/enhancers/batching',
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': [
8
+ 'jest-preset-angular',
9
+ {
10
+ tsconfig: '<rootDir>/tsconfig.spec.json',
11
+ stringifyContentPathRegex: '\\.(html|svg)$',
12
+ },
13
+ ],
14
+ },
15
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16
+ snapshotSerializers: [
17
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
18
+ 'jest-preset-angular/build/serializers/ng-snapshot',
19
+ 'jest-preset-angular/build/serializers/html-comment',
20
+ ],
21
+ };
@@ -0,0 +1,16 @@
1
+ import type { SignalTree } from '../../../lib/types';
2
+ interface BatchingConfig {
3
+ enabled?: boolean;
4
+ maxBatchSize?: number;
5
+ autoFlushDelay?: number;
6
+ batchTimeoutMs?: number;
7
+ }
8
+ interface BatchingSignalTree<T> extends SignalTree<T> {
9
+ batchUpdate(updater: (current: T) => Partial<T>): void;
10
+ }
11
+ export declare function withBatching<T>(config?: BatchingConfig): (tree: SignalTree<T>) => BatchingSignalTree<T>;
12
+ export declare function withHighPerformanceBatching<T>(): (tree: SignalTree<T>) => BatchingSignalTree<T>;
13
+ export declare function flushBatchedUpdates(): void;
14
+ export declare function hasPendingUpdates(): boolean;
15
+ export declare function getBatchQueueSize(): number;
16
+ export {};
@@ -0,0 +1,155 @@
1
+ import { parsePath } from '@signaltree/shared';
2
+ import { isNodeAccessor } from '../../../lib/utils';
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)
31
+ return;
32
+ let queue;
33
+ do {
34
+ if (updateQueue.length === 0)
35
+ return;
36
+ isUpdating = true;
37
+ queue = updateQueue;
38
+ updateQueue = [];
39
+ if (flushTimeoutId !== undefined) {
40
+ clearTimeout(flushTimeoutId);
41
+ flushTimeoutId = undefined;
42
+ }
43
+ queue.sort((a, b) => (b.depth ?? 0) - (a.depth ?? 0));
44
+ try {
45
+ queue.forEach(({ fn }) => fn());
46
+ }
47
+ finally {
48
+ isUpdating = false;
49
+ }
50
+ } while (updateQueue.length > 0);
51
+ }
52
+ function batchUpdates(fn, path) {
53
+ const startTime = performance.now();
54
+ const depth = path ? parsePath(path).length : 0;
55
+ const update = { fn, startTime, depth, path };
56
+ const wasFlushed = addToQueue(update, currentBatchingConfig);
57
+ if (!wasFlushed) {
58
+ const isTimedOut = currentBatchingConfig.batchTimeoutMs &&
59
+ updateQueue.length > 0 &&
60
+ startTime - updateQueue[0].startTime >=
61
+ currentBatchingConfig.batchTimeoutMs;
62
+ if (isTimedOut) {
63
+ flushUpdates();
64
+ }
65
+ else if (!isUpdating && updateQueue.length > 0) {
66
+ queueMicrotask(() => {
67
+ flushUpdates();
68
+ });
69
+ }
70
+ }
71
+ }
72
+ export function withBatching(config = {}) {
73
+ const { enabled = true } = config;
74
+ currentBatchingConfig = { ...currentBatchingConfig, ...config };
75
+ return (tree) => {
76
+ if (!enabled) {
77
+ return tree;
78
+ }
79
+ const originalTreeCall = tree.bind(tree);
80
+ const enhancedTree = function (...args) {
81
+ if (args.length === 0) {
82
+ return originalTreeCall();
83
+ }
84
+ else {
85
+ batchUpdates(() => {
86
+ if (args.length === 1) {
87
+ const arg = args[0];
88
+ if (typeof arg === 'function') {
89
+ originalTreeCall(arg);
90
+ }
91
+ else {
92
+ originalTreeCall(arg);
93
+ }
94
+ }
95
+ });
96
+ }
97
+ };
98
+ Object.setPrototypeOf(enhancedTree, Object.getPrototypeOf(tree));
99
+ Object.assign(enhancedTree, tree);
100
+ if ('state' in tree) {
101
+ Object.defineProperty(enhancedTree, 'state', {
102
+ value: tree.state,
103
+ enumerable: false,
104
+ configurable: true,
105
+ });
106
+ }
107
+ if ('$' in tree) {
108
+ Object.defineProperty(enhancedTree, '$', {
109
+ value: tree['$'],
110
+ enumerable: false,
111
+ configurable: true,
112
+ });
113
+ }
114
+ enhancedTree.batchUpdate = (updater) => {
115
+ batchUpdates(() => {
116
+ const current = originalTreeCall();
117
+ const updates = updater(current);
118
+ Object.entries(updates).forEach(([key, value]) => {
119
+ const property = enhancedTree.state[key];
120
+ if (property && 'set' in property) {
121
+ property.set(value);
122
+ }
123
+ else if (isNodeAccessor(property)) {
124
+ property(value);
125
+ }
126
+ });
127
+ });
128
+ };
129
+ return enhancedTree;
130
+ };
131
+ }
132
+ export function withHighPerformanceBatching() {
133
+ return withBatching({
134
+ enabled: true,
135
+ maxBatchSize: 200,
136
+ batchTimeoutMs: 0,
137
+ });
138
+ }
139
+ export function flushBatchedUpdates() {
140
+ if (updateQueue.length > 0) {
141
+ const queue = updateQueue.slice();
142
+ updateQueue = [];
143
+ isUpdating = false;
144
+ queue.sort((a, b) => (b.depth ?? 0) - (a.depth ?? 0));
145
+ queue.forEach(({ fn }) => {
146
+ fn();
147
+ });
148
+ }
149
+ }
150
+ export function hasPendingUpdates() {
151
+ return updateQueue.length > 0;
152
+ }
153
+ export function getBatchQueueSize() {
154
+ return updateQueue.length;
155
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
2
+ setupZoneTestEnv({
3
+ errorOnUnknownElements: true,
4
+ errorOnUnknownProperties: true,
5
+ });
@@ -0,0 +1 @@
1
+ export * from './lib/computed';
@@ -0,0 +1 @@
1
+ export * from './lib/computed';
@@ -0,0 +1,15 @@
1
+ declare const _default: {
2
+ displayName: string;
3
+ preset: string;
4
+ setupFilesAfterEnv: string[];
5
+ coverageDirectory: string;
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': (string | {
8
+ tsconfig: string;
9
+ stringifyContentPathRegex: string;
10
+ })[];
11
+ };
12
+ transformIgnorePatterns: string[];
13
+ snapshotSerializers: string[];
14
+ };
15
+ export default _default;
@@ -0,0 +1,21 @@
1
+ export default {
2
+ displayName: 'computed',
3
+ preset: '../../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ coverageDirectory: '../../../coverage/packages/core/enhancers/computed',
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': [
8
+ 'jest-preset-angular',
9
+ {
10
+ tsconfig: '<rootDir>/tsconfig.spec.json',
11
+ stringifyContentPathRegex: '\\.(html|svg)$',
12
+ },
13
+ ],
14
+ },
15
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16
+ snapshotSerializers: [
17
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
18
+ 'jest-preset-angular/build/serializers/ng-snapshot',
19
+ 'jest-preset-angular/build/serializers/html-comment',
20
+ ],
21
+ };
@@ -0,0 +1,12 @@
1
+ import { Signal } from '@angular/core';
2
+ import type { TreeNode, SignalTree } from '../../../lib/types';
3
+ export interface ComputedConfig {
4
+ lazy?: boolean;
5
+ memoize?: boolean;
6
+ }
7
+ export type ComputedSignal<T> = Signal<T>;
8
+ export interface ComputedSignalTree<T extends Record<string, unknown>> extends SignalTree<T> {
9
+ computed<U>(computeFn: (tree: TreeNode<T>) => U): ComputedSignal<U>;
10
+ }
11
+ export declare function computedEnhancer(_config?: ComputedConfig): import("../../../lib/types").EnhancerWithMeta<SignalTree<Record<string, unknown>>, ComputedSignalTree<Record<string, unknown>>>;
12
+ export declare function createComputed<T>(dependencies: readonly Signal<unknown>[], computeFn: () => T): ComputedSignal<T>;
@@ -0,0 +1,19 @@
1
+ import { computed } from '@angular/core';
2
+ import { createEnhancer } from '../..';
3
+ export function computedEnhancer(_config = {}) {
4
+ void _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
+ export function createComputed(dependencies, computeFn) {
18
+ return computed(computeFn);
19
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/devtools';
@@ -0,0 +1 @@
1
+ export * from './lib/devtools';
@@ -0,0 +1,15 @@
1
+ declare const _default: {
2
+ displayName: string;
3
+ preset: string;
4
+ setupFilesAfterEnv: string[];
5
+ coverageDirectory: string;
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': (string | {
8
+ tsconfig: string;
9
+ stringifyContentPathRegex: string;
10
+ })[];
11
+ };
12
+ transformIgnorePatterns: string[];
13
+ snapshotSerializers: string[];
14
+ };
15
+ export default _default;
@@ -0,0 +1,21 @@
1
+ export default {
2
+ displayName: 'devtools',
3
+ preset: '../../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ coverageDirectory: '../../../coverage/packages/core/enhancers/devtools',
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': [
8
+ 'jest-preset-angular',
9
+ {
10
+ tsconfig: '<rootDir>/tsconfig.spec.json',
11
+ stringifyContentPathRegex: '\\.(html|svg)$',
12
+ },
13
+ ],
14
+ },
15
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16
+ snapshotSerializers: [
17
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
18
+ 'jest-preset-angular/build/serializers/ng-snapshot',
19
+ 'jest-preset-angular/build/serializers/html-comment',
20
+ ],
21
+ };