@actuallab/fusion 14.3.26 → 14.3.32
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 +112 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @actuallab/fusion
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@actuallab/fusion)
|
|
4
|
+
[](https://fusion.actuallab.net/PartTS-Fusion)
|
|
5
|
+
[](https://github.com/ActualLab/Fusion/blob/master/LICENSE)
|
|
6
|
+
|
|
7
|
+
The TypeScript implementation of [Fusion](https://fusion.actuallab.net/)'s core: memoized
|
|
8
|
+
computations with automatic dependency tracking and cascading invalidation. It gives you
|
|
9
|
+
`Computed<T>`, the `@computeMethod` decorator, `ComputedState<T>`, `MutableState<T>`, and
|
|
10
|
+
`UIActionTracker` — the same abstractions Fusion provides on .NET.
|
|
11
|
+
|
|
12
|
+
This package works standalone (client-side reactive state, no server needed). Add
|
|
13
|
+
[`@actuallab/fusion-rpc`](https://www.npmjs.com/package/@actuallab/fusion-rpc) to consume
|
|
14
|
+
invalidation-aware Compute Services from a .NET server, and
|
|
15
|
+
[`@actuallab/fusion-react`](https://www.npmjs.com/package/@actuallab/fusion-react) to render them
|
|
16
|
+
in React.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @actuallab/fusion
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
ESM-first with a CJS fallback; ships its own `.d.ts`. Depends only on `@actuallab/core`.
|
|
25
|
+
|
|
26
|
+
## Compute methods
|
|
27
|
+
|
|
28
|
+
`@computeMethod` is the equivalent of .NET's `[ComputeMethod]` — it wraps a method with caching and
|
|
29
|
+
dependency tracking. Results are keyed by `JSON.stringify` of the arguments.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { computeMethod } from '@actuallab/fusion';
|
|
33
|
+
|
|
34
|
+
class CounterService {
|
|
35
|
+
private _counters = new Map<string, number>();
|
|
36
|
+
|
|
37
|
+
@computeMethod
|
|
38
|
+
async get(key: string): Promise<number> {
|
|
39
|
+
return this._counters.get(key) ?? 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@computeMethod
|
|
43
|
+
async sum(key1: string, key2: string): Promise<number> {
|
|
44
|
+
// sum() automatically depends on get(key1) and get(key2)
|
|
45
|
+
return (await this.get(key1)) + (await this.get(key2));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
increment(key: string): void {
|
|
49
|
+
this._counters.set(key, (this._counters.get(key) ?? 0) + 1);
|
|
50
|
+
(this.get as any).invalidate(key); // cascades into sum()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
There is no `Invalidation.Begin()` block here: every bound compute method carries an
|
|
56
|
+
`.invalidate(...args)` function. For standalone functions, use `wrapComputeMethod`.
|
|
57
|
+
|
|
58
|
+
## Reactive states
|
|
59
|
+
|
|
60
|
+
`ComputedState<T>` recomputes itself whenever anything it used gets invalidated;
|
|
61
|
+
`MutableState<T>` is set by hand and participates in the same dependency graph.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { ComputedState, MutableState, FixedDelayer } from '@actuallab/fusion';
|
|
65
|
+
|
|
66
|
+
const state = new ComputedState(
|
|
67
|
+
async () => `Count: ${await counters.get('a')}`,
|
|
68
|
+
{ initialValue: 'loading...', updateDelayer: FixedDelayer.get(500) },
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
await state.whenFirstTimeUpdated();
|
|
72
|
+
state.value; // "Count: 0" — and it updates on its own from here on
|
|
73
|
+
state.dispose(); // required: stops the update loop
|
|
74
|
+
|
|
75
|
+
const query = new MutableState('');
|
|
76
|
+
query.set('fusion'); // invalidates every computation that called query.use()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API surface
|
|
80
|
+
|
|
81
|
+
| API | Description |
|
|
82
|
+
|-----|-------------|
|
|
83
|
+
| `Computed<T>`, `ConsistencyState` | Cached computation result: `.value`, `.update()`, `.use()`, `.invalidate()`, `.whenInvalidated()` |
|
|
84
|
+
| `Computed.capture(fn)` | Capture the `Computed<T>` a compute call produced |
|
|
85
|
+
| `computeMethod`, `wrapComputeMethod` | Turn a method / function into a compute function |
|
|
86
|
+
| `ComputedOptions` | Per-method options, e.g. `errorAutoInvalidateDelay` |
|
|
87
|
+
| `ComputedRegistry`, `ComputeFunction`, `ComputeContext` | The kernel, for advanced/custom integrations |
|
|
88
|
+
| `State<T>`, `ComputedState<T>`, `MutableState<T>` | Reactive states |
|
|
89
|
+
| `UpdateDelayer`, `FixedDelayer`, `UIUpdateDelayer`, `defaultUpdateDelayer` | Recompute pacing (≈32 ms floor, retry backoff) |
|
|
90
|
+
| `UIActionTracker`, `uiActions` | Tracks running UI commands, collects errors, enables instant updates |
|
|
91
|
+
|
|
92
|
+
## Notes
|
|
93
|
+
|
|
94
|
+
- **Dependency tracking across `await`.** On Node ≥ 20.16 `AsyncContext` is backed by
|
|
95
|
+
`AsyncLocalStorage`, so it just works. In browsers the child `AsyncContext` is passed to your
|
|
96
|
+
compute method as a trailing argument — accept it and forward it into nested compute calls. See
|
|
97
|
+
[AsyncContext: Why It Matters](https://fusion.actuallab.net/PartTS#asynccontext-why-it-matters).
|
|
98
|
+
- **Dispose your `ComputedState`s.** Otherwise their update loops keep running.
|
|
99
|
+
- **Errors.** Compute methods auto-invalidate an error output after 1 s by default (configurable
|
|
100
|
+
via `@computeMethod({ errorAutoInvalidateDelay })`); states never do — they retry with backoff.
|
|
101
|
+
Cancellation-shaped errors are never cached.
|
|
102
|
+
|
|
103
|
+
## Documentation
|
|
104
|
+
|
|
105
|
+
- [`@actuallab/fusion` reference](https://fusion.actuallab.net/PartTS-Fusion) — full API tables and semantics
|
|
106
|
+
- [TypeScript port overview](https://fusion.actuallab.net/PartTS)
|
|
107
|
+
- [Compute Services](https://fusion.actuallab.net/PartF) and [States](https://fusion.actuallab.net/PartF-ST) — the concepts, in .NET terms
|
|
108
|
+
- [TodoApp sample (React + Fusion)](https://github.com/ActualLab/Fusion.Samples/tree/master/src/TodoApp)
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT — see [LICENSE](https://github.com/ActualLab/Fusion/blob/master/LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actuallab/fusion",
|
|
3
|
-
"version": "14.3.
|
|
3
|
+
"version": "14.3.32",
|
|
4
4
|
"description": "TypeScript implementation of Fusion - real-time state synchronization framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"clean": "rm -rf dist"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@actuallab/core": "14.3.
|
|
29
|
+
"@actuallab/core": "14.3.32"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|