mutts 1.0.0 → 1.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/dist/chunks/{decorator-BXsign4Z.js → decorator-8qjFb7dw.js} +2 -2
- package/dist/chunks/decorator-8qjFb7dw.js.map +1 -0
- package/dist/chunks/{decorator-CPbZNnsX.esm.js → decorator-AbRkXM5O.esm.js} +2 -2
- package/dist/chunks/decorator-AbRkXM5O.esm.js.map +1 -0
- package/dist/decorator.d.ts +1 -1
- package/dist/decorator.esm.js +1 -1
- package/dist/decorator.js +1 -1
- package/dist/destroyable.esm.js +1 -1
- package/dist/destroyable.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/reactive.d.ts +4 -3
- package/dist/reactive.esm.js +61 -57
- package/dist/reactive.esm.js.map +1 -1
- package/dist/reactive.js +61 -56
- package/dist/reactive.js.map +1 -1
- package/dist/std-decorators.esm.js +1 -1
- package/dist/std-decorators.js +1 -1
- package/docs/reactive.md +616 -0
- package/package.json +1 -2
- package/dist/chunks/decorator-BXsign4Z.js.map +0 -1
- package/dist/chunks/decorator-CPbZNnsX.esm.js.map +0 -1
- package/src/decorator.test.ts +0 -495
- package/src/decorator.ts +0 -205
- package/src/destroyable.test.ts +0 -155
- package/src/destroyable.ts +0 -158
- package/src/eventful.test.ts +0 -380
- package/src/eventful.ts +0 -69
- package/src/index.ts +0 -7
- package/src/indexable.test.ts +0 -388
- package/src/indexable.ts +0 -124
- package/src/promiseChain.test.ts +0 -201
- package/src/promiseChain.ts +0 -99
- package/src/reactive/array.test.ts +0 -923
- package/src/reactive/array.ts +0 -352
- package/src/reactive/core.test.ts +0 -1663
- package/src/reactive/core.ts +0 -866
- package/src/reactive/index.ts +0 -28
- package/src/reactive/interface.test.ts +0 -1477
- package/src/reactive/interface.ts +0 -231
- package/src/reactive/map.test.ts +0 -866
- package/src/reactive/map.ts +0 -162
- package/src/reactive/set.test.ts +0 -289
- package/src/reactive/set.ts +0 -142
- package/src/std-decorators.test.ts +0 -679
- package/src/std-decorators.ts +0 -182
- package/src/utils.ts +0 -52
package/docs/reactive.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Reactive Documentation
|
|
2
2
|
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Introduction](#introduction)
|
|
6
|
+
- [Getting Started](#getting-started)
|
|
7
|
+
- [Core API](#core-api)
|
|
8
|
+
- [Effect System](#effect-system)
|
|
9
|
+
- [Evolution Tracking](#evolution-tracking)
|
|
10
|
+
- [Collections](#collections)
|
|
11
|
+
- [ReactiveArray](#reactivearray)
|
|
12
|
+
- [Class Reactivity](#class-reactivity)
|
|
13
|
+
- [Non-Reactive System](#non-reactive-system)
|
|
14
|
+
- [Computed Properties](#computed-properties)
|
|
15
|
+
- [Atomic Operations](#atomic-operations)
|
|
16
|
+
- [Advanced Patterns](#advanced-patterns)
|
|
17
|
+
- [Debugging and Development](#debugging-and-development)
|
|
18
|
+
- [API Reference](#api-reference)
|
|
3
19
|
|
|
4
20
|
## Introduction
|
|
5
21
|
|
|
@@ -12,6 +28,7 @@ Reactivity is a programming paradigm where the system automatically tracks depen
|
|
|
12
28
|
- **Reactive Objects**: Plain JavaScript objects wrapped with reactive capabilities
|
|
13
29
|
- **Effects**: Functions that automatically re-run when their dependencies change
|
|
14
30
|
- **Dependencies**: Reactive properties that an effect depends on
|
|
31
|
+
- **Atomic Operations**: Batching multiple state changes to execute effects only once
|
|
15
32
|
- **Evolution Tracking**: Built-in change history for reactive objects
|
|
16
33
|
- **Collections**: Reactive wrappers for Array, Map, Set, WeakMap, and WeakSet
|
|
17
34
|
|
|
@@ -1509,6 +1526,544 @@ state.count = 5
|
|
|
1509
1526
|
// effect logs and updates title
|
|
1510
1527
|
```
|
|
1511
1528
|
|
|
1529
|
+
## Atomic Operations
|
|
1530
|
+
|
|
1531
|
+
The `atomic` function and `@atomic` decorator are powerful tools for batching reactive effects. When applied to a method or function, they ensure that all effects triggered by reactive state changes within that scope are batched together and executed only once, rather than after each individual change.
|
|
1532
|
+
|
|
1533
|
+
### Overview
|
|
1534
|
+
|
|
1535
|
+
In reactive systems, each state change typically triggers its dependent effects immediately. However, when you need to make multiple related changes as a single unit, this can lead to:
|
|
1536
|
+
|
|
1537
|
+
- Multiple unnecessary effect executions
|
|
1538
|
+
- Inconsistent intermediate states being observed
|
|
1539
|
+
- Performance overhead from redundant computations
|
|
1540
|
+
|
|
1541
|
+
The `atomic` function and `@atomic` decorator solve this by deferring effect execution until the function or method completes, treating all changes as a single atomic operation.
|
|
1542
|
+
|
|
1543
|
+
### Basic Usage
|
|
1544
|
+
|
|
1545
|
+
#### Decorator Syntax
|
|
1546
|
+
|
|
1547
|
+
```typescript
|
|
1548
|
+
import { reactive, effect, atomic } from 'mutts'
|
|
1549
|
+
|
|
1550
|
+
const state = reactive({ a: 0, b: 0, c: 0 })
|
|
1551
|
+
let effectCount = 0
|
|
1552
|
+
|
|
1553
|
+
effect(() => {
|
|
1554
|
+
effectCount++
|
|
1555
|
+
console.log(`Effect ran: a=${state.a}, b=${state.b}, c=${state.c}`)
|
|
1556
|
+
})
|
|
1557
|
+
|
|
1558
|
+
class StateManager {
|
|
1559
|
+
@atomic
|
|
1560
|
+
updateAll() {
|
|
1561
|
+
state.a = 1
|
|
1562
|
+
state.b = 2
|
|
1563
|
+
state.c = 3
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
const manager = new StateManager()
|
|
1568
|
+
manager.updateAll()
|
|
1569
|
+
|
|
1570
|
+
// Output:
|
|
1571
|
+
// Effect ran: a=0, b=0, c=0 (initial run)
|
|
1572
|
+
// Effect ran: a=1, b=2, c=3 (only one additional run despite 3 changes)
|
|
1573
|
+
```
|
|
1574
|
+
|
|
1575
|
+
#### Function Syntax
|
|
1576
|
+
|
|
1577
|
+
For standalone functions or when you need more flexibility, you can use the `atomic` function directly:
|
|
1578
|
+
|
|
1579
|
+
```typescript
|
|
1580
|
+
import { reactive, effect, atomic } from 'mutts'
|
|
1581
|
+
|
|
1582
|
+
const state = reactive({ a: 0, b: 0, c: 0 })
|
|
1583
|
+
let effectCount = 0
|
|
1584
|
+
|
|
1585
|
+
effect(() => {
|
|
1586
|
+
effectCount++
|
|
1587
|
+
console.log(`Effect ran: a=${state.a}, b=${state.b}, c=${state.c}`)
|
|
1588
|
+
})
|
|
1589
|
+
|
|
1590
|
+
// Using atomic function
|
|
1591
|
+
atomic(() => {
|
|
1592
|
+
state.a = 1
|
|
1593
|
+
state.b = 2
|
|
1594
|
+
state.c = 3
|
|
1595
|
+
})
|
|
1596
|
+
|
|
1597
|
+
// Output:
|
|
1598
|
+
// Effect ran: a=0, b=0, c=0 (initial run)
|
|
1599
|
+
// Effect ran: a=1, b=2, c=3 (only one additional run despite 3 changes)
|
|
1600
|
+
```
|
|
1601
|
+
|
|
1602
|
+
#### Returning Values
|
|
1603
|
+
|
|
1604
|
+
The atomic function can return values:
|
|
1605
|
+
|
|
1606
|
+
```typescript
|
|
1607
|
+
const result = atomic(() => {
|
|
1608
|
+
state.a = 10
|
|
1609
|
+
state.b = 20
|
|
1610
|
+
return state.a + state.b
|
|
1611
|
+
})
|
|
1612
|
+
|
|
1613
|
+
console.log(result) // 30
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
#### Atomic Method Return Values
|
|
1617
|
+
|
|
1618
|
+
The `@atomic` decorator also supports return values from methods:
|
|
1619
|
+
|
|
1620
|
+
```typescript
|
|
1621
|
+
@reactive
|
|
1622
|
+
class Calculator {
|
|
1623
|
+
@atomic
|
|
1624
|
+
updateAndCalculate(a: number, b: number) {
|
|
1625
|
+
this.a = a
|
|
1626
|
+
this.b = b
|
|
1627
|
+
return { sum: a + b, product: a * b }
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
const calc = new Calculator()
|
|
1632
|
+
const result = calc.updateAndCalculate(5, 10)
|
|
1633
|
+
console.log(result) // { sum: 15, product: 50 }
|
|
1634
|
+
```
|
|
1635
|
+
|
|
1636
|
+
**Key Points:**
|
|
1637
|
+
- Atomic methods can return any value type (primitives, objects, functions)
|
|
1638
|
+
- Return values are computed during method execution
|
|
1639
|
+
- Effects are batched until the method completes, regardless of return values
|
|
1640
|
+
- Both read-only and state-modifying methods can return values
|
|
1641
|
+
|
|
1642
|
+
```typescript
|
|
1643
|
+
@reactive
|
|
1644
|
+
class DataProcessor {
|
|
1645
|
+
@atomic
|
|
1646
|
+
processData(items: Item[]) {
|
|
1647
|
+
// Read-only method with return value
|
|
1648
|
+
const total = items.reduce((sum, item) => sum + item.value, 0)
|
|
1649
|
+
return total
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
@atomic
|
|
1653
|
+
updateAndProcess(items: Item[]) {
|
|
1654
|
+
// State-modifying method with return value
|
|
1655
|
+
this.items = items
|
|
1656
|
+
this.processedCount = items.length
|
|
1657
|
+
this.lastProcessed = new Date()
|
|
1658
|
+
|
|
1659
|
+
return {
|
|
1660
|
+
count: items.length,
|
|
1661
|
+
total: items.reduce((sum, item) => sum + item.value, 0)
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
```
|
|
1666
|
+
|
|
1667
|
+
### When to Use Each Approach
|
|
1668
|
+
|
|
1669
|
+
#### Use `@atomic` Decorator When:
|
|
1670
|
+
- You're working with class methods
|
|
1671
|
+
- You want to declare atomic behavior at the method level
|
|
1672
|
+
- You prefer declarative syntax
|
|
1673
|
+
- The method is part of a reactive class
|
|
1674
|
+
|
|
1675
|
+
```typescript
|
|
1676
|
+
@reactive
|
|
1677
|
+
class TodoManager {
|
|
1678
|
+
@atomic
|
|
1679
|
+
addTodo(text: string) {
|
|
1680
|
+
this.todos.push({ id: Date.now(), text, completed: false })
|
|
1681
|
+
this.updateStats()
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
```
|
|
1685
|
+
|
|
1686
|
+
#### Use `atomic()` Function When:
|
|
1687
|
+
- You need atomic behavior for standalone functions
|
|
1688
|
+
- You're working with functional code
|
|
1689
|
+
- You need to conditionally apply atomic behavior
|
|
1690
|
+
- You're working outside of classes
|
|
1691
|
+
|
|
1692
|
+
```typescript
|
|
1693
|
+
// Conditional atomic behavior
|
|
1694
|
+
const updateState = (shouldBatch: boolean) => {
|
|
1695
|
+
const updateFn = () => {
|
|
1696
|
+
state.a = 1
|
|
1697
|
+
state.b = 2
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
return shouldBatch ? atomic(updateFn) : updateFn()
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// Functional approach
|
|
1704
|
+
const processItems = (items: Item[]) => {
|
|
1705
|
+
return atomic(() => {
|
|
1706
|
+
items.forEach(item => state.items.set(item.id, item))
|
|
1707
|
+
state.count = state.items.size
|
|
1708
|
+
return state.count
|
|
1709
|
+
})
|
|
1710
|
+
}
|
|
1711
|
+
```
|
|
1712
|
+
|
|
1713
|
+
### Key Behaviors
|
|
1714
|
+
|
|
1715
|
+
#### Immediate Execution, Batched Effects
|
|
1716
|
+
|
|
1717
|
+
The decorated method executes immediately, but effects are deferred until completion:
|
|
1718
|
+
|
|
1719
|
+
```typescript
|
|
1720
|
+
class TestClass {
|
|
1721
|
+
@atomic
|
|
1722
|
+
updateAndLog() {
|
|
1723
|
+
console.log('Method starts')
|
|
1724
|
+
state.a = 1
|
|
1725
|
+
console.log('After setting a')
|
|
1726
|
+
state.b = 2
|
|
1727
|
+
console.log('After setting b')
|
|
1728
|
+
console.log('Method ends')
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
// Execution order:
|
|
1733
|
+
// Method starts
|
|
1734
|
+
// After setting a
|
|
1735
|
+
// After setting b
|
|
1736
|
+
// Method ends
|
|
1737
|
+
// Effect runs with final values
|
|
1738
|
+
```
|
|
1739
|
+
|
|
1740
|
+
#### Nested Atomic Methods
|
|
1741
|
+
|
|
1742
|
+
Multiple atomic methods can be nested, and all effects are batched at the outermost level:
|
|
1743
|
+
|
|
1744
|
+
```typescript
|
|
1745
|
+
class TestClass {
|
|
1746
|
+
@atomic
|
|
1747
|
+
updateA() {
|
|
1748
|
+
state.a = 1
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
@atomic
|
|
1752
|
+
updateB() {
|
|
1753
|
+
state.b = 2
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
@atomic
|
|
1757
|
+
updateAll() {
|
|
1758
|
+
this.updateA()
|
|
1759
|
+
this.updateB()
|
|
1760
|
+
state.c = 3
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
// Calling updateAll() will batch all effects from updateA, updateB, and the direct assignment
|
|
1765
|
+
```
|
|
1766
|
+
|
|
1767
|
+
#### Cascading Effects
|
|
1768
|
+
|
|
1769
|
+
Effects that trigger other effects are also batched within atomic methods:
|
|
1770
|
+
|
|
1771
|
+
```typescript
|
|
1772
|
+
// Create cascading effects
|
|
1773
|
+
effect(() => {
|
|
1774
|
+
state.b = state.a * 2
|
|
1775
|
+
})
|
|
1776
|
+
effect(() => {
|
|
1777
|
+
state.c = state.b + 1
|
|
1778
|
+
})
|
|
1779
|
+
|
|
1780
|
+
class TestClass {
|
|
1781
|
+
@atomic
|
|
1782
|
+
triggerCascade() {
|
|
1783
|
+
state.a = 5 // This triggers the cascade
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// All cascading effects are batched together
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
### Advanced Usage
|
|
1791
|
+
|
|
1792
|
+
#### Working with Reactive Classes
|
|
1793
|
+
|
|
1794
|
+
The `@atomic` decorator works seamlessly with `@reactive` classes:
|
|
1795
|
+
|
|
1796
|
+
```typescript
|
|
1797
|
+
@reactive
|
|
1798
|
+
class Counter {
|
|
1799
|
+
value = 0
|
|
1800
|
+
multiplier = 1
|
|
1801
|
+
|
|
1802
|
+
@atomic
|
|
1803
|
+
updateBoth(newValue: number, newMultiplier: number) {
|
|
1804
|
+
this.value = newValue
|
|
1805
|
+
this.multiplier = newMultiplier
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const counter = new Counter()
|
|
1810
|
+
let effectCount = 0
|
|
1811
|
+
|
|
1812
|
+
effect(() => {
|
|
1813
|
+
effectCount++
|
|
1814
|
+
console.log(`Counter: ${counter.value} * ${counter.multiplier}`)
|
|
1815
|
+
})
|
|
1816
|
+
|
|
1817
|
+
counter.updateBoth(5, 2)
|
|
1818
|
+
// Effect runs only once despite two property changes
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
#### Complex Data Structures
|
|
1822
|
+
|
|
1823
|
+
Atomic methods work with arrays, maps, sets, and other complex data structures:
|
|
1824
|
+
|
|
1825
|
+
```typescript
|
|
1826
|
+
const state = reactive({
|
|
1827
|
+
items: [1, 2, 3],
|
|
1828
|
+
metadata: new Map([['count', 3]]),
|
|
1829
|
+
tags: new Set(['active'])
|
|
1830
|
+
})
|
|
1831
|
+
|
|
1832
|
+
class DataManager {
|
|
1833
|
+
@atomic
|
|
1834
|
+
addItem(item: number) {
|
|
1835
|
+
state.items.push(item)
|
|
1836
|
+
state.metadata.set('count', state.items.length)
|
|
1837
|
+
state.tags.add('modified')
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
@atomic
|
|
1841
|
+
clearAll() {
|
|
1842
|
+
state.items.length = 0
|
|
1843
|
+
state.metadata.clear()
|
|
1844
|
+
state.tags.clear()
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
```
|
|
1848
|
+
|
|
1849
|
+
#### Error Handling
|
|
1850
|
+
|
|
1851
|
+
If an atomic method throws an error, effects are still executed for the changes that were made before the error:
|
|
1852
|
+
|
|
1853
|
+
```typescript
|
|
1854
|
+
class TestClass {
|
|
1855
|
+
@atomic
|
|
1856
|
+
updateWithError() {
|
|
1857
|
+
state.a = 1
|
|
1858
|
+
throw new Error('Something went wrong')
|
|
1859
|
+
// state.b = 2 // This line never executes
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Effect will run once for the change to state.a
|
|
1864
|
+
// state.b remains unchanged due to the error
|
|
1865
|
+
```
|
|
1866
|
+
|
|
1867
|
+
#### Async Operations
|
|
1868
|
+
|
|
1869
|
+
Atomic methods can be async, but effects are still batched:
|
|
1870
|
+
|
|
1871
|
+
```typescript
|
|
1872
|
+
class TestClass {
|
|
1873
|
+
@atomic
|
|
1874
|
+
async updateAsync() {
|
|
1875
|
+
state.a = 1
|
|
1876
|
+
await someAsyncOperation()
|
|
1877
|
+
state.b = 2
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// Effects are batched even with async operations
|
|
1882
|
+
```
|
|
1883
|
+
|
|
1884
|
+
### Performance Benefits
|
|
1885
|
+
|
|
1886
|
+
#### Reduced Effect Executions
|
|
1887
|
+
|
|
1888
|
+
Without `atomic`:
|
|
1889
|
+
```typescript
|
|
1890
|
+
// This would trigger the effect 3 times
|
|
1891
|
+
state.a = 1 // Effect runs
|
|
1892
|
+
state.b = 2 // Effect runs
|
|
1893
|
+
state.c = 3 // Effect runs
|
|
1894
|
+
```
|
|
1895
|
+
|
|
1896
|
+
With `atomic`:
|
|
1897
|
+
```typescript
|
|
1898
|
+
@atomic
|
|
1899
|
+
updateAll() {
|
|
1900
|
+
state.a = 1
|
|
1901
|
+
state.b = 2
|
|
1902
|
+
state.c = 3
|
|
1903
|
+
}
|
|
1904
|
+
// Effect runs only once
|
|
1905
|
+
```
|
|
1906
|
+
|
|
1907
|
+
#### Consistent State
|
|
1908
|
+
|
|
1909
|
+
Atomic operations ensure that effects always see a consistent state:
|
|
1910
|
+
|
|
1911
|
+
```typescript
|
|
1912
|
+
effect(() => {
|
|
1913
|
+
// This will never see inconsistent intermediate states
|
|
1914
|
+
if (state.a > 0 && state.b > 0) {
|
|
1915
|
+
console.log('Both values are positive')
|
|
1916
|
+
}
|
|
1917
|
+
})
|
|
1918
|
+
|
|
1919
|
+
@atomic
|
|
1920
|
+
updateBoth() {
|
|
1921
|
+
state.a = 1 // Effect doesn't run yet
|
|
1922
|
+
state.b = 2 // Effect doesn't run yet
|
|
1923
|
+
// Effect runs once with both values updated
|
|
1924
|
+
}
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
### Best Practices
|
|
1928
|
+
|
|
1929
|
+
#### Use for Related Changes
|
|
1930
|
+
|
|
1931
|
+
Apply `atomic` to methods that make logically related changes:
|
|
1932
|
+
|
|
1933
|
+
```typescript
|
|
1934
|
+
// Good: Related user profile updates
|
|
1935
|
+
@atomic
|
|
1936
|
+
updateProfile(name: string, age: number, email: string) {
|
|
1937
|
+
this.name = name
|
|
1938
|
+
this.age = age
|
|
1939
|
+
this.email = email
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// Good: Complex state initialization
|
|
1943
|
+
@atomic
|
|
1944
|
+
initialize() {
|
|
1945
|
+
this.loading = false
|
|
1946
|
+
this.data = fetchedData
|
|
1947
|
+
this.error = null
|
|
1948
|
+
this.lastUpdated = new Date()
|
|
1949
|
+
}
|
|
1950
|
+
```
|
|
1951
|
+
|
|
1952
|
+
#### Combine with Other Decorators
|
|
1953
|
+
|
|
1954
|
+
The `@atomic` decorator works well with other decorators:
|
|
1955
|
+
|
|
1956
|
+
```typescript
|
|
1957
|
+
@reactive
|
|
1958
|
+
class UserManager {
|
|
1959
|
+
@atomic
|
|
1960
|
+
updateUser(id: string, updates: Partial<User>) {
|
|
1961
|
+
this.users.set(id, { ...this.users.get(id), ...updates })
|
|
1962
|
+
this.lastModified = new Date()
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
```
|
|
1966
|
+
|
|
1967
|
+
#### Consider Performance
|
|
1968
|
+
|
|
1969
|
+
For methods that make many changes, `atomic` provides significant performance benefits:
|
|
1970
|
+
|
|
1971
|
+
```typescript
|
|
1972
|
+
@atomic
|
|
1973
|
+
updateManyItems(items: Item[]) {
|
|
1974
|
+
for (const item of items) {
|
|
1975
|
+
this.items.set(item.id, item)
|
|
1976
|
+
}
|
|
1977
|
+
this.count = this.items.size
|
|
1978
|
+
this.lastUpdate = new Date()
|
|
1979
|
+
}
|
|
1980
|
+
// Without @atomic: effect would run for each item + count + timestamp
|
|
1981
|
+
// With @atomic: effect runs only once
|
|
1982
|
+
```
|
|
1983
|
+
|
|
1984
|
+
### Limitations
|
|
1985
|
+
|
|
1986
|
+
- **Method-only**: The decorator only works on class methods, not standalone functions (use `atomic()` function instead)
|
|
1987
|
+
- **Synchronous batching**: Effects are batched until the method completes, but async operations within the method don't affect the batching
|
|
1988
|
+
- **Error handling**: If a method throws, effects still run for changes made before the error
|
|
1989
|
+
|
|
1990
|
+
### Integration
|
|
1991
|
+
|
|
1992
|
+
The `atomic` function and `@atomic` decorator integrate seamlessly with:
|
|
1993
|
+
|
|
1994
|
+
- `@reactive` classes
|
|
1995
|
+
- `@unreactive` property marking
|
|
1996
|
+
- `effect()` functions
|
|
1997
|
+
- `computed()` values
|
|
1998
|
+
- Native collection types (Array, Map, Set, etc.)
|
|
1999
|
+
|
|
2000
|
+
### Complete Example
|
|
2001
|
+
|
|
2002
|
+
```typescript
|
|
2003
|
+
import { reactive, effect, atomic } from 'mutts'
|
|
2004
|
+
|
|
2005
|
+
@reactive
|
|
2006
|
+
class TodoManager {
|
|
2007
|
+
todos: Todo[] = []
|
|
2008
|
+
filter: 'all' | 'active' | 'completed' = 'all'
|
|
2009
|
+
loading = false
|
|
2010
|
+
|
|
2011
|
+
@atomic
|
|
2012
|
+
addTodo(text: string) {
|
|
2013
|
+
const todo: Todo = {
|
|
2014
|
+
id: Date.now().toString(),
|
|
2015
|
+
text,
|
|
2016
|
+
completed: false,
|
|
2017
|
+
createdAt: new Date()
|
|
2018
|
+
}
|
|
2019
|
+
this.todos.push(todo)
|
|
2020
|
+
this.updateStats()
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
@atomic
|
|
2024
|
+
toggleTodo(id: string) {
|
|
2025
|
+
const todo = this.todos.find(t => t.id === id)
|
|
2026
|
+
if (todo) {
|
|
2027
|
+
todo.completed = !todo.completed
|
|
2028
|
+
this.updateStats()
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
@atomic
|
|
2033
|
+
setFilter(filter: 'all' | 'active' | 'completed') {
|
|
2034
|
+
this.filter = filter
|
|
2035
|
+
this.loading = false
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
private updateStats() {
|
|
2039
|
+
// This method is called from within atomic methods
|
|
2040
|
+
// Effects will be batched until the calling atomic method completes
|
|
2041
|
+
const activeCount = this.todos.filter(t => !t.completed).length
|
|
2042
|
+
const completedCount = this.todos.length - activeCount
|
|
2043
|
+
|
|
2044
|
+
// Update derived state
|
|
2045
|
+
this.activeCount = activeCount
|
|
2046
|
+
this.completedCount = completedCount
|
|
2047
|
+
this.allCompleted = completedCount === this.todos.length && this.todos.length > 0
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// Usage
|
|
2052
|
+
const todoManager = new TodoManager()
|
|
2053
|
+
|
|
2054
|
+
effect(() => {
|
|
2055
|
+
console.log(`Active: ${todoManager.activeCount}, Completed: ${todoManager.completedCount}`)
|
|
2056
|
+
})
|
|
2057
|
+
|
|
2058
|
+
// Adding a todo triggers updateStats, but effect runs only once
|
|
2059
|
+
todoManager.addTodo('Learn MutTs atomic operations')
|
|
2060
|
+
|
|
2061
|
+
// Toggling a todo also triggers updateStats, effect runs only once
|
|
2062
|
+
todoManager.toggleTodo('some-id')
|
|
2063
|
+
```
|
|
2064
|
+
|
|
2065
|
+
This example demonstrates how `atomic` ensures that complex state updates are treated as single, consistent operations, improving both performance and reliability.
|
|
2066
|
+
|
|
1512
2067
|
## Advanced Patterns
|
|
1513
2068
|
|
|
1514
2069
|
### Custom Reactive Objects
|
|
@@ -1738,6 +2293,49 @@ const result = computed(myExpensiveCalculus);
|
|
|
1738
2293
|
By how JS works, writing `computed(()=> ...)` will always be wrong, as the notation `()=> ...` internally is a `new Function(...)`.
|
|
1739
2294
|
So, even if the return value is cached, it will never be used.
|
|
1740
2295
|
|
|
2296
|
+
#### `@atomic`
|
|
2297
|
+
|
|
2298
|
+
Marks a class method as atomic, batching all effects triggered within the method until it completes.
|
|
2299
|
+
|
|
2300
|
+
```typescript
|
|
2301
|
+
class MyClass {
|
|
2302
|
+
@atomic
|
|
2303
|
+
updateMultiple() {
|
|
2304
|
+
this.a = 1
|
|
2305
|
+
this.b = 2
|
|
2306
|
+
this.c = 3
|
|
2307
|
+
// Effects are batched and run only once after this method completes
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
@atomic
|
|
2311
|
+
updateAndReturn() {
|
|
2312
|
+
this.a = 10
|
|
2313
|
+
this.b = 20
|
|
2314
|
+
return { sum: this.a + this.b, product: this.a * this.b }
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
// Function usage
|
|
2319
|
+
const result = atomic(() => {
|
|
2320
|
+
state.a = 1
|
|
2321
|
+
state.b = 2
|
|
2322
|
+
return state.a + state.b
|
|
2323
|
+
})
|
|
2324
|
+
```
|
|
2325
|
+
|
|
2326
|
+
**Use Cases:**
|
|
2327
|
+
- Batching multiple related state changes
|
|
2328
|
+
- Performance optimization for methods with multiple updates
|
|
2329
|
+
- Ensuring consistent state in effects
|
|
2330
|
+
- Reducing unnecessary effect executions
|
|
2331
|
+
- Returning computed values from atomic operations
|
|
2332
|
+
|
|
2333
|
+
**Notes:**
|
|
2334
|
+
- Effects are deferred until the method/function completes
|
|
2335
|
+
- Nested atomic methods are batched at the outermost level
|
|
2336
|
+
- Works with both class methods and standalone functions
|
|
2337
|
+
- Methods and functions can return values (primitives, objects, functions)
|
|
2338
|
+
|
|
1741
2339
|
#### `@unreactive`
|
|
1742
2340
|
|
|
1743
2341
|
Marks a class property as non-reactive. The property change will not be tracked by the reactive system.
|
|
@@ -1803,6 +2401,24 @@ const cleanup = effect((dep) => {
|
|
|
1803
2401
|
});
|
|
1804
2402
|
```
|
|
1805
2403
|
|
|
2404
|
+
#### `atomic<T>(fn: () => T): T`
|
|
2405
|
+
|
|
2406
|
+
Creates an atomic operation that batches all effects triggered within the function until it completes.
|
|
2407
|
+
|
|
2408
|
+
**Use Cases:**
|
|
2409
|
+
- Batching multiple related state changes
|
|
2410
|
+
- Performance optimization for functions with multiple updates
|
|
2411
|
+
- Ensuring consistent state in effects
|
|
2412
|
+
- Reducing unnecessary effect executions
|
|
2413
|
+
|
|
2414
|
+
```typescript
|
|
2415
|
+
const result = atomic(() => {
|
|
2416
|
+
state.a = 1
|
|
2417
|
+
state.b = 2
|
|
2418
|
+
return state.a + state.b
|
|
2419
|
+
})
|
|
2420
|
+
```
|
|
2421
|
+
|
|
1806
2422
|
#### `computed<T>(getter: ComputedFunction<T>): T`
|
|
1807
2423
|
|
|
1808
2424
|
Creates a computed value that caches its result and recomputes when dependencies change.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mutts",
|
|
3
3
|
"description": "Modern UTility TS: A collection of TypeScript utilities",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.1",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
},
|
|
71
71
|
"files": [
|
|
72
72
|
"dist",
|
|
73
|
-
"src",
|
|
74
73
|
"README.md",
|
|
75
74
|
"docs"
|
|
76
75
|
],
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decorator-BXsign4Z.js","sources":["../../src/utils.ts","../../src/decorator.ts"],"sourcesContent":["type ElementTypes<T extends readonly unknown[]> = {\n\t[K in keyof T]: T[K] extends readonly (infer U)[] ? U : T[K]\n}\n\nexport function zip<T extends (readonly unknown[])[]>(...args: T): ElementTypes<T>[] {\n\tif (!args.length) return []\n\tconst minLength = Math.min(...args.map((arr) => arr.length))\n\tconst result: ElementTypes<T>[] = []\n\n\tfor (let i = 0; i < minLength; i++) {\n\t\tconst tuple = args.map((arr) => arr[i]) as ElementTypes<T>\n\t\tresult.push(tuple)\n\t}\n\n\treturn result\n}\n\nconst nativeConstructors = new Set<Function>([\n\tObject,\n\tArray,\n\tDate,\n\tFunction,\n\tSet,\n\tMap,\n\tWeakMap,\n\tWeakSet,\n\tPromise,\n\tError,\n\tTypeError,\n\tReferenceError,\n\tSyntaxError,\n\tRangeError,\n\tURIError,\n\tEvalError,\n\tReflect,\n\tProxy,\n\tRegExp,\n\tString,\n\tNumber,\n\tBoolean,\n] as Function[])\nexport function isConstructor(fn: Function): boolean {\n\treturn fn && (nativeConstructors.has(fn) || fn.toString().startsWith('class '))\n}\n\nexport function renamed<F extends Function>(fct: F, name: string): F {\n\treturn Object.defineProperties(fct, {\n\t\tname: {\n\t\t\tvalue: name,\n\t\t},\n\t})\n}\n","// biome-ignore-all lint/suspicious/noConfusingVoidType: We *love* voids\n// Standardized decorator system that works with both Legacy and Modern decorators\n\nimport { isConstructor } from './utils'\n\nexport class DecoratorError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = 'DecoratorException'\n\t}\n}\n//#region all decorator types\n\n// Used for get/set and method decorators\nexport type LegacyPropertyDecorator<T> = (\n\ttarget: T,\n\tname: string | symbol,\n\tdescriptor: PropertyDescriptor\n) => any\n\nexport type LegacyClassDecorator<T> = (target: T) => any\n\nexport type ModernMethodDecorator<T> = (target: T, context: ClassMethodDecoratorContext) => any\n\nexport type ModernGetterDecorator<T> = (target: T, context: ClassGetterDecoratorContext) => any\n\nexport type ModernSetterDecorator<T> = (target: T, context: ClassSetterDecoratorContext) => any\n\nexport type ModernAccessorDecorator<T> = (target: T, context: ClassAccessorDecoratorContext) => any\n\nexport type ModernClassDecorator<T> = (target: T, context: ClassDecoratorContext) => any\n\n//#endregion\n\ntype DDMethod<T> = (\n\toriginal: (this: T, ...args: any[]) => any,\n\tname: PropertyKey\n) => ((this: T, ...args: any[]) => any) | void\n\ntype DDGetter<T> = (original: (this: T) => any, name: PropertyKey) => ((this: T) => any) | void\n\ntype DDSetter<T> = (\n\toriginal: (this: T, value: any) => void,\n\tname: PropertyKey\n) => ((this: T, value: any) => void) | void\n\ntype DDClass<T> = <Ctor extends new (...args: any[]) => T = new (...args: any[]) => T>(\n\ttarget: Ctor\n) => Ctor | void\nexport interface DecoratorDescription<T> {\n\tmethod?: DDMethod<T>\n\tclass?: DDClass<T>\n\tgetter?: DDGetter<T>\n\tsetter?: DDSetter<T>\n\tdefault?: (...args: any[]) => any\n}\n\nexport type Decorator<T, Description extends DecoratorDescription<T>> = (Description extends {\n\tmethod: DDMethod<T>\n}\n\t? LegacyPropertyDecorator<T> & ModernMethodDecorator<T>\n\t: unknown) &\n\t(Description extends { class: DDClass<new (...args: any[]) => T> }\n\t\t? LegacyClassDecorator<new (...args: any[]) => T> &\n\t\t\t\tModernClassDecorator<new (...args: any[]) => T>\n\t\t: unknown) &\n\t(Description extends { getter: DDGetter<T> }\n\t\t? LegacyPropertyDecorator<T> & ModernGetterDecorator<T> & ModernAccessorDecorator<T>\n\t\t: unknown) &\n\t(Description extends { setter: DDSetter<T> }\n\t\t? LegacyPropertyDecorator<T> & ModernSetterDecorator<T> & ModernAccessorDecorator<T>\n\t\t: unknown) &\n\t(Description extends { default: infer Signature } ? Signature : unknown)\n\nexport type DecoratorFactory<T> = <Description extends DecoratorDescription<T>>(\n\tdescription: Description\n) => (Description extends { method: DDMethod<T> }\n\t? LegacyPropertyDecorator<T> & ModernMethodDecorator<T>\n\t: unknown) &\n\t(Description extends { class: DDClass<new (...args: any[]) => T> }\n\t\t? LegacyClassDecorator<new (...args: any[]) => T> &\n\t\t\t\tModernClassDecorator<new (...args: any[]) => T>\n\t\t: unknown) &\n\t(Description extends { getter: DDGetter<T> }\n\t\t? LegacyPropertyDecorator<T> & ModernGetterDecorator<T> & ModernAccessorDecorator<T>\n\t\t: unknown) &\n\t(Description extends { setter: DDSetter<T> }\n\t\t? LegacyPropertyDecorator<T> & ModernSetterDecorator<T> & ModernAccessorDecorator<T>\n\t\t: unknown) &\n\t(Description extends { default: infer Signature } ? Signature : unknown)\n\nexport function legacyDecorator<T = any>(description: DecoratorDescription<T>): any {\n\treturn function (\n\t\ttarget: any,\n\t\tpropertyKey?: PropertyKey,\n\t\tdescriptor?: PropertyDescriptor,\n\t\t...args: any[]\n\t) {\n\t\tif (propertyKey === undefined) {\n\t\t\tif (isConstructor(target)) {\n\t\t\t\tif (!('class' in description)) throw new Error('Decorator cannot be applied to a class')\n\t\t\t\treturn description.class?.(target)\n\t\t\t}\n\t\t} else if (typeof target === 'object' && ['string', 'symbol'].includes(typeof propertyKey)) {\n\t\t\tif (!descriptor) throw new Error('Decorator cannot be applied to a field')\n\t\t\telse if (typeof descriptor === 'object' && 'configurable' in descriptor) {\n\t\t\t\tif ('get' in descriptor || 'set' in descriptor) {\n\t\t\t\t\tif (!('getter' in description || 'setter' in description))\n\t\t\t\t\t\tthrow new Error('Decorator cannot be applied to a getter or setter')\n\t\t\t\t\tif ('getter' in description) {\n\t\t\t\t\t\tconst newGetter = description.getter?.(descriptor.get, propertyKey)\n\t\t\t\t\t\tif (newGetter) descriptor.get = newGetter\n\t\t\t\t\t}\n\t\t\t\t\tif ('setter' in description) {\n\t\t\t\t\t\tconst newSetter = description.setter?.(descriptor.set, propertyKey)\n\t\t\t\t\t\tif (newSetter) descriptor.set = newSetter\n\t\t\t\t\t}\n\t\t\t\t\treturn descriptor\n\t\t\t\t} else if (typeof descriptor.value === 'function') {\n\t\t\t\t\tif (!('method' in description)) throw new Error('Decorator cannot be applied to a method')\n\t\t\t\t\tconst newMethod = description.method?.(descriptor.value, propertyKey)\n\t\t\t\t\tif (newMethod) descriptor.value = newMethod\n\t\t\t\t\treturn descriptor\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!('default' in description))\n\t\t\tthrow new Error('Decorator do not have a default implementation')\n\t\treturn description.default.call(this, target, propertyKey, descriptor, ...args)\n\t}\n}\n\nexport function modernDecorator<T = any>(description: DecoratorDescription<T>): any {\n\treturn function (target: any, context?: DecoratorContext, ...args: any[]) {\n\t\tif (!context?.kind || typeof context.kind !== 'string') {\n\t\t\tif (!('default' in description))\n\t\t\t\tthrow new Error('Decorator do not have a default implementation')\n\t\t\treturn description.default.call(this, target, context, ...args)\n\t\t}\n\t\tswitch (context.kind) {\n\t\t\tcase 'class':\n\t\t\t\tif (!('class' in description)) throw new Error('Decorator cannot be applied to a class')\n\t\t\t\treturn description.class?.(target)\n\t\t\tcase 'field':\n\t\t\t\tthrow new Error('Decorator cannot be applied to a field')\n\t\t\tcase 'getter':\n\t\t\t\tif (!('getter' in description)) throw new Error('Decorator cannot be applied to a getter')\n\t\t\t\treturn description.getter?.(target, context.name)\n\t\t\tcase 'setter':\n\t\t\t\tif (!('setter' in description)) throw new Error('Decorator cannot be applied to a setter')\n\t\t\t\treturn description.setter?.(target, context.name)\n\t\t\tcase 'method':\n\t\t\t\tif (!('method' in description)) throw new Error('Decorator cannot be applied to a method')\n\t\t\t\treturn description.method?.(target, context.name)\n\t\t\tcase 'accessor': {\n\t\t\t\tif (!('getter' in description || 'setter' in description))\n\t\t\t\t\tthrow new Error('Decorator cannot be applied to a getter or setter')\n\t\t\t\tconst rv: Partial<ClassAccessorDecoratorResult<any, any>> = {}\n\t\t\t\tif ('getter' in description) {\n\t\t\t\t\tconst newGetter = description.getter?.(target.get, context.name)\n\t\t\t\t\tif (newGetter) rv.get = newGetter\n\t\t\t\t}\n\t\t\t\tif ('setter' in description) {\n\t\t\t\t\tconst newSetter = description.setter?.(target.set, context.name)\n\t\t\t\t\tif (newSetter) rv.set = newSetter\n\t\t\t\t}\n\t\t\t\treturn rv\n\t\t\t}\n\t\t\t//return description.accessor?.(target, context.name, target)\n\t\t}\n\t}\n}\n\n/**\n * Detects if the decorator is being called in modern (Modern) or legacy (Legacy) mode\n * based on the arguments passed to the decorator function\n */\nfunction detectDecoratorMode(\n\t_target: any,\n\tcontextOrKey?: any,\n\t_descriptor?: any\n): 'modern' | 'legacy' {\n\t// Modern decorators have a context object as the second parameter\n\t// Legacy decorators have a string/symbol key as the second parameter\n\tif (\n\t\ttypeof contextOrKey === 'object' &&\n\t\tcontextOrKey !== null &&\n\t\ttypeof contextOrKey.kind === 'string'\n\t) {\n\t\treturn 'modern'\n\t}\n\treturn 'legacy'\n}\n\nexport const decorator: DecoratorFactory<any> = (description: DecoratorDescription<any>) => {\n\treturn ((target: any, contextOrKey?: any, ...args: any[]) => {\n\t\tconst mode = detectDecoratorMode(target, contextOrKey, args[0])\n\t\treturn mode === 'modern'\n\t\t\t? modernDecorator(description)(target, contextOrKey, ...args)\n\t\t\t: legacyDecorator(description)(target, contextOrKey, ...args)\n\t}) as any\n}\n\nexport type GenericClassDecorator<T> = LegacyClassDecorator<new (...args: any[]) => T> &\n\tModernClassDecorator<new (...args: any[]) => T>\n"],"names":[],"mappings":"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"}
|