@volynets/reflex 0.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.
- package/LICENSE +21 -0
- package/README.md +365 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/unstable/index.cjs +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/unstable/index.js +1 -0
- package/dist/globals.d.ts +421 -0
- package/dist/unstable/index.d.ts +34 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present Andrii Volynets
|
|
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,365 @@
|
|
|
1
|
+
# `@volynetstyle/reflex`
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@volynetstyle/reflex)
|
|
4
|
+
[](https://www.npmjs.com/package/@volynetstyle/reflex)
|
|
5
|
+
[](https://github.com/volynetstyle/Reflex/blob/main/packages/reflex/LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://github.com/volynetstyle/Reflex)
|
|
8
|
+
[](https://github.com/volynetstyle/Reflex/actions/workflows/ci.yml)
|
|
9
|
+
[](https://vitest.dev/)
|
|
10
|
+
[](https://github.com/volynetstyle/Reflex/blob/main/packages/reflex/package.json)
|
|
11
|
+
[](https://github.com/volynetstyle/Reflex/tree/main/packages/reflex)
|
|
12
|
+
|
|
13
|
+
Small signal-style reactivity on top of the Reflex runtime.
|
|
14
|
+
|
|
15
|
+
`@volynetstyle/reflex` is the product-facing API for building reactive state, derived values, effects, and event-driven state without dropping down to the lower-level runtime primitives.
|
|
16
|
+
|
|
17
|
+
It gives you:
|
|
18
|
+
|
|
19
|
+
- a compact signal-style API
|
|
20
|
+
- runtime-backed execution with explicit effect flushing
|
|
21
|
+
- event sources plus composition helpers like `map()`, `filter()`, `merge()`, `scan()`, and `hold()`
|
|
22
|
+
- predictable semantics for lazy derived values and scheduled effects
|
|
23
|
+
|
|
24
|
+
Under the hood it is built on:
|
|
25
|
+
|
|
26
|
+
- [`@reflex/runtime`](https://github.com/volynetstyle/Reflex/tree/main/packages/%40reflex/runtime) for reactive execution
|
|
27
|
+
- [`@reflex/core`](https://github.com/volynetstyle/Reflex/tree/main/packages/%40reflex/core) for lower-level infrastructure
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @volynetstyle/reflex
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { computed, createRuntime, effect, signal } from "@volynetstyle/reflex";
|
|
39
|
+
|
|
40
|
+
const rt = createRuntime();
|
|
41
|
+
|
|
42
|
+
const [count, setCount] = signal(1);
|
|
43
|
+
const doubled = computed(() => count() * 2);
|
|
44
|
+
|
|
45
|
+
effect(() => {
|
|
46
|
+
console.log("doubled =", doubled());
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
setCount(2);
|
|
50
|
+
rt.flush();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
What is happening here:
|
|
54
|
+
|
|
55
|
+
- `createRuntime()` configures the active Reflex runtime and returns runtime controls
|
|
56
|
+
- `signal()`, `computed()`, and `effect()` use that active runtime
|
|
57
|
+
- `effect()` runs once immediately when created
|
|
58
|
+
- later effect re-runs are scheduled, so with the default runtime you call `rt.flush()` to run them
|
|
59
|
+
- `computed()` stays lazy and does not need `flush()` just to produce the latest value on read
|
|
60
|
+
|
|
61
|
+
## Mental Model
|
|
62
|
+
|
|
63
|
+
Think of the package as one connected model:
|
|
64
|
+
|
|
65
|
+
1. Call `createRuntime()` once during setup.
|
|
66
|
+
2. Build state with `signal()`, `computed()`, `memo()`, and `effect()`.
|
|
67
|
+
3. Create event sources with `rt.event()`.
|
|
68
|
+
4. Compose event sources with `map()`, `filter()`, `merge()`, and use `scan()` / `hold()` to turn events into readable state.
|
|
69
|
+
5. Call `rt.flush()` when you want scheduled effects to run in the default `"flush"` mode.
|
|
70
|
+
|
|
71
|
+
The top-level primitives are not methods on `rt`, but they are still runtime-backed. `createRuntime()` is not only for `event()` and `flush()`: it configures the runtime that the public primitives run against.
|
|
72
|
+
|
|
73
|
+
## Design Goals
|
|
74
|
+
|
|
75
|
+
- Keep the public API small and easy to teach.
|
|
76
|
+
- Preserve explicit runtime control instead of hiding scheduling.
|
|
77
|
+
- Make derived state cheap to read through lazy cached computeds.
|
|
78
|
+
- Support both state-style and event-style reactive flows.
|
|
79
|
+
- Expose low-level escape hatches only when needed, without forcing them into normal usage.
|
|
80
|
+
|
|
81
|
+
## Core Primitives
|
|
82
|
+
|
|
83
|
+
### Signals and derived values
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { computed, createRuntime, memo, signal } from "@volynetstyle/reflex";
|
|
87
|
+
|
|
88
|
+
createRuntime();
|
|
89
|
+
|
|
90
|
+
const [price, setPrice] = signal(100);
|
|
91
|
+
const [tax, setTax] = signal(20);
|
|
92
|
+
|
|
93
|
+
const total = computed(() => price() + tax());
|
|
94
|
+
const warmed = memo(() => total() * 2);
|
|
95
|
+
|
|
96
|
+
console.log(total()); // 120
|
|
97
|
+
console.log(warmed()); // 240
|
|
98
|
+
|
|
99
|
+
setPrice(120);
|
|
100
|
+
setTax(25);
|
|
101
|
+
|
|
102
|
+
console.log(total()); // 145
|
|
103
|
+
console.log(warmed()); // 290
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Events and accumulated state
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { computed, createRuntime, effect, hold, scan } from "@volynetstyle/reflex";
|
|
110
|
+
|
|
111
|
+
const rt = createRuntime();
|
|
112
|
+
const updates = rt.event<number>();
|
|
113
|
+
|
|
114
|
+
const [total, disposeTotal] = scan(updates, 0, (acc, value) => acc + value);
|
|
115
|
+
const [latest, disposeLatest] = hold(updates, 0);
|
|
116
|
+
const summary = computed(() => `${latest()} / ${total()}`);
|
|
117
|
+
|
|
118
|
+
effect(() => {
|
|
119
|
+
console.log(summary());
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
updates.emit(1);
|
|
123
|
+
updates.emit(2);
|
|
124
|
+
rt.flush();
|
|
125
|
+
|
|
126
|
+
disposeTotal();
|
|
127
|
+
disposeLatest();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`scan()` and `hold()` return tuples on purpose:
|
|
131
|
+
|
|
132
|
+
- the first item is the accessor you read from
|
|
133
|
+
- the second item is a disposer that unsubscribes from the event source and releases the internal node
|
|
134
|
+
|
|
135
|
+
### Event composition
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import {
|
|
139
|
+
createRuntime,
|
|
140
|
+
filter,
|
|
141
|
+
map,
|
|
142
|
+
merge,
|
|
143
|
+
subscribeOnce,
|
|
144
|
+
} from "@volynetstyle/reflex";
|
|
145
|
+
|
|
146
|
+
const rt = createRuntime();
|
|
147
|
+
const clicks = rt.event<number>();
|
|
148
|
+
const submits = rt.event<string>();
|
|
149
|
+
|
|
150
|
+
const importantClicks = filter(clicks, (value) => value > 0);
|
|
151
|
+
const labels = merge(
|
|
152
|
+
map(importantClicks, (value) => `click:${value}`),
|
|
153
|
+
map(submits, (value) => `submit:${value}`),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
subscribeOnce(labels, (value) => {
|
|
157
|
+
console.log("first label =", value);
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Guarantees
|
|
162
|
+
|
|
163
|
+
- `computed(fn)` is lazy. It does not run until the first read.
|
|
164
|
+
- `computed(fn)` is cached. Repeated clean reads reuse the last value.
|
|
165
|
+
- `memo(fn)` is `computed(fn)` plus one eager warm-up read.
|
|
166
|
+
- `effect(fn)` runs once immediately on creation.
|
|
167
|
+
- If an effect returns cleanup, that cleanup runs before the next effect run and on dispose.
|
|
168
|
+
- With the default runtime, invalidated effects run on `rt.flush()`.
|
|
169
|
+
- With `createRuntime({ effectStrategy: "eager" })`, invalidated effects flush automatically.
|
|
170
|
+
- Pure signal and computed reads do not require `flush()`.
|
|
171
|
+
- Same-value signal writes do not force recomputation.
|
|
172
|
+
- Derived events created with `map()`, `filter()`, and `merge()` are lazy and subscribe upstream only while observed.
|
|
173
|
+
- `scan()` and `hold()` update only from event deliveries.
|
|
174
|
+
- Nested event emits are delivered after the current delivery finishes, preserving order.
|
|
175
|
+
|
|
176
|
+
## Runtime
|
|
177
|
+
|
|
178
|
+
`createRuntime()` returns an object with the runtime-facing pieces of the model:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const rt = createRuntime({
|
|
182
|
+
effectStrategy: "flush", // or "eager"
|
|
183
|
+
hooks: {
|
|
184
|
+
onEffectInvalidated(node) {
|
|
185
|
+
// low-level integration hook
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Options:
|
|
192
|
+
|
|
193
|
+
- `effectStrategy: "flush" | "eager"` controls whether invalidated effects wait for `rt.flush()` or run automatically
|
|
194
|
+
- `hooks.onEffectInvalidated(node)` is a low-level hook for integrations that want to observe effect invalidation
|
|
195
|
+
|
|
196
|
+
Returned API:
|
|
197
|
+
|
|
198
|
+
- `rt.event<T>()` creates an event source with `emit(value)` and `subscribe(fn)`
|
|
199
|
+
- `rt.flush()` runs queued effects
|
|
200
|
+
- `rt.ctx` exposes the underlying runtime context for low-level integration, debugging, or tests
|
|
201
|
+
|
|
202
|
+
Important notes:
|
|
203
|
+
|
|
204
|
+
- For normal app code, create one runtime near startup and keep using the top-level primitives.
|
|
205
|
+
- `ctx` is low-level. Most users should not need it.
|
|
206
|
+
- Creating a new runtime resets the shared runtime state. It is best treated as app setup or test isolation, not as something you create repeatedly inside feature code.
|
|
207
|
+
|
|
208
|
+
## API Reference
|
|
209
|
+
|
|
210
|
+
### `signal(initialValue)`
|
|
211
|
+
|
|
212
|
+
Creates writable reactive state.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const [value, setValue] = signal(0);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- `value()` reads the current value
|
|
219
|
+
- `setValue(next)` writes a new value
|
|
220
|
+
- `setValue((prev) => next)` supports updater functions
|
|
221
|
+
|
|
222
|
+
### `computed(fn)`
|
|
223
|
+
|
|
224
|
+
Creates a lazy derived accessor.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const doubled = computed(() => count() * 2);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
- tracks dependencies dynamically while `fn` runs
|
|
231
|
+
- caches the last computed value
|
|
232
|
+
- recomputes on demand when dirty and read again
|
|
233
|
+
|
|
234
|
+
### `memo(fn)`
|
|
235
|
+
|
|
236
|
+
Creates a computed accessor and warms it once immediately.
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const total = memo(() => price() + tax());
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Use it when you want computed semantics with an eager first read.
|
|
243
|
+
|
|
244
|
+
### `effect(fn)`
|
|
245
|
+
|
|
246
|
+
Creates a reactive effect.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const stop = effect(() => {
|
|
250
|
+
console.log(count());
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
- runs immediately once
|
|
255
|
+
- tracks reactive reads
|
|
256
|
+
- may return a cleanup function
|
|
257
|
+
- cleanup runs before the next execution and on dispose
|
|
258
|
+
- returns a callable disposer with `.dispose()`
|
|
259
|
+
|
|
260
|
+
### `rt.event<T>()`
|
|
261
|
+
|
|
262
|
+
Creates an event source.
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const clicks = rt.event<number>();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
- `clicks.emit(value)` delivers an event
|
|
269
|
+
- `clicks.subscribe(fn)` subscribes to events
|
|
270
|
+
|
|
271
|
+
### `subscribeOnce(source, fn)`
|
|
272
|
+
|
|
273
|
+
Subscribes to the next value from `source`, then unsubscribes automatically.
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
subscribeOnce(clicks, (value) => {
|
|
277
|
+
console.log("first click =", value);
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `map(source, project)`
|
|
282
|
+
|
|
283
|
+
Projects each event value into a new event stream.
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const labels = map(clicks, (value) => `click:${value}`);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### `filter(source, predicate)`
|
|
290
|
+
|
|
291
|
+
Forwards only the values that satisfy `predicate`.
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
const positive = filter(clicks, (value) => value > 0);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### `merge(...sources)`
|
|
298
|
+
|
|
299
|
+
Combines multiple event sources into one event stream.
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
const all = merge(clicks, submits);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `scan(source, seed, reducer)`
|
|
306
|
+
|
|
307
|
+
Accumulates event values over time and returns `[read, dispose]`.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const [total, dispose] = scan(clicks, 0, (acc, value) => acc + value);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
`reducer` should be a pure event reducer. If you want to combine the accumulated value with reactive state, do that outside the reducer via `computed()`.
|
|
314
|
+
|
|
315
|
+
### `hold(source, initial)`
|
|
316
|
+
|
|
317
|
+
Stores the latest event payload and returns `[read, dispose]`.
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
const [latest, dispose] = hold(updates, "idle");
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Equivalent in behavior to:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
scan(updates, "idle", (_, value) => value);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## FAQ
|
|
330
|
+
|
|
331
|
+
### Are `signal()`, `computed()`, and `effect()` global, or tied to `rt`?
|
|
332
|
+
|
|
333
|
+
They are exported as top-level functions, but they run against the currently configured Reflex runtime. `createRuntime()` sets up that runtime and gives you the runtime controls such as `event()`, `flush()`, and `ctx`.
|
|
334
|
+
|
|
335
|
+
### Do I always need to call `flush()`?
|
|
336
|
+
|
|
337
|
+
No. You need `flush()` for scheduled effects when using the default `effectStrategy: "flush"`. You do not need `flush()` just to read up-to-date `signal()` or `computed()` values.
|
|
338
|
+
|
|
339
|
+
### Is `computed()` lazy or eager?
|
|
340
|
+
|
|
341
|
+
Lazy. It does not run until the first read. After that it behaves like a cached derived value that recomputes only when dirty and read again.
|
|
342
|
+
|
|
343
|
+
### What is the difference between `computed()` and `memo()`?
|
|
344
|
+
|
|
345
|
+
`memo()` is a warmed `computed()`. It performs one eager read immediately after creation, then keeps the same accessor semantics.
|
|
346
|
+
|
|
347
|
+
### Does `effect()` run immediately?
|
|
348
|
+
|
|
349
|
+
Yes. It runs once on creation. Future re-runs happen after invalidation, either on `rt.flush()` or automatically when using the eager effect strategy.
|
|
350
|
+
|
|
351
|
+
### Why do `scan()` and `hold()` return tuples instead of only an accessor?
|
|
352
|
+
|
|
353
|
+
Because they own an event subscription. The accessor lets you read the current accumulated state, and the disposer lets you unsubscribe and clean up explicitly.
|
|
354
|
+
|
|
355
|
+
### Should I use `rt.ctx`?
|
|
356
|
+
|
|
357
|
+
Usually no. `ctx` is a low-level escape hatch for integration code, tests, and runtime debugging.
|
|
358
|
+
|
|
359
|
+
### Can I create multiple runtimes?
|
|
360
|
+
|
|
361
|
+
Treat `createRuntime()` as creating the active runtime for an app instance or test. Creating a new runtime resets shared runtime state, so this is not intended as a pool of concurrently active runtimes inside one reactive graph.
|
|
362
|
+
|
|
363
|
+
## License
|
|
364
|
+
|
|
365
|
+
[MIT](https://github.com/volynetstyle/Reflex/blob/main/packages/reflex/LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class ReactiveEdge{constructor(t,n,i,e,s,r){this.from=t,this.to=n,this.prevOut=i,this.nextOut=e,this.prevIn=s,this.nextIn=r}}function t(t){t.prevOut=null,t.nextOut=null,t.prevIn=null,t.nextIn=null}var n;(t=>{t[t.Producer=1]="Producer",t[t.Consumer=2]="Consumer",t[t.Watcher=4]="Watcher",t[t.Invalid=8]="Invalid",t[t.Changed=16]="Changed",t[t.Visited=32]="Visited",t[t.Disposed=64]="Disposed",t[t.Computing=128]="Computing",t[t.Scheduled=256]="Scheduled",t[t.Tracking=512]="Tracking"})(n||(n={}));const i=n.Producer|n.Consumer|n.Watcher,e=n.Invalid|n.Changed,s=n.Producer,r=n.Changed|n.Consumer,u=n.Changed|n.Watcher;function o(t){t.state&=~n.Computing}function l(t){t.state&=~e}function c(t){return 0!==(t.state&n.Disposed)}function f(t,n,i){const e=i?i.nextIn:t.firstIn;n.prevIn=i,n.nextIn=e,e?e.prevIn=n:t.lastIn=n,i?i.nextIn=n:t.firstIn=n}function h(t,n){const{prevIn:i,nextIn:e}=n;i?i.nextIn=e:t.firstIn=e,e?e.prevIn=i:t.lastIn=i}function a(t,n){const{prevOut:i,nextOut:e}=n;i?i.nextOut=e:t.firstOut=e,e?e.prevOut=i:t.lastOut=i}function d(t,n,i=n.lastIn){const e=t.lastOut,s=i?i.nextIn:n.firstIn,r=new ReactiveEdge(t,n,e,null,i,s);return e?e.nextOut=r:t.firstOut=r,t.lastOut=r,s?s.prevIn=r:n.lastIn=r,i?i.nextIn=r:n.firstIn=r,r}function v(t,n,i,e){for(let s=e?e.nextIn:n.firstIn;null!==s;s=s.nextIn)if(s.from===t)return s.prevIn!==i&&(h(n,s),f(n,s,i)),s;return d(t,n,i)}function p(e){c(e)||((t=>{t.state=t.state&i|n.Disposed})(e),e.depsTail=null,(n=>{let i=n.firstIn;for(n.firstIn=n.lastIn=n.depsTail=null;i;){const n=i.nextIn;a(i.from,i),t(i),i=n}})(e),(n=>{let i=n.firstOut;for(n.firstOut=n.lastOut=null;i;){const n=i.nextOut;i.to.depsTail===i&&(i.to.depsTail=i.prevIn),h(i.to,i),t(i),i=n}})(e),e.compute=null)}const y=Symbol.for("UNINITIALIZED");class ReactiveNode{constructor(t,n,i){this.state=i,this.compute=n,this.firstOut=null,this.firstIn=null,this.lastOut=null,this.lastIn=null,this.depsTail=null,this.payload=t}}function x(t,n){if(!Object.hasOwn(t,n))return;const i=t[n];return"function"==typeof i?i:void 0}class ExecutionContext{constructor(t={}){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null,this.onEffectInvalidatedHook=void 0,this.onReactiveSettledHook=void 0,this.hookMask=0,this.hooks={},Object.defineProperties(this.hooks,{onEffectInvalidated:{enumerable:!0,get:()=>this.onEffectInvalidatedHook,set:t=>{this.setOnEffectInvalidatedHook(t)}},onReactiveSettled:{enumerable:!0,get:()=>this.onReactiveSettledHook,set:t=>{this.setOnReactiveSettledHook(t)}}}),this.setHooks(t)}dispatchWatcherEvent(t){const n=this.onEffectInvalidatedHook;void 0!==n&&n?.(t)}maybeNotifySettled(){if(!(2&this.hookMask))return;if(0!==this.propagationDepth||null!==this.activeComputed)return;const t=this.onReactiveSettledHook;t?.()}enterPropagation(){++this.propagationDepth}leavePropagation(){this.propagationDepth>0&&--this.propagationDepth,this.maybeNotifySettled()}resetState(){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null}setHooks(t={}){const n=x(t,"onEffectInvalidated"),i=x(t,"onReactiveSettled");this.hooks.onEffectInvalidated=n,this.hooks.onReactiveSettled=i}registerWatcherCleanup(t){this.cleanupRegistrar?.(t)}withCleanupRegistrar(t,n){const i=this.cleanupRegistrar;this.cleanupRegistrar=t;try{return n()}finally{this.cleanupRegistrar=i}}setOnEffectInvalidatedHook(t){this.onEffectInvalidatedHook="function"==typeof t?t:void 0,this.updateHookMask(1,void 0!==this.onEffectInvalidatedHook)}setOnReactiveSettledHook(t){this.onReactiveSettledHook="function"==typeof t?t:void 0,this.updateHookMask(2,void 0!==this.onReactiveSettledHook)}updateHookMask(t,n){this.hookMask=n?this.hookMask|t:this.hookMask&~t}}let b=E(void 0);function E(t={}){return new ExecutionContext(t)}function g(){return b}const S=Object.is;function I(t,n=g()){const i=n.activeComputed;if(!i)return;const e=c(t),s=c(i);if(e||s)return;const r=i.depsTail;if(null===r){const n=i.firstIn;return null===n?void(i.depsTail=d(t,i,null)):n.from===t?void(i.depsTail=n):void(i.depsTail=v(t,i,null,n))}if(r.from===t)return;const u=r.nextIn;i.depsTail=null===u||u.from!==t?v(t,i,r,u):u}function w(n,i=g()){const e=n.depsTail,s=null===e?n.firstIn:e.nextIn;null!==s&&(null===e?(n.firstIn=null,n.lastIn=null):(e.nextIn=null,n.lastIn=e),(n=>{for(;n;){const i=n.nextIn;a(n.from,n),t(n),n=i}})(s))}function N(t,i){t.depsTail=null,t.state=t.state&~n.Visited|n.Tracking,(t=>{t.state|=n.Computing})(t);const e=i.activeComputed;i.activeComputed=t;let s=!1;return()=>{s||(s=!0,i.activeComputed=e)}}function R(t,i=g()){if(c(t))return!1;const e=t.payload;let s=e,r=!1;return s=((t,i=g())=>{const e=t.compute,s=N(t,i);let r;try{return r=e(),s(),w(t,i),r}catch(t){throw s(),t}finally{t.state&=~n.Tracking,o(t),i.maybeNotifySettled()}})(t,i),c(t)||(r=!S(e,s),t.payload=s),l(t),r}const k=n.Invalid,m=n.Changed,C=n.Invalid|n.Changed|n.Disposed|n.Visited|n.Tracking;function O(t,n,i){const e=i.onEffectInvalidatedHook;if(void 0===e)return n;try{e(t)}catch(t){return n??t}return n}function D(t,i=g()){if(0!==(t.state&n.Disposed))return;let s=null;for(let r=t.firstOut;null!==r;r=r.nextOut){const t=r.to,u=t.state;if((u&e)!==n.Invalid)continue;const o=u&~n.Invalid|n.Changed;t.state=o,0!==(o&n.Watcher)&&(s=O(t,s,i))}if(null!==s)throw s}const H=[],P=[];function j(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function A(t,i,e,s,r){const u=H,o=P,l=u.length;let c=l,f=k;for(;;){const h=t.to,a=h.state;let d=0;if(d=0===(a&C)?a|f:j(t,a,f),0!==d)if(h.state=d,0!==(d&n.Watcher))s=O(h,s,r);else{const n=h.firstOut;if(null!==n){null!==i&&(u[c]=i,o[c++]=e),t=n,i=n.nextOut,f=e=k;continue}}if(null!==i)t=i,f=e;else{if(l>=c)return u.length=o.length=l,s;t=u[--c],f=e=o[c]}i=t.nextOut}}function T(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;if(t===n)return!0;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function W(t,n,i){const e=R(n,i);return!e||null===t.prevOut&&null===t.nextOut||D(n,i),e}function B(t,i,s,r,u,o){let l=!1;t:for(;;){const c=t.from,f=c.state;if(0!==(i.state&n.Changed))l=!0;else if(0!==(f&n.Changed))l=W(t,c,s);else if(0!==(f&e)){const n=c.firstIn;if(null!==n){r[u++]=t,t=n,i=c;continue}l=W(t,c,s)}if(!l){const e=t.nextIn;if(null!==e){t=e;continue}i.state&=~n.Invalid}for(;u>o;){const e=r[--u];if(l?l=W(e,i,s):i.state&=~n.Invalid,i=e.to,!l){const n=e.nextIn;if(null!==n){t=n;continue t}}}return l}}const L=[],U=n.Producer|n.Disposed,Z=n.Invalid|n.Visited;function q(t){const i=t.state;if(0!==(i&U))return!1;if(0!==(i&n.Changed)||(i&Z)===Z)return!0;const s=t.firstIn;return null===s?(t.state=i&~n.Invalid,!1):((t,i,s)=>{const r=L,u=r.length;let o=u,l=i,c=t,f=!1;for(;;){if(null!==l.nextIn)return B(l,c,s,r,o,u);if(0!==(c.state&n.Changed)){f=!0;break}const t=l.from,i=t.state;if(0!==(i&n.Changed)&&(f=W(l,t,s),f||null===l.nextIn))break;if(0!==(i&e)){const n=t.firstIn;if(null!==n){if(null!==n.nextIn){r[o++]=l;const i=B(n,t,s,r,o,u);return r.length=u,i}r[o++]=l,l=n,c=t;continue}f=W(l,t,s);break}if(c.state&=~n.Invalid,o===u)return!1;l=r[--o],c=l.to}for(;o>u;){const t=r[--o];f?f=W(t,c,s):c.state&=~n.Invalid,c=t.to}return f||(c.state&=~n.Invalid),r.length=u,f})(t,s,g())}var z;function F(t,n=g()){return c(t)||I(t,n),t.payload}function M(t,i){const s=t.state;return 0!==(s&n.Disposed)||0!==(s&e)&&(0!==(s&n.Changed)||q(t)?R(t,i)&&D(t,i):l(t)),t.payload}function V(t,n=z.lazy,i=g()){if(n===z.eager){const n=i.activeComputed;let e;i.activeComputed=null;try{e=M(t,i)}finally{i.activeComputed=n}return e}const e=M(t,i);return c(t)||I(t,i),e}function G(t,i,e=S,s=g()){if(c(t))return;if(e(t.payload,i))return;t.payload=i;const r=t.firstOut;if(null!==r){s.enterPropagation();try{((t,i=k,e=g())=>{if(0!==(t.from.state&n.Disposed))return;const s=((t,i,e,s)=>{for(;;){const r=t.to,u=r.state;let o=0;o=0===(u&C)?u|i:T(t,u,i);const l=t.nextOut;if(o)if(r.state=o,0!==(o&n.Watcher))e=O(r,e,s);else{const n=r.firstOut;if(null!==n){if(null!==l)return A(n,l,i,e,s);t=n,i=k;continue}}if(null===l)return e;t=l}})(t,i,null,e);if(null!==s)throw s})(r,m,s)}finally{s.leavePropagation()}}}function J(t,i=g()){const s=t.state;if(0!==(s&n.Disposed))return;if(0===(s&e)||0===(s&n.Changed)&&!q(t))return void l(t);const r="function"==typeof t.payload?t.payload:null;if(t.payload=y,(t=>{t.state&=~n.Visited})(t),l(t),r?.(),c(t))return;let u=!1;((t,i,e=g())=>{const s=t.compute,r=N(t,e);let u;try{return u=s(),r(),w(t,e),i(u)}catch(t){throw r(),t}finally{t.state&=~n.Tracking,o(t),e.maybeNotifySettled()}})(t,n=>{c(t)||(u="function"==typeof n,u&&(t.payload=n))},i)}(t=>{t[t.lazy=1]="lazy",t[t.eager=2]="eager"})(z||(z={}));class EventSource{constructor(){this.dispatchDepth=0,this.head=null,this.tail=null,this.pendingHead=null}}const K=Symbol("EventSubscriber.owner");function Q(t){return t[K]}function X(t,n){t[K]=n}function Y(t){return t()}function $(t,n){const i=n.prev,e=n.next;null===i?t.head=e:i.next=e,null===e?t.tail=i:e.prev=i,n.prev=null,n.next=null,n.unlinkNext=null,X(n,null)}function _(t,n,i=Y){i(()=>{const i=t.tail;if(null!==i){++t.dispatchDepth;try{let e=t.head;for(;null!==e;){const t=e===i?null:e.next;1&e.state&&e.fn(n),e=t}}finally{--t.dispatchDepth,0===t.dispatchDepth&&null!==t.pendingHead&&(t=>{let n=t.pendingHead;for(t.pendingHead=null;null!==n;){const i=n.unlinkNext;n.unlinkNext=null,$(t,n),n=i}})(t)}}})}const tt=Symbol("UNINITIALIZED");function nt(t){return new ReactiveNode(tt,t,r)}function it(t){t.state&=~n.Scheduled}class EffectScheduler{constructor(t,n){this.queue=[],this.head=0,this.batchDepth=0,this.phase=0,this.mode=t,this.context=n}getContext(){return this.context??g()}enqueue(t){this.isNodeIgnored(t)||((t=>{t.state|=n.Scheduled})(t),this.queue.push(t),this.shouldAutoFlush()&&this.flush())}batch(t){this.enterBatch();try{return t()}finally{this.leaveBatch()}}flush(){if(2&this.phase)return;if(!this.hasPending())return;this.phase=2;const t=this.getContext();try{for(;this.queue.length>this.head;){const n=this.queue[this.head++];it(n),this.shouldSkipNode(n)||J(n,t)}}finally{this.queue.length=0,this.head=0,this.phase=this.batchDepth>0?1:0,0===this.phase&&this.shouldAutoFlush()&&this.flush()}}reset(){this.queue.length=0,this.head=0,this.batchDepth=0,this.phase=0}notifySettled(){this.shouldAutoFlush()&&this.flush()}hasPending(){return this.queue.length>this.head}isNodeIgnored(t){return 0!==(t.state&n.Disposed)||0!==(t.state&n.Scheduled)}shouldSkipNode(t){return 0!==(t.state&n.Disposed)||0===(t.state&e)}shouldAutoFlush(){const t=this.getContext();return 1===this.mode&&0===this.phase&&0===t.propagationDepth&&null===t.activeComputed&&this.hasPending()}enterBatch(){++this.batchDepth,2!==this.phase&&(this.phase=1)}leaveBatch(){--this.batchDepth,0===this.batchDepth&&2!==this.phase&&(this.phase=0,this.shouldAutoFlush()&&this.flush())}}class EventDispatcher{constructor(t=Y){this.queue=[],this.head=0,this.flushing=!1,this.flush=()=>{if(!this.flushing){this.flushing=!0;try{const t=this.queue;for(;t.length>this.head;)_(t[this.head++],t[this.head++])}finally{this.queue.length=0,this.head=0,this.flushing=!1}}},this.runBoundary=t}emit(t,n){this.queue.push(t,n),this.flushing||this.runBoundary(this.flush)}}function et(t){return{subscribe:t}}function st(t,n,i){const e=new ReactiveNode(n,null,s);let r=t.subscribe(t=>{c(e)||G(e,i(e.payload,t))});return[()=>c(e)?e.payload:F(e),()=>{(t=>{p(t)})(e);const t=r;r=void 0,t?.()}]}exports.computed=t=>{const n=nt(t);return()=>V(n)},exports.createRuntime=t=>{const{scheduler:n,dispatcher:i,executionContext:e}=(t=>{const n=t?.hooks,i=E(),e=new EffectScheduler((s=t?.effectStrategy,"eager"===s?1:0),i);var s;const r=new EventDispatcher(t=>e.batch(t));return i.setHooks({...n,onEffectInvalidated(t){e.enqueue(t),n?.onEffectInvalidated?.(t)},onReactiveSettled(){e.notifySettled(),n?.onReactiveSettled?.()}}),{scheduler:e,dispatcher:r,executionContext:i}})(t);return e.resetState(),(t=>{b=t})(e),{get ctx(){return e},event(){const t=new EventSource;return{subscribe(n){return((t,n)=>{const i={fn:n,next:null,prev:null,state:1,unlinkNext:null};return((t,n)=>{if(!(1&n.state))return;if(null!=Q(n))return;const i=t.tail;n.prev=i,n.next=null,n.unlinkNext=null,X(n,t),null!==i?(i.next=n,t.tail=n):t.head=t.tail=n})(t,i),()=>{((t,n)=>{Q(n)===t&&1&n.state&&(n.state&=-2,0===t.dispatchDepth?(n.state|=2,$(t,n)):((t,n)=>{2&n.state||(n.state|=2,n.unlinkNext=t.pendingHead,t.pendingHead=n)})(t,n))})(t,i)}})(t,n)},emit(n){i.emit(t,n)}}},flush(){n.flush()}}},exports.effect=t=>{const n=new ReactiveNode(null,t,u),i=g();J(n,i);const e=()=>(t=>{p(t);const n="function"==typeof t.payload?t.payload:null;n?.(),t.payload=y})(n);return i.registerWatcherCleanup(e),e},exports.filter=(t,n)=>et(i=>t.subscribe(t=>{n(t)&&i(t)})),exports.hold=(t,n)=>st(t,n,(t,n)=>n),exports.map=(t,n)=>et(i=>t.subscribe(t=>{i(n(t))})),exports.memo=t=>{const n=nt(t);return V(n,z.eager),()=>V(n)},exports.merge=(...t)=>et(n=>{const i=t.map(t=>t.subscribe(t=>{n(t)}));return()=>{for(const t of i)t()}}),exports.scan=(t,n,i)=>st(t,n,i),exports.signal=(t,n)=>{const i=new ReactiveNode(t,null,s);return[()=>F(i),t=>{const n="function"!=typeof t?t:t(i.payload);return G(i,n),n}]},exports.subscribeOnce=(t,n)=>{let i,e=!0,s=!1;const r=()=>{if(!e)return;e=!1;const t=i;void 0!==t?(i=void 0,t()):s=!0};if(i=t.subscribe(t=>{e&&(r(),n(t))}),s){const t=i;i=void 0,t?.()}return r};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class ReactiveEdge{constructor(t,n,i,e,s,r){this.from=t,this.to=n,this.prevOut=i,this.nextOut=e,this.prevIn=s,this.nextIn=r}}function t(t){t.prevOut=null,t.nextOut=null,t.prevIn=null,t.nextIn=null}var n;(t=>{t[t.Producer=1]="Producer",t[t.Consumer=2]="Consumer",t[t.Watcher=4]="Watcher",t[t.Invalid=8]="Invalid",t[t.Changed=16]="Changed",t[t.Visited=32]="Visited",t[t.Disposed=64]="Disposed",t[t.Computing=128]="Computing",t[t.Scheduled=256]="Scheduled",t[t.Tracking=512]="Tracking"})(n||(n={}));const i=n.Producer|n.Consumer|n.Watcher,e=n.Invalid|n.Changed,s=n.Producer,r=n.Changed|n.Watcher;function o(t){t.state&=~n.Computing}function l(t){t.state&=~e}function u(t){return 0!==(t.state&n.Disposed)}function c(t,n,i){const e=i?i.nextIn:t.firstIn;n.prevIn=i,n.nextIn=e,e?e.prevIn=n:t.lastIn=n,i?i.nextIn=n:t.firstIn=n}function h(t,n){const{prevIn:i,nextIn:e}=n;i?i.nextIn=e:t.firstIn=e,e?e.prevIn=i:t.lastIn=i}function f(t,n){const{prevOut:i,nextOut:e}=n;i?i.nextOut=e:t.firstOut=e,e?e.prevOut=i:t.lastOut=i}function a(t,n,i=n.lastIn){const e=t.lastOut,s=i?i.nextIn:n.firstIn,r=new ReactiveEdge(t,n,e,null,i,s);return e?e.nextOut=r:t.firstOut=r,t.lastOut=r,s?s.prevIn=r:n.lastIn=r,i?i.nextIn=r:n.firstIn=r,r}function d(t,n,i,e){for(let s=e?e.nextIn:n.firstIn;null!==s;s=s.nextIn)if(s.from===t)return s.prevIn!==i&&(h(n,s),c(n,s,i)),s;return a(t,n,i)}function v(e){u(e)||((t=>{t.state=t.state&i|n.Disposed})(e),e.depsTail=null,(n=>{let i=n.firstIn;for(n.firstIn=n.lastIn=n.depsTail=null;i;){const n=i.nextIn;f(i.from,i),t(i),i=n}})(e),(n=>{let i=n.firstOut;for(n.firstOut=n.lastOut=null;i;){const n=i.nextOut;i.to.depsTail===i&&(i.to.depsTail=i.prevIn),h(i.to,i),t(i),i=n}})(e),e.compute=null)}const p=Symbol.for("UNINITIALIZED");class ReactiveNode{constructor(t,n,i){this.state=i,this.compute=n,this.firstOut=null,this.firstIn=null,this.lastOut=null,this.lastIn=null,this.depsTail=null,this.payload=t}}function y(t,n){if(!Object.hasOwn(t,n))return;const i=t[n];return"function"==typeof i?i:void 0}class ExecutionContext{constructor(t={}){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null,this.onEffectInvalidatedHook=void 0,this.onReactiveSettledHook=void 0,this.hookMask=0,this.hooks={},Object.defineProperties(this.hooks,{onEffectInvalidated:{enumerable:!0,get:()=>this.onEffectInvalidatedHook,set:t=>{this.setOnEffectInvalidatedHook(t)}},onReactiveSettled:{enumerable:!0,get:()=>this.onReactiveSettledHook,set:t=>{this.setOnReactiveSettledHook(t)}}}),this.setHooks(t)}dispatchWatcherEvent(t){const n=this.onEffectInvalidatedHook;void 0!==n&&n?.(t)}maybeNotifySettled(){if(!(2&this.hookMask))return;if(0!==this.propagationDepth||null!==this.activeComputed)return;const t=this.onReactiveSettledHook;t?.()}enterPropagation(){++this.propagationDepth}leavePropagation(){this.propagationDepth>0&&--this.propagationDepth,this.maybeNotifySettled()}resetState(){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null}setHooks(t={}){const n=y(t,"onEffectInvalidated"),i=y(t,"onReactiveSettled");this.hooks.onEffectInvalidated=n,this.hooks.onReactiveSettled=i}registerWatcherCleanup(t){this.cleanupRegistrar?.(t)}withCleanupRegistrar(t,n){const i=this.cleanupRegistrar;this.cleanupRegistrar=t;try{return n()}finally{this.cleanupRegistrar=i}}setOnEffectInvalidatedHook(t){this.onEffectInvalidatedHook="function"==typeof t?t:void 0,this.updateHookMask(1,void 0!==this.onEffectInvalidatedHook)}setOnReactiveSettledHook(t){this.onReactiveSettledHook="function"==typeof t?t:void 0,this.updateHookMask(2,void 0!==this.onReactiveSettledHook)}updateHookMask(t,n){this.hookMask=n?this.hookMask|t:this.hookMask&~t}}let R=((t={})=>new ExecutionContext(t))(void 0);function g(){return R}const b=Object.is;function k(n,i=g()){const e=n.depsTail,s=null===e?n.firstIn:e.nextIn;null!==s&&(null===e?(n.firstIn=null,n.lastIn=null):(e.nextIn=null,n.lastIn=e),(n=>{for(;n;){const i=n.nextIn;f(n.from,n),t(n),n=i}})(s))}function w(t,i){t.depsTail=null,t.state=t.state&~n.Visited|n.Tracking,(t=>{t.state|=n.Computing})(t);const e=i.activeComputed;i.activeComputed=t;let s=!1;return()=>{s||(s=!0,i.activeComputed=e)}}function I(t,i=g()){if(u(t))return!1;const e=t.payload;let s=e,r=!1;return s=((t,i=g())=>{const e=t.compute,s=w(t,i);let r;try{return r=e(),s(),k(t,i),r}catch(t){throw s(),t}finally{t.state&=~n.Tracking,o(t),i.maybeNotifySettled()}})(t,i),u(t)||(r=!b(e,s),t.payload=s),l(t),r}const C=n.Invalid,E=n.Changed,x=n.Invalid|n.Changed|n.Disposed|n.Visited|n.Tracking;function S(t,n,i){const e=i.onEffectInvalidatedHook;if(void 0===e)return n;try{e(t)}catch(t){return n??t}return n}function j(t,i=g()){if(0!==(t.state&n.Disposed))return;let s=null;for(let r=t.firstOut;null!==r;r=r.nextOut){const t=r.to,o=t.state;if((o&e)!==n.Invalid)continue;const l=o&~n.Invalid|n.Changed;t.state=l,0!==(l&n.Watcher)&&(s=S(t,s,i))}if(null!==s)throw s}const m=[],O=[];function N(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function H(t,i,e,s,r){const o=m,l=O,u=o.length;let c=u,h=C;for(;;){const f=t.to,a=f.state;let d=0;if(d=0===(a&x)?a|h:N(t,a,h),0!==d)if(f.state=d,0!==(d&n.Watcher))s=S(f,s,r);else{const n=f.firstOut;if(null!==n){null!==i&&(o[c]=i,l[c++]=e),t=n,i=n.nextOut,h=e=C;continue}}if(null!==i)t=i,h=e;else{if(u>=c)return o.length=l.length=u,s;t=o[--c],h=e=l[c]}i=t.nextOut}}function L(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;if(t===n)return!0;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function P(t,n,i){const e=I(n,i);return!e||null===t.prevOut&&null===t.nextOut||j(n,i),e}function W(t,i,s,r,o,l){let u=!1;t:for(;;){const c=t.from,h=c.state;if(0!==(i.state&n.Changed))u=!0;else if(0!==(h&n.Changed))u=P(t,c,s);else if(0!==(h&e)){const n=c.firstIn;if(null!==n){r[o++]=t,t=n,i=c;continue}u=P(t,c,s)}if(!u){const e=t.nextIn;if(null!==e){t=e;continue}i.state&=~n.Invalid}for(;o>l;){const e=r[--o];if(u?u=P(e,i,s):i.state&=~n.Invalid,i=e.to,!u){const n=e.nextIn;if(null!==n){t=n;continue t}}}return u}}const q=[],A=n.Producer|n.Disposed,D=n.Invalid|n.Visited;var T;function z(t,n=g()){return u(t)||((t,n=g())=>{const i=n.activeComputed;if(!i)return;const e=u(t),s=u(i);if(e||s)return;const r=i.depsTail;if(null===r){const n=i.firstIn;return null===n?void(i.depsTail=a(t,i,null)):n.from===t?void(i.depsTail=n):void(i.depsTail=d(t,i,null,n))}if(r.from===t)return;const o=r.nextIn;i.depsTail=null===o||o.from!==t?d(t,i,r,o):o})(t,n),t.payload}function M(t,i,e=b,s=g()){if(u(t))return;if(e(t.payload,i))return;t.payload=i;const r=t.firstOut;if(null!==r){s.enterPropagation();try{((t,i=C,e=g())=>{if(0!==(t.from.state&n.Disposed))return;const s=((t,i,e,s)=>{for(;;){const r=t.to,o=r.state;let l=0;l=0===(o&x)?o|i:L(t,o,i);const u=t.nextOut;if(l)if(r.state=l,0!==(l&n.Watcher))e=S(r,e,s);else{const n=r.firstOut;if(null!==n){if(null!==u)return H(n,u,i,e,s);t=n,i=C;continue}}if(null===u)return e;t=u}})(t,i,null,e);if(null!==s)throw s})(r,E,s)}finally{s.leavePropagation()}}}function U(){return new ReactiveNode(0,null,s)}function V(t){return new ReactiveNode(null,t,r)}(t=>{t[t.lazy=1]="lazy",t[t.eager=2]="eager"})(T||(T={}));class ResourceRequest{constructor(t,n){this.owner=t,this.token=n}alive(){return this.owner.isAlive(this.token)}resolve(t){return this.owner.resolve(this.token,t)}reject(t){return this.owner.reject(this.token,t)}}class ResourceCore{track(){z(this.stateNode,this.context)}bump(){M(this.stateNode,this.stateNode.payload+1)}isAlive(t){return!this.disposed&&this.token===t}start(){const t=this.disposed?this.token:this.token+1;return this.disposed||(this.token=t,this.status="pending",this.error=void 0,this.bump()),new ResourceRequest(this,t)}clear(){this.disposed||(this.token+=1,this.status="idle",this.value=void 0,this.error=void 0,this.bump())}dispose(){this.disposed||(this.disposed=!0,this.token+=1,this.status="idle",this.value=void 0,this.error=void 0,this.bump(),null!==this.watcher&&((t=>{v(t);const n="function"==typeof t.payload?t.payload:null;n?.(),t.payload=p})(this.watcher),this.watcher=null),null!==this.refetchNode&&(v(this.refetchNode),this.refetchNode=null))}resolve(t,n){return!!this.isAlive(t)&&(this.status="resolved",this.value=n,this.error=void 0,this.bump(),!0)}reject(t,n){return!!this.isAlive(t)&&(this.status="rejected",this.error=n,this.bump(),!0)}settle(t,n){var i;"object"==typeof(i=t)&&null!==i&&"then"in i&&"function"==typeof i.then?t.then(t=>{n.resolve(t)},t=>{n.reject(t)}):n.resolve(t)}runLoad(t){const n=this.start();let i;try{i=t(n)}catch(t){return void n.reject(t)}this.settle(i,n)}runSourceLoad(t,n){const i=this.start();let e;try{e=n(t,i)}catch(t){return void i.reject(t)}this.settle(e,i)}refetch(){this.disposed||null===this.refetchNode||M(this.refetchNode,this.refetchNode.payload+1)}constructor(){this.context=g(),this.stateNode=U(),this.status="idle",this.value=void 0,this.error=void 0,this.token=0,this.disposed=!1,this.watcher=null,this.refetchNode=null}}exports.isPending=t=>"pending"===t.status(),exports.resource=(t,i)=>{const s=new ResourceCore,r={status:()=>(s.track(),s.status),value:()=>(s.track(),s.value),error:()=>(s.track(),s.error),token:()=>(s.track(),s.token),clear(){s.clear()},dispose(){s.dispose()}};if("function"==typeof t){if(s.refetchNode=U(),"function"==typeof i){const n=t,e=i;s.watcher=V(()=>{let t;z(s.refetchNode,s.context);try{t=n()}catch(t){return void s.start().reject(t)}s.runSourceLoad(t,e)})}else{const n=t;s.watcher=V(()=>{z(s.refetchNode,s.context),s.runLoad(n)})}return((t,i=g())=>{const s=t.state;if(0!==(s&n.Disposed))return;if(0===(s&e)||0===(s&n.Changed)&&!(t=>{const i=t.state;if(0!==(i&A))return!1;if(0!==(i&n.Changed)||(i&D)===D)return!0;const s=t.firstIn;return null===s?(t.state=i&~n.Invalid,!1):((t,i,s)=>{const r=q,o=r.length;let l=o,u=i,c=t,h=!1;for(;;){if(null!==u.nextIn)return W(u,c,s,r,l,o);if(0!==(c.state&n.Changed)){h=!0;break}const t=u.from,i=t.state;if(0!==(i&n.Changed)&&(h=P(u,t,s),h||null===u.nextIn))break;if(0!==(i&e)){const n=t.firstIn;if(null!==n){if(null!==n.nextIn){r[l++]=u;const i=W(n,t,s,r,l,o);return r.length=o,i}r[l++]=u,u=n,c=t;continue}h=P(u,t,s);break}if(c.state&=~n.Invalid,l===o)return!1;u=r[--l],c=u.to}for(;l>o;){const t=r[--l];h?h=P(t,c,s):c.state&=~n.Invalid,c=t.to}return h||(c.state&=~n.Invalid),r.length=o,h})(t,s,g())})(t))return void l(t);const r="function"==typeof t.payload?t.payload:null;if(t.payload=p,(t=>{t.state&=~n.Visited})(t),l(t),r?.(),u(t))return;let c=!1;((t,i,e=g())=>{const s=t.compute,r=w(t,e);let l;try{return l=s(),r(),k(t,e),i(l)}catch(t){throw r(),t}finally{t.state&=~n.Tracking,o(t),e.maybeNotifySettled()}})(t,n=>{u(t)||(c="function"==typeof n,c&&(t.payload=n))},i)})(s.watcher,s.context),s.context.registerWatcherCleanup(()=>{s.dispose()}),{...r,refetch(){s.refetch()}}}return s.context.registerWatcherCleanup(()=>{s.dispose()}),{...r,start(){return s.start()}}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class ReactiveEdge{constructor(t,n,i,e,u,s){this.from=t,this.to=n,this.prevOut=i,this.nextOut=e,this.prevIn=u,this.nextIn=s}}function t(t){t.prevOut=null,t.nextOut=null,t.prevIn=null,t.nextIn=null}var n;(t=>{t[t.Producer=1]="Producer",t[t.Consumer=2]="Consumer",t[t.Watcher=4]="Watcher",t[t.Invalid=8]="Invalid",t[t.Changed=16]="Changed",t[t.Visited=32]="Visited",t[t.Disposed=64]="Disposed",t[t.Computing=128]="Computing",t[t.Scheduled=256]="Scheduled",t[t.Tracking=512]="Tracking"})(n||(n={}));const i=n.Producer|n.Consumer|n.Watcher,e=n.Invalid|n.Changed,u=n.Producer,s=n.Changed|n.Consumer,r=n.Changed|n.Watcher;function o(t){t.state&=~n.Computing}function l(t){t.state&=~e}function c(t){return 0!==(t.state&n.Disposed)}function f(t,n,i){const e=i?i.nextIn:t.firstIn;n.prevIn=i,n.nextIn=e,e?e.prevIn=n:t.lastIn=n,i?i.nextIn=n:t.firstIn=n}function h(t,n){const{prevIn:i,nextIn:e}=n;i?i.nextIn=e:t.firstIn=e,e?e.prevIn=i:t.lastIn=i}function a(t,n){const{prevOut:i,nextOut:e}=n;i?i.nextOut=e:t.firstOut=e,e?e.prevOut=i:t.lastOut=i}function d(t,n,i=n.lastIn){const e=t.lastOut,u=i?i.nextIn:n.firstIn,s=new ReactiveEdge(t,n,e,null,i,u);return e?e.nextOut=s:t.firstOut=s,t.lastOut=s,u?u.prevIn=s:n.lastIn=s,i?i.nextIn=s:n.firstIn=s,s}function v(t,n,i,e){for(let u=e?e.nextIn:n.firstIn;null!==u;u=u.nextIn)if(u.from===t)return u.prevIn!==i&&(h(n,u),f(n,u,i)),u;return d(t,n,i)}function y(e){c(e)||((t=>{t.state=t.state&i|n.Disposed})(e),e.depsTail=null,(n=>{let i=n.firstIn;for(n.firstIn=n.lastIn=n.depsTail=null;i;){const n=i.nextIn;a(i.from,i),t(i),i=n}})(e),(n=>{let i=n.firstOut;for(n.firstOut=n.lastOut=null;i;){const n=i.nextOut;i.to.depsTail===i&&(i.to.depsTail=i.prevIn),h(i.to,i),t(i),i=n}})(e),e.compute=null)}const p=Symbol.for("UNINITIALIZED");class ReactiveNode{constructor(t,n,i){this.state=i,this.compute=n,this.firstOut=null,this.firstIn=null,this.lastOut=null,this.lastIn=null,this.depsTail=null,this.payload=t}}function b(t,n){if(!Object.hasOwn(t,n))return;const i=t[n];return"function"==typeof i?i:void 0}class ExecutionContext{constructor(t={}){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null,this.onEffectInvalidatedHook=void 0,this.onReactiveSettledHook=void 0,this.hookMask=0,this.hooks={},Object.defineProperties(this.hooks,{onEffectInvalidated:{enumerable:!0,get:()=>this.onEffectInvalidatedHook,set:t=>{this.setOnEffectInvalidatedHook(t)}},onReactiveSettled:{enumerable:!0,get:()=>this.onReactiveSettledHook,set:t=>{this.setOnReactiveSettledHook(t)}}}),this.setHooks(t)}dispatchWatcherEvent(t){const n=this.onEffectInvalidatedHook;void 0!==n&&n?.(t)}maybeNotifySettled(){if(!(2&this.hookMask))return;if(0!==this.propagationDepth||null!==this.activeComputed)return;const t=this.onReactiveSettledHook;t?.()}enterPropagation(){++this.propagationDepth}leavePropagation(){this.propagationDepth>0&&--this.propagationDepth,this.maybeNotifySettled()}resetState(){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null}setHooks(t={}){const n=b(t,"onEffectInvalidated"),i=b(t,"onReactiveSettled");this.hooks.onEffectInvalidated=n,this.hooks.onReactiveSettled=i}registerWatcherCleanup(t){this.cleanupRegistrar?.(t)}withCleanupRegistrar(t,n){const i=this.cleanupRegistrar;this.cleanupRegistrar=t;try{return n()}finally{this.cleanupRegistrar=i}}setOnEffectInvalidatedHook(t){this.onEffectInvalidatedHook="function"==typeof t?t:void 0,this.updateHookMask(1,void 0!==this.onEffectInvalidatedHook)}setOnReactiveSettledHook(t){this.onReactiveSettledHook="function"==typeof t?t:void 0,this.updateHookMask(2,void 0!==this.onReactiveSettledHook)}updateHookMask(t,n){this.hookMask=n?this.hookMask|t:this.hookMask&~t}}let E=g(void 0);function g(t={}){return new ExecutionContext(t)}function S(){return E}const I=Object.is;function w(t,n=S()){const i=n.activeComputed;if(!i)return;const e=c(t),u=c(i);if(e||u)return;const s=i.depsTail;if(null===s){const n=i.firstIn;return null===n?void(i.depsTail=d(t,i,null)):n.from===t?void(i.depsTail=n):void(i.depsTail=v(t,i,null,n))}if(s.from===t)return;const r=s.nextIn;i.depsTail=null===r||r.from!==t?v(t,i,s,r):r}function x(n,i=S()){const e=n.depsTail,u=null===e?n.firstIn:e.nextIn;null!==u&&(null===e?(n.firstIn=null,n.lastIn=null):(e.nextIn=null,n.lastIn=e),(n=>{for(;n;){const i=n.nextIn;a(n.from,n),t(n),n=i}})(u))}function N(t,i){t.depsTail=null,t.state=t.state&~n.Visited|n.Tracking,(t=>{t.state|=n.Computing})(t);const e=i.activeComputed;i.activeComputed=t;let u=!1;return()=>{u||(u=!0,i.activeComputed=e)}}function R(t,i=S()){if(c(t))return!1;const e=t.payload;let u=e,s=!1;return u=((t,i=S())=>{const e=t.compute,u=N(t,i);let s;try{return s=e(),u(),x(t,i),s}catch(t){throw u(),t}finally{t.state&=~n.Tracking,o(t),i.maybeNotifySettled()}})(t,i),c(t)||(s=!I(e,u),t.payload=u),l(t),s}const k=n.Invalid,m=n.Changed,C=n.Invalid|n.Changed|n.Disposed|n.Visited|n.Tracking;function O(t,n,i){const e=i.onEffectInvalidatedHook;if(void 0===e)return n;try{e(t)}catch(t){return n??t}return n}function D(t,i=S()){if(0!==(t.state&n.Disposed))return;let u=null;for(let s=t.firstOut;null!==s;s=s.nextOut){const t=s.to,r=t.state;if((r&e)!==n.Invalid)continue;const o=r&~n.Invalid|n.Changed;t.state=o,0!==(o&n.Watcher)&&(u=O(t,u,i))}if(null!==u)throw u}const H=[],P=[];function j(t,i,u){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|u:((t,n)=>{if(null===n)return!1;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function A(t,i,e,u,s){const r=H,o=P,l=r.length;let c=l,f=k;for(;;){const h=t.to,a=h.state;let d=0;if(d=0===(a&C)?a|f:j(t,a,f),0!==d)if(h.state=d,0!==(d&n.Watcher))u=O(h,u,s);else{const n=h.firstOut;if(null!==n){null!==i&&(r[c]=i,o[c++]=e),t=n,i=n.nextOut,f=e=k;continue}}if(null!==i)t=i,f=e;else{if(l>=c)return r.length=o.length=l,u;t=r[--c],f=e=o[c]}i=t.nextOut}}function T(t,i,u){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|u:((t,n)=>{if(null===n)return!1;if(t===n)return!0;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function W(t,n,i){const e=R(n,i);return!e||null===t.prevOut&&null===t.nextOut||D(n,i),e}function B(t,i,u,s,r,o){let l=!1;t:for(;;){const c=t.from,f=c.state;if(0!==(i.state&n.Changed))l=!0;else if(0!==(f&n.Changed))l=W(t,c,u);else if(0!==(f&e)){const n=c.firstIn;if(null!==n){s[r++]=t,t=n,i=c;continue}l=W(t,c,u)}if(!l){const e=t.nextIn;if(null!==e){t=e;continue}i.state&=~n.Invalid}for(;r>o;){const e=s[--r];if(l?l=W(e,i,u):i.state&=~n.Invalid,i=e.to,!l){const n=e.nextIn;if(null!==n){t=n;continue t}}}return l}}const L=[],U=n.Producer|n.Disposed,Z=n.Invalid|n.Visited;function q(t){const i=t.state;if(0!==(i&U))return!1;if(0!==(i&n.Changed)||(i&Z)===Z)return!0;const u=t.firstIn;return null===u?(t.state=i&~n.Invalid,!1):((t,i,u)=>{const s=L,r=s.length;let o=r,l=i,c=t,f=!1;for(;;){if(null!==l.nextIn)return B(l,c,u,s,o,r);if(0!==(c.state&n.Changed)){f=!0;break}const t=l.from,i=t.state;if(0!==(i&n.Changed)&&(f=W(l,t,u),f||null===l.nextIn))break;if(0!==(i&e)){const n=t.firstIn;if(null!==n){if(null!==n.nextIn){s[o++]=l;const i=B(n,t,u,s,o,r);return s.length=r,i}s[o++]=l,l=n,c=t;continue}f=W(l,t,u);break}if(c.state&=~n.Invalid,o===r)return!1;l=s[--o],c=l.to}for(;o>r;){const t=s[--o];f?f=W(t,c,u):c.state&=~n.Invalid,c=t.to}return f||(c.state&=~n.Invalid),s.length=r,f})(t,u,S())}var z;function F(t,n=S()){return c(t)||w(t,n),t.payload}function M(t,i){const u=t.state;return 0!==(u&n.Disposed)||0!==(u&e)&&(0!==(u&n.Changed)||q(t)?R(t,i)&&D(t,i):l(t)),t.payload}function V(t,n=z.lazy,i=S()){if(n===z.eager){const n=i.activeComputed;let e;i.activeComputed=null;try{e=M(t,i)}finally{i.activeComputed=n}return e}const e=M(t,i);return c(t)||w(t,i),e}function G(t,i,e=I,u=S()){if(c(t))return;if(e(t.payload,i))return;t.payload=i;const s=t.firstOut;if(null!==s){u.enterPropagation();try{((t,i=k,e=S())=>{if(0!==(t.from.state&n.Disposed))return;const u=((t,i,e,u)=>{for(;;){const s=t.to,r=s.state;let o=0;o=0===(r&C)?r|i:T(t,r,i);const l=t.nextOut;if(o)if(s.state=o,0!==(o&n.Watcher))e=O(s,e,u);else{const n=s.firstOut;if(null!==n){if(null!==l)return A(n,l,i,e,u);t=n,i=k;continue}}if(null===l)return e;t=l}})(t,i,null,e);if(null!==u)throw u})(s,m,u)}finally{u.leavePropagation()}}}function J(t,i=S()){const u=t.state;if(0!==(u&n.Disposed))return;if(0===(u&e)||0===(u&n.Changed)&&!q(t))return void l(t);const s="function"==typeof t.payload?t.payload:null;if(t.payload=p,(t=>{t.state&=~n.Visited})(t),l(t),s?.(),c(t))return;let r=!1;((t,i,e=S())=>{const u=t.compute,s=N(t,e);let r;try{return r=u(),s(),x(t,e),i(r)}catch(t){throw s(),t}finally{t.state&=~n.Tracking,o(t),e.maybeNotifySettled()}})(t,n=>{c(t)||(r="function"==typeof n,r&&(t.payload=n))},i)}(t=>{t[t.lazy=1]="lazy",t[t.eager=2]="eager"})(z||(z={}));class EventSource{constructor(){this.dispatchDepth=0,this.head=null,this.tail=null,this.pendingHead=null}}const K=Symbol("EventSubscriber.owner");function Q(t){return t[K]}function X(t,n){t[K]=n}function Y(t){return t()}function $(t,n){const i=n.prev,e=n.next;null===i?t.head=e:i.next=e,null===e?t.tail=i:e.prev=i,n.prev=null,n.next=null,n.unlinkNext=null,X(n,null)}function _(t,n,i=Y){i(()=>{const i=t.tail;if(null!==i){++t.dispatchDepth;try{let e=t.head;for(;null!==e;){const t=e===i?null:e.next;1&e.state&&e.fn(n),e=t}}finally{--t.dispatchDepth,0===t.dispatchDepth&&null!==t.pendingHead&&(t=>{let n=t.pendingHead;for(t.pendingHead=null;null!==n;){const i=n.unlinkNext;n.unlinkNext=null,$(t,n),n=i}})(t)}}})}const tt=Symbol("UNINITIALIZED");function nt(t){return new ReactiveNode(tt,t,s)}function it(t){t.state&=~n.Scheduled}function et(t){const n=new ReactiveNode(null,t,r),i=S();J(n,i);const e=()=>(t=>{y(t);const n="function"==typeof t.payload?t.payload:null;n?.(),t.payload=p})(n);return i.registerWatcherCleanup(e),e}class EffectScheduler{constructor(t,n){this.queue=[],this.head=0,this.batchDepth=0,this.phase=0,this.mode=t,this.context=n}getContext(){return this.context??S()}enqueue(t){this.isNodeIgnored(t)||((t=>{t.state|=n.Scheduled})(t),this.queue.push(t),this.shouldAutoFlush()&&this.flush())}batch(t){this.enterBatch();try{return t()}finally{this.leaveBatch()}}flush(){if(2&this.phase)return;if(!this.hasPending())return;this.phase=2;const t=this.getContext();try{for(;this.queue.length>this.head;){const n=this.queue[this.head++];it(n),this.shouldSkipNode(n)||J(n,t)}}finally{this.queue.length=0,this.head=0,this.phase=this.batchDepth>0?1:0,0===this.phase&&this.shouldAutoFlush()&&this.flush()}}reset(){this.queue.length=0,this.head=0,this.batchDepth=0,this.phase=0}notifySettled(){this.shouldAutoFlush()&&this.flush()}hasPending(){return this.queue.length>this.head}isNodeIgnored(t){return 0!==(t.state&n.Disposed)||0!==(t.state&n.Scheduled)}shouldSkipNode(t){return 0!==(t.state&n.Disposed)||0===(t.state&e)}shouldAutoFlush(){const t=this.getContext();return 1===this.mode&&0===this.phase&&0===t.propagationDepth&&null===t.activeComputed&&this.hasPending()}enterBatch(){++this.batchDepth,2!==this.phase&&(this.phase=1)}leaveBatch(){--this.batchDepth,0===this.batchDepth&&2!==this.phase&&(this.phase=0,this.shouldAutoFlush()&&this.flush())}}class EventDispatcher{constructor(t=Y){this.queue=[],this.head=0,this.flushing=!1,this.flush=()=>{if(!this.flushing){this.flushing=!0;try{const t=this.queue;for(;t.length>this.head;)_(t[this.head++],t[this.head++])}finally{this.queue.length=0,this.head=0,this.flushing=!1}}},this.runBoundary=t}emit(t,n){this.queue.push(t,n),this.flushing||this.runBoundary(this.flush)}}function ut(t){const{scheduler:n,dispatcher:i,executionContext:e}=(t=>{const n=t?.hooks,i=g(),e=new EffectScheduler((u=t?.effectStrategy,"eager"===u?1:0),i);var u;const s=new EventDispatcher(t=>e.batch(t));return i.setHooks({...n,onEffectInvalidated(t){e.enqueue(t),n?.onEffectInvalidated?.(t)},onReactiveSettled(){e.notifySettled(),n?.onReactiveSettled?.()}}),{scheduler:e,dispatcher:s,executionContext:i}})(t);return e.resetState(),(t=>{E=t})(e),{get ctx(){return e},event(){const t=new EventSource;return{subscribe(n){return((t,n)=>{const i={fn:n,next:null,prev:null,state:1,unlinkNext:null};return((t,n)=>{if(!(1&n.state))return;if(null!=Q(n))return;const i=t.tail;n.prev=i,n.next=null,n.unlinkNext=null,X(n,t),null!==i?(i.next=n,t.tail=n):t.head=t.tail=n})(t,i),()=>{((t,n)=>{Q(n)===t&&1&n.state&&(n.state&=-2,0===t.dispatchDepth?(n.state|=2,$(t,n)):((t,n)=>{2&n.state||(n.state|=2,n.unlinkNext=t.pendingHead,t.pendingHead=n)})(t,n))})(t,i)}})(t,n)},emit(n){i.emit(t,n)}}},flush(){n.flush()}}}function st(t){const n=nt(t);return()=>V(n)}function rt(t){const n=nt(t);return V(n,z.eager),()=>V(n)}function ot(t){return{subscribe:t}}function lt(t,n){let i,e=!0,u=!1;const s=()=>{if(!e)return;e=!1;const t=i;void 0!==t?(i=void 0,t()):u=!0};if(i=t.subscribe(t=>{e&&(s(),n(t))}),u){const t=i;i=void 0,t?.()}return s}function ct(t,n){return ot(i=>t.subscribe(t=>{i(n(t))}))}function ft(t,n){return ot(i=>t.subscribe(t=>{n(t)&&i(t)}))}function ht(...t){return ot(n=>{const i=t.map(t=>t.subscribe(t=>{n(t)}));return()=>{for(const t of i)t()}})}function at(t,n,i){return vt(t,n,i)}function dt(t,n){return vt(t,n,(t,n)=>n)}function vt(t,n,i){const e=new ReactiveNode(n,null,u);let s=t.subscribe(t=>{c(e)||G(e,i(e.payload,t))});return[()=>c(e)?e.payload:F(e),()=>{(t=>{y(t)})(e);const t=s;s=void 0,t?.()}]}function yt(t,n){const i=new ReactiveNode(t,null,u);return[()=>F(i),t=>{const n="function"!=typeof t?t:t(i.payload);return G(i,n),n}]}export{st as computed,ut as createRuntime,et as effect,ft as filter,dt as hold,ct as map,rt as memo,ht as merge,at as scan,yt as signal,lt as subscribeOnce};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class ReactiveEdge{constructor(t,n,i,e,s,r){this.from=t,this.to=n,this.prevOut=i,this.nextOut=e,this.prevIn=s,this.nextIn=r}}function t(t){t.prevOut=null,t.nextOut=null,t.prevIn=null,t.nextIn=null}var n;(t=>{t[t.Producer=1]="Producer",t[t.Consumer=2]="Consumer",t[t.Watcher=4]="Watcher",t[t.Invalid=8]="Invalid",t[t.Changed=16]="Changed",t[t.Visited=32]="Visited",t[t.Disposed=64]="Disposed",t[t.Computing=128]="Computing",t[t.Scheduled=256]="Scheduled",t[t.Tracking=512]="Tracking"})(n||(n={}));const i=n.Producer|n.Consumer|n.Watcher,e=n.Invalid|n.Changed,s=n.Producer,r=n.Changed|n.Watcher;function o(t){t.state&=~n.Computing}function u(t){t.state&=~e}function l(t){return 0!==(t.state&n.Disposed)}function c(t,n,i){const e=i?i.nextIn:t.firstIn;n.prevIn=i,n.nextIn=e,e?e.prevIn=n:t.lastIn=n,i?i.nextIn=n:t.firstIn=n}function h(t,n){const{prevIn:i,nextIn:e}=n;i?i.nextIn=e:t.firstIn=e,e?e.prevIn=i:t.lastIn=i}function f(t,n){const{prevOut:i,nextOut:e}=n;i?i.nextOut=e:t.firstOut=e,e?e.prevOut=i:t.lastOut=i}function a(t,n,i=n.lastIn){const e=t.lastOut,s=i?i.nextIn:n.firstIn,r=new ReactiveEdge(t,n,e,null,i,s);return e?e.nextOut=r:t.firstOut=r,t.lastOut=r,s?s.prevIn=r:n.lastIn=r,i?i.nextIn=r:n.firstIn=r,r}function d(t,n,i,e){for(let s=e?e.nextIn:n.firstIn;null!==s;s=s.nextIn)if(s.from===t)return s.prevIn!==i&&(h(n,s),c(n,s,i)),s;return a(t,n,i)}function v(e){l(e)||((t=>{t.state=t.state&i|n.Disposed})(e),e.depsTail=null,(n=>{let i=n.firstIn;for(n.firstIn=n.lastIn=n.depsTail=null;i;){const n=i.nextIn;f(i.from,i),t(i),i=n}})(e),(n=>{let i=n.firstOut;for(n.firstOut=n.lastOut=null;i;){const n=i.nextOut;i.to.depsTail===i&&(i.to.depsTail=i.prevIn),h(i.to,i),t(i),i=n}})(e),e.compute=null)}const p=Symbol.for("UNINITIALIZED");class ReactiveNode{constructor(t,n,i){this.state=i,this.compute=n,this.firstOut=null,this.firstIn=null,this.lastOut=null,this.lastIn=null,this.depsTail=null,this.payload=t}}function y(t,n){if(!Object.hasOwn(t,n))return;const i=t[n];return"function"==typeof i?i:void 0}class ExecutionContext{constructor(t={}){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null,this.onEffectInvalidatedHook=void 0,this.onReactiveSettledHook=void 0,this.hookMask=0,this.hooks={},Object.defineProperties(this.hooks,{onEffectInvalidated:{enumerable:!0,get:()=>this.onEffectInvalidatedHook,set:t=>{this.setOnEffectInvalidatedHook(t)}},onReactiveSettled:{enumerable:!0,get:()=>this.onReactiveSettledHook,set:t=>{this.setOnReactiveSettledHook(t)}}}),this.setHooks(t)}dispatchWatcherEvent(t){const n=this.onEffectInvalidatedHook;void 0!==n&&n?.(t)}maybeNotifySettled(){if(!(2&this.hookMask))return;if(0!==this.propagationDepth||null!==this.activeComputed)return;const t=this.onReactiveSettledHook;t?.()}enterPropagation(){++this.propagationDepth}leavePropagation(){this.propagationDepth>0&&--this.propagationDepth,this.maybeNotifySettled()}resetState(){this.activeComputed=null,this.propagationDepth=0,this.cleanupRegistrar=null}setHooks(t={}){const n=y(t,"onEffectInvalidated"),i=y(t,"onReactiveSettled");this.hooks.onEffectInvalidated=n,this.hooks.onReactiveSettled=i}registerWatcherCleanup(t){this.cleanupRegistrar?.(t)}withCleanupRegistrar(t,n){const i=this.cleanupRegistrar;this.cleanupRegistrar=t;try{return n()}finally{this.cleanupRegistrar=i}}setOnEffectInvalidatedHook(t){this.onEffectInvalidatedHook="function"==typeof t?t:void 0,this.updateHookMask(1,void 0!==this.onEffectInvalidatedHook)}setOnReactiveSettledHook(t){this.onReactiveSettledHook="function"==typeof t?t:void 0,this.updateHookMask(2,void 0!==this.onReactiveSettledHook)}updateHookMask(t,n){this.hookMask=n?this.hookMask|t:this.hookMask&~t}}let R=((t={})=>new ExecutionContext(t))(void 0);function g(){return R}const b=Object.is;function k(n,i=g()){const e=n.depsTail,s=null===e?n.firstIn:e.nextIn;null!==s&&(null===e?(n.firstIn=null,n.lastIn=null):(e.nextIn=null,n.lastIn=e),(n=>{for(;n;){const i=n.nextIn;f(n.from,n),t(n),n=i}})(s))}function w(t,i){t.depsTail=null,t.state=t.state&~n.Visited|n.Tracking,(t=>{t.state|=n.Computing})(t);const e=i.activeComputed;i.activeComputed=t;let s=!1;return()=>{s||(s=!0,i.activeComputed=e)}}function I(t,i=g()){if(l(t))return!1;const e=t.payload;let s=e,r=!1;return s=((t,i=g())=>{const e=t.compute,s=w(t,i);let r;try{return r=e(),s(),k(t,i),r}catch(t){throw s(),t}finally{t.state&=~n.Tracking,o(t),i.maybeNotifySettled()}})(t,i),l(t)||(r=!b(e,s),t.payload=s),u(t),r}const C=n.Invalid,E=n.Changed,S=n.Invalid|n.Changed|n.Disposed|n.Visited|n.Tracking;function j(t,n,i){const e=i.onEffectInvalidatedHook;if(void 0===e)return n;try{e(t)}catch(t){return n??t}return n}function m(t,i=g()){if(0!==(t.state&n.Disposed))return;let s=null;for(let r=t.firstOut;null!==r;r=r.nextOut){const t=r.to,o=t.state;if((o&e)!==n.Invalid)continue;const u=o&~n.Invalid|n.Changed;t.state=u,0!==(u&n.Watcher)&&(s=j(t,s,i))}if(null!==s)throw s}const x=[],O=[];function N(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function H(t,i,e,s,r){const o=x,u=O,l=o.length;let c=l,h=C;for(;;){const f=t.to,a=f.state;let d=0;if(d=0===(a&S)?a|h:N(t,a,h),0!==d)if(f.state=d,0!==(d&n.Watcher))s=j(f,s,r);else{const n=f.firstOut;if(null!==n){null!==i&&(o[c]=i,u[c++]=e),t=n,i=n.nextOut,h=e=C;continue}}if(null!==i)t=i,h=e;else{if(l>=c)return o.length=u.length=l,s;t=o[--c],h=e=u[c]}i=t.nextOut}}function L(t,i,s){return 0!==(i&(e|n.Disposed))?0:0===(i&n.Tracking)?i&~n.Visited|s:((t,n)=>{if(null===n)return!1;if(t===n)return!0;for(let i=t.prevIn;null!==i;i=i.prevIn)if(i===n)return!1;return!0})(t,t.to.depsTail)?i|n.Visited|n.Invalid:0}function P(t,n,i){const e=I(n,i);return!e||null===t.prevOut&&null===t.nextOut||m(n,i),e}function W(t,i,s,r,o,u){let l=!1;t:for(;;){const c=t.from,h=c.state;if(0!==(i.state&n.Changed))l=!0;else if(0!==(h&n.Changed))l=P(t,c,s);else if(0!==(h&e)){const n=c.firstIn;if(null!==n){r[o++]=t,t=n,i=c;continue}l=P(t,c,s)}if(!l){const e=t.nextIn;if(null!==e){t=e;continue}i.state&=~n.Invalid}for(;o>u;){const e=r[--o];if(l?l=P(e,i,s):i.state&=~n.Invalid,i=e.to,!l){const n=e.nextIn;if(null!==n){t=n;continue t}}}return l}}const q=[],A=n.Producer|n.Disposed,D=n.Invalid|n.Visited;var T;function z(t,n=g()){return l(t)||((t,n=g())=>{const i=n.activeComputed;if(!i)return;const e=l(t),s=l(i);if(e||s)return;const r=i.depsTail;if(null===r){const n=i.firstIn;return null===n?void(i.depsTail=a(t,i,null)):n.from===t?void(i.depsTail=n):void(i.depsTail=d(t,i,null,n))}if(r.from===t)return;const o=r.nextIn;i.depsTail=null===o||o.from!==t?d(t,i,r,o):o})(t,n),t.payload}function M(t,i,e=b,s=g()){if(l(t))return;if(e(t.payload,i))return;t.payload=i;const r=t.firstOut;if(null!==r){s.enterPropagation();try{((t,i=C,e=g())=>{if(0!==(t.from.state&n.Disposed))return;const s=((t,i,e,s)=>{for(;;){const r=t.to,o=r.state;let u=0;u=0===(o&S)?o|i:L(t,o,i);const l=t.nextOut;if(u)if(r.state=u,0!==(u&n.Watcher))e=j(r,e,s);else{const n=r.firstOut;if(null!==n){if(null!==l)return H(n,l,i,e,s);t=n,i=C;continue}}if(null===l)return e;t=l}})(t,i,null,e);if(null!==s)throw s})(r,E,s)}finally{s.leavePropagation()}}}function U(){return new ReactiveNode(0,null,s)}function V(t){return new ReactiveNode(null,t,r)}function Z(t){return"pending"===t.status()}(t=>{t[t.lazy=1]="lazy",t[t.eager=2]="eager"})(T||(T={}));class ResourceRequest{constructor(t,n){this.owner=t,this.token=n}alive(){return this.owner.isAlive(this.token)}resolve(t){return this.owner.resolve(this.token,t)}reject(t){return this.owner.reject(this.token,t)}}class ResourceCore{track(){z(this.stateNode,this.context)}bump(){M(this.stateNode,this.stateNode.payload+1)}isAlive(t){return!this.disposed&&this.token===t}start(){const t=this.disposed?this.token:this.token+1;return this.disposed||(this.token=t,this.status="pending",this.error=void 0,this.bump()),new ResourceRequest(this,t)}clear(){this.disposed||(this.token+=1,this.status="idle",this.value=void 0,this.error=void 0,this.bump())}dispose(){this.disposed||(this.disposed=!0,this.token+=1,this.status="idle",this.value=void 0,this.error=void 0,this.bump(),null!==this.watcher&&((t=>{v(t);const n="function"==typeof t.payload?t.payload:null;n?.(),t.payload=p})(this.watcher),this.watcher=null),null!==this.refetchNode&&(v(this.refetchNode),this.refetchNode=null))}resolve(t,n){return!!this.isAlive(t)&&(this.status="resolved",this.value=n,this.error=void 0,this.bump(),!0)}reject(t,n){return!!this.isAlive(t)&&(this.status="rejected",this.error=n,this.bump(),!0)}settle(t,n){var i;"object"==typeof(i=t)&&null!==i&&"then"in i&&"function"==typeof i.then?t.then(t=>{n.resolve(t)},t=>{n.reject(t)}):n.resolve(t)}runLoad(t){const n=this.start();let i;try{i=t(n)}catch(t){return void n.reject(t)}this.settle(i,n)}runSourceLoad(t,n){const i=this.start();let e;try{e=n(t,i)}catch(t){return void i.reject(t)}this.settle(e,i)}refetch(){this.disposed||null===this.refetchNode||M(this.refetchNode,this.refetchNode.payload+1)}constructor(){this.context=g(),this.stateNode=U(),this.status="idle",this.value=void 0,this.error=void 0,this.token=0,this.disposed=!1,this.watcher=null,this.refetchNode=null}}function B(t,i){const s=new ResourceCore,r={status:()=>(s.track(),s.status),value:()=>(s.track(),s.value),error:()=>(s.track(),s.error),token:()=>(s.track(),s.token),clear(){s.clear()},dispose(){s.dispose()}};if("function"==typeof t){if(s.refetchNode=U(),"function"==typeof i){const n=t,e=i;s.watcher=V(()=>{let t;z(s.refetchNode,s.context);try{t=n()}catch(t){return void s.start().reject(t)}s.runSourceLoad(t,e)})}else{const n=t;s.watcher=V(()=>{z(s.refetchNode,s.context),s.runLoad(n)})}return((t,i=g())=>{const s=t.state;if(0!==(s&n.Disposed))return;if(0===(s&e)||0===(s&n.Changed)&&!(t=>{const i=t.state;if(0!==(i&A))return!1;if(0!==(i&n.Changed)||(i&D)===D)return!0;const s=t.firstIn;return null===s?(t.state=i&~n.Invalid,!1):((t,i,s)=>{const r=q,o=r.length;let u=o,l=i,c=t,h=!1;for(;;){if(null!==l.nextIn)return W(l,c,s,r,u,o);if(0!==(c.state&n.Changed)){h=!0;break}const t=l.from,i=t.state;if(0!==(i&n.Changed)&&(h=P(l,t,s),h||null===l.nextIn))break;if(0!==(i&e)){const n=t.firstIn;if(null!==n){if(null!==n.nextIn){r[u++]=l;const i=W(n,t,s,r,u,o);return r.length=o,i}r[u++]=l,l=n,c=t;continue}h=P(l,t,s);break}if(c.state&=~n.Invalid,u===o)return!1;l=r[--u],c=l.to}for(;u>o;){const t=r[--u];h?h=P(t,c,s):c.state&=~n.Invalid,c=t.to}return h||(c.state&=~n.Invalid),r.length=o,h})(t,s,g())})(t))return void u(t);const r="function"==typeof t.payload?t.payload:null;if(t.payload=p,(t=>{t.state&=~n.Visited})(t),u(t),r?.(),l(t))return;let c=!1;((t,i,e=g())=>{const s=t.compute,r=w(t,e);let u;try{return u=s(),r(),k(t,e),i(u)}catch(t){throw r(),t}finally{t.state&=~n.Tracking,o(t),e.maybeNotifySettled()}})(t,n=>{l(t)||(c="function"==typeof n,c&&(t.payload=n))},i)})(s.watcher,s.context),s.context.registerWatcherCleanup(()=>{s.dispose()}),{...r,refetch(){s.refetch()}}}return s.context.registerWatcherCleanup(()=>{s.dispose()}),{...r,start(){return s.start()}}}export{Z as isPending,B as resource};
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cleanup function returned from an effect.
|
|
5
|
+
*/
|
|
6
|
+
type Destructor = () => void;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Effect callback.
|
|
10
|
+
* May return a cleanup function.
|
|
11
|
+
*/
|
|
12
|
+
type EffectFn = () => void | Destructor;
|
|
13
|
+
|
|
14
|
+
type AnyFn = (...args: unknown[]) => unknown;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Direct value that can be assigned via `.set(value)`.
|
|
18
|
+
* Function values are excluded to avoid ambiguity with updater functions.
|
|
19
|
+
*/
|
|
20
|
+
type DirectValue<T> = Exclude<T, AnyFn>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Functional updater.
|
|
24
|
+
*/
|
|
25
|
+
type Updater<T> = (prev: T) => T;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Accepted input for writable reactive values.
|
|
29
|
+
*/
|
|
30
|
+
type SetInput<T> = DirectValue<T> | Updater<T>;
|
|
31
|
+
|
|
32
|
+
interface RequiredSetter<T> {
|
|
33
|
+
(value: SetInput<T>): T;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface OptionalSetter<T> {
|
|
37
|
+
(): T;
|
|
38
|
+
(value: SetInput<T>): T;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* If T includes undefined, calling `set()` with no arguments is allowed.
|
|
43
|
+
*/
|
|
44
|
+
type Setter<T> = undefined extends T ? OptionalSetter<T> : RequiredSetter<T>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Nominal brand helper for semantically distinct reactive primitives.
|
|
48
|
+
*/
|
|
49
|
+
interface Brand<K extends string> {
|
|
50
|
+
readonly __brand?: K;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Callable tracked read.
|
|
55
|
+
*/
|
|
56
|
+
interface Accessor<T> {
|
|
57
|
+
(): T;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Property-based read.
|
|
62
|
+
*/
|
|
63
|
+
interface ValueReadable<T> {
|
|
64
|
+
readonly value: T;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Untracked read.
|
|
69
|
+
*/
|
|
70
|
+
interface Peekable<T> {
|
|
71
|
+
peek(): T;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Writable capability.
|
|
76
|
+
*/
|
|
77
|
+
interface Writable<T> {
|
|
78
|
+
set: Setter<T>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface Disposable {
|
|
82
|
+
(): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Standard readable reactive value.
|
|
87
|
+
*/
|
|
88
|
+
interface Readable<T> extends Accessor<T>, ValueReadable<T> {}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Readable value with untracked read.
|
|
92
|
+
*/
|
|
93
|
+
interface PeekableReadable<T> extends Readable<T>, Peekable<T> {}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Writable signal-like value.
|
|
97
|
+
*/
|
|
98
|
+
interface WritableReadable<T> extends Readable<T>, Writable<T> {}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Writable signal-like value with untracked read.
|
|
102
|
+
*/
|
|
103
|
+
interface PeekableWritableReadable<T>
|
|
104
|
+
extends WritableReadable<T>,
|
|
105
|
+
Peekable<T> {}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Mutable signal.
|
|
109
|
+
*/
|
|
110
|
+
interface Signal<T> extends PeekableWritableReadable<T>, Brand<"signal"> {}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Computed reactive value.
|
|
114
|
+
*/
|
|
115
|
+
interface Computed<T> extends PeekableReadable<T>, Brand<"computed"> {}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Memoized derived value.
|
|
119
|
+
*/
|
|
120
|
+
interface Memo<T> extends PeekableReadable<T>, Brand<"memo"> {}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Derived reactive value.
|
|
124
|
+
*/
|
|
125
|
+
interface Derived<T> extends PeekableReadable<T>, Brand<"derived"> {}
|
|
126
|
+
|
|
127
|
+
interface Effect<T> extends Readable<T>, Brand<"effect">, Disposable {}
|
|
128
|
+
|
|
129
|
+
interface Scan<T> extends Readable<T>, Brand<"scan">, Disposable {}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Push-based realtime source.
|
|
133
|
+
*/
|
|
134
|
+
interface Realtime<T> extends PeekableWritableReadable<T>, Brand<"realtime"> {
|
|
135
|
+
subscribe(cb: () => void): () => void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Async iterable stream source.
|
|
140
|
+
*/
|
|
141
|
+
interface Stream<T> extends PeekableWritableReadable<T>, Brand<"stream"> {
|
|
142
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Common readonly view over reactive values.
|
|
147
|
+
*/
|
|
148
|
+
type ReadableLike<T> =
|
|
149
|
+
| Signal<T>
|
|
150
|
+
| Computed<T>
|
|
151
|
+
| Memo<T>
|
|
152
|
+
| Derived<T>
|
|
153
|
+
| Realtime<T>
|
|
154
|
+
| Stream<T>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Common writable view over reactive values.
|
|
158
|
+
*/
|
|
159
|
+
type WritableLike<T> = Signal<T> | Realtime<T> | Stream<T>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Extract value type from a reactive value.
|
|
163
|
+
*/
|
|
164
|
+
type ValueOf<T> =
|
|
165
|
+
T extends Accessor<infer V>
|
|
166
|
+
? V
|
|
167
|
+
: T extends ValueReadable<infer V>
|
|
168
|
+
? V
|
|
169
|
+
: never;
|
|
170
|
+
|
|
171
|
+
const __DEV__: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
declare function computed<T>(fn: () => T): Accessor<T>;
|
|
175
|
+
declare function memo<T>(fn: () => T): Accessor<T>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Marker interface for all reactive entities participating
|
|
179
|
+
* in the runtime graph.
|
|
180
|
+
*
|
|
181
|
+
* Architectural role:
|
|
182
|
+
* - Defines the common root type for nodes, signals,
|
|
183
|
+
* computations, effects, and other reactive primitives.
|
|
184
|
+
* - Enables structural polymorphism across the runtime graph.
|
|
185
|
+
*
|
|
186
|
+
* Semantics:
|
|
187
|
+
* - This interface intentionally declares no members.
|
|
188
|
+
* - Concrete reactive types define their own operational
|
|
189
|
+
* state and invariants.
|
|
190
|
+
*
|
|
191
|
+
* Design intent:
|
|
192
|
+
* - Acts as a type-level boundary for the reactive subsystem.
|
|
193
|
+
* - Prevents non-reactive structures from being treated
|
|
194
|
+
* as runtime graph participants.
|
|
195
|
+
*
|
|
196
|
+
* Runtime guarantees:
|
|
197
|
+
* - Implementations must participate in the propagation model.
|
|
198
|
+
* - Lifecycle, scheduling, and versioning policies are defined
|
|
199
|
+
* by the runtime layer, not by this interface.
|
|
200
|
+
*
|
|
201
|
+
* Note:
|
|
202
|
+
* This is a nominal grouping construct, not a behavioral contract.
|
|
203
|
+
*/
|
|
204
|
+
interface Reactivable {
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
type ComputeFn<T> = (() => T) | null;
|
|
208
|
+
declare class ReactiveNode<T = unknown> implements Reactivable {
|
|
209
|
+
state: number;
|
|
210
|
+
compute: ComputeFn<T>;
|
|
211
|
+
firstOut: ReactiveEdge | null;
|
|
212
|
+
firstIn: ReactiveEdge | null;
|
|
213
|
+
lastOut: ReactiveEdge | null;
|
|
214
|
+
lastIn: ReactiveEdge | null;
|
|
215
|
+
depsTail: ReactiveEdge | null;
|
|
216
|
+
payload: T;
|
|
217
|
+
constructor(payload: T | undefined, compute: ComputeFn<T>, state: number);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Plain-object edge shaped after alien-signals' Link.
|
|
222
|
+
* A single edge lives in both the source's outgoing list and the target's
|
|
223
|
+
* incoming list, so pointer rewrites must keep both views in sync.
|
|
224
|
+
*/
|
|
225
|
+
declare class ReactiveEdge {
|
|
226
|
+
from: ReactiveNode;
|
|
227
|
+
to: ReactiveNode;
|
|
228
|
+
prevOut: ReactiveEdge | null;
|
|
229
|
+
nextOut: ReactiveEdge | null;
|
|
230
|
+
prevIn: ReactiveEdge | null;
|
|
231
|
+
nextIn: ReactiveEdge | null;
|
|
232
|
+
constructor(from: ReactiveNode, to: ReactiveNode, prevOut: ReactiveEdge | null, nextOut: ReactiveEdge | null, prevIn: ReactiveEdge | null, nextIn: ReactiveEdge | null);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface EngineHooks {
|
|
236
|
+
onEffectInvalidated?(node: ReactiveNode): void;
|
|
237
|
+
onReactiveSettled?(): void;
|
|
238
|
+
}
|
|
239
|
+
type CleanupRegistrar = (cleanup: () => void) => void;
|
|
240
|
+
type OnEffectInvalidatedHook = EngineHooks["onEffectInvalidated"];
|
|
241
|
+
type OnReactiveSettledHook = EngineHooks["onReactiveSettled"];
|
|
242
|
+
/**
|
|
243
|
+
* ExecutionContext управляет состоянием вычисления и уведомлениями host'у.
|
|
244
|
+
*
|
|
245
|
+
* Ключевые принципы:
|
|
246
|
+
* - Контекст НЕ глобальный - это объект, передаваемый по параметрам
|
|
247
|
+
* - Host полностью контролирует scheduling эффектов
|
|
248
|
+
* - Контекст только отслеживает текущее состояние вычисления
|
|
249
|
+
*
|
|
250
|
+
* Поля:
|
|
251
|
+
* - activeComputed: текущий узел в процессе вычисления (для trackRead)
|
|
252
|
+
* - propagationDepth: глубина каскада инвалидаций
|
|
253
|
+
* - cleanupRegistrar: функция для регистрации cleanup в эффектах
|
|
254
|
+
*/
|
|
255
|
+
declare class ExecutionContext {
|
|
256
|
+
activeComputed: ReactiveNode | null;
|
|
257
|
+
propagationDepth: number;
|
|
258
|
+
cleanupRegistrar: CleanupRegistrar | null;
|
|
259
|
+
readonly hooks: EngineHooks;
|
|
260
|
+
onEffectInvalidatedHook: OnEffectInvalidatedHook;
|
|
261
|
+
onReactiveSettledHook: OnReactiveSettledHook;
|
|
262
|
+
private hookMask;
|
|
263
|
+
constructor(hooks?: EngineHooks);
|
|
264
|
+
dispatchWatcherEvent(node: ReactiveNode): void;
|
|
265
|
+
maybeNotifySettled(): void;
|
|
266
|
+
enterPropagation(): void;
|
|
267
|
+
leavePropagation(): void;
|
|
268
|
+
resetState(): void;
|
|
269
|
+
setHooks(hooks?: EngineHooks): void;
|
|
270
|
+
registerWatcherCleanup(cleanup: () => void): void;
|
|
271
|
+
withCleanupRegistrar<T>(registrar: CleanupRegistrar | null, fn: () => T): T;
|
|
272
|
+
private setOnEffectInvalidatedHook;
|
|
273
|
+
private setOnReactiveSettledHook;
|
|
274
|
+
private updateHookMask;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
declare function effect(fn: EffectFn): Destructor;
|
|
279
|
+
|
|
280
|
+
type EffectStrategy = "flush" | "eager";
|
|
281
|
+
|
|
282
|
+
interface RuntimeOptions {
|
|
283
|
+
hooks?: EngineHooks;
|
|
284
|
+
effectStrategy?: EffectStrategy;
|
|
285
|
+
}
|
|
286
|
+
interface Event<T> {
|
|
287
|
+
subscribe(fn: (value: T) => void): Destructor;
|
|
288
|
+
}
|
|
289
|
+
interface EventSource<T> extends Event<T> {
|
|
290
|
+
emit(value: T): void;
|
|
291
|
+
}
|
|
292
|
+
interface Runtime {
|
|
293
|
+
event<T>(): EventSource<T>;
|
|
294
|
+
flush(): void;
|
|
295
|
+
readonly ctx: ExecutionContext;
|
|
296
|
+
}
|
|
297
|
+
declare function createRuntime(options?: RuntimeOptions): Runtime;
|
|
298
|
+
|
|
299
|
+
type EventValue<E extends Event<unknown>> = E extends Event<infer T> ? T : never;
|
|
300
|
+
/**
|
|
301
|
+
* Subscribes to the first value from `source`, then unsubscribes automatically.
|
|
302
|
+
*
|
|
303
|
+
* The subscription is disposed before `fn` runs, so nested emits triggered from
|
|
304
|
+
* inside `fn` will not deliver a second time to the same callback.
|
|
305
|
+
*/
|
|
306
|
+
declare function subscribeOnce<T>(source: Event<T>, fn: (value: T) => void): Destructor;
|
|
307
|
+
/**
|
|
308
|
+
* Projects each event value from `source` into a new event stream.
|
|
309
|
+
*/
|
|
310
|
+
declare function map<T, U>(source: Event<T>, project: (value: T) => U): Event<U>;
|
|
311
|
+
/**
|
|
312
|
+
* Forwards only the values from `source` that satisfy `predicate`.
|
|
313
|
+
*/
|
|
314
|
+
declare function filter<T, S extends T>(source: Event<T>, predicate: (value: T) => value is S): Event<S>;
|
|
315
|
+
declare function filter<T>(source: Event<T>, predicate: (value: T) => boolean): Event<T>;
|
|
316
|
+
/**
|
|
317
|
+
* Merges multiple event sources into one event stream.
|
|
318
|
+
*
|
|
319
|
+
* The resulting event preserves the delivery order defined by the upstream
|
|
320
|
+
* sources and their runtime dispatcher.
|
|
321
|
+
*/
|
|
322
|
+
declare function merge<const Sources extends readonly Event<unknown>[]>(...sources: Sources): Event<EventValue<Sources[number]>>;
|
|
323
|
+
/**
|
|
324
|
+
* Creates an accumulator derived from an event stream.
|
|
325
|
+
*
|
|
326
|
+
* `scan` listens to `source` and applies `reducer` to the current accumulated
|
|
327
|
+
* state and each incoming event value. The result becomes the next stored state.
|
|
328
|
+
*
|
|
329
|
+
* It is analogous to `Array.prototype.reduce`, but for a stream of events over time.
|
|
330
|
+
*
|
|
331
|
+
* @typeParam T - Event payload type.
|
|
332
|
+
* @typeParam A - Accumulator state type.
|
|
333
|
+
*
|
|
334
|
+
* @param source - Event source to subscribe to.
|
|
335
|
+
* @param seed - Initial accumulator state used before the first event arrives.
|
|
336
|
+
* @param reducer - Pure function that receives the current accumulated state and
|
|
337
|
+
* the next event value, and returns the next accumulated state.
|
|
338
|
+
*
|
|
339
|
+
* @returns A tuple:
|
|
340
|
+
* - `read` - accessor that returns the current accumulated state.
|
|
341
|
+
* - `dispose` - destructor that unsubscribes from the source and disposes the internal node.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* const rt = createRuntime();
|
|
346
|
+
* const increments = rt.event<number>();
|
|
347
|
+
*
|
|
348
|
+
* const [total, dispose] = scan(increments, 0, (acc, value) => acc + value);
|
|
349
|
+
*
|
|
350
|
+
* increments.emit(1);
|
|
351
|
+
* increments.emit(2);
|
|
352
|
+
*
|
|
353
|
+
* console.log(total()); // 3
|
|
354
|
+
*
|
|
355
|
+
* dispose();
|
|
356
|
+
* ```
|
|
357
|
+
*
|
|
358
|
+
* @remarks
|
|
359
|
+
* - `seed` is used as the initial state until the first event is delivered.
|
|
360
|
+
* - `reducer` should be pure and synchronous.
|
|
361
|
+
* - `reducer` should derive the next state only from the previous accumulated
|
|
362
|
+
* state and the current event value.
|
|
363
|
+
* - The accumulated value is updated only in response to `source` events.
|
|
364
|
+
* - Do not read signals, computeds, or other reactive values inside
|
|
365
|
+
* `reducer`. `scan` does not track reactive dependencies read there.
|
|
366
|
+
* - If you need to combine event-driven state with reactive state, first
|
|
367
|
+
* derive the accumulator with `scan`, then combine it outside via
|
|
368
|
+
* `computed()`.
|
|
369
|
+
* - To stop receiving updates and release subscriptions, call `dispose`.
|
|
370
|
+
*
|
|
371
|
+
* @see hold
|
|
372
|
+
*/
|
|
373
|
+
declare function scan<T, A>(source: Event<T>, seed: A, reducer: (acc: A, value: T) => A): [read: Accessor<A>, dispose: Destructor];
|
|
374
|
+
/**
|
|
375
|
+
* Stores the latest value emitted by an event source.
|
|
376
|
+
*
|
|
377
|
+
* `hold` is a specialized form of {@link scan} that replaces the current state
|
|
378
|
+
* with each new event value.
|
|
379
|
+
*
|
|
380
|
+
* @typeParam T - Event payload type.
|
|
381
|
+
*
|
|
382
|
+
* @param source - Event source to subscribe to.
|
|
383
|
+
* @param initial - Initial value returned before the first event arrives.
|
|
384
|
+
*
|
|
385
|
+
* @returns A tuple:
|
|
386
|
+
* - `read` - accessor that returns the latest observed event value.
|
|
387
|
+
* - `dispose` - destructor that unsubscribes from the source and disposes the internal node.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```ts
|
|
391
|
+
* const rt = createRuntime();
|
|
392
|
+
* const updates = rt.event<string>();
|
|
393
|
+
*
|
|
394
|
+
* const [latest, dispose] = hold(updates, "idle");
|
|
395
|
+
*
|
|
396
|
+
* console.log(latest()); // "idle"
|
|
397
|
+
*
|
|
398
|
+
* updates.emit("loading");
|
|
399
|
+
* console.log(latest()); // "loading"
|
|
400
|
+
*
|
|
401
|
+
* updates.emit("done");
|
|
402
|
+
* console.log(latest()); // "done"
|
|
403
|
+
*
|
|
404
|
+
* dispose();
|
|
405
|
+
* ```
|
|
406
|
+
*
|
|
407
|
+
* @remarks
|
|
408
|
+
* - `initial` is returned until the first event is delivered.
|
|
409
|
+
* - Equivalent to:
|
|
410
|
+
* `scan(source, initial, (_, value) => value)`
|
|
411
|
+
*
|
|
412
|
+
* @see scan
|
|
413
|
+
*/
|
|
414
|
+
declare function hold<T>(source: Event<T>, initial: T): [read: Accessor<T>, dispose: Destructor];
|
|
415
|
+
|
|
416
|
+
interface SignalOptions {
|
|
417
|
+
name: string;
|
|
418
|
+
}
|
|
419
|
+
declare function signal<T>(initialValue: T, options?: SignalOptions): readonly [value: Accessor<T>, setValue: Setter<T>];
|
|
420
|
+
|
|
421
|
+
export { computed, createRuntime, effect, filter, hold, map, memo, merge, scan, signal, subscribeOnce };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference path="../globals.d.ts" />
|
|
2
|
+
|
|
3
|
+
type ResourceStatus = "idle" | "pending" | "resolved" | "rejected";
|
|
4
|
+
interface ResourceGuard {
|
|
5
|
+
readonly token: number;
|
|
6
|
+
alive(): boolean;
|
|
7
|
+
}
|
|
8
|
+
interface ResourceHandle<T, E = unknown> extends ResourceGuard {
|
|
9
|
+
resolve(value: T): boolean;
|
|
10
|
+
reject(error: E): boolean;
|
|
11
|
+
}
|
|
12
|
+
interface Resource<T, E = unknown> {
|
|
13
|
+
readonly status: Accessor<ResourceStatus>;
|
|
14
|
+
readonly value: Accessor<T | undefined>;
|
|
15
|
+
readonly error: Accessor<E | undefined>;
|
|
16
|
+
readonly token: Accessor<number>;
|
|
17
|
+
clear(): void;
|
|
18
|
+
dispose(): void;
|
|
19
|
+
}
|
|
20
|
+
interface ManualResource<T, E = unknown> extends Resource<T, E> {
|
|
21
|
+
start(): ResourceHandle<T, E>;
|
|
22
|
+
}
|
|
23
|
+
interface AsyncResource<T, E = unknown> extends Resource<T, E> {
|
|
24
|
+
refetch(): void;
|
|
25
|
+
}
|
|
26
|
+
type ResourceJob<T> = (guard: ResourceGuard) => T | PromiseLike<T>;
|
|
27
|
+
type ResourceLoader<S, T> = (source: S, guard: ResourceGuard) => T | PromiseLike<T>;
|
|
28
|
+
declare function isPending(resource: Resource<unknown, unknown>): boolean;
|
|
29
|
+
declare function resource<T, E = unknown>(): ManualResource<T, E>;
|
|
30
|
+
declare function resource<T, E = unknown>(load: ResourceJob<T>): AsyncResource<T, E>;
|
|
31
|
+
declare function resource<S, T, E = unknown>(source: Accessor<S>, load: ResourceLoader<S, T>): AsyncResource<T, E>;
|
|
32
|
+
|
|
33
|
+
export { isPending, resource };
|
|
34
|
+
export type { AsyncResource, ManualResource, Resource, ResourceGuard, ResourceHandle, ResourceJob, ResourceLoader, ResourceStatus };
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@volynets/reflex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Public Reflex facade with a connected runtime",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/cjs/index.cjs",
|
|
8
|
+
"module": "./dist/esm/index.js",
|
|
9
|
+
"types": "./dist/globals.d.ts",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/globals.d.ts",
|
|
14
|
+
"import": "./dist/esm/index.js",
|
|
15
|
+
"require": "./dist/cjs/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./unstable": {
|
|
18
|
+
"types": "./dist/unstable/index.d.ts",
|
|
19
|
+
"import": "./dist/esm/unstable/index.js",
|
|
20
|
+
"require": "./dist/cjs/unstable/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/cjs",
|
|
25
|
+
"dist/esm",
|
|
26
|
+
"dist/globals.d.ts",
|
|
27
|
+
"dist/unstable"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"clean": "node -e \"require('node:fs').rmSync('build',{recursive:true,force:true});require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
34
|
+
"build": "pnpm clean && pnpm build:ts && pnpm build:types && pnpm build:npm",
|
|
35
|
+
"build:ts": "node ../../node_modules/typescript/bin/tsc -p tsconfig.build.json",
|
|
36
|
+
"build:types": "rollup -c rollup.dts.config.ts --configPlugin @rollup/plugin-swc && node ../../scripts/write-globals-dts.mjs . && node ./scripts/write-subpath-dts.mjs .",
|
|
37
|
+
"build:npm": "rollup -c rollup.config.ts --configPlugin @rollup/plugin-swc",
|
|
38
|
+
"build:clean": "pnpm build",
|
|
39
|
+
"lint": "eslint .",
|
|
40
|
+
"lint:fix": "eslint . --fix",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"bench": "vitest bench",
|
|
43
|
+
"dev": "node ../../node_modules/typescript/bin/tsc -w -p tsconfig.build.json",
|
|
44
|
+
"typecheck": "node ../../node_modules/typescript/bin/tsc --noEmit -p tsconfig.typecheck.json",
|
|
45
|
+
"prepublishOnly": "pnpm lint && pnpm typecheck && pnpm build:clean"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@reflex/runtime": "workspace:*"
|
|
49
|
+
}
|
|
50
|
+
}
|