@esportsplus/reactivity 0.30.3 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +14 -11
- package/build/constants.d.ts +2 -1
- package/build/constants.js +2 -1
- package/build/reactive/array.js +3 -0
- package/build/reactive/object.js +7 -9
- package/build/system.d.ts +2 -1
- package/build/system.js +31 -20
- package/build/types.d.ts +2 -3
- package/package.json +1 -1
- package/src/constants.ts +3 -1
- package/src/reactive/array.ts +4 -0
- package/src/reactive/object.ts +12 -12
- package/src/system.ts +42 -28
- package/src/types.ts +2 -8
- package/tests/array.ts +369 -0
- package/tests/async-computed.ts +239 -0
- package/tests/bench/array.ts +77 -0
- package/tests/bench/reactive-object.ts +59 -0
- package/tests/bench/system.ts +142 -1
- package/tests/compiler.ts +326 -0
- package/tests/objects.ts +84 -0
- package/tests/reactive.ts +210 -0
- package/tests/system.ts +509 -0
- package/tests/tsconfig.json +17 -0
package/{readme.md → README.md}
RENAMED
|
@@ -135,7 +135,7 @@ The library requires a build-time transformer to convert `reactive()` calls into
|
|
|
135
135
|
```typescript
|
|
136
136
|
// vite.config.ts
|
|
137
137
|
import { defineConfig } from 'vite';
|
|
138
|
-
import reactivity from '@esportsplus/reactivity/
|
|
138
|
+
import reactivity from '@esportsplus/reactivity/compiler/vite';
|
|
139
139
|
|
|
140
140
|
export default defineConfig({
|
|
141
141
|
plugins: [
|
|
@@ -153,7 +153,7 @@ For direct TypeScript compilation using `ttsc` or `ts-patch`:
|
|
|
153
153
|
{
|
|
154
154
|
"compilerOptions": {
|
|
155
155
|
"plugins": [
|
|
156
|
-
{ "transform": "@esportsplus/reactivity/
|
|
156
|
+
{ "transform": "@esportsplus/reactivity/compiler/tsc" }
|
|
157
157
|
]
|
|
158
158
|
}
|
|
159
159
|
}
|
|
@@ -221,9 +221,9 @@ let user = new ReactiveObject_1();
|
|
|
221
221
|
| `reactive(() => expr)` | Creates a computed value (compile-time only) |
|
|
222
222
|
| `reactive({...})` | Creates a reactive object with signals and computeds |
|
|
223
223
|
| `reactive([...])` | Creates a reactive array |
|
|
224
|
-
| `effect(fn)` | Runs a function that re-executes when dependencies change |
|
|
225
|
-
| `root(fn)` | Creates an untracked scope
|
|
226
|
-
| `onCleanup(fn)` | Registers a cleanup function for the current effect |
|
|
224
|
+
| `effect(fn)` | Runs a function that re-executes when dependencies change. Returns a dispose function |
|
|
225
|
+
| `root(fn)` | Creates an untracked scope. If `fn` accepts an argument, a dispose function is provided |
|
|
226
|
+
| `onCleanup(fn)` | Registers a cleanup function for the current effect/computed |
|
|
227
227
|
|
|
228
228
|
### Low-Level Functions
|
|
229
229
|
|
|
@@ -235,6 +235,7 @@ These are typically only used by the transformer output:
|
|
|
235
235
|
| `computed(fn)` | Creates a raw computed |
|
|
236
236
|
| `read(node)` | Reads a signal or computed value |
|
|
237
237
|
| `write(signal, value)` | Sets a signal value |
|
|
238
|
+
| `asyncComputed(fn)` | Creates a signal that resolves an async computed. Initial value is `undefined` |
|
|
238
239
|
| `dispose(computed)` | Disposes a computed and its dependencies |
|
|
239
240
|
|
|
240
241
|
### Type Guards
|
|
@@ -271,7 +272,9 @@ Symbol constants for type identification:
|
|
|
271
272
|
|------|-------------|
|
|
272
273
|
| `Signal<T>` | Signal node type |
|
|
273
274
|
| `Computed<T>` | Computed node type |
|
|
275
|
+
| `Link` | Dependency graph link between nodes |
|
|
274
276
|
| `Reactive<T>` | Utility type for inferring reactive object/array types |
|
|
277
|
+
| `TransformResult` | Compiler transform output metadata |
|
|
275
278
|
|
|
276
279
|
## ReactiveArray
|
|
277
280
|
|
|
@@ -279,14 +282,14 @@ Symbol constants for type identification:
|
|
|
279
282
|
|
|
280
283
|
| Method | Description |
|
|
281
284
|
|--------|-------------|
|
|
282
|
-
|
|
|
283
|
-
|
|
|
284
|
-
| `
|
|
285
|
-
| `dispose()` | Disposes all nested reactive objects |
|
|
286
|
-
| `on(event, listener)` | Subscribes to an array event |
|
|
285
|
+
| `clear()` | Removes all items, disposes nested reactive objects, and dispatches `clear` event |
|
|
286
|
+
| `concat(...items)` | Appends items **in place** (mutating, returns `this`). Unlike `Array.prototype.concat` |
|
|
287
|
+
| `dispatch(event, value?)` | Manually dispatches an event to registered listeners |
|
|
288
|
+
| `dispose()` | Disposes all nested reactive objects and empties the array |
|
|
289
|
+
| `on(event, listener)` | Subscribes to an array event. Deduplicates by reference |
|
|
287
290
|
| `once(event, listener)` | Subscribes to an event once |
|
|
288
291
|
|
|
289
|
-
All standard array methods (`push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse
|
|
292
|
+
All standard array methods (`push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse`) are overridden and trigger corresponding events. Empty calls to `push`, `unshift`, and `concat` are no-ops.
|
|
290
293
|
|
|
291
294
|
### Events
|
|
292
295
|
|
package/build/constants.d.ts
CHANGED
|
@@ -12,5 +12,6 @@ declare const STATE_CHECK: number;
|
|
|
12
12
|
declare const STATE_DIRTY: number;
|
|
13
13
|
declare const STATE_RECOMPUTING: number;
|
|
14
14
|
declare const STATE_IN_HEAP: number;
|
|
15
|
+
declare const STATE_COMPUTED: number;
|
|
15
16
|
declare const STATE_NOTIFY_MASK: number;
|
|
16
|
-
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
|
|
17
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
|
package/build/constants.js
CHANGED
|
@@ -12,5 +12,6 @@ const STATE_CHECK = 1 << 0;
|
|
|
12
12
|
const STATE_DIRTY = 1 << 1;
|
|
13
13
|
const STATE_RECOMPUTING = 1 << 2;
|
|
14
14
|
const STATE_IN_HEAP = 1 << 3;
|
|
15
|
+
const STATE_COMPUTED = 1 << 4;
|
|
15
16
|
const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
16
|
-
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
|
|
17
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
|
package/build/reactive/array.js
CHANGED
|
@@ -191,6 +191,9 @@ class ReactiveArray extends Array {
|
|
|
191
191
|
return removed;
|
|
192
192
|
}
|
|
193
193
|
unshift(...items) {
|
|
194
|
+
if (!items.length) {
|
|
195
|
+
return this.length;
|
|
196
|
+
}
|
|
194
197
|
let length = super.unshift(...items);
|
|
195
198
|
write(this._length, length);
|
|
196
199
|
this.dispatch('unshift', { items });
|
package/build/reactive/object.js
CHANGED
|
@@ -49,21 +49,19 @@ class ReactiveObject {
|
|
|
49
49
|
return root(() => {
|
|
50
50
|
let node = computed(value);
|
|
51
51
|
if (isPromise(node.value)) {
|
|
52
|
-
let factory = node,
|
|
53
|
-
node = signal(undefined);
|
|
52
|
+
let factory = node, out = signal(undefined), v = 0;
|
|
54
53
|
(this.disposers ??= []).push(effect(() => {
|
|
55
|
-
let id = ++
|
|
56
|
-
read(factory).then((
|
|
57
|
-
if (id !==
|
|
54
|
+
let id = ++v;
|
|
55
|
+
read(factory).then((resolved) => {
|
|
56
|
+
if (id !== v) {
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
|
-
write(
|
|
59
|
+
write(out, resolved);
|
|
61
60
|
});
|
|
62
61
|
}));
|
|
62
|
+
return out;
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
(this.disposers ??= []).push(() => dispose(node));
|
|
66
|
-
}
|
|
64
|
+
(this.disposers ??= []).push(() => dispose(node));
|
|
67
65
|
return node;
|
|
68
66
|
});
|
|
69
67
|
}
|
package/build/system.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Computed, Signal } from './types.js';
|
|
2
|
+
declare const asyncComputed: <T>(fn: Computed<Promise<T>>["fn"]) => Signal<T | undefined>;
|
|
2
3
|
declare const computed: <T>(fn: Computed<T>["fn"]) => Computed<T>;
|
|
3
4
|
declare const dispose: <T>(computed: Computed<T>) => void;
|
|
4
5
|
declare const effect: <T>(fn: Computed<T>["fn"]) => () => void;
|
|
@@ -12,5 +13,5 @@ declare const root: {
|
|
|
12
13
|
};
|
|
13
14
|
declare const signal: <T>(value: T) => Signal<T>;
|
|
14
15
|
declare const write: <T>(signal: Signal<T>, value: T) => void;
|
|
15
|
-
export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
|
|
16
|
+
export { asyncComputed, computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
|
|
16
17
|
export type { Computed, Signal };
|
package/build/system.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
2
2
|
import { isObject } from '@esportsplus/utilities';
|
|
3
|
-
let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0,
|
|
3
|
+
let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0;
|
|
4
4
|
function cleanup(computed) {
|
|
5
5
|
if (!computed.cleanup) {
|
|
6
6
|
return;
|
|
@@ -82,9 +82,10 @@ function link(dep, sub) {
|
|
|
82
82
|
prevSub.sub === sub) {
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
let pooled =
|
|
85
|
+
let pooled = linkPoolHead, newLink = sub.depsTail =
|
|
86
86
|
dep.subsTail = pooled
|
|
87
|
-
? (
|
|
87
|
+
? (linkPoolHead = pooled.nextDep,
|
|
88
|
+
pooled.dep = dep,
|
|
88
89
|
pooled.sub = sub,
|
|
89
90
|
pooled.nextDep = nextDep,
|
|
90
91
|
pooled.prevSub = prevSub,
|
|
@@ -136,7 +137,7 @@ function recompute(computed, del) {
|
|
|
136
137
|
let o = observer, ok = true, value;
|
|
137
138
|
observer = computed;
|
|
138
139
|
computed.depsTail = null;
|
|
139
|
-
computed.state = STATE_RECOMPUTING;
|
|
140
|
+
computed.state = STATE_COMPUTED | STATE_RECOMPUTING;
|
|
140
141
|
depth++;
|
|
141
142
|
version++;
|
|
142
143
|
try {
|
|
@@ -147,7 +148,7 @@ function recompute(computed, del) {
|
|
|
147
148
|
}
|
|
148
149
|
depth--;
|
|
149
150
|
observer = o;
|
|
150
|
-
computed.state =
|
|
151
|
+
computed.state = STATE_COMPUTED;
|
|
151
152
|
let depsTail = computed.depsTail, remove = depsTail ? depsTail.nextDep : computed.deps;
|
|
152
153
|
if (remove) {
|
|
153
154
|
do {
|
|
@@ -220,21 +221,20 @@ function unlink(link) {
|
|
|
220
221
|
if (prevSub) {
|
|
221
222
|
prevSub.nextSub = nextSub;
|
|
222
223
|
}
|
|
223
|
-
else if ((dep.subs = nextSub) === null && dep.
|
|
224
|
+
else if ((dep.subs = nextSub) === null && dep.state & STATE_COMPUTED) {
|
|
224
225
|
dispose(dep);
|
|
225
226
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
227
|
+
link.dep = link.sub = null;
|
|
228
|
+
link.nextSub = link.prevSub = null;
|
|
229
|
+
link.nextDep = linkPoolHead;
|
|
230
|
+
linkPoolHead = link;
|
|
231
231
|
return nextDep;
|
|
232
232
|
}
|
|
233
233
|
function update(computed) {
|
|
234
234
|
if (computed.state & STATE_CHECK) {
|
|
235
235
|
for (let link = computed.deps; link; link = link.nextDep) {
|
|
236
236
|
let dep = link.dep;
|
|
237
|
-
if (dep.
|
|
237
|
+
if (dep.state & STATE_COMPUTED) {
|
|
238
238
|
update(dep);
|
|
239
239
|
if (computed.state & STATE_DIRTY) {
|
|
240
240
|
break;
|
|
@@ -245,8 +245,20 @@ function update(computed) {
|
|
|
245
245
|
if (computed.state & STATE_DIRTY) {
|
|
246
246
|
recompute(computed, true);
|
|
247
247
|
}
|
|
248
|
-
computed.state =
|
|
248
|
+
computed.state = STATE_COMPUTED;
|
|
249
249
|
}
|
|
250
|
+
const asyncComputed = (fn) => {
|
|
251
|
+
let factory = computed(fn), node = signal(undefined), v = 0;
|
|
252
|
+
onCleanup(effect(() => {
|
|
253
|
+
let id = ++v;
|
|
254
|
+
read(factory).then((value) => {
|
|
255
|
+
if (id === v) {
|
|
256
|
+
write(node, value);
|
|
257
|
+
}
|
|
258
|
+
}, () => { });
|
|
259
|
+
}));
|
|
260
|
+
return node;
|
|
261
|
+
};
|
|
250
262
|
const computed = (fn) => {
|
|
251
263
|
let self = {
|
|
252
264
|
cleanup: null,
|
|
@@ -256,10 +268,9 @@ const computed = (fn) => {
|
|
|
256
268
|
height: 0,
|
|
257
269
|
nextHeap: undefined,
|
|
258
270
|
prevHeap: null,
|
|
259
|
-
state:
|
|
271
|
+
state: STATE_COMPUTED,
|
|
260
272
|
subs: null,
|
|
261
273
|
subsTail: null,
|
|
262
|
-
type: COMPUTED,
|
|
263
274
|
value: undefined,
|
|
264
275
|
};
|
|
265
276
|
self.prevHeap = self;
|
|
@@ -303,7 +314,7 @@ const effect = (fn) => {
|
|
|
303
314
|
};
|
|
304
315
|
};
|
|
305
316
|
const isComputed = (value) => {
|
|
306
|
-
return isObject(value) && value.
|
|
317
|
+
return isObject(value) && !!(value.state & STATE_COMPUTED);
|
|
307
318
|
};
|
|
308
319
|
const isSignal = (value) => {
|
|
309
320
|
return isObject(value) && value.type === SIGNAL;
|
|
@@ -328,7 +339,7 @@ const onCleanup = (fn) => {
|
|
|
328
339
|
const read = (node) => {
|
|
329
340
|
if (observer) {
|
|
330
341
|
link(node, observer);
|
|
331
|
-
if (node.
|
|
342
|
+
if (node.state & STATE_COMPUTED) {
|
|
332
343
|
let height = node.height;
|
|
333
344
|
if (height >= observer.height) {
|
|
334
345
|
observer.height = height + 1;
|
|
@@ -353,7 +364,7 @@ const root = (fn) => {
|
|
|
353
364
|
observer = null;
|
|
354
365
|
root.disposables = 0;
|
|
355
366
|
if (tracking) {
|
|
356
|
-
scope = self = { cleanup: null };
|
|
367
|
+
scope = self = { cleanup: null, state: STATE_COMPUTED };
|
|
357
368
|
value = fn(c = () => dispose(self));
|
|
358
369
|
}
|
|
359
370
|
else {
|
|
@@ -391,4 +402,4 @@ const write = (signal, value) => {
|
|
|
391
402
|
}
|
|
392
403
|
schedule();
|
|
393
404
|
};
|
|
394
|
-
export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
|
|
405
|
+
export { asyncComputed, computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
|
package/build/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { SIGNAL } from './constants.js';
|
|
3
3
|
import { ReactiveArray } from './reactive/index.js';
|
|
4
4
|
interface Computed<T> {
|
|
5
5
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
@@ -9,10 +9,9 @@ interface Computed<T> {
|
|
|
9
9
|
height: number;
|
|
10
10
|
nextHeap: Computed<unknown> | undefined;
|
|
11
11
|
prevHeap: Computed<unknown>;
|
|
12
|
-
state:
|
|
12
|
+
state: number;
|
|
13
13
|
subs: Link | null;
|
|
14
14
|
subsTail: Link | null;
|
|
15
|
-
type: typeof COMPUTED;
|
|
16
15
|
value: T;
|
|
17
16
|
}
|
|
18
17
|
interface Link {
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -26,6 +26,8 @@ const STATE_RECOMPUTING = 1 << 2;
|
|
|
26
26
|
|
|
27
27
|
const STATE_IN_HEAP = 1 << 3;
|
|
28
28
|
|
|
29
|
+
const STATE_COMPUTED = 1 << 4;
|
|
30
|
+
|
|
29
31
|
const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
30
32
|
|
|
31
33
|
|
|
@@ -35,5 +37,5 @@ export {
|
|
|
35
37
|
PACKAGE_NAME,
|
|
36
38
|
SIGNAL,
|
|
37
39
|
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
38
|
-
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
40
|
+
STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
39
41
|
};
|
package/src/reactive/array.ts
CHANGED
package/src/reactive/object.ts
CHANGED
|
@@ -72,29 +72,29 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
|
72
72
|
let node: Computed<ReturnType<T>> | Signal<ReturnType<T> | undefined> = computed(value);
|
|
73
73
|
|
|
74
74
|
if (isPromise(node.value)) {
|
|
75
|
-
let factory = node
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
node = signal<ReturnType<T> | undefined>(undefined);
|
|
75
|
+
let factory = node as Computed<ReturnType<T>>,
|
|
76
|
+
out = signal<ReturnType<T> | undefined>(undefined),
|
|
77
|
+
v = 0;
|
|
79
78
|
|
|
80
79
|
(this.disposers ??= []).push(
|
|
81
80
|
effect(() => {
|
|
82
|
-
let id = ++
|
|
81
|
+
let id = ++v;
|
|
83
82
|
|
|
84
|
-
(read(factory) as Promise<ReturnType<T>>).then((
|
|
85
|
-
if (id !==
|
|
83
|
+
(read(factory) as Promise<ReturnType<T>>).then((resolved) => {
|
|
84
|
+
if (id !== v) {
|
|
86
85
|
return;
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
write(
|
|
88
|
+
write(out as Signal<typeof resolved>, resolved);
|
|
90
89
|
});
|
|
91
90
|
})
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
(this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return out;
|
|
96
94
|
}
|
|
97
95
|
|
|
96
|
+
(this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
|
|
97
|
+
|
|
98
98
|
return node;
|
|
99
99
|
});
|
|
100
100
|
}
|
package/src/system.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
COMPUTED,
|
|
3
2
|
SIGNAL,
|
|
4
3
|
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
5
|
-
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP,
|
|
4
|
+
STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
6
5
|
} from './constants';
|
|
7
6
|
import { Computed, Link, Signal } from './types';
|
|
8
7
|
import { isObject } from '@esportsplus/utilities';
|
|
@@ -12,8 +11,7 @@ let depth = 0,
|
|
|
12
11
|
heap: (Computed<unknown> | undefined)[] = new Array(64),
|
|
13
12
|
heap_i = 0,
|
|
14
13
|
heap_n = 0,
|
|
15
|
-
|
|
16
|
-
linkPoolMax = 1000,
|
|
14
|
+
linkPoolHead: Link | null = null,
|
|
17
15
|
microtask = queueMicrotask,
|
|
18
16
|
notified = false,
|
|
19
17
|
observer: Computed<unknown> | null = null,
|
|
@@ -138,11 +136,12 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
|
|
|
138
136
|
return;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
let pooled =
|
|
139
|
+
let pooled = linkPoolHead,
|
|
142
140
|
newLink =
|
|
143
141
|
sub.depsTail =
|
|
144
142
|
dep.subsTail = pooled
|
|
145
|
-
? (
|
|
143
|
+
? (linkPoolHead = pooled.nextDep,
|
|
144
|
+
pooled.dep = dep,
|
|
146
145
|
pooled.sub = sub,
|
|
147
146
|
pooled.nextDep = nextDep,
|
|
148
147
|
pooled.prevSub = prevSub,
|
|
@@ -206,7 +205,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
|
|
|
206
205
|
|
|
207
206
|
observer = computed;
|
|
208
207
|
computed.depsTail = null;
|
|
209
|
-
computed.state = STATE_RECOMPUTING;
|
|
208
|
+
computed.state = STATE_COMPUTED | STATE_RECOMPUTING;
|
|
210
209
|
|
|
211
210
|
depth++;
|
|
212
211
|
version++;
|
|
@@ -220,7 +219,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
|
|
|
220
219
|
|
|
221
220
|
depth--;
|
|
222
221
|
observer = o;
|
|
223
|
-
computed.state =
|
|
222
|
+
computed.state = STATE_COMPUTED;
|
|
224
223
|
|
|
225
224
|
let depsTail = computed.depsTail as Link | null,
|
|
226
225
|
remove = depsTail ? depsTail.nextDep : computed.deps;
|
|
@@ -323,16 +322,14 @@ function unlink(link: Link): Link | null {
|
|
|
323
322
|
if (prevSub) {
|
|
324
323
|
prevSub.nextSub = nextSub;
|
|
325
324
|
}
|
|
326
|
-
else if ((dep.subs = nextSub) === null && dep.
|
|
327
|
-
dispose(dep);
|
|
325
|
+
else if ((dep.subs = nextSub) === null && (dep as Computed<unknown>).state & STATE_COMPUTED) {
|
|
326
|
+
dispose(dep as Computed<unknown>);
|
|
328
327
|
}
|
|
329
328
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
linkPool.push(link);
|
|
335
|
-
}
|
|
329
|
+
link.dep = link.sub = null as any;
|
|
330
|
+
link.nextSub = link.prevSub = null;
|
|
331
|
+
link.nextDep = linkPoolHead;
|
|
332
|
+
linkPoolHead = link;
|
|
336
333
|
|
|
337
334
|
return nextDep;
|
|
338
335
|
}
|
|
@@ -342,8 +339,8 @@ function update<T>(computed: Computed<T>): void {
|
|
|
342
339
|
for (let link = computed.deps; link; link = link.nextDep) {
|
|
343
340
|
let dep = link.dep;
|
|
344
341
|
|
|
345
|
-
if (dep.
|
|
346
|
-
update(dep);
|
|
342
|
+
if ((dep as Computed<unknown>).state & STATE_COMPUTED) {
|
|
343
|
+
update(dep as Computed<unknown>);
|
|
347
344
|
|
|
348
345
|
if (computed.state & STATE_DIRTY) {
|
|
349
346
|
break;
|
|
@@ -356,10 +353,28 @@ function update<T>(computed: Computed<T>): void {
|
|
|
356
353
|
recompute(computed, true);
|
|
357
354
|
}
|
|
358
355
|
|
|
359
|
-
computed.state =
|
|
356
|
+
computed.state = STATE_COMPUTED;
|
|
360
357
|
}
|
|
361
358
|
|
|
362
359
|
|
|
360
|
+
const asyncComputed = <T>(fn: Computed<Promise<T>>['fn']): Signal<T | undefined> => {
|
|
361
|
+
let factory = computed(fn),
|
|
362
|
+
node = signal<T | undefined>(undefined),
|
|
363
|
+
v = 0;
|
|
364
|
+
|
|
365
|
+
onCleanup(effect(() => {
|
|
366
|
+
let id = ++v;
|
|
367
|
+
|
|
368
|
+
(read(factory) as Promise<T>).then((value) => {
|
|
369
|
+
if (id === v) {
|
|
370
|
+
write(node, value);
|
|
371
|
+
}
|
|
372
|
+
}, () => {});
|
|
373
|
+
}));
|
|
374
|
+
|
|
375
|
+
return node;
|
|
376
|
+
};
|
|
377
|
+
|
|
363
378
|
const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
364
379
|
let self: Computed<T> = {
|
|
365
380
|
cleanup: null,
|
|
@@ -369,10 +384,9 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
|
369
384
|
height: 0,
|
|
370
385
|
nextHeap: undefined,
|
|
371
386
|
prevHeap: null as any,
|
|
372
|
-
state:
|
|
387
|
+
state: STATE_COMPUTED,
|
|
373
388
|
subs: null,
|
|
374
389
|
subsTail: null,
|
|
375
|
-
type: COMPUTED,
|
|
376
390
|
value: undefined as T,
|
|
377
391
|
};
|
|
378
392
|
|
|
@@ -429,7 +443,7 @@ const effect = <T>(fn: Computed<T>['fn']) => {
|
|
|
429
443
|
};
|
|
430
444
|
|
|
431
445
|
const isComputed = (value: unknown): value is Computed<unknown> => {
|
|
432
|
-
return isObject(value) && value.
|
|
446
|
+
return isObject(value) && !!((value as unknown as Computed<unknown>).state & STATE_COMPUTED);
|
|
433
447
|
};
|
|
434
448
|
|
|
435
449
|
const isSignal = (value: unknown): value is Signal<unknown> => {
|
|
@@ -462,14 +476,14 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
|
|
|
462
476
|
if (observer) {
|
|
463
477
|
link(node, observer);
|
|
464
478
|
|
|
465
|
-
if (node.
|
|
466
|
-
let height = node.height;
|
|
479
|
+
if ((node as Computed<unknown>).state & STATE_COMPUTED) {
|
|
480
|
+
let height = (node as Computed<T>).height;
|
|
467
481
|
|
|
468
482
|
if (height >= observer.height) {
|
|
469
483
|
observer.height = height + 1;
|
|
470
484
|
}
|
|
471
485
|
|
|
472
|
-
if (height >= heap_i || node.state & STATE_NOTIFY_MASK) {
|
|
486
|
+
if (height >= heap_i || (node as Computed<T>).state & STATE_NOTIFY_MASK) {
|
|
473
487
|
if (!notified) {
|
|
474
488
|
notified = true;
|
|
475
489
|
|
|
@@ -480,7 +494,7 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
|
|
|
480
494
|
}
|
|
481
495
|
}
|
|
482
496
|
|
|
483
|
-
update(node);
|
|
497
|
+
update(node as Computed<T>);
|
|
484
498
|
}
|
|
485
499
|
}
|
|
486
500
|
}
|
|
@@ -501,7 +515,7 @@ const root = <T>(fn: ((dispose: VoidFunction) => T) | (() => T)) => {
|
|
|
501
515
|
root.disposables = 0;
|
|
502
516
|
|
|
503
517
|
if (tracking) {
|
|
504
|
-
scope = self = { cleanup: null } as Computed<unknown>;
|
|
518
|
+
scope = self = { cleanup: null, state: STATE_COMPUTED } as Computed<unknown>;
|
|
505
519
|
value = (fn as (dispose: VoidFunction) => T)(c = () => dispose(self!));
|
|
506
520
|
}
|
|
507
521
|
else {
|
|
@@ -553,7 +567,7 @@ const write = <T>(signal: Signal<T>, value: T) => {
|
|
|
553
567
|
|
|
554
568
|
|
|
555
569
|
export {
|
|
556
|
-
computed,
|
|
570
|
+
asyncComputed, computed,
|
|
557
571
|
dispose,
|
|
558
572
|
effect,
|
|
559
573
|
isComputed, isSignal,
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { SIGNAL } from './constants';
|
|
3
3
|
import { ReactiveArray } from './reactive';
|
|
4
4
|
|
|
5
5
|
|
|
@@ -11,15 +11,9 @@ interface Computed<T> {
|
|
|
11
11
|
height: number;
|
|
12
12
|
nextHeap: Computed<unknown> | undefined;
|
|
13
13
|
prevHeap: Computed<unknown>;
|
|
14
|
-
state:
|
|
15
|
-
typeof STATE_CHECK |
|
|
16
|
-
typeof STATE_DIRTY |
|
|
17
|
-
typeof STATE_IN_HEAP |
|
|
18
|
-
typeof STATE_NONE |
|
|
19
|
-
typeof STATE_RECOMPUTING;
|
|
14
|
+
state: number;
|
|
20
15
|
subs: Link | null;
|
|
21
16
|
subsTail: Link | null;
|
|
22
|
-
type: typeof COMPUTED;
|
|
23
17
|
value: T;
|
|
24
18
|
}
|
|
25
19
|
|