@esportsplus/reactivity 0.11.7 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/constants.d.ts +5 -2
- package/build/constants.js +5 -2
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/reactive/array.d.ts +7 -3
- package/build/reactive/array.js +49 -54
- package/build/reactive/index.d.ts +3 -6
- package/build/reactive/object.d.ts +6 -4
- package/build/reactive/object.js +20 -22
- package/build/system.d.ts +4 -5
- package/build/system.js +47 -50
- package/build/types.d.ts +8 -6
- package/build/types.js +1 -1
- package/package.json +1 -1
- package/src/constants.ts +11 -2
- package/src/index.ts +1 -0
- package/src/reactive/array.ts +60 -67
- package/src/reactive/index.ts +9 -6
- package/src/reactive/object.ts +26 -34
- package/src/system.ts +54 -56
- package/src/types.ts +8 -6
- package/build/reactive/disposable.d.ts +0 -4
- package/build/reactive/disposable.js +0 -6
- package/src/reactive/disposable.ts +0 -8
package/build/constants.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
declare const
|
|
1
|
+
declare const COMPUTED: unique symbol;
|
|
2
|
+
declare const REACTIVE_ARRAY: unique symbol;
|
|
3
|
+
declare const REACTIVE_OBJECT: unique symbol;
|
|
4
|
+
declare const SIGNAL: unique symbol;
|
|
2
5
|
declare const STABILIZER_IDLE = 0;
|
|
3
6
|
declare const STABILIZER_RESCHEDULE = 1;
|
|
4
7
|
declare const STABILIZER_RUNNING = 2;
|
|
@@ -8,4 +11,4 @@ declare const STATE_CHECK: number;
|
|
|
8
11
|
declare const STATE_DIRTY: number;
|
|
9
12
|
declare const STATE_RECOMPUTING: number;
|
|
10
13
|
declare const STATE_IN_HEAP: number;
|
|
11
|
-
export {
|
|
14
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
|
package/build/constants.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
const
|
|
1
|
+
const COMPUTED = Symbol('computed');
|
|
2
|
+
const REACTIVE_ARRAY = Symbol('reactive.array');
|
|
3
|
+
const REACTIVE_OBJECT = Symbol('reactive.object');
|
|
4
|
+
const SIGNAL = Symbol('signal');
|
|
2
5
|
const STABILIZER_IDLE = 0;
|
|
3
6
|
const STABILIZER_RESCHEDULE = 1;
|
|
4
7
|
const STABILIZER_RUNNING = 2;
|
|
@@ -8,4 +11,4 @@ const STATE_CHECK = 1 << 0;
|
|
|
8
11
|
const STATE_DIRTY = 1 << 1;
|
|
9
12
|
const STATE_RECOMPUTING = 1 << 2;
|
|
10
13
|
const STATE_IN_HEAP = 1 << 3;
|
|
11
|
-
export {
|
|
14
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { REACTIVE_ARRAY } from '../constants.js';
|
|
1
2
|
import { Computed, Infer } from '../types.js';
|
|
2
3
|
import { ReactiveObject } from './object.js';
|
|
3
|
-
import { Disposable } from './disposable.js';
|
|
4
4
|
type API<T> = Infer<T>[] & ReactiveArray<T>;
|
|
5
5
|
type Events<T> = {
|
|
6
6
|
pop: {
|
|
@@ -27,19 +27,21 @@ type Events<T> = {
|
|
|
27
27
|
items: Item<T>[];
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
|
-
type Item<T> = Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never
|
|
30
|
+
type Item<T> = T | Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
|
|
31
31
|
type Listener<V> = {
|
|
32
32
|
once?: boolean;
|
|
33
33
|
(value: V): void;
|
|
34
34
|
};
|
|
35
35
|
type Value<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : T extends Array<infer U> ? API<U> : T;
|
|
36
|
-
declare class ReactiveArray<T>
|
|
36
|
+
declare class ReactiveArray<T> {
|
|
37
|
+
[REACTIVE_ARRAY]: boolean;
|
|
37
38
|
private data;
|
|
38
39
|
private listeners;
|
|
39
40
|
private proxy;
|
|
40
41
|
constructor(data: Item<T>[], proxy: API<T>);
|
|
41
42
|
get length(): number;
|
|
42
43
|
set length(n: number);
|
|
44
|
+
private cleanup;
|
|
43
45
|
at(i: number): T | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
|
|
44
46
|
dispatch<K extends keyof Events<T>, V>(event: K, value?: V): void;
|
|
45
47
|
dispose(): void;
|
|
@@ -54,5 +56,7 @@ declare class ReactiveArray<T> extends Disposable {
|
|
|
54
56
|
splice(start: number, deleteCount?: number, ...input: T[]): Item<T>[];
|
|
55
57
|
unshift(...input: T[]): number;
|
|
56
58
|
}
|
|
59
|
+
declare const isReactiveArray: (value: any) => value is ReactiveArray<any>;
|
|
57
60
|
export default function array<T>(input: T[]): API<T>;
|
|
61
|
+
export { isReactiveArray };
|
|
58
62
|
export type { API as ReactiveArray };
|
package/build/reactive/array.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { isFunction,
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
class ReactiveArray
|
|
1
|
+
import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
|
|
2
|
+
import { REACTIVE_ARRAY } from '../constants.js';
|
|
3
|
+
import { computed, dispose, isComputed, onCleanup, read, root } from '../system.js';
|
|
4
|
+
import object, { isReactiveObject } from './object.js';
|
|
5
|
+
class ReactiveArray {
|
|
6
|
+
[REACTIVE_ARRAY] = true;
|
|
6
7
|
data;
|
|
7
8
|
listeners = null;
|
|
8
9
|
proxy;
|
|
9
10
|
constructor(data, proxy) {
|
|
10
|
-
super();
|
|
11
11
|
this.data = data;
|
|
12
12
|
this.proxy = proxy;
|
|
13
13
|
}
|
|
@@ -20,6 +20,14 @@ class ReactiveArray extends Disposable {
|
|
|
20
20
|
}
|
|
21
21
|
this.splice(n);
|
|
22
22
|
}
|
|
23
|
+
cleanup(item) {
|
|
24
|
+
if (isReactiveObject(item)) {
|
|
25
|
+
item.dispose();
|
|
26
|
+
}
|
|
27
|
+
else if (isComputed(item)) {
|
|
28
|
+
dispose(item);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
23
31
|
at(i) {
|
|
24
32
|
let value = this.data[i];
|
|
25
33
|
if (isComputed(value)) {
|
|
@@ -49,17 +57,9 @@ class ReactiveArray extends Disposable {
|
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
dispose() {
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
let value = data[i];
|
|
55
|
-
if (isInstanceOf(value, Disposable)) {
|
|
56
|
-
value.dispose();
|
|
57
|
-
}
|
|
58
|
-
else if (isComputed(value)) {
|
|
59
|
-
dispose(value);
|
|
60
|
-
}
|
|
60
|
+
for (let i = 0, n = this.data.length; i < n; i++) {
|
|
61
|
+
this.cleanup(this.data[i]);
|
|
61
62
|
}
|
|
62
|
-
this.listeners = null;
|
|
63
63
|
}
|
|
64
64
|
map(fn, i, n) {
|
|
65
65
|
let { data, proxy } = this, values = [];
|
|
@@ -103,9 +103,7 @@ class ReactiveArray extends Disposable {
|
|
|
103
103
|
pop() {
|
|
104
104
|
let item = this.data.pop();
|
|
105
105
|
if (item !== undefined) {
|
|
106
|
-
|
|
107
|
-
dispose(item);
|
|
108
|
-
}
|
|
106
|
+
this.cleanup(item);
|
|
109
107
|
this.dispatch('pop', { item });
|
|
110
108
|
}
|
|
111
109
|
return item;
|
|
@@ -123,9 +121,7 @@ class ReactiveArray extends Disposable {
|
|
|
123
121
|
shift() {
|
|
124
122
|
let item = this.data.shift();
|
|
125
123
|
if (item !== undefined) {
|
|
126
|
-
|
|
127
|
-
dispose(item);
|
|
128
|
-
}
|
|
124
|
+
this.cleanup(item);
|
|
129
125
|
this.dispatch('shift', { item });
|
|
130
126
|
}
|
|
131
127
|
return item;
|
|
@@ -139,10 +135,7 @@ class ReactiveArray extends Disposable {
|
|
|
139
135
|
let items = factory(input), removed = this.data.splice(start, deleteCount, ...items);
|
|
140
136
|
if (items.length > 0 || removed.length > 0) {
|
|
141
137
|
for (let i = 0, n = removed.length; i < n; i++) {
|
|
142
|
-
|
|
143
|
-
if (isComputed(item)) {
|
|
144
|
-
dispose(item);
|
|
145
|
-
}
|
|
138
|
+
this.cleanup(removed[i]);
|
|
146
139
|
}
|
|
147
140
|
this.dispatch('splice', {
|
|
148
141
|
deleteCount,
|
|
@@ -174,39 +167,41 @@ function factory(input) {
|
|
|
174
167
|
}
|
|
175
168
|
return items;
|
|
176
169
|
}
|
|
170
|
+
const isReactiveArray = (value) => {
|
|
171
|
+
return isObject(value) && REACTIVE_ARRAY in value;
|
|
172
|
+
};
|
|
177
173
|
export default function array(input) {
|
|
178
|
-
let proxy =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
174
|
+
let { array, proxy } = root(() => {
|
|
175
|
+
let proxy = new Proxy({}, {
|
|
176
|
+
get(_, key) {
|
|
177
|
+
if (isNumber(key)) {
|
|
178
|
+
let value = wrapped[key];
|
|
179
|
+
if (isComputed(value)) {
|
|
180
|
+
return read(value);
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
184
183
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
else if (key in a) {
|
|
188
|
-
return a[key];
|
|
189
|
-
}
|
|
190
|
-
return wrapped[key];
|
|
191
|
-
},
|
|
192
|
-
set(_, key, value) {
|
|
193
|
-
if (isNumber(key)) {
|
|
194
|
-
let host = wrapped[key];
|
|
195
|
-
if (host === undefined || !isComputed(host)) {
|
|
196
|
-
a.splice(key, 1, value);
|
|
184
|
+
else if (key in array) {
|
|
185
|
+
return array[key];
|
|
197
186
|
}
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
return wrapped[key];
|
|
188
|
+
},
|
|
189
|
+
set(_, key, value) {
|
|
190
|
+
if (isNumber(key)) {
|
|
191
|
+
array.splice(key, 1, value);
|
|
192
|
+
return true;
|
|
200
193
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return
|
|
194
|
+
else if (key === 'length') {
|
|
195
|
+
return array.length = value;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
205
198
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
199
|
+
}), wrapped = factory(input);
|
|
200
|
+
let array = new ReactiveArray(wrapped, proxy);
|
|
201
|
+
return { array, proxy };
|
|
202
|
+
});
|
|
203
|
+
onCleanup(() => array.dispose());
|
|
210
204
|
return proxy;
|
|
211
205
|
}
|
|
212
206
|
;
|
|
207
|
+
export { isReactiveArray };
|
|
@@ -3,10 +3,7 @@ import object from './object.js';
|
|
|
3
3
|
type API<T> = T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
|
|
4
4
|
type Input<T> = T extends {
|
|
5
5
|
dispose: any;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
} ? {
|
|
9
|
-
never: '[ dispose, signals ] are reserved keys';
|
|
10
|
-
} : T extends Record<PropertyKey, unknown> | unknown[] ? T : never;
|
|
11
|
-
declare const _default: <T>(input: Input<T>) => API<T>;
|
|
6
|
+
} ? never : T extends Record<PropertyKey, unknown> ? T : T extends unknown[] ? Input<T[number]>[] : never;
|
|
7
|
+
declare const _default: <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>) => API<T>;
|
|
12
8
|
export default _default;
|
|
9
|
+
export type { Input };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Prettify } from '@esportsplus/utilities';
|
|
2
2
|
import { Infer } from '../types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { REACTIVE_OBJECT } from '../constants.js';
|
|
4
4
|
type API<T extends Record<PropertyKey, unknown>> = Prettify<{
|
|
5
5
|
[K in keyof T]: Infer<T[K]>;
|
|
6
6
|
}> & ReactiveObject<T>;
|
|
7
|
-
declare class ReactiveObject<T extends Record<PropertyKey, unknown>>
|
|
8
|
-
|
|
7
|
+
declare class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
8
|
+
[REACTIVE_OBJECT]: boolean;
|
|
9
9
|
constructor(data: T);
|
|
10
10
|
dispose(): void;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
declare const isReactiveObject: (value: any) => value is ReactiveObject<any>;
|
|
13
|
+
export default function object<T extends Record<PropertyKey, unknown>>(input: T): ReactiveObject<T>;
|
|
14
|
+
export { isReactiveObject };
|
|
13
15
|
export type { API as ReactiveObject };
|
package/build/reactive/object.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
|
-
import { defineProperty, isArray, isFunction,
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
disposable = {};
|
|
1
|
+
import { defineProperty, isArray, isFunction, isObject, isPromise } from '@esportsplus/utilities';
|
|
2
|
+
import { computed, dispose, effect, isComputed, onCleanup, read, root, set, signal } from '../system.js';
|
|
3
|
+
import { REACTIVE_OBJECT } from '../constants.js';
|
|
4
|
+
import array, { isReactiveArray } from './array.js';
|
|
5
|
+
class ReactiveObject {
|
|
6
|
+
[REACTIVE_OBJECT] = true;
|
|
8
7
|
constructor(data) {
|
|
9
|
-
super();
|
|
10
|
-
let disposable = this.disposable, triggers = {};
|
|
11
8
|
for (let key in data) {
|
|
12
9
|
let value = data[key];
|
|
13
10
|
if (isArray(value)) {
|
|
14
|
-
let a =
|
|
11
|
+
let a = array(value);
|
|
15
12
|
defineProperty(this, key, {
|
|
16
13
|
enumerable: true,
|
|
17
14
|
get() {
|
|
18
|
-
read(t);
|
|
19
15
|
return a;
|
|
20
|
-
},
|
|
21
|
-
set(v) {
|
|
22
|
-
a = disposable[key] = array(v);
|
|
23
|
-
set(t, !!t.value);
|
|
24
16
|
}
|
|
25
17
|
});
|
|
26
18
|
}
|
|
@@ -31,7 +23,7 @@ class ReactiveObject extends Disposable {
|
|
|
31
23
|
get() {
|
|
32
24
|
if (c === undefined) {
|
|
33
25
|
root(() => {
|
|
34
|
-
c =
|
|
26
|
+
c = computed(value);
|
|
35
27
|
if (isPromise(c.value)) {
|
|
36
28
|
let factory = c, version = 0;
|
|
37
29
|
c = signal(undefined);
|
|
@@ -66,19 +58,25 @@ class ReactiveObject extends Disposable {
|
|
|
66
58
|
}
|
|
67
59
|
}
|
|
68
60
|
dispose() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
let value;
|
|
62
|
+
for (let key in this) {
|
|
63
|
+
value = this[key];
|
|
64
|
+
if (isReactiveArray(value) || isReactiveObject(value)) {
|
|
72
65
|
value.dispose();
|
|
73
66
|
}
|
|
74
|
-
else {
|
|
67
|
+
else if (isComputed(value)) {
|
|
75
68
|
dispose(value);
|
|
76
69
|
}
|
|
77
70
|
}
|
|
78
|
-
this.disposable = {};
|
|
79
71
|
}
|
|
80
72
|
}
|
|
73
|
+
const isReactiveObject = (value) => {
|
|
74
|
+
return isObject(value) && REACTIVE_OBJECT in value;
|
|
75
|
+
};
|
|
81
76
|
export default function object(input) {
|
|
82
|
-
|
|
77
|
+
let object = root(() => new ReactiveObject(input));
|
|
78
|
+
onCleanup(() => object.dispose());
|
|
79
|
+
return object;
|
|
83
80
|
}
|
|
84
81
|
;
|
|
82
|
+
export { isReactiveObject };
|
package/build/system.d.ts
CHANGED
|
@@ -7,8 +7,7 @@ declare const isSignal: (value: unknown) => value is Signal<unknown>;
|
|
|
7
7
|
declare const onCleanup: (fn: VoidFunction) => typeof fn;
|
|
8
8
|
declare const read: <T>(node: Signal<T> | Computed<T>) => T;
|
|
9
9
|
declare const root: <T>(fn: () => T) => T;
|
|
10
|
-
declare const signal:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal };
|
|
10
|
+
declare const set: <T>(signal: Signal<T>, value: T) => void;
|
|
11
|
+
declare const signal: <T>(value: T) => Signal<T>;
|
|
12
|
+
export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, set, signal };
|
|
13
|
+
export type { Computed, Signal };
|
package/build/system.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
2
|
-
import {
|
|
3
|
-
let depth = 0, heap = new Array(
|
|
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(2048), index = 0, length = 0, microtask = queueMicrotask, notified = false, observer = null, stabilizer = STABILIZER_IDLE, version = 0;
|
|
4
4
|
function cleanup(node) {
|
|
5
5
|
if (!node.cleanup) {
|
|
6
6
|
return;
|
|
7
7
|
}
|
|
8
8
|
let cleanup = node.cleanup;
|
|
9
9
|
if (isArray(cleanup)) {
|
|
10
|
-
for (let i = 0
|
|
10
|
+
for (let i = 0, n = cleanup.length; i < n; i++) {
|
|
11
11
|
cleanup[i]();
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -58,7 +58,7 @@ function insertIntoHeap(computed) {
|
|
|
58
58
|
if (height > length) {
|
|
59
59
|
length = height;
|
|
60
60
|
if (height >= heap.length) {
|
|
61
|
-
heap.length
|
|
61
|
+
heap.length = Math.round(heap.length * 1.5);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -165,31 +165,31 @@ function recompute(computed, del) {
|
|
|
165
165
|
function schedule() {
|
|
166
166
|
if (stabilizer === STABILIZER_IDLE && !depth) {
|
|
167
167
|
stabilizer = STABILIZER_SCHEDULED;
|
|
168
|
-
|
|
168
|
+
microtask(stabilize);
|
|
169
169
|
}
|
|
170
170
|
else if (stabilizer === STABILIZER_RUNNING) {
|
|
171
171
|
stabilizer = STABILIZER_RESCHEDULE;
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
function stabilize() {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
187
|
-
scheduler(stabilize);
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
stabilizer = STABILIZER_IDLE;
|
|
175
|
+
let o = observer;
|
|
176
|
+
stabilizer = STABILIZER_RUNNING;
|
|
177
|
+
for (index = 0; index <= length; index++) {
|
|
178
|
+
let computed = heap[index];
|
|
179
|
+
heap[index] = undefined;
|
|
180
|
+
while (computed !== undefined) {
|
|
181
|
+
let next = computed.nextHeap;
|
|
182
|
+
recompute(computed, false);
|
|
183
|
+
computed = next;
|
|
191
184
|
}
|
|
192
|
-
}
|
|
185
|
+
}
|
|
186
|
+
observer = o;
|
|
187
|
+
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
188
|
+
microtask(stabilize);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
stabilizer = STABILIZER_IDLE;
|
|
192
|
+
}
|
|
193
193
|
}
|
|
194
194
|
function unlink(link) {
|
|
195
195
|
let { dep, nextDep, nextSub, prevSub } = link;
|
|
@@ -202,11 +202,8 @@ function unlink(link) {
|
|
|
202
202
|
if (prevSub !== null) {
|
|
203
203
|
prevSub.nextSub = nextSub;
|
|
204
204
|
}
|
|
205
|
-
else {
|
|
206
|
-
dep
|
|
207
|
-
if (nextSub === null && 'fn' in dep) {
|
|
208
|
-
dispose(dep);
|
|
209
|
-
}
|
|
205
|
+
else if ((dep.subs = nextSub) === null && 'fn' in dep) {
|
|
206
|
+
dispose(dep);
|
|
210
207
|
}
|
|
211
208
|
return nextDep;
|
|
212
209
|
}
|
|
@@ -216,9 +213,9 @@ function update(computed) {
|
|
|
216
213
|
let dep = link.dep;
|
|
217
214
|
if ('fn' in dep) {
|
|
218
215
|
update(dep);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
if (computed.state & STATE_DIRTY) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
222
219
|
}
|
|
223
220
|
}
|
|
224
221
|
}
|
|
@@ -229,7 +226,7 @@ function update(computed) {
|
|
|
229
226
|
}
|
|
230
227
|
const computed = (fn) => {
|
|
231
228
|
let self = {
|
|
232
|
-
[
|
|
229
|
+
[COMPUTED]: true,
|
|
233
230
|
cleanup: null,
|
|
234
231
|
deps: null,
|
|
235
232
|
depsTail: null,
|
|
@@ -254,6 +251,7 @@ const computed = (fn) => {
|
|
|
254
251
|
schedule();
|
|
255
252
|
}
|
|
256
253
|
link(self, observer);
|
|
254
|
+
onCleanup(() => dispose(self));
|
|
257
255
|
}
|
|
258
256
|
else {
|
|
259
257
|
recompute(self, false);
|
|
@@ -276,24 +274,23 @@ const effect = (fn) => {
|
|
|
276
274
|
};
|
|
277
275
|
};
|
|
278
276
|
const isComputed = (value) => {
|
|
279
|
-
return isObject(value) &&
|
|
277
|
+
return isObject(value) && COMPUTED in value;
|
|
280
278
|
};
|
|
281
279
|
const isSignal = (value) => {
|
|
282
|
-
return isObject(value) &&
|
|
280
|
+
return isObject(value) && SIGNAL in value;
|
|
283
281
|
};
|
|
284
282
|
const onCleanup = (fn) => {
|
|
285
283
|
if (!observer) {
|
|
286
284
|
return fn;
|
|
287
285
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
node.cleanup = fn;
|
|
286
|
+
if (!observer.cleanup) {
|
|
287
|
+
observer.cleanup = fn;
|
|
291
288
|
}
|
|
292
|
-
else if (isArray(
|
|
293
|
-
|
|
289
|
+
else if (isArray(observer.cleanup)) {
|
|
290
|
+
observer.cleanup.push(fn);
|
|
294
291
|
}
|
|
295
292
|
else {
|
|
296
|
-
|
|
293
|
+
observer.cleanup = [observer.cleanup, fn];
|
|
297
294
|
}
|
|
298
295
|
return fn;
|
|
299
296
|
};
|
|
@@ -328,15 +325,7 @@ const root = (fn) => {
|
|
|
328
325
|
observer = o;
|
|
329
326
|
return value;
|
|
330
327
|
};
|
|
331
|
-
const
|
|
332
|
-
return {
|
|
333
|
-
[REACTIVE]: true,
|
|
334
|
-
subs: null,
|
|
335
|
-
subsTail: null,
|
|
336
|
-
value,
|
|
337
|
-
};
|
|
338
|
-
};
|
|
339
|
-
signal.set = (signal, value) => {
|
|
328
|
+
const set = (signal, value) => {
|
|
340
329
|
if (signal.value === value) {
|
|
341
330
|
return;
|
|
342
331
|
}
|
|
@@ -350,4 +339,12 @@ signal.set = (signal, value) => {
|
|
|
350
339
|
}
|
|
351
340
|
schedule();
|
|
352
341
|
};
|
|
353
|
-
|
|
342
|
+
const signal = (value) => {
|
|
343
|
+
return {
|
|
344
|
+
[SIGNAL]: true,
|
|
345
|
+
subs: null,
|
|
346
|
+
subsTail: null,
|
|
347
|
+
value,
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, set, signal };
|
package/build/types.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { onCleanup } from './system.js';
|
|
1
|
+
import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
3
2
|
import { ReactiveArray } from './reactive/array.js';
|
|
4
3
|
import { ReactiveObject } from './reactive/object.js';
|
|
5
|
-
interface Computed<T>
|
|
6
|
-
[
|
|
4
|
+
interface Computed<T> {
|
|
5
|
+
[COMPUTED]: true;
|
|
7
6
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
8
7
|
deps: Link | null;
|
|
9
8
|
depsTail: Link | null;
|
|
10
|
-
fn: (
|
|
9
|
+
fn: (onCleanup?: (fn: VoidFunction) => typeof fn) => T;
|
|
11
10
|
height: number;
|
|
12
11
|
nextHeap: Computed<unknown> | undefined;
|
|
13
12
|
prevHeap: Computed<unknown>;
|
|
14
13
|
state: typeof STATE_CHECK | typeof STATE_DIRTY | typeof STATE_IN_HEAP | typeof STATE_NONE | typeof STATE_RECOMPUTING;
|
|
14
|
+
subs: Link | null;
|
|
15
|
+
subsTail: Link | null;
|
|
16
|
+
value: T;
|
|
15
17
|
}
|
|
16
18
|
type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefined : T extends (...args: any[]) => infer R ? R : T extends (infer U)[] ? ReactiveArray<U> : T extends ReactiveObject<any> ? T : T extends Record<PropertyKey, unknown> ? {
|
|
17
19
|
[K in keyof T]: T[K];
|
|
@@ -26,7 +28,7 @@ interface Link {
|
|
|
26
28
|
}
|
|
27
29
|
type Reactive<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : ReactiveArray<T>;
|
|
28
30
|
type Signal<T> = {
|
|
29
|
-
[
|
|
31
|
+
[SIGNAL]: true;
|
|
30
32
|
subs: Link | null;
|
|
31
33
|
subsTail: Link | null;
|
|
32
34
|
value: T;
|
package/build/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { COMPUTED, SIGNAL } from './constants.js';
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const COMPUTED = Symbol('computed');
|
|
2
|
+
|
|
3
|
+
const REACTIVE_ARRAY = Symbol('reactive.array');
|
|
4
|
+
|
|
5
|
+
const REACTIVE_OBJECT = Symbol('reactive.object');
|
|
6
|
+
|
|
7
|
+
const SIGNAL = Symbol('signal');
|
|
2
8
|
|
|
3
9
|
|
|
4
10
|
const STABILIZER_IDLE = 0;
|
|
@@ -22,7 +28,10 @@ const STATE_IN_HEAP = 1 << 3;
|
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
export {
|
|
25
|
-
|
|
31
|
+
COMPUTED,
|
|
32
|
+
REACTIVE_ARRAY,
|
|
33
|
+
REACTIVE_OBJECT,
|
|
34
|
+
SIGNAL,
|
|
26
35
|
STABILIZER_IDLE,
|
|
27
36
|
STABILIZER_RESCHEDULE,
|
|
28
37
|
STABILIZER_RUNNING,
|
package/src/index.ts
CHANGED
package/src/reactive/array.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { isFunction,
|
|
2
|
-
import {
|
|
1
|
+
import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
|
|
2
|
+
import { REACTIVE_ARRAY } from '~/constants';
|
|
3
|
+
import { computed, dispose, isComputed, onCleanup, read, root } from '~/system';
|
|
3
4
|
import { Computed, Infer } from '~/types';
|
|
4
|
-
import object, { ReactiveObject } from './object';
|
|
5
|
-
import { Disposable } from './disposable';
|
|
5
|
+
import object, { isReactiveObject, ReactiveObject } from './object';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
type API<T> = Infer<T>[] & ReactiveArray<T>;
|
|
@@ -33,7 +33,7 @@ type Events<T> = {
|
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
type Item<T> = Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never
|
|
36
|
+
type Item<T> = T | Computed<T> | API<T> | ReactiveObject< T extends Record<PropertyKey, unknown> ? T : never >;
|
|
37
37
|
|
|
38
38
|
type Listener<V> = {
|
|
39
39
|
once?: boolean;
|
|
@@ -48,15 +48,15 @@ type Value<T> =
|
|
|
48
48
|
: T;
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class ReactiveArray<T>
|
|
51
|
+
class ReactiveArray<T> {
|
|
52
|
+
[REACTIVE_ARRAY] = true;
|
|
53
|
+
|
|
52
54
|
private data: Item<T>[];
|
|
53
55
|
private listeners: Record<string, (Listener<any> | null)[]> | null = null;
|
|
54
56
|
private proxy: API<T>;
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
constructor(data: Item<T>[], proxy: API<T>) {
|
|
58
|
-
super();
|
|
59
|
-
|
|
60
60
|
this.data = data;
|
|
61
61
|
this.proxy = proxy;
|
|
62
62
|
}
|
|
@@ -75,6 +75,16 @@ class ReactiveArray<T> extends Disposable {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
private cleanup(item: Item<T>) {
|
|
79
|
+
if (isReactiveObject(item)) {
|
|
80
|
+
item.dispose();
|
|
81
|
+
}
|
|
82
|
+
else if (isComputed(item)) {
|
|
83
|
+
dispose(item);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
78
88
|
at(i: number) {
|
|
79
89
|
let value = this.data[i];
|
|
80
90
|
|
|
@@ -113,20 +123,9 @@ class ReactiveArray<T> extends Disposable {
|
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
dispose() {
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
for (let i = 0, n = data.length; i < n; i++) {
|
|
119
|
-
let value = data[i];
|
|
120
|
-
|
|
121
|
-
if (isInstanceOf(value, Disposable)) {
|
|
122
|
-
value.dispose();
|
|
123
|
-
}
|
|
124
|
-
else if (isComputed(value)) {
|
|
125
|
-
dispose(value);
|
|
126
|
-
}
|
|
126
|
+
for (let i = 0, n = this.data.length; i < n; i++) {
|
|
127
|
+
this.cleanup(this.data[i]);
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
this.listeners = null;
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
map<R>(
|
|
@@ -194,10 +193,7 @@ class ReactiveArray<T> extends Disposable {
|
|
|
194
193
|
let item = this.data.pop();
|
|
195
194
|
|
|
196
195
|
if (item !== undefined) {
|
|
197
|
-
|
|
198
|
-
dispose(item);
|
|
199
|
-
}
|
|
200
|
-
|
|
196
|
+
this.cleanup(item);
|
|
201
197
|
this.dispatch('pop', { item });
|
|
202
198
|
}
|
|
203
199
|
|
|
@@ -224,10 +220,7 @@ class ReactiveArray<T> extends Disposable {
|
|
|
224
220
|
let item = this.data.shift();
|
|
225
221
|
|
|
226
222
|
if (item !== undefined) {
|
|
227
|
-
|
|
228
|
-
dispose(item);
|
|
229
|
-
}
|
|
230
|
-
|
|
223
|
+
this.cleanup(item);
|
|
231
224
|
this.dispatch('shift', { item });
|
|
232
225
|
}
|
|
233
226
|
|
|
@@ -250,11 +243,7 @@ class ReactiveArray<T> extends Disposable {
|
|
|
250
243
|
|
|
251
244
|
if (items.length > 0 || removed.length > 0) {
|
|
252
245
|
for (let i = 0, n = removed.length; i < n; i++) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (isComputed(item)) {
|
|
256
|
-
dispose(item);
|
|
257
|
-
}
|
|
246
|
+
this.cleanup(removed[i]);
|
|
258
247
|
}
|
|
259
248
|
|
|
260
249
|
this.dispatch('splice', {
|
|
@@ -299,48 +288,52 @@ function factory<T>(input: T[]) {
|
|
|
299
288
|
}
|
|
300
289
|
|
|
301
290
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (isNumber(key)) {
|
|
306
|
-
let value = wrapped[key];
|
|
307
|
-
|
|
308
|
-
if (isComputed(value)) {
|
|
309
|
-
return read(value);
|
|
310
|
-
}
|
|
291
|
+
const isReactiveArray = (value: any): value is ReactiveArray<any> => {
|
|
292
|
+
return isObject(value) && REACTIVE_ARRAY in value;
|
|
293
|
+
};
|
|
311
294
|
|
|
312
|
-
return value;
|
|
313
|
-
}
|
|
314
|
-
else if (key in a) {
|
|
315
|
-
return a[key as keyof typeof a];
|
|
316
|
-
}
|
|
317
295
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
296
|
+
export default function array<T>(input: T[]) {
|
|
297
|
+
let { array, proxy } = root(() => {
|
|
298
|
+
let proxy = new Proxy({}, {
|
|
299
|
+
get(_: any, key: any) {
|
|
300
|
+
if (isNumber(key)) {
|
|
301
|
+
let value = wrapped[key];
|
|
302
|
+
|
|
303
|
+
if (isComputed(value)) {
|
|
304
|
+
return read(value);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return value;
|
|
308
|
+
}
|
|
309
|
+
else if (key in array) {
|
|
310
|
+
return array[key as keyof typeof array];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return wrapped[key];
|
|
314
|
+
},
|
|
315
|
+
set(_: any, key: any, value: any) {
|
|
316
|
+
if (isNumber(key)) {
|
|
317
|
+
array.splice(key, 1, value);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
else if (key === 'length') {
|
|
321
|
+
return array.length = value;
|
|
322
|
+
}
|
|
323
323
|
|
|
324
|
-
if (host === undefined || !isComputed(host)) {
|
|
325
|
-
a.splice(key, 1, value);
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
324
|
return false;
|
|
329
325
|
}
|
|
326
|
+
}) as API<T>,
|
|
327
|
+
wrapped = factory(input);
|
|
330
328
|
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
else if (key === 'length') {
|
|
334
|
-
return a.length = value;
|
|
335
|
-
}
|
|
329
|
+
let array = new ReactiveArray(wrapped, proxy);
|
|
336
330
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}) as API<T>,
|
|
340
|
-
wrapped = factory(input);
|
|
331
|
+
return { array, proxy };
|
|
332
|
+
});
|
|
341
333
|
|
|
342
|
-
|
|
334
|
+
onCleanup(() => array.dispose());
|
|
343
335
|
|
|
344
336
|
return proxy;
|
|
345
337
|
};
|
|
338
|
+
export { isReactiveArray };
|
|
346
339
|
export type { API as ReactiveArray };
|
package/src/reactive/index.ts
CHANGED
|
@@ -11,14 +11,16 @@ type API<T> =
|
|
|
11
11
|
: never;
|
|
12
12
|
|
|
13
13
|
type Input<T> =
|
|
14
|
-
T extends { dispose: any }
|
|
15
|
-
?
|
|
16
|
-
: T extends Record<PropertyKey, unknown>
|
|
17
|
-
|
|
14
|
+
T extends { dispose: any }
|
|
15
|
+
? never
|
|
16
|
+
: T extends Record<PropertyKey, unknown>
|
|
17
|
+
? T
|
|
18
|
+
: T extends unknown[]
|
|
19
|
+
? Input<T[number]>[]
|
|
18
20
|
: never;
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
export default <T>(input: Input<T>): API<T> => {
|
|
23
|
+
export default <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>): API<T> => {
|
|
22
24
|
if (isArray(input)) {
|
|
23
25
|
return array(input) as API<T>;
|
|
24
26
|
}
|
|
@@ -27,4 +29,5 @@ export default <T>(input: Input<T>): API<T> => {
|
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
|
|
30
|
-
};
|
|
32
|
+
};
|
|
33
|
+
export type { Input };
|
package/src/reactive/object.ts
CHANGED
|
@@ -1,45 +1,27 @@
|
|
|
1
|
-
import { defineProperty, isArray, isFunction,
|
|
2
|
-
import
|
|
3
|
-
import { computed, dispose, effect, read, root, signal } from '~/system';
|
|
1
|
+
import { defineProperty, isArray, isFunction, isObject, isPromise, Prettify } from '@esportsplus/utilities';
|
|
2
|
+
import { computed, dispose, effect, isComputed, onCleanup, read, root, set, signal } from '~/system';
|
|
4
3
|
import { Computed, Infer, Signal } from '~/types';
|
|
5
|
-
import {
|
|
4
|
+
import { REACTIVE_OBJECT } from '~/constants';
|
|
5
|
+
import array, { isReactiveArray } 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>;
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable {
|
|
15
|
-
private disposable: Record<
|
|
16
|
-
PropertyKey,
|
|
17
|
-
Computed<any> | ReactiveArray<any> | ReactiveObject<any>
|
|
18
|
-
> = {};
|
|
19
|
-
|
|
11
|
+
class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
12
|
+
[REACTIVE_OBJECT] = true;
|
|
20
13
|
|
|
21
14
|
constructor(data: T) {
|
|
22
|
-
super();
|
|
23
|
-
|
|
24
|
-
let disposable = this.disposable,
|
|
25
|
-
triggers: Record<string, Signal<boolean>> = {};
|
|
26
|
-
|
|
27
15
|
for (let key in data) {
|
|
28
16
|
let value = data[key];
|
|
29
17
|
|
|
30
18
|
if (isArray(value)) {
|
|
31
|
-
let a =
|
|
32
|
-
t = triggers[key] = signal(false);
|
|
19
|
+
let a = array(value);
|
|
33
20
|
|
|
34
21
|
defineProperty(this, key, {
|
|
35
22
|
enumerable: true,
|
|
36
23
|
get() {
|
|
37
|
-
read(t);
|
|
38
24
|
return a;
|
|
39
|
-
},
|
|
40
|
-
set(v: typeof value) {
|
|
41
|
-
a = disposable[key] = array(v);
|
|
42
|
-
set(t, !!t.value);
|
|
43
25
|
}
|
|
44
26
|
});
|
|
45
27
|
}
|
|
@@ -51,7 +33,7 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
51
33
|
get() {
|
|
52
34
|
if (c === undefined) {
|
|
53
35
|
root(() => {
|
|
54
|
-
c =
|
|
36
|
+
c = computed(value as Computed<T[typeof key]>['fn']);
|
|
55
37
|
|
|
56
38
|
if (isPromise(c.value)) {
|
|
57
39
|
let factory = c,
|
|
@@ -67,7 +49,7 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
67
49
|
return;
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
set(c
|
|
52
|
+
set(c as Signal<typeof value>, value);
|
|
71
53
|
});
|
|
72
54
|
});
|
|
73
55
|
}
|
|
@@ -96,23 +78,33 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
96
78
|
|
|
97
79
|
|
|
98
80
|
dispose() {
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
let value;
|
|
82
|
+
|
|
83
|
+
for (let key in this) {
|
|
84
|
+
value = this[key];
|
|
101
85
|
|
|
102
|
-
if (
|
|
86
|
+
if (isReactiveArray(value) || isReactiveObject(value)) {
|
|
103
87
|
value.dispose();
|
|
104
88
|
}
|
|
105
|
-
else {
|
|
89
|
+
else if (isComputed(value)) {
|
|
106
90
|
dispose(value);
|
|
107
91
|
}
|
|
108
92
|
}
|
|
109
|
-
|
|
110
|
-
this.disposable = {};
|
|
111
93
|
}
|
|
112
94
|
}
|
|
113
95
|
|
|
114
96
|
|
|
97
|
+
const isReactiveObject = (value: any): value is ReactiveObject<any> => {
|
|
98
|
+
return isObject(value) && REACTIVE_OBJECT in value;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
|
|
115
102
|
export default function object<T extends Record<PropertyKey, unknown>>(input: T) {
|
|
116
|
-
|
|
103
|
+
let object = root(() => new ReactiveObject<T>(input));
|
|
104
|
+
|
|
105
|
+
onCleanup(() => object.dispose());
|
|
106
|
+
|
|
107
|
+
return object;
|
|
117
108
|
};
|
|
109
|
+
export { isReactiveObject };
|
|
118
110
|
export type { API as ReactiveObject };
|
package/src/system.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
1
|
+
import { isArray, isObject, } from '@esportsplus/utilities';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
COMPUTED, SIGNAL,
|
|
4
4
|
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
5
5
|
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING
|
|
6
6
|
} from './constants';
|
|
7
|
-
import { Computed, Link, Signal
|
|
7
|
+
import { Computed, Link, Signal } from './types';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
let depth = 0,
|
|
11
|
-
heap: (Computed<unknown> | undefined)[] = new Array(
|
|
11
|
+
heap: (Computed<unknown> | undefined)[] = new Array(2048),
|
|
12
12
|
index = 0,
|
|
13
13
|
length = 0,
|
|
14
|
+
microtask = queueMicrotask,
|
|
14
15
|
notified = false,
|
|
15
16
|
observer: Computed<unknown> | null = null,
|
|
16
|
-
scheduler = queueMicrotask,
|
|
17
17
|
stabilizer = STABILIZER_IDLE,
|
|
18
18
|
version = 0;
|
|
19
19
|
|
|
@@ -26,7 +26,7 @@ function cleanup<T>(node: Computed<T>): void {
|
|
|
26
26
|
let cleanup = node.cleanup;
|
|
27
27
|
|
|
28
28
|
if (isArray(cleanup)) {
|
|
29
|
-
for (let i = 0
|
|
29
|
+
for (let i = 0, n = cleanup.length; i < n; i++) {
|
|
30
30
|
cleanup[i]();
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -98,7 +98,7 @@ function insertIntoHeap<T>(computed: Computed<T>) {
|
|
|
98
98
|
|
|
99
99
|
// Simple auto adjust to avoid manual management within apps.
|
|
100
100
|
if (height >= heap.length) {
|
|
101
|
-
heap.length
|
|
101
|
+
heap.length = Math.round(heap.length * 1.5);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -245,7 +245,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
|
|
|
245
245
|
function schedule() {
|
|
246
246
|
if (stabilizer === STABILIZER_IDLE && !depth) {
|
|
247
247
|
stabilizer = STABILIZER_SCHEDULED;
|
|
248
|
-
|
|
248
|
+
microtask(stabilize);
|
|
249
249
|
}
|
|
250
250
|
else if (stabilizer === STABILIZER_RUNNING) {
|
|
251
251
|
stabilizer = STABILIZER_RESCHEDULE;
|
|
@@ -253,30 +253,32 @@ function schedule() {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
function stabilize() {
|
|
256
|
-
|
|
257
|
-
stabilizer = STABILIZER_RUNNING;
|
|
256
|
+
let o = observer;
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
let computed = heap[index];
|
|
258
|
+
stabilizer = STABILIZER_RUNNING;
|
|
261
259
|
|
|
262
|
-
|
|
260
|
+
for (index = 0; index <= length; index++) {
|
|
261
|
+
let computed = heap[index];
|
|
263
262
|
|
|
264
|
-
|
|
265
|
-
let next = computed.nextHeap;
|
|
263
|
+
heap[index] = undefined;
|
|
266
264
|
|
|
267
|
-
|
|
265
|
+
while (computed !== undefined) {
|
|
266
|
+
let next = computed.nextHeap;
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
}
|
|
268
|
+
recompute(computed, false);
|
|
272
269
|
|
|
273
|
-
|
|
274
|
-
scheduler(stabilize);
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
stabilizer = STABILIZER_IDLE;
|
|
270
|
+
computed = next;
|
|
278
271
|
}
|
|
279
|
-
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
observer = o;
|
|
275
|
+
|
|
276
|
+
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
277
|
+
microtask(stabilize);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
stabilizer = STABILIZER_IDLE;
|
|
281
|
+
}
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
|
|
@@ -293,12 +295,8 @@ function unlink(link: Link): Link | null {
|
|
|
293
295
|
if (prevSub !== null) {
|
|
294
296
|
prevSub.nextSub = nextSub;
|
|
295
297
|
}
|
|
296
|
-
|
|
297
|
-
dep
|
|
298
|
-
|
|
299
|
-
if (nextSub === null && 'fn' in dep) {
|
|
300
|
-
dispose(dep);
|
|
301
|
-
}
|
|
298
|
+
else if ((dep.subs = nextSub) === null && 'fn' in dep) {
|
|
299
|
+
dispose(dep);
|
|
302
300
|
}
|
|
303
301
|
|
|
304
302
|
return nextDep;
|
|
@@ -311,10 +309,10 @@ function update<T>(computed: Computed<T>): void {
|
|
|
311
309
|
|
|
312
310
|
if ('fn' in dep) {
|
|
313
311
|
update(dep);
|
|
314
|
-
}
|
|
315
312
|
|
|
316
|
-
|
|
317
|
-
|
|
313
|
+
if (computed.state & STATE_DIRTY) {
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
318
316
|
}
|
|
319
317
|
}
|
|
320
318
|
}
|
|
@@ -329,7 +327,7 @@ function update<T>(computed: Computed<T>): void {
|
|
|
329
327
|
|
|
330
328
|
const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
331
329
|
let self: Computed<T> = {
|
|
332
|
-
[
|
|
330
|
+
[COMPUTED]: true,
|
|
333
331
|
cleanup: null,
|
|
334
332
|
deps: null,
|
|
335
333
|
depsTail: null,
|
|
@@ -357,6 +355,7 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
|
357
355
|
}
|
|
358
356
|
|
|
359
357
|
link(self, observer);
|
|
358
|
+
onCleanup(() => dispose(self));
|
|
360
359
|
}
|
|
361
360
|
else {
|
|
362
361
|
recompute(self, false);
|
|
@@ -388,11 +387,11 @@ const effect = <T>(fn: Computed<T>['fn']) => {
|
|
|
388
387
|
};
|
|
389
388
|
|
|
390
389
|
const isComputed = (value: unknown): value is Computed<unknown> => {
|
|
391
|
-
return isObject(value) &&
|
|
390
|
+
return isObject(value) && COMPUTED in value;
|
|
392
391
|
};
|
|
393
392
|
|
|
394
393
|
const isSignal = (value: unknown): value is Signal<unknown> => {
|
|
395
|
-
return isObject(value) &&
|
|
394
|
+
return isObject(value) && SIGNAL in value;
|
|
396
395
|
};
|
|
397
396
|
|
|
398
397
|
const onCleanup = (fn: VoidFunction): typeof fn => {
|
|
@@ -400,16 +399,14 @@ const onCleanup = (fn: VoidFunction): typeof fn => {
|
|
|
400
399
|
return fn;
|
|
401
400
|
}
|
|
402
401
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (!node.cleanup) {
|
|
406
|
-
node.cleanup = fn;
|
|
402
|
+
if (!observer.cleanup) {
|
|
403
|
+
observer.cleanup = fn;
|
|
407
404
|
}
|
|
408
|
-
else if (isArray(
|
|
409
|
-
|
|
405
|
+
else if (isArray(observer.cleanup)) {
|
|
406
|
+
observer.cleanup.push(fn);
|
|
410
407
|
}
|
|
411
408
|
else {
|
|
412
|
-
|
|
409
|
+
observer.cleanup = [observer.cleanup, fn];
|
|
413
410
|
}
|
|
414
411
|
|
|
415
412
|
return fn;
|
|
@@ -460,16 +457,7 @@ const root = <T>(fn: () => T) => {
|
|
|
460
457
|
return value;
|
|
461
458
|
};
|
|
462
459
|
|
|
463
|
-
const
|
|
464
|
-
return {
|
|
465
|
-
[REACTIVE]: true,
|
|
466
|
-
subs: null,
|
|
467
|
-
subsTail: null,
|
|
468
|
-
value,
|
|
469
|
-
};
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
signal.set = <T>(signal: Signal<T>, value: T) => {
|
|
460
|
+
const set = <T>(signal: Signal<T>, value: T) => {
|
|
473
461
|
if (signal.value === value) {
|
|
474
462
|
return;
|
|
475
463
|
}
|
|
@@ -488,6 +476,15 @@ signal.set = <T>(signal: Signal<T>, value: T) => {
|
|
|
488
476
|
schedule();
|
|
489
477
|
};
|
|
490
478
|
|
|
479
|
+
const signal = <T>(value: T): Signal<T> => {
|
|
480
|
+
return {
|
|
481
|
+
[SIGNAL]: true,
|
|
482
|
+
subs: null,
|
|
483
|
+
subsTail: null,
|
|
484
|
+
value,
|
|
485
|
+
};
|
|
486
|
+
};
|
|
487
|
+
|
|
491
488
|
|
|
492
489
|
export {
|
|
493
490
|
computed,
|
|
@@ -496,5 +493,6 @@ export {
|
|
|
496
493
|
isComputed, isSignal,
|
|
497
494
|
onCleanup,
|
|
498
495
|
read, root,
|
|
499
|
-
signal
|
|
500
|
-
};
|
|
496
|
+
set, signal
|
|
497
|
+
};
|
|
498
|
+
export type { Computed, Signal };
|
package/src/types.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { onCleanup } from './system';
|
|
1
|
+
import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
|
|
3
2
|
import { ReactiveArray } from './reactive/array';
|
|
4
3
|
import { ReactiveObject } from './reactive/object';
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
interface Computed<T>
|
|
8
|
-
[
|
|
6
|
+
interface Computed<T> {
|
|
7
|
+
[COMPUTED]: true;
|
|
9
8
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
10
9
|
deps: Link | null;
|
|
11
10
|
depsTail: Link | null;
|
|
12
|
-
fn: (
|
|
11
|
+
fn: (onCleanup?: (fn: VoidFunction) => typeof fn) => T;
|
|
13
12
|
height: number;
|
|
14
13
|
nextHeap: Computed<unknown> | undefined;
|
|
15
14
|
prevHeap: Computed<unknown>;
|
|
@@ -19,6 +18,9 @@ interface Computed<T> extends Signal<T> {
|
|
|
19
18
|
typeof STATE_IN_HEAP |
|
|
20
19
|
typeof STATE_NONE |
|
|
21
20
|
typeof STATE_RECOMPUTING;
|
|
21
|
+
subs: Link | null;
|
|
22
|
+
subsTail: Link | null;
|
|
23
|
+
value: T;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
type Infer<T> =
|
|
@@ -48,7 +50,7 @@ type Reactive<T> = T extends Record<PropertyKey, unknown>
|
|
|
48
50
|
: ReactiveArray<T>;
|
|
49
51
|
|
|
50
52
|
type Signal<T> = {
|
|
51
|
-
[
|
|
53
|
+
[SIGNAL]: true;
|
|
52
54
|
subs: Link | null;
|
|
53
55
|
subsTail: Link | null;
|
|
54
56
|
value: T;
|