@esportsplus/reactivity 0.12.3 → 0.12.4
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/constants.d.ts +2 -1
- package/build/constants.js +2 -1
- package/build/reactive/array.d.ts +0 -2
- package/build/reactive/array.js +22 -20
- package/build/reactive/object.d.ts +1 -0
- package/build/reactive/object.js +31 -31
- package/build/system.js +26 -19
- package/package.json +1 -1
- package/src/constants.ts +3 -0
- package/src/reactive/array.ts +31 -28
- package/src/reactive/object.ts +43 -37
- package/src/system.ts +33 -23
package/build/constants.d.ts
CHANGED
|
@@ -11,4 +11,5 @@ declare const STATE_CHECK: number;
|
|
|
11
11
|
declare const STATE_DIRTY: number;
|
|
12
12
|
declare const STATE_RECOMPUTING: number;
|
|
13
13
|
declare const STATE_IN_HEAP: number;
|
|
14
|
-
|
|
14
|
+
declare const STATE_NOTIFY_MASK: number;
|
|
15
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STATE_NOTIFY_MASK, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
|
package/build/constants.js
CHANGED
|
@@ -11,4 +11,5 @@ const STATE_CHECK = 1 << 0;
|
|
|
11
11
|
const STATE_DIRTY = 1 << 1;
|
|
12
12
|
const STATE_RECOMPUTING = 1 << 2;
|
|
13
13
|
const STATE_IN_HEAP = 1 << 3;
|
|
14
|
-
|
|
14
|
+
const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
15
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STATE_NOTIFY_MASK, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
|
|
@@ -57,8 +57,6 @@ declare class ReactiveArray<T> {
|
|
|
57
57
|
splice(start: number, deleteCount?: number, ...input: T[]): Item<T>[];
|
|
58
58
|
unshift(...input: T[]): number;
|
|
59
59
|
}
|
|
60
|
-
declare const isReactiveArray: (value: any) => value is ReactiveArray<any>;
|
|
61
60
|
declare const _default: <T>(input: T[]) => API<T>;
|
|
62
61
|
export default _default;
|
|
63
|
-
export { isReactiveArray };
|
|
64
62
|
export type { API as ReactiveArray };
|
package/build/reactive/array.js
CHANGED
|
@@ -63,7 +63,7 @@ class ReactiveArray {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
map(fn, i, n) {
|
|
66
|
-
let { data, proxy } = this
|
|
66
|
+
let { data, proxy } = this;
|
|
67
67
|
if (i === undefined) {
|
|
68
68
|
i = 0;
|
|
69
69
|
}
|
|
@@ -71,9 +71,10 @@ class ReactiveArray {
|
|
|
71
71
|
n = data.length;
|
|
72
72
|
}
|
|
73
73
|
n = Math.min(n, data.length);
|
|
74
|
+
let values = new Array(n - i);
|
|
74
75
|
for (; i < n; i++) {
|
|
75
76
|
let item = data[i];
|
|
76
|
-
values
|
|
77
|
+
values[i] = fn.call(proxy, (isComputed(item) ? item.value : item), i);
|
|
77
78
|
}
|
|
78
79
|
return values;
|
|
79
80
|
}
|
|
@@ -86,14 +87,18 @@ class ReactiveArray {
|
|
|
86
87
|
if (listeners === undefined) {
|
|
87
88
|
this.listeners[event] = [listener];
|
|
88
89
|
}
|
|
89
|
-
else
|
|
90
|
-
let
|
|
91
|
-
|
|
92
|
-
listeners
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
else {
|
|
91
|
+
let hole = listeners.length;
|
|
92
|
+
for (let i = 0, n = hole; i < n; i++) {
|
|
93
|
+
let l = listeners[i];
|
|
94
|
+
if (l === listener) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
else if (l === null && hole === n) {
|
|
98
|
+
hole = i;
|
|
99
|
+
}
|
|
96
100
|
}
|
|
101
|
+
listeners[hole] = listener;
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
@@ -153,24 +158,21 @@ class ReactiveArray {
|
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
function factory(input) {
|
|
156
|
-
let
|
|
157
|
-
for (let i = 0
|
|
161
|
+
let n = input.length, output = new Array(n);
|
|
162
|
+
for (let i = 0; i < n; i++) {
|
|
158
163
|
let value = input[i];
|
|
159
164
|
if (isFunction(value)) {
|
|
160
|
-
|
|
165
|
+
output[i] = computed(value);
|
|
161
166
|
}
|
|
162
167
|
else if (isObject(value)) {
|
|
163
|
-
|
|
168
|
+
output[i] = object(value);
|
|
164
169
|
}
|
|
165
170
|
else {
|
|
166
|
-
|
|
171
|
+
output[i] = value;
|
|
167
172
|
}
|
|
168
173
|
}
|
|
169
|
-
return
|
|
174
|
+
return output;
|
|
170
175
|
}
|
|
171
|
-
const isReactiveArray = (value) => {
|
|
172
|
-
return isObject(value) && REACTIVE_ARRAY in value;
|
|
173
|
-
};
|
|
174
176
|
export default (input) => {
|
|
175
177
|
let proxy = new Proxy({}, {
|
|
176
178
|
get(_, key) {
|
|
@@ -192,7 +194,8 @@ export default (input) => {
|
|
|
192
194
|
return true;
|
|
193
195
|
}
|
|
194
196
|
else if (key === 'length') {
|
|
195
|
-
|
|
197
|
+
array.length = value;
|
|
198
|
+
return true;
|
|
196
199
|
}
|
|
197
200
|
return false;
|
|
198
201
|
}
|
|
@@ -200,4 +203,3 @@ export default (input) => {
|
|
|
200
203
|
let array = new ReactiveArray(wrapped, proxy);
|
|
201
204
|
return proxy;
|
|
202
205
|
};
|
|
203
|
-
export { isReactiveArray };
|
package/build/reactive/object.js
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
import { defineProperty, isArray, isFunction, isObject, isPromise } from '@esportsplus/utilities';
|
|
2
|
-
import { computed, dispose, effect,
|
|
2
|
+
import { computed, dispose, effect, read, root, set, signal } from '../system.js';
|
|
3
3
|
import { REACTIVE_OBJECT } from '../constants.js';
|
|
4
|
-
import array
|
|
4
|
+
import array from './array.js';
|
|
5
5
|
class ReactiveObject {
|
|
6
6
|
[REACTIVE_OBJECT] = true;
|
|
7
|
+
disposers = null;
|
|
7
8
|
constructor(data) {
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
let keys = Object.keys(data);
|
|
10
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
11
|
+
let key = keys[i], value = data[key];
|
|
10
12
|
if (isArray(value)) {
|
|
11
|
-
let
|
|
13
|
+
let node = array(value);
|
|
14
|
+
(this.disposers ??= []).push(() => node.dispose());
|
|
12
15
|
defineProperty(this, key, {
|
|
13
16
|
enumerable: true,
|
|
14
|
-
|
|
15
|
-
return a;
|
|
16
|
-
}
|
|
17
|
+
value: node
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
else if (isFunction(value)) {
|
|
20
|
-
let
|
|
21
|
+
let node;
|
|
21
22
|
defineProperty(this, key, {
|
|
22
23
|
enumerable: true,
|
|
23
|
-
get() {
|
|
24
|
-
if (
|
|
24
|
+
get: () => {
|
|
25
|
+
if (node === undefined) {
|
|
25
26
|
root(() => {
|
|
26
|
-
|
|
27
|
-
if (isPromise(
|
|
28
|
-
let factory =
|
|
29
|
-
|
|
30
|
-
effect(() => {
|
|
27
|
+
node = computed(value);
|
|
28
|
+
if (isPromise(node.value)) {
|
|
29
|
+
let factory = node, version = 0;
|
|
30
|
+
node = signal(undefined);
|
|
31
|
+
(this.disposers ??= []).push(effect(() => {
|
|
31
32
|
let id = ++version;
|
|
32
|
-
read(factory).then((
|
|
33
|
+
read(factory).then((v) => {
|
|
33
34
|
if (id !== version) {
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
|
-
set(
|
|
37
|
+
set(node, v);
|
|
37
38
|
});
|
|
38
|
-
});
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
(this.disposers ??= []).push(() => dispose(node));
|
|
39
43
|
}
|
|
40
44
|
});
|
|
41
45
|
}
|
|
42
|
-
return read(
|
|
46
|
+
return read(node);
|
|
43
47
|
}
|
|
44
48
|
});
|
|
45
49
|
}
|
|
46
50
|
else {
|
|
47
|
-
let
|
|
51
|
+
let node = signal(value);
|
|
48
52
|
defineProperty(this, key, {
|
|
49
53
|
enumerable: true,
|
|
50
54
|
get() {
|
|
51
|
-
return read(
|
|
55
|
+
return read(node);
|
|
52
56
|
},
|
|
53
57
|
set(v) {
|
|
54
|
-
set(
|
|
58
|
+
set(node, v);
|
|
55
59
|
}
|
|
56
60
|
});
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
dispose() {
|
|
61
|
-
let
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
value.dispose();
|
|
66
|
-
}
|
|
67
|
-
else if (isComputed(value)) {
|
|
68
|
-
dispose(value);
|
|
65
|
+
let disposers = this.disposers;
|
|
66
|
+
if (disposers) {
|
|
67
|
+
for (let i = 0, n = disposers.length; i < n; i++) {
|
|
68
|
+
disposers[i]();
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
}
|
package/build/system.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isArray, isObject } from '@esportsplus/utilities';
|
|
2
|
-
import { COMPUTED, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
3
|
-
let depth = 0, heap = new Array(
|
|
2
|
+
import { COMPUTED, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
3
|
+
let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0, microtask = queueMicrotask, notified = false, observer = null, stabilizer = STABILIZER_IDLE, version = 0;
|
|
4
4
|
function cleanup(computed) {
|
|
5
5
|
if (!computed.cleanup) {
|
|
6
6
|
return;
|
|
@@ -55,10 +55,10 @@ function insertIntoHeap(computed) {
|
|
|
55
55
|
computed.prevHeap = tail;
|
|
56
56
|
heapAtHeight.prevHeap = computed;
|
|
57
57
|
}
|
|
58
|
-
if (height >
|
|
59
|
-
|
|
58
|
+
if (height > heap_n) {
|
|
59
|
+
heap_n = height;
|
|
60
60
|
if (height >= heap.length) {
|
|
61
|
-
heap.length = Math.
|
|
61
|
+
heap.length = Math.max(height + 1, Math.ceil(heap.length * 2));
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -106,7 +106,7 @@ function link(dep, sub) {
|
|
|
106
106
|
}
|
|
107
107
|
function notify(computed, newState = STATE_DIRTY) {
|
|
108
108
|
let state = computed.state;
|
|
109
|
-
if ((state &
|
|
109
|
+
if ((state & STATE_NOTIFY_MASK) >= newState) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
computed.state = state | newState;
|
|
@@ -122,7 +122,9 @@ function recompute(computed, del) {
|
|
|
122
122
|
computed.nextHeap = undefined;
|
|
123
123
|
computed.prevHeap = computed;
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
if (computed.cleanup) {
|
|
126
|
+
cleanup(computed);
|
|
127
|
+
}
|
|
126
128
|
let o = observer, ok = true, value;
|
|
127
129
|
observer = computed;
|
|
128
130
|
computed.depsTail = null;
|
|
@@ -175,15 +177,18 @@ function stabilize() {
|
|
|
175
177
|
let o = observer;
|
|
176
178
|
observer = null;
|
|
177
179
|
stabilizer = STABILIZER_RUNNING;
|
|
178
|
-
for (
|
|
179
|
-
let computed = heap[
|
|
180
|
-
heap[
|
|
180
|
+
for (heap_i = 0; heap_i <= heap_n; heap_i++) {
|
|
181
|
+
let computed = heap[heap_i];
|
|
182
|
+
heap[heap_i] = undefined;
|
|
181
183
|
while (computed !== undefined) {
|
|
182
184
|
let next = computed.nextHeap;
|
|
183
185
|
recompute(computed, false);
|
|
184
186
|
computed = next;
|
|
185
187
|
}
|
|
186
188
|
}
|
|
189
|
+
while (heap_n > 0 && heap[heap_n] === undefined) {
|
|
190
|
+
heap_n--;
|
|
191
|
+
}
|
|
187
192
|
observer = o;
|
|
188
193
|
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
189
194
|
microtask(stabilize);
|
|
@@ -193,7 +198,7 @@ function stabilize() {
|
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
function unlink(link) {
|
|
196
|
-
let
|
|
201
|
+
let dep = link.dep, nextDep = link.nextDep, nextSub = link.nextSub, prevSub = link.prevSub;
|
|
197
202
|
if (nextSub !== null) {
|
|
198
203
|
nextSub.prevSub = prevSub;
|
|
199
204
|
}
|
|
@@ -267,7 +272,9 @@ const dispose = (computed) => {
|
|
|
267
272
|
dep = unlink(dep);
|
|
268
273
|
}
|
|
269
274
|
computed.deps = null;
|
|
270
|
-
|
|
275
|
+
if (computed.cleanup) {
|
|
276
|
+
cleanup(computed);
|
|
277
|
+
}
|
|
271
278
|
};
|
|
272
279
|
const effect = (fn) => {
|
|
273
280
|
let c = computed(fn);
|
|
@@ -285,14 +292,15 @@ const onCleanup = (fn) => {
|
|
|
285
292
|
if (!observer) {
|
|
286
293
|
return fn;
|
|
287
294
|
}
|
|
288
|
-
|
|
295
|
+
let cleanup = observer.cleanup;
|
|
296
|
+
if (!cleanup) {
|
|
289
297
|
observer.cleanup = fn;
|
|
290
298
|
}
|
|
291
|
-
else if (isArray(
|
|
292
|
-
|
|
299
|
+
else if (isArray(cleanup)) {
|
|
300
|
+
cleanup.push(fn);
|
|
293
301
|
}
|
|
294
302
|
else {
|
|
295
|
-
observer.cleanup = [
|
|
303
|
+
observer.cleanup = [cleanup, fn];
|
|
296
304
|
}
|
|
297
305
|
return fn;
|
|
298
306
|
};
|
|
@@ -304,11 +312,10 @@ const read = (node) => {
|
|
|
304
312
|
if (height >= observer.height) {
|
|
305
313
|
observer.height = height + 1;
|
|
306
314
|
}
|
|
307
|
-
if (height >=
|
|
308
|
-
node.state & (STATE_DIRTY | STATE_CHECK)) {
|
|
315
|
+
if (height >= heap_i || node.state & STATE_NOTIFY_MASK) {
|
|
309
316
|
if (!notified) {
|
|
310
317
|
notified = true;
|
|
311
|
-
for (let i = 0; i <=
|
|
318
|
+
for (let i = 0; i <= heap_n; i++) {
|
|
312
319
|
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
313
320
|
notify(computed);
|
|
314
321
|
}
|
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_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
export {
|
|
31
33
|
COMPUTED,
|
|
@@ -33,6 +35,7 @@ export {
|
|
|
33
35
|
REACTIVE_OBJECT,
|
|
34
36
|
SIGNAL,
|
|
35
37
|
STABILIZER_IDLE,
|
|
38
|
+
STATE_NOTIFY_MASK,
|
|
36
39
|
STABILIZER_RESCHEDULE,
|
|
37
40
|
STABILIZER_RUNNING,
|
|
38
41
|
STABILIZER_SCHEDULED,
|
package/src/reactive/array.ts
CHANGED
|
@@ -53,7 +53,9 @@ class ReactiveArray<T> {
|
|
|
53
53
|
disposables: number = 0;
|
|
54
54
|
|
|
55
55
|
private data: Item<T>[];
|
|
56
|
+
|
|
56
57
|
private listeners: Record<string, (Listener<any> | null)[]> | null = null;
|
|
58
|
+
|
|
57
59
|
private proxy: API<T>;
|
|
58
60
|
|
|
59
61
|
|
|
@@ -134,8 +136,7 @@ class ReactiveArray<T> {
|
|
|
134
136
|
i?: number,
|
|
135
137
|
n?: number
|
|
136
138
|
) {
|
|
137
|
-
let { data, proxy } = this
|
|
138
|
-
values: R[] = [];
|
|
139
|
+
let { data, proxy } = this;
|
|
139
140
|
|
|
140
141
|
if (i === undefined) {
|
|
141
142
|
i = 0;
|
|
@@ -147,15 +148,15 @@ class ReactiveArray<T> {
|
|
|
147
148
|
|
|
148
149
|
n = Math.min(n, data.length);
|
|
149
150
|
|
|
151
|
+
let values: R[] = new Array(n - i);
|
|
152
|
+
|
|
150
153
|
for (; i < n; i++) {
|
|
151
154
|
let item = data[i];
|
|
152
155
|
|
|
153
|
-
values.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
i
|
|
158
|
-
)
|
|
156
|
+
values[i] = fn.call(
|
|
157
|
+
proxy,
|
|
158
|
+
(isComputed(item) ? item.value : item) as Value<T>,
|
|
159
|
+
i
|
|
159
160
|
);
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -172,15 +173,21 @@ class ReactiveArray<T> {
|
|
|
172
173
|
if (listeners === undefined) {
|
|
173
174
|
this.listeners[event] = [listener];
|
|
174
175
|
}
|
|
175
|
-
else
|
|
176
|
-
let
|
|
176
|
+
else {
|
|
177
|
+
let hole = listeners.length;
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
listeners
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
for (let i = 0, n = hole; i < n; i++) {
|
|
180
|
+
let l = listeners[i];
|
|
181
|
+
|
|
182
|
+
if (l === listener) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
else if (l === null && hole === n) {
|
|
186
|
+
hole = i;
|
|
187
|
+
}
|
|
183
188
|
}
|
|
189
|
+
|
|
190
|
+
listeners[hole] = listener;
|
|
184
191
|
}
|
|
185
192
|
}
|
|
186
193
|
}
|
|
@@ -269,31 +276,27 @@ class ReactiveArray<T> {
|
|
|
269
276
|
|
|
270
277
|
|
|
271
278
|
function factory<T>(input: T[]) {
|
|
272
|
-
let
|
|
279
|
+
let n = input.length,
|
|
280
|
+
output: Item<T>[] = new Array(n);
|
|
273
281
|
|
|
274
|
-
for (let i = 0
|
|
282
|
+
for (let i = 0; i < n; i++) {
|
|
275
283
|
let value = input[i];
|
|
276
284
|
|
|
277
285
|
if (isFunction(value)) {
|
|
278
|
-
|
|
286
|
+
output[i] = computed(value as Computed<T>['fn']);
|
|
279
287
|
}
|
|
280
288
|
else if (isObject(value)) {
|
|
281
|
-
|
|
289
|
+
output[i] = object(value) as Item<T>;
|
|
282
290
|
}
|
|
283
291
|
else {
|
|
284
|
-
|
|
292
|
+
output[i] = value;
|
|
285
293
|
}
|
|
286
294
|
}
|
|
287
295
|
|
|
288
|
-
return
|
|
296
|
+
return output;
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
|
|
292
|
-
const isReactiveArray = (value: any): value is ReactiveArray<any> => {
|
|
293
|
-
return isObject(value) && REACTIVE_ARRAY in value;
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
|
|
297
300
|
export default <T>(input: T[]) => {
|
|
298
301
|
let proxy = new Proxy({}, {
|
|
299
302
|
get(_: any, key: any) {
|
|
@@ -318,7 +321,8 @@ export default <T>(input: T[]) => {
|
|
|
318
321
|
return true;
|
|
319
322
|
}
|
|
320
323
|
else if (key === 'length') {
|
|
321
|
-
|
|
324
|
+
array.length = value;
|
|
325
|
+
return true;
|
|
322
326
|
}
|
|
323
327
|
|
|
324
328
|
return false;
|
|
@@ -330,5 +334,4 @@ export default <T>(input: T[]) => {
|
|
|
330
334
|
|
|
331
335
|
return proxy;
|
|
332
336
|
};
|
|
333
|
-
export { isReactiveArray };
|
|
334
337
|
export type { API as ReactiveArray };
|
package/src/reactive/object.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineProperty, isArray, isFunction, isObject, isPromise, Prettify } from '@esportsplus/utilities';
|
|
2
|
-
import { computed, dispose, effect,
|
|
2
|
+
import { computed, dispose, effect, read, root, set, signal } from '~/system';
|
|
3
3
|
import { Computed, Infer, Signal } from '~/types';
|
|
4
4
|
import { REACTIVE_OBJECT } from '~/constants';
|
|
5
|
-
import array
|
|
5
|
+
import array from './array';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
|
|
@@ -12,65 +12,76 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
|
12
12
|
[REACTIVE_OBJECT] = true;
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
private disposers: VoidFunction[] | null = null;
|
|
16
|
+
|
|
17
|
+
|
|
15
18
|
constructor(data: T) {
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
let keys = Object.keys(data);
|
|
20
|
+
|
|
21
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
22
|
+
let key = keys[i],
|
|
23
|
+
value = data[key];
|
|
18
24
|
|
|
19
25
|
if (isArray(value)) {
|
|
20
|
-
let
|
|
26
|
+
let node = array(value);
|
|
27
|
+
|
|
28
|
+
(this.disposers ??= []).push( () => node.dispose() );
|
|
21
29
|
|
|
22
30
|
defineProperty(this, key, {
|
|
23
31
|
enumerable: true,
|
|
24
|
-
|
|
25
|
-
return a;
|
|
26
|
-
}
|
|
32
|
+
value: node
|
|
27
33
|
});
|
|
28
34
|
}
|
|
29
35
|
else if (isFunction(value)) {
|
|
30
|
-
let
|
|
36
|
+
let node: Computed<T[typeof key]> | Signal<T[typeof key] | undefined> | undefined;
|
|
31
37
|
|
|
32
38
|
defineProperty(this, key, {
|
|
33
39
|
enumerable: true,
|
|
34
|
-
get() {
|
|
35
|
-
if (
|
|
40
|
+
get: () => {
|
|
41
|
+
if (node === undefined) {
|
|
36
42
|
root(() => {
|
|
37
|
-
|
|
43
|
+
node = computed(value as Computed<T[typeof key]>['fn']);
|
|
38
44
|
|
|
39
|
-
if (isPromise(
|
|
40
|
-
let factory =
|
|
45
|
+
if (isPromise(node.value)) {
|
|
46
|
+
let factory = node,
|
|
41
47
|
version = 0;
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
node = signal<T[typeof key] | undefined>(undefined);
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
(this.disposers ??= []).push(
|
|
52
|
+
effect(() => {
|
|
53
|
+
let id = ++version;
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
(read(factory) as Promise<T[typeof key]>).then((v) => {
|
|
56
|
+
if (id !== version) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
set(node as Signal<typeof v>, v);
|
|
61
|
+
});
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
(this.disposers ??= []).push(() => dispose(node as Computed<T[typeof key]>));
|
|
56
67
|
}
|
|
57
68
|
});
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
return read(
|
|
71
|
+
return read(node!);
|
|
61
72
|
}
|
|
62
73
|
});
|
|
63
74
|
}
|
|
64
75
|
else {
|
|
65
|
-
let
|
|
76
|
+
let node = signal(value);
|
|
66
77
|
|
|
67
78
|
defineProperty(this, key, {
|
|
68
79
|
enumerable: true,
|
|
69
80
|
get() {
|
|
70
|
-
return read(
|
|
81
|
+
return read(node);
|
|
71
82
|
},
|
|
72
83
|
set(v: typeof value) {
|
|
73
|
-
set(
|
|
84
|
+
set(node, v);
|
|
74
85
|
}
|
|
75
86
|
});
|
|
76
87
|
}
|
|
@@ -79,16 +90,11 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
|
79
90
|
|
|
80
91
|
|
|
81
92
|
dispose() {
|
|
82
|
-
let
|
|
93
|
+
let disposers = this.disposers;
|
|
83
94
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (isReactiveArray(value) || isReactiveObject(value)) {
|
|
88
|
-
value.dispose();
|
|
89
|
-
}
|
|
90
|
-
else if (isComputed(value)) {
|
|
91
|
-
dispose(value);
|
|
95
|
+
if (disposers) {
|
|
96
|
+
for (let i = 0, n = disposers.length; i < n; i++) {
|
|
97
|
+
disposers[i]();
|
|
92
98
|
}
|
|
93
99
|
}
|
|
94
100
|
}
|
package/src/system.ts
CHANGED
|
@@ -2,15 +2,15 @@ import { isArray, isObject } from '@esportsplus/utilities';
|
|
|
2
2
|
import {
|
|
3
3
|
COMPUTED, SIGNAL,
|
|
4
4
|
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
5
|
-
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING
|
|
5
|
+
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
6
6
|
} from './constants';
|
|
7
7
|
import { Computed, Link, Signal } from './types';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
let depth = 0,
|
|
11
|
-
heap: (Computed<unknown> | undefined)[] = new Array(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
heap: (Computed<unknown> | undefined)[] = new Array(64),
|
|
12
|
+
heap_i = 0,
|
|
13
|
+
heap_n = 0,
|
|
14
14
|
microtask = queueMicrotask,
|
|
15
15
|
notified = false,
|
|
16
16
|
observer: Computed<unknown> | null = null,
|
|
@@ -93,12 +93,12 @@ function insertIntoHeap<T>(computed: Computed<T>) {
|
|
|
93
93
|
heapAtHeight.prevHeap = computed;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
if (height >
|
|
97
|
-
|
|
96
|
+
if (height > heap_n) {
|
|
97
|
+
heap_n = height;
|
|
98
98
|
|
|
99
99
|
// Simple auto adjust to avoid manual management within apps.
|
|
100
100
|
if (height >= heap.length) {
|
|
101
|
-
heap.length = Math.
|
|
101
|
+
heap.length = Math.max(height + 1, Math.ceil(heap.length * 2));
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -163,7 +163,7 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
|
|
|
163
163
|
function notify<T>(computed: Computed<T>, newState = STATE_DIRTY) {
|
|
164
164
|
let state = computed.state;
|
|
165
165
|
|
|
166
|
-
if ((state &
|
|
166
|
+
if ((state & STATE_NOTIFY_MASK) >= newState) {
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -183,7 +183,9 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
|
|
|
183
183
|
computed.prevHeap = computed;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
if (computed.cleanup) {
|
|
187
|
+
cleanup(computed);
|
|
188
|
+
}
|
|
187
189
|
|
|
188
190
|
let o = observer,
|
|
189
191
|
ok = true,
|
|
@@ -258,10 +260,10 @@ function stabilize() {
|
|
|
258
260
|
observer = null;
|
|
259
261
|
stabilizer = STABILIZER_RUNNING;
|
|
260
262
|
|
|
261
|
-
for (
|
|
262
|
-
let computed = heap[
|
|
263
|
+
for (heap_i = 0; heap_i <= heap_n; heap_i++) {
|
|
264
|
+
let computed = heap[heap_i];
|
|
263
265
|
|
|
264
|
-
heap[
|
|
266
|
+
heap[heap_i] = undefined;
|
|
265
267
|
|
|
266
268
|
while (computed !== undefined) {
|
|
267
269
|
let next = computed.nextHeap;
|
|
@@ -272,6 +274,10 @@ function stabilize() {
|
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
while (heap_n > 0 && heap[heap_n] === undefined) {
|
|
278
|
+
heap_n--;
|
|
279
|
+
}
|
|
280
|
+
|
|
275
281
|
observer = o;
|
|
276
282
|
|
|
277
283
|
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
@@ -284,7 +290,10 @@ function stabilize() {
|
|
|
284
290
|
|
|
285
291
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
|
|
286
292
|
function unlink(link: Link): Link | null {
|
|
287
|
-
let
|
|
293
|
+
let dep = link.dep,
|
|
294
|
+
nextDep = link.nextDep,
|
|
295
|
+
nextSub = link.nextSub,
|
|
296
|
+
prevSub = link.prevSub;
|
|
288
297
|
|
|
289
298
|
if (nextSub !== null) {
|
|
290
299
|
nextSub.prevSub = prevSub;
|
|
@@ -377,7 +386,9 @@ const dispose = <T>(computed: Computed<T>) => {
|
|
|
377
386
|
|
|
378
387
|
computed.deps = null;
|
|
379
388
|
|
|
380
|
-
|
|
389
|
+
if (computed.cleanup) {
|
|
390
|
+
cleanup(computed);
|
|
391
|
+
}
|
|
381
392
|
};
|
|
382
393
|
|
|
383
394
|
const effect = <T>(fn: Computed<T>['fn']) => {
|
|
@@ -401,14 +412,16 @@ const onCleanup = (fn: VoidFunction): typeof fn => {
|
|
|
401
412
|
return fn;
|
|
402
413
|
}
|
|
403
414
|
|
|
404
|
-
|
|
415
|
+
let cleanup = observer.cleanup;
|
|
416
|
+
|
|
417
|
+
if (!cleanup) {
|
|
405
418
|
observer.cleanup = fn;
|
|
406
419
|
}
|
|
407
|
-
else if (isArray(
|
|
408
|
-
|
|
420
|
+
else if (isArray(cleanup)) {
|
|
421
|
+
cleanup.push(fn);
|
|
409
422
|
}
|
|
410
423
|
else {
|
|
411
|
-
observer.cleanup = [
|
|
424
|
+
observer.cleanup = [cleanup, fn];
|
|
412
425
|
}
|
|
413
426
|
|
|
414
427
|
return fn;
|
|
@@ -425,14 +438,11 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
|
|
|
425
438
|
observer.height = height + 1;
|
|
426
439
|
}
|
|
427
440
|
|
|
428
|
-
if (
|
|
429
|
-
height >= index ||
|
|
430
|
-
node.state & (STATE_DIRTY | STATE_CHECK)
|
|
431
|
-
) {
|
|
441
|
+
if (height >= heap_i || node.state & STATE_NOTIFY_MASK) {
|
|
432
442
|
if (!notified) {
|
|
433
443
|
notified = true;
|
|
434
444
|
|
|
435
|
-
for (let i = 0; i <=
|
|
445
|
+
for (let i = 0; i <= heap_n; i++) {
|
|
436
446
|
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
437
447
|
notify(computed);
|
|
438
448
|
}
|