@esportsplus/reactivity 0.7.1 → 0.8.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/build/index.d.ts +1 -2
- package/build/index.js +1 -2
- package/build/reactive/array.js +3 -6
- package/build/reactive/{promise.d.ts → async.d.ts} +2 -2
- package/build/reactive/{promise.js → async.js} +2 -2
- package/build/reactive/index.d.ts +5 -4
- package/build/reactive/index.js +15 -10
- package/build/reactive/object.js +5 -22
- package/build/signal.d.ts +5 -6
- package/build/signal.js +112 -107
- package/build/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/index.ts +1 -2
- package/src/reactive/array.ts +3 -6
- package/src/reactive/{promise.ts → async.ts} +2 -2
- package/src/reactive/index.ts +32 -22
- package/src/reactive/object.ts +5 -25
- package/src/signal.ts +126 -119
- package/src/types.ts +2 -2
- package/build/scheduler.d.ts +0 -4
- package/build/scheduler.js +0 -17
- package/src/scheduler.ts +0 -27
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
package/build/reactive/array.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import { computed, dispose, isComputed, read } from '../signal.js';
|
|
3
3
|
import object from './object.js';
|
|
4
4
|
import { Disposable } from './disposable.js';
|
|
@@ -162,10 +162,7 @@ function factory(input) {
|
|
|
162
162
|
let items = [];
|
|
163
163
|
for (let i = 0, n = input.length; i < n; i++) {
|
|
164
164
|
let value = input[i];
|
|
165
|
-
if (
|
|
166
|
-
items[i] = array(value);
|
|
167
|
-
}
|
|
168
|
-
else if (isFunction(value)) {
|
|
165
|
+
if (isFunction(value)) {
|
|
169
166
|
items[i] = computed(value);
|
|
170
167
|
}
|
|
171
168
|
else if (isObject(value)) {
|
|
@@ -196,7 +193,7 @@ export default function array(input) {
|
|
|
196
193
|
if (isNumber(key)) {
|
|
197
194
|
let host = wrapped[key];
|
|
198
195
|
if (host === undefined || !isComputed(host)) {
|
|
199
|
-
|
|
196
|
+
a.splice(key, 1, value);
|
|
200
197
|
}
|
|
201
198
|
else {
|
|
202
199
|
return false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import CustomFunction from '@esportsplus/custom-function';
|
|
2
|
-
declare class
|
|
2
|
+
declare class ReactiveAsyncFunction<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
|
|
3
3
|
private arguments;
|
|
4
4
|
private okay;
|
|
5
5
|
private response;
|
|
@@ -9,5 +9,5 @@ declare class ReactivePromise<A extends unknown[], R extends Promise<unknown>> e
|
|
|
9
9
|
get input(): A | null;
|
|
10
10
|
get ok(): boolean | null;
|
|
11
11
|
}
|
|
12
|
-
declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) =>
|
|
12
|
+
declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => ReactiveAsyncFunction<A, R>;
|
|
13
13
|
export default _default;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import CustomFunction from '@esportsplus/custom-function';
|
|
2
2
|
import { read, root, signal } from '../signal.js';
|
|
3
3
|
let { set } = signal;
|
|
4
|
-
class
|
|
4
|
+
class ReactiveAsyncFunction extends CustomFunction {
|
|
5
5
|
arguments;
|
|
6
6
|
okay;
|
|
7
7
|
response;
|
|
@@ -44,5 +44,5 @@ class ReactivePromise extends CustomFunction {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
export default (fn) => {
|
|
47
|
-
return new
|
|
47
|
+
return new ReactiveAsyncFunction(fn);
|
|
48
48
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { Computed } from '../types.js';
|
|
1
2
|
import array from './array.js';
|
|
3
|
+
import async from './async.js';
|
|
2
4
|
import object from './object.js';
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
type Guard<T> = T extends (...args: unknown[]) => Promise<unknown> ? T : T extends {
|
|
5
|
+
type API<T> = T extends (...args: infer A) => Promise<infer R> ? ReturnType<typeof async<A, Promise<R>>> : T extends (...args: unknown[]) => unknown ? void : T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
|
|
6
|
+
type Input<T> = T extends (...args: unknown[]) => Promise<unknown> ? T : T extends (...args: unknown[]) => unknown ? Computed<T>['fn'] : T extends {
|
|
6
7
|
dispose: any;
|
|
7
8
|
} | {
|
|
8
9
|
signals: any;
|
|
9
10
|
} ? {
|
|
10
11
|
never: '[ dispose, signals ] are reserved keys';
|
|
11
12
|
} : T extends Record<PropertyKey, unknown> | unknown[] ? T : never;
|
|
12
|
-
declare const _default: <T>(
|
|
13
|
+
declare const _default: <T>(input: Input<T>) => API<T>;
|
|
13
14
|
export default _default;
|
package/build/reactive/index.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { isArray,
|
|
1
|
+
import { isArray, isAsyncFunction, isFunction, isObject } from '@esportsplus/utilities';
|
|
2
|
+
import { computed } from '../signal.js';
|
|
2
3
|
import array from './array.js';
|
|
4
|
+
import async from './async.js';
|
|
3
5
|
import object from './object.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return array(data);
|
|
6
|
+
export default (input) => {
|
|
7
|
+
if (isArray(input)) {
|
|
8
|
+
return array(input);
|
|
8
9
|
}
|
|
9
|
-
else if (
|
|
10
|
-
return
|
|
10
|
+
else if (isAsyncFunction(input)) {
|
|
11
|
+
return async(input);
|
|
11
12
|
}
|
|
12
|
-
else if (
|
|
13
|
-
|
|
13
|
+
else if (isFunction(input)) {
|
|
14
|
+
computed(input);
|
|
15
|
+
return undefined;
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
else if (isObject(input)) {
|
|
18
|
+
return object(input);
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
|
|
16
21
|
};
|
package/build/reactive/object.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf
|
|
1
|
+
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf } from '@esportsplus/utilities';
|
|
2
2
|
import array from './array.js';
|
|
3
3
|
import { computed, dispose, read, signal } from '../signal.js';
|
|
4
4
|
import { Disposable } from './disposable.js';
|
|
5
|
-
import
|
|
5
|
+
import async from './async.js';
|
|
6
6
|
let { set } = signal;
|
|
7
7
|
class ReactiveObject extends Disposable {
|
|
8
8
|
disposable = {};
|
|
@@ -20,13 +20,13 @@ class ReactiveObject extends Disposable {
|
|
|
20
20
|
return a;
|
|
21
21
|
},
|
|
22
22
|
set(v) {
|
|
23
|
-
set(t, !!t.value);
|
|
24
23
|
a = disposable[key] = array(v);
|
|
24
|
+
set(t, !!t.value);
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
-
if (isAsyncFunction(value)) {
|
|
29
|
-
let p =
|
|
28
|
+
else if (isAsyncFunction(value)) {
|
|
29
|
+
let p = async(value);
|
|
30
30
|
defineProperty(this, key, {
|
|
31
31
|
enumerable: true,
|
|
32
32
|
get() {
|
|
@@ -43,28 +43,11 @@ class ReactiveObject extends Disposable {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
else if (isObject(value)) {
|
|
47
|
-
let o = disposable[key] = new ReactiveObject(value), t = triggers[key] = signal(false);
|
|
48
|
-
defineProperty(this, key, {
|
|
49
|
-
enumerable: true,
|
|
50
|
-
get() {
|
|
51
|
-
read(t);
|
|
52
|
-
return o;
|
|
53
|
-
},
|
|
54
|
-
set(v) {
|
|
55
|
-
set(t, !!t.value);
|
|
56
|
-
o = disposable[key] = new ReactiveObject(v);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
46
|
else {
|
|
61
47
|
let s = signal(value);
|
|
62
48
|
defineProperty(this, key, {
|
|
63
49
|
enumerable: true,
|
|
64
50
|
get() {
|
|
65
|
-
if (s === undefined) {
|
|
66
|
-
s = signal(value);
|
|
67
|
-
}
|
|
68
51
|
return read(s);
|
|
69
52
|
},
|
|
70
53
|
set(v) {
|
package/build/signal.d.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Computed, Signal } from './types.js';
|
|
2
2
|
declare const computed: <T>(fn: Computed<T>["fn"]) => Computed<T>;
|
|
3
|
-
declare const dispose: <T>(
|
|
3
|
+
declare const dispose: <T>(computed: Computed<T>) => void;
|
|
4
4
|
declare const isComputed: (value: unknown) => value is Computed<unknown>;
|
|
5
|
-
declare const isReactive: (value: unknown) => value is Computed<unknown> | Signal<unknown>;
|
|
6
5
|
declare const isSignal: (value: unknown) => value is Signal<unknown>;
|
|
7
|
-
declare const
|
|
8
|
-
declare const read: <T>(
|
|
6
|
+
declare const onCleanup: (fn: VoidFunction) => typeof fn;
|
|
7
|
+
declare const read: <T>(node: Signal<T> | Computed<T>) => T;
|
|
9
8
|
declare const root: <T>(fn: () => T) => T;
|
|
10
9
|
declare const signal: {
|
|
11
10
|
<T>(value: T): Signal<T>;
|
|
12
|
-
set<T>(
|
|
11
|
+
set<T>(signal: Signal<T>, value: T): void;
|
|
13
12
|
};
|
|
14
13
|
declare const stabilize: () => void;
|
|
15
|
-
export { computed, dispose, isComputed,
|
|
14
|
+
export { computed, dispose, isComputed, isSignal, onCleanup, read, root, signal, stabilize };
|
package/build/signal.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
1
|
+
import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
3
|
-
|
|
4
|
-
let dirtyHeap = new Array(2000), maxDirty = 0, markedHeap = false, minDirty = 0, observer = null;
|
|
3
|
+
let heap = new Array(2000), iHeap = 0, nHeap = 0, notifiedHeap = false, observer = null, scheduled = false, scheduler = null;
|
|
5
4
|
function cleanup(node) {
|
|
6
5
|
if (!node.cleanup) {
|
|
7
6
|
return;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
let cleanup = node.cleanup;
|
|
9
|
+
if (isArray(cleanup)) {
|
|
10
|
+
for (let i = 0; i < cleanup.length; i++) {
|
|
11
|
+
cleanup[i]();
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
15
|
-
|
|
15
|
+
cleanup();
|
|
16
16
|
}
|
|
17
17
|
node.cleanup = null;
|
|
18
18
|
}
|
|
19
|
-
function deleteFromHeap(
|
|
20
|
-
let state =
|
|
19
|
+
function deleteFromHeap(computed) {
|
|
20
|
+
let state = computed.state;
|
|
21
21
|
if (!(state & STATE_IN_HEAP)) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
let height =
|
|
26
|
-
if (
|
|
27
|
-
|
|
24
|
+
computed.state = state & ~STATE_IN_HEAP;
|
|
25
|
+
let height = computed.height;
|
|
26
|
+
if (computed.prevHeap === computed) {
|
|
27
|
+
heap[height] = undefined;
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
|
-
let next =
|
|
31
|
-
if (
|
|
32
|
-
|
|
30
|
+
let next = computed.nextHeap, dhh = heap[height], end = next ?? dhh;
|
|
31
|
+
if (computed === dhh) {
|
|
32
|
+
heap[height] = next;
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
|
|
35
|
+
computed.prevHeap.nextHeap = next;
|
|
36
36
|
}
|
|
37
|
-
end.prevHeap =
|
|
37
|
+
end.prevHeap = computed.prevHeap;
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
computed.nextHeap = undefined;
|
|
40
|
+
computed.prevHeap = computed;
|
|
41
41
|
}
|
|
42
|
-
function insertIntoHeap(
|
|
43
|
-
let state =
|
|
42
|
+
function insertIntoHeap(computed) {
|
|
43
|
+
let state = computed.state;
|
|
44
44
|
if (state & STATE_IN_HEAP) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
let height =
|
|
47
|
+
computed.state = state | STATE_IN_HEAP;
|
|
48
|
+
let height = computed.height, heapAtHeight = heap[height];
|
|
49
49
|
if (heapAtHeight === undefined) {
|
|
50
|
-
|
|
50
|
+
heap[height] = computed;
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
53
|
let tail = heapAtHeight.prevHeap;
|
|
54
|
-
tail.nextHeap =
|
|
55
|
-
|
|
56
|
-
heapAtHeight.prevHeap =
|
|
57
|
-
}
|
|
58
|
-
if (height >
|
|
59
|
-
|
|
60
|
-
if (height >=
|
|
61
|
-
|
|
54
|
+
tail.nextHeap = computed;
|
|
55
|
+
computed.prevHeap = tail;
|
|
56
|
+
heapAtHeight.prevHeap = computed;
|
|
57
|
+
}
|
|
58
|
+
if (height > nHeap) {
|
|
59
|
+
nHeap = height;
|
|
60
|
+
if (height >= heap.length) {
|
|
61
|
+
heap.length += 250;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -96,49 +96,38 @@ function link(dep, sub) {
|
|
|
96
96
|
dep.subs = newLink;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
markedHeap = true;
|
|
104
|
-
for (let i = 0; i <= maxDirty; i++) {
|
|
105
|
-
for (let el = dirtyHeap[i]; el !== undefined; el = el.nextHeap) {
|
|
106
|
-
markNode(el);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function markNode(el, newState = STATE_DIRTY) {
|
|
111
|
-
let state = el.state;
|
|
99
|
+
function notify(computed, newState = STATE_DIRTY) {
|
|
100
|
+
let state = computed.state;
|
|
112
101
|
if ((state & (STATE_CHECK | STATE_DIRTY)) >= newState) {
|
|
113
102
|
return;
|
|
114
103
|
}
|
|
115
|
-
|
|
116
|
-
for (let link =
|
|
117
|
-
|
|
104
|
+
computed.state = state | newState;
|
|
105
|
+
for (let link = computed.subs; link !== null; link = link.nextSub) {
|
|
106
|
+
notify(link.sub, STATE_CHECK);
|
|
118
107
|
}
|
|
119
108
|
}
|
|
120
|
-
function recompute(
|
|
109
|
+
function recompute(computed, del) {
|
|
121
110
|
if (del) {
|
|
122
|
-
deleteFromHeap(
|
|
111
|
+
deleteFromHeap(computed);
|
|
123
112
|
}
|
|
124
113
|
else {
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
computed.nextHeap = undefined;
|
|
115
|
+
computed.prevHeap = computed;
|
|
127
116
|
}
|
|
128
|
-
cleanup(
|
|
117
|
+
cleanup(computed);
|
|
129
118
|
let o = observer, ok = true, value;
|
|
130
|
-
observer =
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
observer = computed;
|
|
120
|
+
computed.depsTail = null;
|
|
121
|
+
computed.state = STATE_RECOMPUTING;
|
|
133
122
|
try {
|
|
134
|
-
value =
|
|
123
|
+
value = computed.fn(onCleanup);
|
|
135
124
|
}
|
|
136
125
|
catch (e) {
|
|
137
126
|
ok = false;
|
|
138
127
|
}
|
|
139
128
|
observer = o;
|
|
140
|
-
|
|
141
|
-
let depsTail =
|
|
129
|
+
computed.state = STATE_NONE;
|
|
130
|
+
let depsTail = computed.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
|
|
142
131
|
if (toRemove !== null) {
|
|
143
132
|
do {
|
|
144
133
|
toRemove = unlink(toRemove);
|
|
@@ -147,21 +136,25 @@ function recompute(el, del) {
|
|
|
147
136
|
depsTail.nextDep = null;
|
|
148
137
|
}
|
|
149
138
|
else {
|
|
150
|
-
|
|
139
|
+
computed.deps = null;
|
|
151
140
|
}
|
|
152
141
|
}
|
|
153
|
-
if (ok && value !==
|
|
154
|
-
|
|
155
|
-
for (let
|
|
156
|
-
let o =
|
|
142
|
+
if (ok && value !== computed.value) {
|
|
143
|
+
computed.value = value;
|
|
144
|
+
for (let c = computed.subs; c !== null; c = c.nextSub) {
|
|
145
|
+
let o = c.sub, state = o.state;
|
|
157
146
|
if (state & STATE_CHECK) {
|
|
158
147
|
o.state = state | STATE_DIRTY;
|
|
159
148
|
}
|
|
160
149
|
insertIntoHeap(o);
|
|
161
150
|
}
|
|
162
151
|
}
|
|
163
|
-
if (
|
|
164
|
-
|
|
152
|
+
if (!scheduled && scheduler) {
|
|
153
|
+
scheduled = true;
|
|
154
|
+
scheduler(stabilize);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
throw new Error('@esportsplus/reactivity: stabilize.scheduler has not been set to process updates.');
|
|
165
158
|
}
|
|
166
159
|
}
|
|
167
160
|
function unlink(link) {
|
|
@@ -183,22 +176,22 @@ function unlink(link) {
|
|
|
183
176
|
}
|
|
184
177
|
return nextDep;
|
|
185
178
|
}
|
|
186
|
-
function update(
|
|
187
|
-
if (
|
|
188
|
-
for (let
|
|
189
|
-
let dep =
|
|
179
|
+
function update(computed) {
|
|
180
|
+
if (computed.state & STATE_CHECK) {
|
|
181
|
+
for (let link = computed.deps; link; link = link.nextDep) {
|
|
182
|
+
let dep = link.dep;
|
|
190
183
|
if ('fn' in dep) {
|
|
191
184
|
update(dep);
|
|
192
185
|
}
|
|
193
|
-
if (
|
|
186
|
+
if (computed.state & STATE_DIRTY) {
|
|
194
187
|
break;
|
|
195
188
|
}
|
|
196
189
|
}
|
|
197
190
|
}
|
|
198
|
-
if (
|
|
199
|
-
recompute(
|
|
191
|
+
if (computed.state & STATE_DIRTY) {
|
|
192
|
+
recompute(computed, true);
|
|
200
193
|
}
|
|
201
|
-
|
|
194
|
+
computed.state = STATE_NONE;
|
|
202
195
|
}
|
|
203
196
|
const computed = (fn) => {
|
|
204
197
|
let self = {
|
|
@@ -232,25 +225,22 @@ const computed = (fn) => {
|
|
|
232
225
|
}
|
|
233
226
|
return self;
|
|
234
227
|
};
|
|
235
|
-
const dispose = (
|
|
236
|
-
deleteFromHeap(
|
|
237
|
-
let dep =
|
|
228
|
+
const dispose = (computed) => {
|
|
229
|
+
deleteFromHeap(computed);
|
|
230
|
+
let dep = computed.deps;
|
|
238
231
|
while (dep !== null) {
|
|
239
232
|
dep = unlink(dep);
|
|
240
233
|
}
|
|
241
|
-
|
|
242
|
-
cleanup(
|
|
234
|
+
computed.deps = null;
|
|
235
|
+
cleanup(computed);
|
|
243
236
|
};
|
|
244
237
|
const isComputed = (value) => {
|
|
245
238
|
return isObject(value) && REACTIVE in value && 'fn' in value;
|
|
246
239
|
};
|
|
247
|
-
const isReactive = (value) => {
|
|
248
|
-
return isObject(value) && REACTIVE in value;
|
|
249
|
-
};
|
|
250
240
|
const isSignal = (value) => {
|
|
251
241
|
return isObject(value) && REACTIVE in value && 'fn' in value === false;
|
|
252
242
|
};
|
|
253
|
-
const
|
|
243
|
+
const onCleanup = (fn) => {
|
|
254
244
|
if (!observer) {
|
|
255
245
|
return fn;
|
|
256
246
|
}
|
|
@@ -266,22 +256,29 @@ const oncleanup = (fn) => {
|
|
|
266
256
|
}
|
|
267
257
|
return fn;
|
|
268
258
|
};
|
|
269
|
-
const read = (
|
|
259
|
+
const read = (node) => {
|
|
270
260
|
if (observer) {
|
|
271
|
-
link(
|
|
272
|
-
if ('fn' in
|
|
273
|
-
let height =
|
|
261
|
+
link(node, observer);
|
|
262
|
+
if ('fn' in node) {
|
|
263
|
+
let height = node.height;
|
|
274
264
|
if (height >= observer.height) {
|
|
275
265
|
observer.height = height + 1;
|
|
276
266
|
}
|
|
277
|
-
if (height >=
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
267
|
+
if (height >= iHeap ||
|
|
268
|
+
node.state & (STATE_DIRTY | STATE_CHECK)) {
|
|
269
|
+
if (!notifiedHeap) {
|
|
270
|
+
notifiedHeap = true;
|
|
271
|
+
for (let i = 0; i <= nHeap; i++) {
|
|
272
|
+
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
273
|
+
notify(computed);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
update(node);
|
|
281
278
|
}
|
|
282
279
|
}
|
|
283
280
|
}
|
|
284
|
-
return
|
|
281
|
+
return node.value;
|
|
285
282
|
};
|
|
286
283
|
const root = (fn) => {
|
|
287
284
|
let o = observer;
|
|
@@ -298,28 +295,36 @@ const signal = (value) => {
|
|
|
298
295
|
value,
|
|
299
296
|
};
|
|
300
297
|
};
|
|
301
|
-
signal.set = (
|
|
302
|
-
if (
|
|
298
|
+
signal.set = (signal, value) => {
|
|
299
|
+
if (signal.value === value) {
|
|
303
300
|
return;
|
|
304
301
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
302
|
+
notifiedHeap = false;
|
|
303
|
+
signal.value = value;
|
|
304
|
+
for (let link = signal.subs; link !== null; link = link.nextSub) {
|
|
308
305
|
insertIntoHeap(link.sub);
|
|
309
306
|
}
|
|
310
307
|
};
|
|
311
308
|
const stabilize = () => {
|
|
312
309
|
root(() => {
|
|
313
|
-
for (
|
|
314
|
-
let
|
|
315
|
-
|
|
316
|
-
while (
|
|
317
|
-
let next =
|
|
318
|
-
recompute(
|
|
319
|
-
|
|
310
|
+
for (iHeap = 0; iHeap <= nHeap; iHeap++) {
|
|
311
|
+
let computed = heap[iHeap];
|
|
312
|
+
heap[iHeap] = undefined;
|
|
313
|
+
while (computed !== undefined) {
|
|
314
|
+
let next = computed.nextHeap;
|
|
315
|
+
recompute(computed, false);
|
|
316
|
+
computed = next;
|
|
320
317
|
}
|
|
321
318
|
}
|
|
322
|
-
|
|
319
|
+
scheduled = false;
|
|
323
320
|
});
|
|
324
321
|
};
|
|
325
|
-
|
|
322
|
+
defineProperty(stabilize, 'scheduler', {
|
|
323
|
+
get() {
|
|
324
|
+
return scheduler;
|
|
325
|
+
},
|
|
326
|
+
set(s) {
|
|
327
|
+
scheduler = s;
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
export { computed, dispose, isComputed, isSignal, onCleanup, read, root, signal, stabilize };
|
package/build/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
2
|
-
import {
|
|
2
|
+
import { onCleanup } from './signal.js';
|
|
3
3
|
import { ReactiveArray } from './reactive/array.js';
|
|
4
4
|
import { ReactiveObject } from './reactive/object.js';
|
|
5
5
|
interface Computed<T> extends Signal<T> {
|
|
@@ -7,7 +7,7 @@ interface Computed<T> extends Signal<T> {
|
|
|
7
7
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
8
8
|
deps: Link | null;
|
|
9
9
|
depsTail: Link | null;
|
|
10
|
-
fn: (oc?: typeof
|
|
10
|
+
fn: (oc?: typeof onCleanup) => T;
|
|
11
11
|
height: number;
|
|
12
12
|
nextHeap: Computed<unknown> | undefined;
|
|
13
13
|
prevHeap: Computed<unknown>;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/reactive/array.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import { computed, dispose, isComputed, read } from '~/signal';
|
|
3
3
|
import { Computed, Infer } from '~/types';
|
|
4
4
|
import object, { ReactiveObject } from './object';
|
|
@@ -284,10 +284,7 @@ function factory<T>(input: T[]) {
|
|
|
284
284
|
for (let i = 0, n = input.length; i < n; i++) {
|
|
285
285
|
let value = input[i];
|
|
286
286
|
|
|
287
|
-
if (
|
|
288
|
-
items[i] = array(value);
|
|
289
|
-
}
|
|
290
|
-
else if (isFunction(value)) {
|
|
287
|
+
if (isFunction(value)) {
|
|
291
288
|
items[i] = computed(value as Computed<T>['fn']);
|
|
292
289
|
}
|
|
293
290
|
else if (isObject(value)) {
|
|
@@ -325,7 +322,7 @@ export default function array<T>(input: T[]) {
|
|
|
325
322
|
let host = wrapped[key];
|
|
326
323
|
|
|
327
324
|
if (host === undefined || !isComputed(host)) {
|
|
328
|
-
|
|
325
|
+
a.splice(key, 1, value);
|
|
329
326
|
}
|
|
330
327
|
else {
|
|
331
328
|
return false;
|
|
@@ -6,7 +6,7 @@ import { Signal } from '~/types';
|
|
|
6
6
|
let { set } = signal;
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class ReactiveAsyncFunction<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
|
|
10
10
|
private arguments: Signal<A | null>;
|
|
11
11
|
private okay: Signal<boolean | null>;
|
|
12
12
|
private response: Signal<Awaited<R> | null>;
|
|
@@ -63,5 +63,5 @@ class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends C
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
export default <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => {
|
|
66
|
-
return new
|
|
66
|
+
return new ReactiveAsyncFunction(fn);
|
|
67
67
|
};
|
package/src/reactive/index.ts
CHANGED
|
@@ -1,38 +1,48 @@
|
|
|
1
|
-
import { isArray,
|
|
1
|
+
import { isArray, isAsyncFunction, isFunction, isObject } from '@esportsplus/utilities';
|
|
2
|
+
import { computed } from '~/signal';
|
|
3
|
+
import { Computed } from '~/types';
|
|
2
4
|
import array from './array';
|
|
5
|
+
import async from './async';
|
|
3
6
|
import object from './object';
|
|
4
|
-
import promise from './promise';
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
type API<T> =
|
|
8
10
|
T extends (...args: infer A) => Promise<infer R>
|
|
9
|
-
? ReturnType<typeof
|
|
10
|
-
: T extends
|
|
11
|
-
?
|
|
12
|
-
: T extends unknown
|
|
13
|
-
? ReturnType<typeof
|
|
14
|
-
:
|
|
11
|
+
? ReturnType<typeof async<A, Promise<R>>>
|
|
12
|
+
: T extends (...args: unknown[]) => unknown
|
|
13
|
+
? void
|
|
14
|
+
: T extends Record<PropertyKey, unknown>
|
|
15
|
+
? ReturnType<typeof object<T>>
|
|
16
|
+
: T extends unknown[]
|
|
17
|
+
? ReturnType<typeof array<T>>
|
|
18
|
+
: never;
|
|
15
19
|
|
|
16
|
-
type
|
|
20
|
+
type Input<T> =
|
|
17
21
|
T extends (...args: unknown[]) => Promise<unknown>
|
|
18
22
|
? T
|
|
19
|
-
: T extends
|
|
20
|
-
?
|
|
21
|
-
: T extends
|
|
22
|
-
?
|
|
23
|
-
:
|
|
23
|
+
: T extends (...args: unknown[]) => unknown
|
|
24
|
+
? Computed<T>['fn']
|
|
25
|
+
: T extends { dispose: any } | { signals: any }
|
|
26
|
+
? { never: '[ dispose, signals ] are reserved keys' }
|
|
27
|
+
: T extends Record<PropertyKey, unknown> | unknown[]
|
|
28
|
+
? T
|
|
29
|
+
: never;
|
|
24
30
|
|
|
25
31
|
|
|
26
|
-
export default <T>(
|
|
27
|
-
if (isArray(
|
|
28
|
-
return array(
|
|
32
|
+
export default <T>(input: Input<T>): API<T> => {
|
|
33
|
+
if (isArray(input)) {
|
|
34
|
+
return array(input) as API<T>;
|
|
29
35
|
}
|
|
30
|
-
else if (
|
|
31
|
-
return
|
|
36
|
+
else if (isAsyncFunction(input)) {
|
|
37
|
+
return async(input) as API<T>;
|
|
32
38
|
}
|
|
33
|
-
else if (
|
|
34
|
-
|
|
39
|
+
else if (isFunction(input)) {
|
|
40
|
+
computed(input as Computed<T>['fn']);
|
|
41
|
+
return undefined as API<T>;
|
|
42
|
+
}
|
|
43
|
+
else if (isObject(input)) {
|
|
44
|
+
return object(input) as API<T>;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(
|
|
47
|
+
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
|
|
38
48
|
};
|
package/src/reactive/object.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf,
|
|
1
|
+
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, Prettify } from '@esportsplus/utilities';
|
|
2
2
|
import array, { ReactiveArray } from './array';
|
|
3
3
|
import { computed, dispose, read, signal } from '~/signal';
|
|
4
4
|
import { Computed, Infer, Signal } from '~/types';
|
|
5
5
|
import { Disposable } from './disposable';
|
|
6
|
-
import
|
|
6
|
+
import async from './async';
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
|
|
@@ -39,13 +39,13 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
39
39
|
return a;
|
|
40
40
|
},
|
|
41
41
|
set(v: typeof value) {
|
|
42
|
-
set(t, !!t.value);
|
|
43
42
|
a = disposable[key] = array(v);
|
|
43
|
+
set(t, !!t.value);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
if (isAsyncFunction(value)) {
|
|
48
|
-
let p =
|
|
47
|
+
else if (isAsyncFunction(value)) {
|
|
48
|
+
let p = async(value);
|
|
49
49
|
|
|
50
50
|
defineProperty(this, key, {
|
|
51
51
|
enumerable: true,
|
|
@@ -64,32 +64,12 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
-
else if (isObject(value)) {
|
|
68
|
-
let o = disposable[key] = new ReactiveObject(value),
|
|
69
|
-
t = triggers[key] = signal(false);
|
|
70
|
-
|
|
71
|
-
defineProperty(this, key, {
|
|
72
|
-
enumerable: true,
|
|
73
|
-
get() {
|
|
74
|
-
read(t);
|
|
75
|
-
return o;
|
|
76
|
-
},
|
|
77
|
-
set(v: typeof value) {
|
|
78
|
-
set(t, !!t.value);
|
|
79
|
-
o = disposable[key] = new ReactiveObject(v);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
67
|
else {
|
|
84
68
|
let s = signal(value);
|
|
85
69
|
|
|
86
70
|
defineProperty(this, key, {
|
|
87
71
|
enumerable: true,
|
|
88
72
|
get() {
|
|
89
|
-
if (s === undefined) {
|
|
90
|
-
s = signal(value);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
73
|
return read(s as Signal<typeof value>);
|
|
94
74
|
},
|
|
95
75
|
set(v: typeof value) {
|
package/src/signal.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
1
|
+
import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
|
|
3
3
|
import { Computed, Link, Signal, } from './types';
|
|
4
|
-
import { state } from './scheduler';
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
observer: Computed<unknown> | null = null
|
|
6
|
+
let heap: (Computed<unknown> | undefined)[] = new Array(2000),
|
|
7
|
+
iHeap = 0,
|
|
8
|
+
nHeap = 0,
|
|
9
|
+
notifiedHeap = false,
|
|
10
|
+
observer: Computed<unknown> | null = null,
|
|
11
|
+
scheduled = false,
|
|
12
|
+
scheduler: ((task: VoidFunction) => void) | null = null;
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
function cleanup<T>(node: Computed<T>): void {
|
|
@@ -16,80 +17,82 @@ function cleanup<T>(node: Computed<T>): void {
|
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
let cleanup = node.cleanup;
|
|
21
|
+
|
|
22
|
+
if (isArray(cleanup)) {
|
|
23
|
+
for (let i = 0; i < cleanup.length; i++) {
|
|
24
|
+
cleanup[i]();
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
else {
|
|
25
|
-
|
|
28
|
+
cleanup();
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
node.cleanup = null;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
function deleteFromHeap<T>(
|
|
32
|
-
let state =
|
|
34
|
+
function deleteFromHeap<T>(computed: Computed<T>) {
|
|
35
|
+
let state = computed.state;
|
|
33
36
|
|
|
34
37
|
if (!(state & STATE_IN_HEAP)) {
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
computed.state = state & ~STATE_IN_HEAP;
|
|
39
42
|
|
|
40
|
-
let height =
|
|
43
|
+
let height = computed.height;
|
|
41
44
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
45
|
+
if (computed.prevHeap === computed) {
|
|
46
|
+
heap[height] = undefined;
|
|
44
47
|
}
|
|
45
48
|
else {
|
|
46
|
-
let next =
|
|
47
|
-
dhh =
|
|
49
|
+
let next = computed.nextHeap,
|
|
50
|
+
dhh = heap[height]!,
|
|
48
51
|
end = next ?? dhh;
|
|
49
52
|
|
|
50
|
-
if (
|
|
51
|
-
|
|
53
|
+
if (computed === dhh) {
|
|
54
|
+
heap[height] = next;
|
|
52
55
|
}
|
|
53
56
|
else {
|
|
54
|
-
|
|
57
|
+
computed.prevHeap.nextHeap = next;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
end.prevHeap =
|
|
60
|
+
end.prevHeap = computed.prevHeap;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
computed.nextHeap = undefined;
|
|
64
|
+
computed.prevHeap = computed;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
function insertIntoHeap<T>(
|
|
65
|
-
let state =
|
|
67
|
+
function insertIntoHeap<T>(computed: Computed<T>) {
|
|
68
|
+
let state = computed.state;
|
|
66
69
|
|
|
67
70
|
if (state & STATE_IN_HEAP) {
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
computed.state = state | STATE_IN_HEAP;
|
|
72
75
|
|
|
73
|
-
let height =
|
|
74
|
-
heapAtHeight =
|
|
76
|
+
let height = computed.height,
|
|
77
|
+
heapAtHeight = heap[height];
|
|
75
78
|
|
|
76
79
|
if (heapAtHeight === undefined) {
|
|
77
|
-
|
|
80
|
+
heap[height] = computed;
|
|
78
81
|
}
|
|
79
82
|
else {
|
|
80
83
|
let tail = heapAtHeight.prevHeap;
|
|
81
84
|
|
|
82
|
-
tail.nextHeap =
|
|
83
|
-
|
|
84
|
-
heapAtHeight.prevHeap =
|
|
85
|
+
tail.nextHeap = computed;
|
|
86
|
+
computed.prevHeap = tail;
|
|
87
|
+
heapAtHeight.prevHeap = computed;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
if (height >
|
|
88
|
-
|
|
90
|
+
if (height > nHeap) {
|
|
91
|
+
nHeap = height;
|
|
89
92
|
|
|
90
93
|
// Simple auto adjust to avoid manual management within apps.
|
|
91
|
-
if (height >=
|
|
92
|
-
|
|
94
|
+
if (height >= heap.length) {
|
|
95
|
+
heap.length += 250;
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
}
|
|
@@ -139,65 +142,51 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
|
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
markedHeap = true;
|
|
148
|
-
|
|
149
|
-
for (let i = 0; i <= maxDirty; i++) {
|
|
150
|
-
for (let el = dirtyHeap[i]; el !== undefined; el = el.nextHeap) {
|
|
151
|
-
markNode(el);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function markNode<T>(el: Computed<T>, newState = STATE_DIRTY) {
|
|
157
|
-
let state = el.state;
|
|
145
|
+
function notify<T>(computed: Computed<T>, newState = STATE_DIRTY) {
|
|
146
|
+
let state = computed.state;
|
|
158
147
|
|
|
159
148
|
if ((state & (STATE_CHECK | STATE_DIRTY)) >= newState) {
|
|
160
149
|
return;
|
|
161
150
|
}
|
|
162
151
|
|
|
163
|
-
|
|
152
|
+
computed.state = state | newState;
|
|
164
153
|
|
|
165
|
-
for (let link =
|
|
166
|
-
|
|
154
|
+
for (let link = computed.subs; link !== null; link = link.nextSub) {
|
|
155
|
+
notify(link.sub, STATE_CHECK);
|
|
167
156
|
}
|
|
168
157
|
}
|
|
169
158
|
|
|
170
|
-
function recompute<T>(
|
|
159
|
+
function recompute<T>(computed: Computed<T>, del: boolean) {
|
|
171
160
|
if (del) {
|
|
172
|
-
deleteFromHeap(
|
|
161
|
+
deleteFromHeap(computed);
|
|
173
162
|
}
|
|
174
163
|
else {
|
|
175
|
-
|
|
176
|
-
|
|
164
|
+
computed.nextHeap = undefined;
|
|
165
|
+
computed.prevHeap = computed;
|
|
177
166
|
}
|
|
178
167
|
|
|
179
|
-
cleanup(
|
|
168
|
+
cleanup(computed);
|
|
180
169
|
|
|
181
170
|
let o = observer,
|
|
182
171
|
ok = true,
|
|
183
172
|
value;
|
|
184
173
|
|
|
185
|
-
observer =
|
|
186
|
-
|
|
187
|
-
|
|
174
|
+
observer = computed;
|
|
175
|
+
computed.depsTail = null;
|
|
176
|
+
computed.state = STATE_RECOMPUTING;
|
|
188
177
|
|
|
189
178
|
try {
|
|
190
|
-
value =
|
|
179
|
+
value = computed.fn(onCleanup);
|
|
191
180
|
}
|
|
192
181
|
catch (e) {
|
|
193
182
|
ok = false;
|
|
194
183
|
}
|
|
195
184
|
|
|
196
185
|
observer = o;
|
|
197
|
-
|
|
186
|
+
computed.state = STATE_NONE;
|
|
198
187
|
|
|
199
|
-
let depsTail =
|
|
200
|
-
toRemove = depsTail !== null ? depsTail.nextDep :
|
|
188
|
+
let depsTail = computed.depsTail as Link | null,
|
|
189
|
+
toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
|
|
201
190
|
|
|
202
191
|
if (toRemove !== null) {
|
|
203
192
|
do {
|
|
@@ -209,15 +198,15 @@ function recompute<T>(el: Computed<T>, del: boolean) {
|
|
|
209
198
|
depsTail.nextDep = null;
|
|
210
199
|
}
|
|
211
200
|
else {
|
|
212
|
-
|
|
201
|
+
computed.deps = null;
|
|
213
202
|
}
|
|
214
203
|
}
|
|
215
204
|
|
|
216
|
-
if (ok && value !==
|
|
217
|
-
|
|
205
|
+
if (ok && value !== computed.value) {
|
|
206
|
+
computed.value = value as T;
|
|
218
207
|
|
|
219
|
-
for (let
|
|
220
|
-
let o =
|
|
208
|
+
for (let c = computed.subs; c !== null; c = c.nextSub) {
|
|
209
|
+
let o = c.sub,
|
|
221
210
|
state = o.state;
|
|
222
211
|
|
|
223
212
|
if (state & STATE_CHECK) {
|
|
@@ -228,8 +217,12 @@ function recompute<T>(el: Computed<T>, del: boolean) {
|
|
|
228
217
|
}
|
|
229
218
|
}
|
|
230
219
|
|
|
231
|
-
if (
|
|
232
|
-
|
|
220
|
+
if (!scheduled && scheduler) {
|
|
221
|
+
scheduled = true;
|
|
222
|
+
scheduler(stabilize);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
throw new Error('@esportsplus/reactivity: stabilize.scheduler has not been set to process updates.');
|
|
233
226
|
}
|
|
234
227
|
}
|
|
235
228
|
|
|
@@ -261,26 +254,26 @@ function unlink(link: Link): Link | null {
|
|
|
261
254
|
return nextDep;
|
|
262
255
|
}
|
|
263
256
|
|
|
264
|
-
function update<T>(
|
|
265
|
-
if (
|
|
266
|
-
for (let
|
|
267
|
-
let dep =
|
|
257
|
+
function update<T>(computed: Computed<T>): void {
|
|
258
|
+
if (computed.state & STATE_CHECK) {
|
|
259
|
+
for (let link = computed.deps; link; link = link.nextDep) {
|
|
260
|
+
let dep = link.dep;
|
|
268
261
|
|
|
269
262
|
if ('fn' in dep) {
|
|
270
263
|
update(dep);
|
|
271
264
|
}
|
|
272
265
|
|
|
273
|
-
if (
|
|
266
|
+
if (computed.state & STATE_DIRTY) {
|
|
274
267
|
break;
|
|
275
268
|
}
|
|
276
269
|
}
|
|
277
270
|
}
|
|
278
271
|
|
|
279
|
-
if (
|
|
280
|
-
recompute(
|
|
272
|
+
if (computed.state & STATE_DIRTY) {
|
|
273
|
+
recompute(computed, true);
|
|
281
274
|
}
|
|
282
275
|
|
|
283
|
-
|
|
276
|
+
computed.state = STATE_NONE;
|
|
284
277
|
}
|
|
285
278
|
|
|
286
279
|
|
|
@@ -321,33 +314,29 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
|
321
314
|
return self;
|
|
322
315
|
};
|
|
323
316
|
|
|
324
|
-
const dispose = <T>(
|
|
325
|
-
deleteFromHeap(
|
|
317
|
+
const dispose = <T>(computed: Computed<T>) => {
|
|
318
|
+
deleteFromHeap(computed);
|
|
326
319
|
|
|
327
|
-
let dep =
|
|
320
|
+
let dep = computed.deps;
|
|
328
321
|
|
|
329
322
|
while (dep !== null) {
|
|
330
323
|
dep = unlink(dep);
|
|
331
324
|
}
|
|
332
325
|
|
|
333
|
-
|
|
326
|
+
computed.deps = null;
|
|
334
327
|
|
|
335
|
-
cleanup(
|
|
336
|
-
}
|
|
328
|
+
cleanup(computed);
|
|
329
|
+
};
|
|
337
330
|
|
|
338
331
|
const isComputed = (value: unknown): value is Computed<unknown> => {
|
|
339
332
|
return isObject(value) && REACTIVE in value && 'fn' in value;
|
|
340
333
|
};
|
|
341
334
|
|
|
342
|
-
const isReactive = (value: unknown): value is Computed<unknown> | Signal<unknown> => {
|
|
343
|
-
return isObject(value) && REACTIVE in value;
|
|
344
|
-
};
|
|
345
|
-
|
|
346
335
|
const isSignal = (value: unknown): value is Signal<unknown> => {
|
|
347
336
|
return isObject(value) && REACTIVE in value && 'fn' in value === false;
|
|
348
337
|
};
|
|
349
338
|
|
|
350
|
-
const
|
|
339
|
+
const onCleanup = (fn: VoidFunction): typeof fn => {
|
|
351
340
|
if (!observer) {
|
|
352
341
|
return fn;
|
|
353
342
|
}
|
|
@@ -367,28 +356,37 @@ const oncleanup = (fn: VoidFunction): typeof fn => {
|
|
|
367
356
|
return fn;
|
|
368
357
|
};
|
|
369
358
|
|
|
370
|
-
const read = <T>(
|
|
359
|
+
const read = <T>(node: Signal<T> | Computed<T>): T => {
|
|
371
360
|
if (observer) {
|
|
372
|
-
link(
|
|
361
|
+
link(node, observer);
|
|
373
362
|
|
|
374
|
-
if ('fn' in
|
|
375
|
-
let height =
|
|
363
|
+
if ('fn' in node) {
|
|
364
|
+
let height = node.height;
|
|
376
365
|
|
|
377
366
|
if (height >= observer.height) {
|
|
378
367
|
observer.height = height + 1;
|
|
379
368
|
}
|
|
380
369
|
|
|
381
370
|
if (
|
|
382
|
-
height >=
|
|
383
|
-
|
|
371
|
+
height >= iHeap ||
|
|
372
|
+
node.state & (STATE_DIRTY | STATE_CHECK)
|
|
384
373
|
) {
|
|
385
|
-
|
|
386
|
-
|
|
374
|
+
if (!notifiedHeap) {
|
|
375
|
+
notifiedHeap = true;
|
|
376
|
+
|
|
377
|
+
for (let i = 0; i <= nHeap; i++) {
|
|
378
|
+
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
379
|
+
notify(computed);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
update(node);
|
|
387
385
|
}
|
|
388
386
|
}
|
|
389
387
|
}
|
|
390
388
|
|
|
391
|
-
return
|
|
389
|
+
return node.value;
|
|
392
390
|
};
|
|
393
391
|
|
|
394
392
|
const root = <T>(fn: () => T) => {
|
|
@@ -412,45 +410,54 @@ const signal = <T>(value: T): Signal<T> => {
|
|
|
412
410
|
};
|
|
413
411
|
};
|
|
414
412
|
|
|
415
|
-
signal.set = <T>(
|
|
416
|
-
if (
|
|
413
|
+
signal.set = <T>(signal: Signal<T>, value: T) => {
|
|
414
|
+
if (signal.value === value) {
|
|
417
415
|
return;
|
|
418
416
|
}
|
|
419
417
|
|
|
420
|
-
|
|
418
|
+
notifiedHeap = false;
|
|
419
|
+
signal.value = value;
|
|
421
420
|
|
|
422
|
-
for (let link =
|
|
423
|
-
markedHeap = false;
|
|
421
|
+
for (let link = signal.subs; link !== null; link = link.nextSub) {
|
|
424
422
|
insertIntoHeap(link.sub);
|
|
425
423
|
}
|
|
426
424
|
};
|
|
427
425
|
|
|
428
426
|
const stabilize = () => {
|
|
429
427
|
root(() => {
|
|
430
|
-
for (
|
|
431
|
-
let
|
|
428
|
+
for (iHeap = 0; iHeap <= nHeap; iHeap++) {
|
|
429
|
+
let computed = heap[iHeap];
|
|
432
430
|
|
|
433
|
-
|
|
431
|
+
heap[iHeap] = undefined;
|
|
434
432
|
|
|
435
|
-
while (
|
|
436
|
-
let next =
|
|
433
|
+
while (computed !== undefined) {
|
|
434
|
+
let next = computed.nextHeap;
|
|
437
435
|
|
|
438
|
-
recompute(
|
|
436
|
+
recompute(computed, false);
|
|
439
437
|
|
|
440
|
-
|
|
438
|
+
computed = next;
|
|
441
439
|
}
|
|
442
440
|
}
|
|
443
441
|
|
|
444
|
-
|
|
442
|
+
scheduled = false;
|
|
445
443
|
});
|
|
446
444
|
};
|
|
447
445
|
|
|
446
|
+
defineProperty(stabilize, 'scheduler', {
|
|
447
|
+
get() {
|
|
448
|
+
return scheduler;
|
|
449
|
+
},
|
|
450
|
+
set(s: typeof scheduler) {
|
|
451
|
+
scheduler = s;
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
448
455
|
|
|
449
456
|
export {
|
|
450
457
|
computed,
|
|
451
458
|
dispose,
|
|
452
|
-
isComputed,
|
|
453
|
-
|
|
459
|
+
isComputed, isSignal,
|
|
460
|
+
onCleanup,
|
|
454
461
|
read, root,
|
|
455
462
|
signal, stabilize
|
|
456
463
|
};
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
|
|
2
|
-
import {
|
|
2
|
+
import { onCleanup } from './signal';
|
|
3
3
|
import { ReactiveArray } from './reactive/array';
|
|
4
4
|
import { ReactiveObject } from './reactive/object';
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ interface Computed<T> extends Signal<T> {
|
|
|
9
9
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
10
10
|
deps: Link | null;
|
|
11
11
|
depsTail: Link | null;
|
|
12
|
-
fn: (oc?: typeof
|
|
12
|
+
fn: (oc?: typeof onCleanup) => T;
|
|
13
13
|
height: number;
|
|
14
14
|
nextHeap: Computed<unknown> | undefined;
|
|
15
15
|
prevHeap: Computed<unknown>;
|
package/build/scheduler.d.ts
DELETED
package/build/scheduler.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { STATE_DIRTY, STATE_NONE } from './constants.js';
|
|
2
|
-
import { computed, dispose, read, signal, stabilize } from './signal.js';
|
|
3
|
-
let c = null;
|
|
4
|
-
const scheduler = (schedule) => {
|
|
5
|
-
if (c) {
|
|
6
|
-
dispose(c);
|
|
7
|
-
}
|
|
8
|
-
c = computed(() => {
|
|
9
|
-
if (read(state) !== STATE_DIRTY) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
schedule(stabilize);
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
const state = signal(STATE_NONE);
|
|
16
|
-
export default scheduler;
|
|
17
|
-
export { state };
|
package/src/scheduler.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { STATE_DIRTY, STATE_NONE } from './constants';
|
|
2
|
-
import { computed, dispose, read, signal, stabilize } from './signal';
|
|
3
|
-
import { Computed } from './types';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
let c: Computed<void> | null = null;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const scheduler = (schedule: (task: VoidFunction) => void) => {
|
|
10
|
-
if (c) {
|
|
11
|
-
dispose(c);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
c = computed(() => {
|
|
15
|
-
if (read(state) !== STATE_DIRTY) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
schedule(stabilize);
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const state = signal(STATE_NONE);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export default scheduler;
|
|
27
|
-
export { state };
|