@fluixi/reactive 1.0.0-alpha.100
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/LICENSE +21 -0
- package/README.md +280 -0
- package/dist/cdn/reactive.cjs +2803 -0
- package/dist/cdn/reactive.d.ts +11 -0
- package/dist/cdn/reactive.d.ts.map +1 -0
- package/dist/cdn/reactive.global.js +2 -0
- package/dist/cdn/reactive.js +10 -0
- package/dist/cdn/reactive.mjs +2787 -0
- package/dist/cdn/signal.cjs +299 -0
- package/dist/cdn/signal.d.ts +19 -0
- package/dist/cdn/signal.d.ts.map +1 -0
- package/dist/cdn/signal.global.js +2 -0
- package/dist/cdn/signal.js +18 -0
- package/dist/cdn/signal.mjs +276 -0
- package/dist/index.cjs +2803 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +2787 -0
- package/dist/lib/index.cjs +2804 -0
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +11 -0
- package/dist/lib/index.mjs +2789 -0
- package/dist/lib/observable/index.cjs +386 -0
- package/dist/lib/observable/index.d.ts +4 -0
- package/dist/lib/observable/index.d.ts.map +1 -0
- package/dist/lib/observable/index.js +3 -0
- package/dist/lib/observable/index.mjs +364 -0
- package/dist/lib/observable/observable.cjs +384 -0
- package/dist/lib/observable/observable.d.ts +246 -0
- package/dist/lib/observable/observable.d.ts.map +1 -0
- package/dist/lib/observable/observable.js +268 -0
- package/dist/lib/observable/observable.mjs +361 -0
- package/dist/lib/observable/subscription.cjs +180 -0
- package/dist/lib/observable/subscription.d.ts +58 -0
- package/dist/lib/observable/subscription.d.ts.map +1 -0
- package/dist/lib/observable/subscription.js +177 -0
- package/dist/lib/observable/subscription.mjs +157 -0
- package/dist/lib/observable/types.cjs +18 -0
- package/dist/lib/observable/types.d.ts +44 -0
- package/dist/lib/observable/types.d.ts.map +1 -0
- package/dist/lib/observable/types.js +1 -0
- package/dist/lib/observable/types.mjs +0 -0
- package/dist/lib/predicates.cjs +44 -0
- package/dist/lib/predicates.d.ts +37 -0
- package/dist/lib/predicates.d.ts.map +1 -0
- package/dist/lib/predicates.js +62 -0
- package/dist/lib/predicates.mjs +23 -0
- package/dist/lib/signal/api.cjs +592 -0
- package/dist/lib/signal/api.d.ts +227 -0
- package/dist/lib/signal/api.d.ts.map +1 -0
- package/dist/lib/signal/api.js +228 -0
- package/dist/lib/signal/api.mjs +569 -0
- package/dist/lib/signal/cached-resource.cjs +726 -0
- package/dist/lib/signal/cached-resource.d.ts +86 -0
- package/dist/lib/signal/cached-resource.d.ts.map +1 -0
- package/dist/lib/signal/cached-resource.js +250 -0
- package/dist/lib/signal/cached-resource.mjs +704 -0
- package/dist/lib/signal/graph/children.cjs +326 -0
- package/dist/lib/signal/graph/children.d.ts +2 -0
- package/dist/lib/signal/graph/children.d.ts.map +1 -0
- package/dist/lib/signal/graph/children.js +30 -0
- package/dist/lib/signal/graph/children.mjs +303 -0
- package/dist/lib/signal/graph/compat.cjs +590 -0
- package/dist/lib/signal/graph/compat.d.ts +66 -0
- package/dist/lib/signal/graph/compat.d.ts.map +1 -0
- package/dist/lib/signal/graph/compat.js +197 -0
- package/dist/lib/signal/graph/compat.mjs +568 -0
- package/dist/lib/signal/graph/core.cjs +295 -0
- package/dist/lib/signal/graph/core.d.ts +99 -0
- package/dist/lib/signal/graph/core.d.ts.map +1 -0
- package/dist/lib/signal/graph/core.js +268 -0
- package/dist/lib/signal/graph/core.mjs +273 -0
- package/dist/lib/signal/graph/debounced.cjs +680 -0
- package/dist/lib/signal/graph/debounced.d.ts +67 -0
- package/dist/lib/signal/graph/debounced.d.ts.map +1 -0
- package/dist/lib/signal/graph/debounced.js +92 -0
- package/dist/lib/signal/graph/debounced.mjs +657 -0
- package/dist/lib/signal/graph/derived.cjs +508 -0
- package/dist/lib/signal/graph/derived.d.ts +14 -0
- package/dist/lib/signal/graph/derived.d.ts.map +1 -0
- package/dist/lib/signal/graph/derived.js +65 -0
- package/dist/lib/signal/graph/derived.mjs +485 -0
- package/dist/lib/signal/graph/detect.cjs +66 -0
- package/dist/lib/signal/graph/detect.d.ts +17 -0
- package/dist/lib/signal/graph/detect.d.ts.map +1 -0
- package/dist/lib/signal/graph/detect.js +39 -0
- package/dist/lib/signal/graph/detect.mjs +45 -0
- package/dist/lib/signal/graph/error-boundary.cjs +313 -0
- package/dist/lib/signal/graph/error-boundary.d.ts +16 -0
- package/dist/lib/signal/graph/error-boundary.d.ts.map +1 -0
- package/dist/lib/signal/graph/error-boundary.js +34 -0
- package/dist/lib/signal/graph/error-boundary.mjs +290 -0
- package/dist/lib/signal/graph/index.cjs +305 -0
- package/dist/lib/signal/graph/index.d.ts +22 -0
- package/dist/lib/signal/graph/index.d.ts.map +1 -0
- package/dist/lib/signal/graph/index.js +21 -0
- package/dist/lib/signal/graph/index.mjs +283 -0
- package/dist/lib/signal/graph/observe.cjs +198 -0
- package/dist/lib/signal/graph/observe.d.ts +185 -0
- package/dist/lib/signal/graph/observe.d.ts.map +1 -0
- package/dist/lib/signal/graph/observe.js +198 -0
- package/dist/lib/signal/graph/observe.mjs +175 -0
- package/dist/lib/signal/graph/resource.cjs +632 -0
- package/dist/lib/signal/graph/resource.d.ts +39 -0
- package/dist/lib/signal/graph/resource.d.ts.map +1 -0
- package/dist/lib/signal/graph/resource.js +165 -0
- package/dist/lib/signal/graph/resource.mjs +609 -0
- package/dist/lib/signal/graph/runtime.cjs +522 -0
- package/dist/lib/signal/graph/runtime.d.ts +80 -0
- package/dist/lib/signal/graph/runtime.d.ts.map +1 -0
- package/dist/lib/signal/graph/runtime.js +358 -0
- package/dist/lib/signal/graph/runtime.mjs +499 -0
- package/dist/lib/signal/graph/signal-next.cjs +1249 -0
- package/dist/lib/signal/graph/signal-next.d.ts +38 -0
- package/dist/lib/signal/graph/signal-next.d.ts.map +1 -0
- package/dist/lib/signal/graph/signal-next.js +44 -0
- package/dist/lib/signal/graph/signal-next.mjs +1227 -0
- package/dist/lib/signal/graph/ssr-hooks.cjs +89 -0
- package/dist/lib/signal/graph/ssr-hooks.d.ts +51 -0
- package/dist/lib/signal/graph/ssr-hooks.d.ts.map +1 -0
- package/dist/lib/signal/graph/ssr-hooks.js +80 -0
- package/dist/lib/signal/graph/ssr-hooks.mjs +68 -0
- package/dist/lib/signal/graph/state.cjs +72 -0
- package/dist/lib/signal/graph/state.d.ts +76 -0
- package/dist/lib/signal/graph/state.d.ts.map +1 -0
- package/dist/lib/signal/graph/state.js +99 -0
- package/dist/lib/signal/graph/state.mjs +49 -0
- package/dist/lib/signal/graph/suspense.cjs +313 -0
- package/dist/lib/signal/graph/suspense.d.ts +13 -0
- package/dist/lib/signal/graph/suspense.d.ts.map +1 -0
- package/dist/lib/signal/graph/suspense.js +36 -0
- package/dist/lib/signal/graph/suspense.mjs +290 -0
- package/dist/lib/signal/index-legacy.cjs +2582 -0
- package/dist/lib/signal/index-legacy.d.ts +12 -0
- package/dist/lib/signal/index-legacy.d.ts.map +1 -0
- package/dist/lib/signal/index-legacy.js +11 -0
- package/dist/lib/signal/index-legacy.mjs +2560 -0
- package/dist/lib/signal/index.cjs +1350 -0
- package/dist/lib/signal/index.d.ts +19 -0
- package/dist/lib/signal/index.d.ts.map +1 -0
- package/dist/lib/signal/index.js +19 -0
- package/dist/lib/signal/index.mjs +1328 -0
- package/dist/lib/signal/list.cjs +561 -0
- package/dist/lib/signal/list.d.ts +39 -0
- package/dist/lib/signal/list.d.ts.map +1 -0
- package/dist/lib/signal/list.js +172 -0
- package/dist/lib/signal/list.mjs +538 -0
- package/dist/lib/signal/resource-api.cjs +638 -0
- package/dist/lib/signal/resource-api.d.ts +19 -0
- package/dist/lib/signal/resource-api.d.ts.map +1 -0
- package/dist/lib/signal/resource-api.js +27 -0
- package/dist/lib/signal/resource-api.mjs +615 -0
- package/dist/lib/signal/signal.cjs +2035 -0
- package/dist/lib/signal/signal.d.ts +303 -0
- package/dist/lib/signal/signal.d.ts.map +1 -0
- package/dist/lib/signal/signal.js +1233 -0
- package/dist/lib/signal/signal.mjs +2012 -0
- package/dist/lib/signal/types.cjs +38 -0
- package/dist/lib/signal/types.d.ts +133 -0
- package/dist/lib/signal/types.d.ts.map +1 -0
- package/dist/lib/signal/types.js +38 -0
- package/dist/lib/signal/types.mjs +17 -0
- package/dist/lib/signal/utilities.cjs +1024 -0
- package/dist/lib/signal/utilities.d.ts +247 -0
- package/dist/lib/signal/utilities.d.ts.map +1 -0
- package/dist/lib/signal/utilities.js +346 -0
- package/dist/lib/signal/utilities.mjs +1002 -0
- package/dist/lib/store/api.cjs +662 -0
- package/dist/lib/store/api.d.ts +40 -0
- package/dist/lib/store/api.d.ts.map +1 -0
- package/dist/lib/store/api.js +63 -0
- package/dist/lib/store/api.mjs +646 -0
- package/dist/lib/store/index.cjs +741 -0
- package/dist/lib/store/index.d.ts +16 -0
- package/dist/lib/store/index.d.ts.map +1 -0
- package/dist/lib/store/index.js +13 -0
- package/dist/lib/store/index.mjs +726 -0
- package/dist/lib/store/setter.types.cjs +18 -0
- package/dist/lib/store/setter.types.d.ts +131 -0
- package/dist/lib/store/setter.types.d.ts.map +1 -0
- package/dist/lib/store/setter.types.js +65 -0
- package/dist/lib/store/setter.types.mjs +0 -0
- package/dist/lib/store/store.cjs +708 -0
- package/dist/lib/store/store.d.ts +142 -0
- package/dist/lib/store/store.d.ts.map +1 -0
- package/dist/lib/store/store.js +812 -0
- package/dist/lib/store/store.mjs +694 -0
- package/dist/lib/store/types.cjs +18 -0
- package/dist/lib/store/types.d.ts +57 -0
- package/dist/lib/store/types.d.ts.map +1 -0
- package/dist/lib/store/types.js +1 -0
- package/dist/lib/store/types.mjs +0 -0
- package/dist/lib/store/utils.cjs +43 -0
- package/dist/lib/store/utils.d.ts +13 -0
- package/dist/lib/store/utils.d.ts.map +1 -0
- package/dist/lib/store/utils.js +29 -0
- package/dist/lib/store/utils.mjs +22 -0
- package/dist/lib/subject.cjs +450 -0
- package/dist/lib/subject.d.ts +14 -0
- package/dist/lib/subject.d.ts.map +1 -0
- package/dist/lib/subject.js +81 -0
- package/dist/lib/subject.mjs +427 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/version.generated.cjs +27 -0
- package/dist/version.generated.d.ts +3 -0
- package/dist/version.generated.d.ts.map +1 -0
- package/dist/version.generated.js +3 -0
- package/dist/version.generated.mjs +7 -0
- package/package.json +115 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ibrahima Touré
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/fluixi/assets/main/logos/128x128.png" alt="Fluixi" width="120" height="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @fluixi/reactive
|
|
6
|
+
|
|
7
|
+
**Fine-grained signals, memos, effects and stores on a TC39 Signals core — standalone, no framework required.**
|
|
8
|
+
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+

