@signaltree/core 4.0.15 → 4.1.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.
Files changed (109) hide show
  1. package/README.md +106 -38
  2. package/dist/constants.js +6 -0
  3. package/dist/deep-clone.js +80 -0
  4. package/dist/deep-equal.js +41 -0
  5. package/dist/enhancers/batching/lib/batching.js +141 -135
  6. package/dist/enhancers/computed/lib/computed.js +18 -16
  7. package/dist/enhancers/devtools/lib/devtools.js +303 -260
  8. package/dist/enhancers/entities/lib/entities.js +109 -104
  9. package/dist/enhancers/index.js +65 -77
  10. package/dist/enhancers/memoization/lib/memoization.js +339 -351
  11. package/dist/enhancers/middleware/lib/async-helpers.js +71 -79
  12. package/dist/enhancers/middleware/lib/middleware.js +126 -169
  13. package/dist/enhancers/presets/lib/presets.js +82 -71
  14. package/dist/enhancers/serialization/constants.js +14 -13
  15. package/dist/enhancers/serialization/lib/serialization.js +615 -623
  16. package/dist/enhancers/time-travel/lib/time-travel.js +178 -177
  17. package/dist/index.d.ts +1 -17
  18. package/dist/index.js +19 -16
  19. package/dist/is-built-in-object.js +23 -0
  20. package/dist/lib/constants.js +51 -55
  21. package/dist/lib/memory/memory-manager.js +152 -154
  22. package/dist/lib/performance/diff-engine.js +141 -141
  23. package/dist/lib/performance/path-index.js +139 -137
  24. package/dist/lib/performance/update-engine.js +171 -176
  25. package/dist/lib/security/security-validator.js +110 -128
  26. package/dist/lib/signal-tree.js +577 -611
  27. package/dist/lib/types.js +3 -9
  28. package/dist/lib/utils.js +236 -268
  29. package/dist/lru-cache.js +64 -0
  30. package/dist/parse-path.js +13 -0
  31. package/package.json +30 -16
  32. package/src/index.d.ts +17 -0
  33. package/{dist → src}/lib/utils.d.ts +1 -0
  34. package/dist/enhancers/batching/index.js +0 -1
  35. package/dist/enhancers/batching/jest.config.js +0 -21
  36. package/dist/enhancers/batching/test-setup.js +0 -5
  37. package/dist/enhancers/computed/index.js +0 -1
  38. package/dist/enhancers/computed/jest.config.js +0 -21
  39. package/dist/enhancers/devtools/index.js +0 -1
  40. package/dist/enhancers/devtools/jest.config.js +0 -21
  41. package/dist/enhancers/devtools/test-setup.js +0 -5
  42. package/dist/enhancers/entities/index.js +0 -1
  43. package/dist/enhancers/entities/jest.config.js +0 -21
  44. package/dist/enhancers/entities/test-setup.js +0 -5
  45. package/dist/enhancers/memoization/index.js +0 -1
  46. package/dist/enhancers/memoization/jest.config.js +0 -21
  47. package/dist/enhancers/memoization/test-setup.js +0 -5
  48. package/dist/enhancers/middleware/index.js +0 -2
  49. package/dist/enhancers/middleware/jest.config.js +0 -21
  50. package/dist/enhancers/middleware/test-setup.js +0 -5
  51. package/dist/enhancers/presets/index.js +0 -1
  52. package/dist/enhancers/presets/jest.config.js +0 -21
  53. package/dist/enhancers/presets/test-setup.js +0 -5
  54. package/dist/enhancers/serialization/index.js +0 -2
  55. package/dist/enhancers/serialization/jest.config.js +0 -21
  56. package/dist/enhancers/serialization/test-setup.js +0 -5
  57. package/dist/enhancers/time-travel/index.js +0 -1
  58. package/dist/enhancers/time-travel/jest.config.js +0 -21
  59. package/dist/enhancers/time-travel/lib/utils.js +0 -1
  60. package/dist/enhancers/time-travel/test-setup.js +0 -5
  61. package/dist/enhancers/types.js +0 -0
  62. /package/{dist → src}/enhancers/batching/index.d.ts +0 -0
  63. /package/{dist → src}/enhancers/batching/jest.config.d.ts +0 -0
  64. /package/{dist → src}/enhancers/batching/lib/batching.d.ts +0 -0
  65. /package/{dist → src}/enhancers/batching/test-setup.d.ts +0 -0
  66. /package/{dist → src}/enhancers/computed/index.d.ts +0 -0
  67. /package/{dist → src}/enhancers/computed/jest.config.d.ts +0 -0
  68. /package/{dist → src}/enhancers/computed/lib/computed.d.ts +0 -0
  69. /package/{dist → src}/enhancers/devtools/index.d.ts +0 -0
  70. /package/{dist → src}/enhancers/devtools/jest.config.d.ts +0 -0
  71. /package/{dist → src}/enhancers/devtools/lib/devtools.d.ts +0 -0
  72. /package/{dist → src}/enhancers/devtools/test-setup.d.ts +0 -0
  73. /package/{dist → src}/enhancers/entities/index.d.ts +0 -0
  74. /package/{dist → src}/enhancers/entities/jest.config.d.ts +0 -0
  75. /package/{dist → src}/enhancers/entities/lib/entities.d.ts +0 -0
  76. /package/{dist → src}/enhancers/entities/test-setup.d.ts +0 -0
  77. /package/{dist → src}/enhancers/index.d.ts +0 -0
  78. /package/{dist → src}/enhancers/memoization/index.d.ts +0 -0
  79. /package/{dist → src}/enhancers/memoization/jest.config.d.ts +0 -0
  80. /package/{dist → src}/enhancers/memoization/lib/memoization.d.ts +0 -0
  81. /package/{dist → src}/enhancers/memoization/test-setup.d.ts +0 -0
  82. /package/{dist → src}/enhancers/middleware/index.d.ts +0 -0
  83. /package/{dist → src}/enhancers/middleware/jest.config.d.ts +0 -0
  84. /package/{dist → src}/enhancers/middleware/lib/async-helpers.d.ts +0 -0
  85. /package/{dist → src}/enhancers/middleware/lib/middleware.d.ts +0 -0
  86. /package/{dist → src}/enhancers/middleware/test-setup.d.ts +0 -0
  87. /package/{dist → src}/enhancers/presets/index.d.ts +0 -0
  88. /package/{dist → src}/enhancers/presets/jest.config.d.ts +0 -0
  89. /package/{dist → src}/enhancers/presets/lib/presets.d.ts +0 -0
  90. /package/{dist → src}/enhancers/presets/test-setup.d.ts +0 -0
  91. /package/{dist → src}/enhancers/serialization/constants.d.ts +0 -0
  92. /package/{dist → src}/enhancers/serialization/index.d.ts +0 -0
  93. /package/{dist → src}/enhancers/serialization/jest.config.d.ts +0 -0
  94. /package/{dist → src}/enhancers/serialization/lib/serialization.d.ts +0 -0
  95. /package/{dist → src}/enhancers/serialization/test-setup.d.ts +0 -0
  96. /package/{dist → src}/enhancers/time-travel/index.d.ts +0 -0
  97. /package/{dist → src}/enhancers/time-travel/jest.config.d.ts +0 -0
  98. /package/{dist → src}/enhancers/time-travel/lib/time-travel.d.ts +0 -0
  99. /package/{dist → src}/enhancers/time-travel/lib/utils.d.ts +0 -0
  100. /package/{dist → src}/enhancers/time-travel/test-setup.d.ts +0 -0
  101. /package/{dist → src}/enhancers/types.d.ts +0 -0
  102. /package/{dist → src}/lib/constants.d.ts +0 -0
  103. /package/{dist → src}/lib/memory/memory-manager.d.ts +0 -0
  104. /package/{dist → src}/lib/performance/diff-engine.d.ts +0 -0
  105. /package/{dist → src}/lib/performance/path-index.d.ts +0 -0
  106. /package/{dist → src}/lib/performance/update-engine.d.ts +0 -0
  107. /package/{dist → src}/lib/security/security-validator.d.ts +0 -0
  108. /package/{dist → src}/lib/signal-tree.d.ts +0 -0
  109. /package/{dist → src}/lib/types.d.ts +0 -0
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:
@@ -1457,20 +1484,44 @@ While `@signaltree/core` includes comprehensive built-in enhancers for most use
1457
1484
 
