@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.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -0
  3. package/dist/cdn/reactive.cjs +2803 -0
  4. package/dist/cdn/reactive.d.ts +11 -0
  5. package/dist/cdn/reactive.d.ts.map +1 -0
  6. package/dist/cdn/reactive.global.js +2 -0
  7. package/dist/cdn/reactive.js +10 -0
  8. package/dist/cdn/reactive.mjs +2787 -0
  9. package/dist/cdn/signal.cjs +299 -0
  10. package/dist/cdn/signal.d.ts +19 -0
  11. package/dist/cdn/signal.d.ts.map +1 -0
  12. package/dist/cdn/signal.global.js +2 -0
  13. package/dist/cdn/signal.js +18 -0
  14. package/dist/cdn/signal.mjs +276 -0
  15. package/dist/index.cjs +2803 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/index.mjs +2787 -0
  20. package/dist/lib/index.cjs +2804 -0
  21. package/dist/lib/index.d.ts +8 -0
  22. package/dist/lib/index.d.ts.map +1 -0
  23. package/dist/lib/index.js +11 -0
  24. package/dist/lib/index.mjs +2789 -0
  25. package/dist/lib/observable/index.cjs +386 -0
  26. package/dist/lib/observable/index.d.ts +4 -0
  27. package/dist/lib/observable/index.d.ts.map +1 -0
  28. package/dist/lib/observable/index.js +3 -0
  29. package/dist/lib/observable/index.mjs +364 -0
  30. package/dist/lib/observable/observable.cjs +384 -0
  31. package/dist/lib/observable/observable.d.ts +246 -0
  32. package/dist/lib/observable/observable.d.ts.map +1 -0
  33. package/dist/lib/observable/observable.js +268 -0
  34. package/dist/lib/observable/observable.mjs +361 -0
  35. package/dist/lib/observable/subscription.cjs +180 -0
  36. package/dist/lib/observable/subscription.d.ts +58 -0
  37. package/dist/lib/observable/subscription.d.ts.map +1 -0
  38. package/dist/lib/observable/subscription.js +177 -0
  39. package/dist/lib/observable/subscription.mjs +157 -0
  40. package/dist/lib/observable/types.cjs +18 -0
  41. package/dist/lib/observable/types.d.ts +44 -0
  42. package/dist/lib/observable/types.d.ts.map +1 -0
  43. package/dist/lib/observable/types.js +1 -0
  44. package/dist/lib/observable/types.mjs +0 -0
  45. package/dist/lib/predicates.cjs +44 -0
  46. package/dist/lib/predicates.d.ts +37 -0
  47. package/dist/lib/predicates.d.ts.map +1 -0
  48. package/dist/lib/predicates.js +62 -0
  49. package/dist/lib/predicates.mjs +23 -0
  50. package/dist/lib/signal/api.cjs +592 -0
  51. package/dist/lib/signal/api.d.ts +227 -0
  52. package/dist/lib/signal/api.d.ts.map +1 -0
  53. package/dist/lib/signal/api.js +228 -0
  54. package/dist/lib/signal/api.mjs +569 -0
  55. package/dist/lib/signal/cached-resource.cjs +726 -0
  56. package/dist/lib/signal/cached-resource.d.ts +86 -0
  57. package/dist/lib/signal/cached-resource.d.ts.map +1 -0
  58. package/dist/lib/signal/cached-resource.js +250 -0
  59. package/dist/lib/signal/cached-resource.mjs +704 -0
  60. package/dist/lib/signal/graph/children.cjs +326 -0
  61. package/dist/lib/signal/graph/children.d.ts +2 -0
  62. package/dist/lib/signal/graph/children.d.ts.map +1 -0
  63. package/dist/lib/signal/graph/children.js +30 -0
  64. package/dist/lib/signal/graph/children.mjs +303 -0
  65. package/dist/lib/signal/graph/compat.cjs +590 -0
  66. package/dist/lib/signal/graph/compat.d.ts +66 -0
  67. package/dist/lib/signal/graph/compat.d.ts.map +1 -0
  68. package/dist/lib/signal/graph/compat.js +197 -0
  69. package/dist/lib/signal/graph/compat.mjs +568 -0
  70. package/dist/lib/signal/graph/core.cjs +295 -0
  71. package/dist/lib/signal/graph/core.d.ts +99 -0
  72. package/dist/lib/signal/graph/core.d.ts.map +1 -0
  73. package/dist/lib/signal/graph/core.js +268 -0
  74. package/dist/lib/signal/graph/core.mjs +273 -0
  75. package/dist/lib/signal/graph/debounced.cjs +680 -0
  76. package/dist/lib/signal/graph/debounced.d.ts +67 -0
  77. package/dist/lib/signal/graph/debounced.d.ts.map +1 -0
  78. package/dist/lib/signal/graph/debounced.js +92 -0
  79. package/dist/lib/signal/graph/debounced.mjs +657 -0
  80. package/dist/lib/signal/graph/derived.cjs +508 -0
  81. package/dist/lib/signal/graph/derived.d.ts +14 -0
  82. package/dist/lib/signal/graph/derived.d.ts.map +1 -0
  83. package/dist/lib/signal/graph/derived.js +65 -0
  84. package/dist/lib/signal/graph/derived.mjs +485 -0
  85. package/dist/lib/signal/graph/detect.cjs +66 -0
  86. package/dist/lib/signal/graph/detect.d.ts +17 -0
  87. package/dist/lib/signal/graph/detect.d.ts.map +1 -0
  88. package/dist/lib/signal/graph/detect.js +39 -0
  89. package/dist/lib/signal/graph/detect.mjs +45 -0
  90. package/dist/lib/signal/graph/error-boundary.cjs +313 -0
  91. package/dist/lib/signal/graph/error-boundary.d.ts +16 -0
  92. package/dist/lib/signal/graph/error-boundary.d.ts.map +1 -0
  93. package/dist/lib/signal/graph/error-boundary.js +34 -0
  94. package/dist/lib/signal/graph/error-boundary.mjs +290 -0
  95. package/dist/lib/signal/graph/index.cjs +305 -0
  96. package/dist/lib/signal/graph/index.d.ts +22 -0
  97. package/dist/lib/signal/graph/index.d.ts.map +1 -0
  98. package/dist/lib/signal/graph/index.js +21 -0
  99. package/dist/lib/signal/graph/index.mjs +283 -0
  100. package/dist/lib/signal/graph/observe.cjs +198 -0
  101. package/dist/lib/signal/graph/observe.d.ts +185 -0
  102. package/dist/lib/signal/graph/observe.d.ts.map +1 -0
  103. package/dist/lib/signal/graph/observe.js +198 -0
  104. package/dist/lib/signal/graph/observe.mjs +175 -0
  105. package/dist/lib/signal/graph/resource.cjs +632 -0
  106. package/dist/lib/signal/graph/resource.d.ts +39 -0
  107. package/dist/lib/signal/graph/resource.d.ts.map +1 -0
  108. package/dist/lib/signal/graph/resource.js +165 -0
  109. package/dist/lib/signal/graph/resource.mjs +609 -0
  110. package/dist/lib/signal/graph/runtime.cjs +522 -0
  111. package/dist/lib/signal/graph/runtime.d.ts +80 -0
  112. package/dist/lib/signal/graph/runtime.d.ts.map +1 -0
  113. package/dist/lib/signal/graph/runtime.js +358 -0
  114. package/dist/lib/signal/graph/runtime.mjs +499 -0
  115. package/dist/lib/signal/graph/signal-next.cjs +1249 -0
  116. package/dist/lib/signal/graph/signal-next.d.ts +38 -0
  117. package/dist/lib/signal/graph/signal-next.d.ts.map +1 -0
  118. package/dist/lib/signal/graph/signal-next.js +44 -0
  119. package/dist/lib/signal/graph/signal-next.mjs +1227 -0
  120. package/dist/lib/signal/graph/ssr-hooks.cjs +89 -0
  121. package/dist/lib/signal/graph/ssr-hooks.d.ts +51 -0
  122. package/dist/lib/signal/graph/ssr-hooks.d.ts.map +1 -0
  123. package/dist/lib/signal/graph/ssr-hooks.js +80 -0
  124. package/dist/lib/signal/graph/ssr-hooks.mjs +68 -0
  125. package/dist/lib/signal/graph/state.cjs +72 -0
  126. package/dist/lib/signal/graph/state.d.ts +76 -0
  127. package/dist/lib/signal/graph/state.d.ts.map +1 -0
  128. package/dist/lib/signal/graph/state.js +99 -0
  129. package/dist/lib/signal/graph/state.mjs +49 -0
  130. package/dist/lib/signal/graph/suspense.cjs +313 -0
  131. package/dist/lib/signal/graph/suspense.d.ts +13 -0
  132. package/dist/lib/signal/graph/suspense.d.ts.map +1 -0
  133. package/dist/lib/signal/graph/suspense.js +36 -0
  134. package/dist/lib/signal/graph/suspense.mjs +290 -0
  135. package/dist/lib/signal/index-legacy.cjs +2582 -0
  136. package/dist/lib/signal/index-legacy.d.ts +12 -0
  137. package/dist/lib/signal/index-legacy.d.ts.map +1 -0
  138. package/dist/lib/signal/index-legacy.js +11 -0
  139. package/dist/lib/signal/index-legacy.mjs +2560 -0
  140. package/dist/lib/signal/index.cjs +1350 -0
  141. package/dist/lib/signal/index.d.ts +19 -0
  142. package/dist/lib/signal/index.d.ts.map +1 -0
  143. package/dist/lib/signal/index.js +19 -0
  144. package/dist/lib/signal/index.mjs +1328 -0
  145. package/dist/lib/signal/list.cjs +561 -0
  146. package/dist/lib/signal/list.d.ts +39 -0
  147. package/dist/lib/signal/list.d.ts.map +1 -0
  148. package/dist/lib/signal/list.js +172 -0
  149. package/dist/lib/signal/list.mjs +538 -0
  150. package/dist/lib/signal/resource-api.cjs +638 -0
  151. package/dist/lib/signal/resource-api.d.ts +19 -0
  152. package/dist/lib/signal/resource-api.d.ts.map +1 -0
  153. package/dist/lib/signal/resource-api.js +27 -0
  154. package/dist/lib/signal/resource-api.mjs +615 -0
  155. package/dist/lib/signal/signal.cjs +2035 -0
  156. package/dist/lib/signal/signal.d.ts +303 -0
  157. package/dist/lib/signal/signal.d.ts.map +1 -0
  158. package/dist/lib/signal/signal.js +1233 -0
  159. package/dist/lib/signal/signal.mjs +2012 -0
  160. package/dist/lib/signal/types.cjs +38 -0
  161. package/dist/lib/signal/types.d.ts +133 -0
  162. package/dist/lib/signal/types.d.ts.map +1 -0
  163. package/dist/lib/signal/types.js +38 -0
  164. package/dist/lib/signal/types.mjs +17 -0
  165. package/dist/lib/signal/utilities.cjs +1024 -0
  166. package/dist/lib/signal/utilities.d.ts +247 -0
  167. package/dist/lib/signal/utilities.d.ts.map +1 -0
  168. package/dist/lib/signal/utilities.js +346 -0
  169. package/dist/lib/signal/utilities.mjs +1002 -0
  170. package/dist/lib/store/api.cjs +662 -0
  171. package/dist/lib/store/api.d.ts +40 -0
  172. package/dist/lib/store/api.d.ts.map +1 -0
  173. package/dist/lib/store/api.js +63 -0
  174. package/dist/lib/store/api.mjs +646 -0
  175. package/dist/lib/store/index.cjs +741 -0
  176. package/dist/lib/store/index.d.ts +16 -0
  177. package/dist/lib/store/index.d.ts.map +1 -0
  178. package/dist/lib/store/index.js +13 -0
  179. package/dist/lib/store/index.mjs +726 -0
  180. package/dist/lib/store/setter.types.cjs +18 -0
  181. package/dist/lib/store/setter.types.d.ts +131 -0
  182. package/dist/lib/store/setter.types.d.ts.map +1 -0
  183. package/dist/lib/store/setter.types.js +65 -0
  184. package/dist/lib/store/setter.types.mjs +0 -0
  185. package/dist/lib/store/store.cjs +708 -0
  186. package/dist/lib/store/store.d.ts +142 -0
  187. package/dist/lib/store/store.d.ts.map +1 -0
  188. package/dist/lib/store/store.js +812 -0
  189. package/dist/lib/store/store.mjs +694 -0
  190. package/dist/lib/store/types.cjs +18 -0
  191. package/dist/lib/store/types.d.ts +57 -0
  192. package/dist/lib/store/types.d.ts.map +1 -0
  193. package/dist/lib/store/types.js +1 -0
  194. package/dist/lib/store/types.mjs +0 -0
  195. package/dist/lib/store/utils.cjs +43 -0
  196. package/dist/lib/store/utils.d.ts +13 -0
  197. package/dist/lib/store/utils.d.ts.map +1 -0
  198. package/dist/lib/store/utils.js +29 -0
  199. package/dist/lib/store/utils.mjs +22 -0
  200. package/dist/lib/subject.cjs +450 -0
  201. package/dist/lib/subject.d.ts +14 -0
  202. package/dist/lib/subject.d.ts.map +1 -0
  203. package/dist/lib/subject.js +81 -0
  204. package/dist/lib/subject.mjs +427 -0
  205. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  206. package/dist/version.generated.cjs +27 -0
  207. package/dist/version.generated.d.ts +3 -0
  208. package/dist/version.generated.d.ts.map +1 -0
  209. package/dist/version.generated.js +3 -0
  210. package/dist/version.generated.mjs +7 -0
  211. 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: MIT](https://img.shields.io/badge/License-MIT-22c55e.svg)](./LICENSE)
10
+ ![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript&logoColor=white)
11
+ [![npm](https://img.shields.io/npm/v/@fluixi/reactive?logo=npm)](https://www.npmjs.com/package/@fluixi/reactive)
12
+ ![Inspired by SolidJS](https://img.shields.io/badge/inspired%20by-SolidJS-2c4f7c)
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.