|
|
11
|
+
[](https://www.npmjs.com/package/@fluixi/reactive)
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
A fine-grained reactive system built on the **TC39 Signals** graph: glitch-free, lazy,
|
|
17
|
+
push-pull. It has no DOM dependency and no framework dependency, so it works in the
|
|
18
|
+
browser, on the server, or on its own.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
### Core
|
|
23
|
+
- **Signals, memos, effects** — fine-grained state, derived values, automatic tracking
|
|
24
|
+
- **Stores** — nested reactive objects where reading a property *is* the read
|
|
25
|
+
- **Resources** — async values that integrate with suspense
|
|
26
|
+
- **Batching** — `batch()` collects writes so dependents run once
|
|
27
|
+
|
|
28
|
+
### Beyond the basics
|
|
29
|
+
- **Transitions** — `startTransition` defers non-critical effects so the view stays interactive
|
|
30
|
+
- **Suspense and error boundaries** — as plain values, so any view layer can wrap them
|
|
31
|
+
- **Context** — provide a value for the duration of a call, without prop drilling
|
|
32
|
+
- **Owners** — explicit ownership and disposal, so a graph tears down completely
|
|
33
|
+
- **Debounced primitives** — signal, resource and function variants
|
|
34
|
+
- **Observable interop** — `Observable`/`Subject` for RxJS-shaped code
|
|
35
|
+
- **TypeScript** — full type safety and inference
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @fluixi/reactive
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### Basic Usage
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { signal, memo, effect, batch } from '@fluixi/reactive';
|
|
49
|
+
|
|
50
|
+
// Reactive state — call it to read, .set() to write
|
|
51
|
+
const count = signal(0);
|
|
52
|
+
|
|
53
|
+
// Derived state (cached)
|
|
54
|
+
const doubled = memo(() => count() * 2);
|
|
55
|
+
|
|
56
|
+
// Side effects (auto-tracking)
|
|
57
|
+
effect(() => {
|
|
58
|
+
console.log(`Count: ${count()}, Doubled: ${doubled()}`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
count.set(1); // Effect runs: "Count: 1, Doubled: 2"
|
|
62
|
+
|
|
63
|
+
// Batch multiple updates
|
|
64
|
+
batch(() => {
|
|
65
|
+
count.set(2);
|
|
66
|
+
count.set(3);
|
|
67
|
+
}); // Effect runs once: "Count: 3, Doubled: 6"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### The tuple form
|
|
71
|
+
|
|
72
|
+
`createSignal` returns the same signal as a `[read, write]` pair, and `createMemo` /
|
|
73
|
+
`createEffect` are `memo` / `effect` under their original names:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { createSignal, createMemo, createEffect } from '@fluixi/reactive';
|
|
77
|
+
|
|
78
|
+
const [count, setCount] = createSignal(0);
|
|
79
|
+
const doubled = createMemo(() => count() * 2);
|
|
80
|
+
createEffect(() => console.log(count(), doubled()));
|
|
81
|
+
setCount(1);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`signal` **is** the accessor `createSignal` returns with the setter attached, so a read is
|
|
85
|
+
the same call either way. They track each other and mix freely in one module — pick
|
|
86
|
+
whichever reads better, and keep the tuple when you want the halves apart.
|
|
87
|
+
|
|
88
|
+
### Stores and resources
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { store } from '@fluixi/reactive/store';
|
|
92
|
+
import { resource } from '@fluixi/reactive/signal';
|
|
93
|
+
|
|
94
|
+
const cart = store({ items: [], total: 0 });
|
|
95
|
+
cart.total; // reading a property IS the reactive read
|
|
96
|
+
cart.set('total', 42); // only readers of `total` re-run
|
|
97
|
+
|
|
98
|
+
const user = resource(() => fetch('/api/me').then((r) => r.json()));
|
|
99
|
+
user(); // the value — suspends under a Suspense boundary
|
|
100
|
+
user.loading; // boolean
|
|
101
|
+
user.refetch();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Transitions, context and boundaries
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import {
|
|
108
|
+
startTransition, useTransition,
|
|
109
|
+
createContext, useContext, provide,
|
|
110
|
+
createErrorBoundary, provideErrorBoundary,
|
|
111
|
+
} from '@fluixi/reactive';
|
|
112
|
+
|
|
113
|
+
// Non-blocking updates — `isPending` is a signal you can read while it runs
|
|
114
|
+
const [isPending, start] = useTransition();
|
|
115
|
+
start(() => setLargeDataSet(expensiveComputation()));
|
|
116
|
+
|
|
117
|
+
// Context: create it, provide a value for the duration of a call, consume inside
|
|
118
|
+
const ThemeContext = createContext({ mode: 'light' });
|
|
119
|
+
provide(ThemeContext, { mode: 'dark' }, () => {
|
|
120
|
+
useContext(ThemeContext); // { mode: 'dark' }
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Boundaries are values here, not components. A view layer wraps these into
|
|
124
|
+
// <ErrorBoundary> / <Suspense>; on their own they are plain functions.
|
|
125
|
+
const boundary = createErrorBoundary();
|
|
126
|
+
provideErrorBoundary(boundary, () => risky());
|
|
127
|
+
boundary.error(); // the thrown value, or undefined
|
|
128
|
+
boundary.reset();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Debouncing
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { createDebounce, createDebouncedSignal, createDebouncedResource } from '@fluixi/reactive';
|
|
135
|
+
|
|
136
|
+
const [term, setTerm, controls] = createDebouncedSignal('', 300);
|
|
137
|
+
term(); // settles 300ms after the last write
|
|
138
|
+
controls.immediate(); // the value as written — bind the input to this one
|
|
139
|
+
controls.pending(); // a write is waiting (not the same as a request in flight)
|
|
140
|
+
controls.flush(); // settle now
|
|
141
|
+
|
|
142
|
+
// Search-as-you-type as one primitive: the query debounces, the fetch follows it,
|
|
143
|
+
// and an empty query doesn't fetch at all.
|
|
144
|
+
const [results, search] = createDebouncedResource('', (q) => fetchResults(q), { delay: 300 });
|
|
145
|
+
search.setQuery('fluix');
|
|
146
|
+
results.loading;
|
|
147
|
+
|
|
148
|
+
// Or debounce any function
|
|
149
|
+
const save = createDebounce((draft) => persist(draft), 500);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Batching
|
|
153
|
+
|
|
154
|
+
A write marks its dependents dirty and schedules them; memos stay lazy and recompute on
|
|
155
|
+
read, so the graph is glitch-free — nothing observes a half-applied update.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
batch(() => {
|
|
159
|
+
a.set(1);
|
|
160
|
+
b.set(2);
|
|
161
|
+
c.set(3);
|
|
162
|
+
}); // dependents run once, after the batch closes
|
|
163
|
+
|
|
164
|
+
await flush(); // await the pending queue — mostly useful in tests
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`startTransition` is the other half: it runs `fn` synchronously and captures the non-pure
|
|
168
|
+
effects it would fire into a deferred queue, so an expensive update doesn't block the
|
|
169
|
+
current view. Pure computations still settle immediately.
|
|
170
|
+
|
|
171
|
+
## API Reference
|
|
172
|
+
|
|
173
|
+
### Core API
|
|
174
|
+
|
|
175
|
+
- `createSignal(initialValue, options?)` - Create reactive state
|
|
176
|
+
- `createEffect(fn, options?)` - Run side effects
|
|
177
|
+
- `createMemo(fn, initialValue?, options?)` - Create derived state
|
|
178
|
+
- `batch(fn)` - Batch multiple updates
|
|
179
|
+
- `untrack(fn)` - Read signals without tracking
|
|
180
|
+
- `createRoot(fn)` - Create disposal root
|
|
181
|
+
|
|
182
|
+
### Transitions
|
|
183
|
+
|
|
184
|
+
- `startTransition(fn)` - Mark updates as low priority
|
|
185
|
+
- `useTransition()` - Track transition state
|
|
186
|
+
|
|
187
|
+
### Boundaries
|
|
188
|
+
|
|
189
|
+
- `createSuspenseBoundary()` / `provideSuspense(boundary, fn)` - Async boundary as a value
|
|
190
|
+
- `createErrorBoundary()` / `provideErrorBoundary(boundary, fn)` - Error boundary as a value
|
|
191
|
+
- `trackResourcePromise(promise)` - Register a promise with the enclosing suspense
|
|
192
|
+
|
|
193
|
+
### Context
|
|
194
|
+
|
|
195
|
+
- `createContext(defaultValue)` - Create context
|
|
196
|
+
- `provide(context, value, fn)` - Supply a value for the duration of a call
|
|
197
|
+
- `useContext(context)` - Consume context
|
|
198
|
+
|
|
199
|
+
### Other effects
|
|
200
|
+
|
|
201
|
+
- `createRenderEffect(fn)` - Runs before paint, for DOM writes
|
|
202
|
+
- `createComputed(fn)` - Runs eagerly, unlike a memo
|
|
203
|
+
- `on(deps, fn, options?)` - Explicit dependencies
|
|
204
|
+
|
|
205
|
+
### Observation
|
|
206
|
+
|
|
207
|
+
- `observeReactiveNodes(observer)` - Watch nodes being created, recomputed and disposed. What @fluixi/devtools attaches to; unwatched, it costs one null check per node.
|
|
208
|
+
|
|
209
|
+
### Owner Management
|
|
210
|
+
|
|
211
|
+
- `getOwner()` - Get current reactive owner
|
|
212
|
+
- `runWithOwner(owner, fn)` - Run with specific owner
|
|
213
|
+
|
|
214
|
+
### Lists
|
|
215
|
+
|
|
216
|
+
- `mapArray(list, fn)` - Map keyed by identity; a row survives a reorder
|
|
217
|
+
- `indexArray(list, fn)` - Map keyed by index
|
|
218
|
+
- `keyArray(list, key, fn)` - Map keyed by an explicit key
|
|
219
|
+
|
|
220
|
+
### Utilities
|
|
221
|
+
|
|
222
|
+
- `createSelector(source, fn)` - Keyed selector; only the row that changed re-runs
|
|
223
|
+
- `createResource(fetcher, source?)` / `resource(...)` - Async resource
|
|
224
|
+
- `createStore(initial)` / `store(initial)` - Nested reactive object
|
|
225
|
+
- `createDeferred(source, options?)` - A value that trails behind under load
|
|
226
|
+
- `createDebounce(fn, ms)` / `createDebouncedSignal(initial, ms)` / `createDebouncedResource(...)`
|
|
227
|
+
- `flush()` - Await the pending effect queue
|
|
228
|
+
- `unwrap(store)` / `untrackStore(fn)` - Escape hatches out of a store proxy
|
|
229
|
+
- `onCleanup(fn)` / `disposeScope(owner)` - Teardown
|
|
230
|
+
|
|
231
|
+
## Documentation
|
|
232
|
+
|
|
233
|
+
- [Signals](https://docs.fluixi.com/v1/en/signals) — state, reading and writing
|
|
234
|
+
- [Derived values](https://docs.fluixi.com/v1/en/derived-values) — memos and selectors
|
|
235
|
+
- [Effects](https://docs.fluixi.com/v1/en/effects) — side effects and cleanup
|
|
236
|
+
- [Stores](https://docs.fluixi.com/v1/en/stores) — nested reactive objects
|
|
237
|
+
- [Async](https://docs.fluixi.com/v1/en/async) — resources, suspense, transitions
|
|
238
|
+
- [TC39 Signals](https://docs.fluixi.com/v1/en/tc39-signals) — the model underneath
|
|
239
|
+
|
|
240
|
+
## Compiler intrinsics
|
|
241
|
+
|
|
242
|
+
Inside an app compiled by `@fluixi/compiler`, these primitives have `$`-prefixed twins that
|
|
243
|
+
need no import — `$signal`, `$memo`, `$effect`, `$store`, `$resource`. They compile to the
|
|
244
|
+
calls above, so there is one reactive graph and no difference in behaviour. This package
|
|
245
|
+
stays importable exactly as documented here, which is what a library that never runs the
|
|
246
|
+
compiler needs.
|
|
247
|
+
|
|
248
|
+
## Performance tips
|
|
249
|
+
|
|
250
|
+
1. **Batch related updates** — `batch(() => { a.set(1); b.set(2); })` runs dependents once.
|
|
251
|
+
2. **Transitions for expensive updates** — `startTransition(() => list.set(compute()))`
|
|
252
|
+
keeps the current view interactive while the next one is prepared.
|
|
253
|
+
3. **Memo the expensive part** — a memo costs a node; it pays for itself when the
|
|
254
|
+
computation is heavier than the bookkeeping.
|
|
255
|
+
4. **Read narrowly** — reading a whole object subscribes you to every write to it. Reach
|
|
256
|
+
for the property you need, or derive it.
|
|
257
|
+
5. **Key your lists** — `mapArray` keeps a row's nodes across a reorder; `indexArray`
|
|
258
|
+
rebuilds them.
|
|
259
|
+
|
|
260
|
+
## Building
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
nx build reactive
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Running Tests
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
nx test reactive
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
MIT
|
|
275
|
+
|
|
276
|
+
## Credits
|
|
277
|
+
|
|
278
|
+
The graph implements the **TC39 Signals** model. The ergonomics owe a lot to prior art —
|
|
279
|
+
SolidJS for fine-grained primitives and scheduling, React for transitions and suspense as
|
|
280
|
+
concepts, Vue 3 for reactivity design. None of that was invented here.
|