1458
1485
  ### 📝 @signaltree/ng-forms
1459
1486
 
1460
- **Angular Reactive Forms integration for SignalTree**
1487
+ **Angular Forms integration for SignalTree (Angular 17+)**
1461
1488
 
1462
- Seamlessly connect Angular Reactive Forms with your SignalTree state for two-way data binding, validation, and form control.
1489
+ Seamlessly connect Angular Forms with your SignalTree state for two-way data binding, validation, and form control.
1463
1490
 
1464
1491
  ```bash
1465
1492
  npm install @signaltree/ng-forms
1466
1493
  ```
1467
1494
 
1468
1495
  **Features:**
1496
+
1469
1497
  - 🔗 Two-way binding between forms and SignalTree state
1470
1498
  - ✅ Built-in validation integration
1471
1499
  - 🎯 Type-safe form controls
1472
1500
  - 🔄 Automatic sync between form state and tree state
1473
1501
  - 📊 Form status tracking (valid, pristine, touched, etc.)
1502
+ - ⚡ Native Signal Forms support (Angular 20.3+)
1503
+ - 🔧 Legacy bridge for Angular 17-19 (deprecated, will be removed with Angular 21)
1504
+
1505
+ **Signal Forms (Angular 20.3+ recommended)**
1506
+
1507
+ Use Angular's Signal Forms `connect()` API directly with SignalTree:
1508
+
1509
+ ```ts
1510
+ import { toWritableSignal } from '@signaltree/core';
1511
+
1512
+ const tree = signalTree({
1513
+ user: { name: '', email: '' },
1514
+ });
1515
+
1516
+ // Leaves are WritableSignal<T>
1517
+ nameControl.connect(tree.$.user.name);
1518
+
1519
+ // Convert a slice to a WritableSignal<T>
1520
+ const userSignal = toWritableSignal(tree.$.user);
1521
+ userGroupControl.connect(userSignal);
1522
+ ```
1523
+
1524
+ The `@signaltree/ng-forms` package supports Angular 17+ and will prefer `connect()` when available (Angular 20.3+). Angular 17-19 uses a legacy bridge that will be deprecated when Angular 21 is released.
1474
1525
 
1475
1526
  **Quick Example:**
1476
1527
 
@@ -1479,7 +1530,7 @@ import { signalTree } from '@signaltree/core';
1479
1530
  import { bindFormToTree } from '@signaltree/ng-forms';
1480
1531
 
1481
1532
  const tree = signalTree({
1482
- user: { name: '', email: '', age: 0 }
1533
+ user: { name: '', email: '', age: 0 },
1483
1534
  });
1484
1535
 
1485
1536
  @Component({
@@ -1489,13 +1540,13 @@ const tree = signalTree({
1489
1540
  <input formControlName="email" type="email" />
1490
1541
  <input formControlName="age" type="number" />
1491
1542
  </form>
1492
- `
1543
+ `,
1493
1544
  })
1494
1545
  class UserFormComponent {
1495
1546
  form = new FormGroup({
1496
1547
  name: new FormControl(''),
1497
1548
  email: new FormControl(''),
1498
- age: new FormControl(0)
1549
+ age: new FormControl(0),
1499
1550
  });
1500
1551
 
1501
1552
  constructor() {
@@ -1506,6 +1557,7 @@ class UserFormComponent {
1506
1557
  ```
1507
1558
 
1508
1559
  **When to use:**
1560
+
1509
1561
  - Building forms with Angular Reactive Forms
1510
1562
  - Need validation integration
1511
1563
  - Two-way data binding between forms and state
@@ -1526,6 +1578,7 @@ npm install @signaltree/enterprise
1526
1578
  ```
1527
1579
 
1528
1580
  **Features:**
1581
+
1529
1582
  - ⚡ PathIndex for O(k) lookup time regardless of tree size
1530
1583
  - 🗜️ Advanced memory optimization algorithms
1531
1584
  - 📊 Performance profiling and monitoring
@@ -1541,24 +1594,34 @@ import { withEnterpriseOptimizations } from '@signaltree/enterprise';
1541
1594
  const tree = signalTree({
1542
1595
  // Large application state with hundreds of signals
1543
1596
  modules: {
1544
- auth: { /* ... */ },
1545
- data: { /* ... */ },
1546
- ui: { /* ... */ },
1597
+ auth: {
1598
+ /* ... */
1599
+ },
1600
+ data: {
1601
+ /* ... */
1602
+ },
1603
+ ui: {
1604
+ /* ... */
1605
+ },
1547
1606
  // ... many more modules
1548
- }
1549
- }).with(withEnterpriseOptimizations({
1550
- enablePathIndex: true,
1551
- enableMemoryOptimizations: true,
1552
- enablePerformanceMonitoring: true
1553
- }));
1607
+ },
1608
+ }).with(
1609
+ withEnterpriseOptimizations({
1610
+ enablePathIndex: true,
1611
+ enableMemoryOptimizations: true,
1612
+ enablePerformanceMonitoring: true,
1613
+ })
1614
+ );
1554
1615
  ```
1555
1616
 
1556
1617
  **Performance Benefits:**
1618
+
1557
1619
  - **Constant-time lookups:** O(k) lookup where k is path depth, not total signal count
1558
1620
  - **Memory efficiency:** Up to 40% reduction in memory usage for large trees
1559
1621
  - **Faster updates:** Optimized update batching for high-frequency scenarios
1560
1622
 
1561
1623
  **When to use:**
1624
+
1562
1625
  - Applications with 500+ signals
1563
1626
  - Complex nested state structures (10+ levels deep)
1564
1627
  - High-frequency state updates
@@ -1580,6 +1643,7 @@ npm install --save-dev @signaltree/guardrails
1580
1643
  ```
1581
1644
 
1582
1645
  **Features:**
1646
+
1583
1647
  - 🔥 Hot-path detection - identifies frequently accessed signals
1584
1648
  - 💾 Memory leak detection - tracks signal cleanup issues
1585
1649
  - 📊 Performance budgets - enforces performance thresholds
@@ -1595,15 +1659,17 @@ import { withGuardrails } from '@signaltree/guardrails';
1595
1659
 
1596
1660
  const tree = signalTree({
1597
1661
  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
- }));
1662
+ posts: [] as Post[],
1663
+ }).with(
1664
+ withGuardrails({
1665
+ hotPathThreshold: 100, // Warn if signal accessed >100 times/sec
1666
+ memoryLeakThreshold: 50, // Warn if >50 uncleaned signals
1667
+ budgets: {
1668
+ updateTime: 16, // Warn if updates take >16ms
1669
+ signalCount: 1000, // Warn if >1000 signals created
1670
+ },
1671
+ })
1672
+ );
1607
1673
 
1608
1674
  // Development warnings will appear in console
1609
1675
  // Production builds get no-op functions (0 overhead)
@@ -1646,6 +1712,7 @@ if (leaks.length > 0) {
1646
1712
  In production builds, all guardrails functions become no-ops with zero runtime cost.
1647
1713
 
1648
1714
  **When to use:**
1715
+
1649
1716
  - During active development
1650
1717
  - Performance optimization phase
1651
1718
  - Debugging state management issues
@@ -1667,6 +1734,7 @@ npm install --save-dev @signaltree/callable-syntax
1667
1734
  ```
1668
1735
 
1669
1736
  **Features:**
1737
+
1670
1738
  - 🍬 Syntactic sugar for signal updates
1671
1739
  - ⚡ Zero runtime overhead (build-time transform)
1672
1740
  - ✅ Full TypeScript type safety
@@ -1677,11 +1745,11 @@ npm install --save-dev @signaltree/callable-syntax
1677
1745
 
1678
1746
  ```typescript
1679
1747
  // 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)
1748
+ tree.$.name('Jane'); // Transformed to: tree.$.name.set('Jane')
1749
+ tree.$.count((n) => n + 1); // Transformed to: tree.$.count.update((n) => n + 1)
1682
1750
 
1683
1751
  // Reading always works directly (no transform needed)
1684
- const name = tree.$.name(); // Direct Angular signal API
1752
+ const name = tree.$.name(); // Direct Angular signal API
1685
1753
  ```
1686
1754
 
1687
1755
  **Setup (tsconfig.json):**
@@ -1689,9 +1757,7 @@ const name = tree.$.name(); // Direct Angular signal API
1689
1757
  ```json
1690
1758
  {
1691
1759
  "compilerOptions": {
1692
- "plugins": [
1693
- { "transform": "@signaltree/callable-syntax" }
1694
- ]
1760
+ "plugins": [{ "transform": "@signaltree/callable-syntax" }]
1695
1761
  }
1696
1762
  }
1697
1763
  ```
@@ -1702,25 +1768,26 @@ const name = tree.$.name(); // Direct Angular signal API
1702
1768
  import { callableSyntaxTransform } from '@signaltree/callable-syntax/rollup';
1703
1769
 
1704
1770
  export default {
1705
- plugins: [
1706
- callableSyntaxTransform()
1707
- ]
1771
+ plugins: [callableSyntaxTransform()],
1708
1772
  };
1709
1773
  ```
1710
1774
 
1711
1775
  **Important Notes:**
1776
+
1712
1777
  - **Optional:** You can always use direct `.set(value)` or `.update(fn)` syntax
1713
1778
  - **Build-time only:** No runtime code is added to your bundle
1714
1779
  - **Function-valued leaves:** When storing functions, use `.set(fn)` directly
1715
1780
  - **Type-safe:** Full TypeScript support via module augmentation
1716
1781
 
1717
1782
  **When to use:**
1783
+
1718
1784
  - Prefer shorter, more concise syntax
1719
1785
  - Team convention favors callable style
1720
1786
  - Migrating from other signal libraries with similar syntax
1721
1787
  - Want familiar DX without runtime overhead
1722
1788
 
1723
1789
  **When to skip:**
1790
+
1724
1791
  - Team prefers explicit `.set/.update` syntax
1725
1792
  - Build pipeline doesn't support transformers
1726
1793
  - Storing functions as signal values (use direct `.set`)
@@ -1732,6 +1799,7 @@ export default {
1732
1799
  ## Package Selection Guide
1733
1800
 
1734
1801
  **Start with just `@signaltree/core`** - it includes comprehensive enhancers for most applications:
1802
+
1735
1803
  - Performance optimization (batching, memoization)
1736
1804
  - Data management (entities, async operations)
1737
1805
  - Development tools (devtools, time-travel)
@@ -1739,12 +1807,12 @@ export default {
1739
1807
 
1740
1808
  **Add companion packages when you need:**
1741
1809
 
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) |
1810
+ | Package | When to Add | Bundle Impact |
1811
+ | ----------------------------- | ---------------------------------- | ---------------- |
1812
+ | `@signaltree/ng-forms` | Angular Reactive Forms integration | ~10KB gzipped |
1813
+ | `@signaltree/enterprise` | 500+ signals, large-scale apps | ~8KB gzipped |
1814
+ | `@signaltree/guardrails` | Development performance monitoring | 0KB (dev-only) |
1815
+ | `@signaltree/callable-syntax` | Prefer callable syntax sugar | 0KB (build-time) |
1748
1816
 
1749
1817
  **Typical Installation Patterns:**
1750
1818
 
@@ -0,0 +1,6 @@
1
+ const SHARED_DEFAULTS = Object.freeze({
2
+ PATH_CACHE_SIZE: 1000
3
+ });
4
+ const DEFAULT_PATH_CACHE_SIZE = SHARED_DEFAULTS.PATH_CACHE_SIZE;
5
+
6
+ export { DEFAULT_PATH_CACHE_SIZE, SHARED_DEFAULTS };
@@ -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 };