@esportsplus/reactivity 0.5.0 → 0.6.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/reactive/index.d.ts +6 -3
- package/build/reactive/index.js +9 -9
- package/build/reactive/object.d.ts +1 -1
- package/build/reactive/object.js +28 -18
- package/build/reactive/promise.d.ts +13 -0
- package/build/reactive/promise.js +48 -0
- package/build/signal.d.ts +2 -2
- package/build/signal.js +18 -18
- package/package.json +1 -1
- package/src/reactive/index.ts +26 -17
- package/src/reactive/object.ts +30 -19
- package/src/reactive/promise.ts +67 -0
- package/src/signal.ts +30 -30
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import array from './array.js';
|
|
2
|
+
import object from './object.js';
|
|
3
|
+
import promise from './promise.js';
|
|
4
|
+
type API<T> = T extends (...args: infer A) => Promise<infer R> ? ReturnType<typeof promise<A, Promise<R>>> : T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
|
|
5
|
+
type Guard<T> = T extends (...args: unknown[]) => Promise<unknown> ? T : T extends {
|
|
3
6
|
dispose: any;
|
|
4
7
|
} | {
|
|
5
8
|
signals: any;
|
|
6
9
|
} ? {
|
|
7
10
|
never: '[ dispose, signals ] are reserved keys';
|
|
8
11
|
} : T extends Record<PropertyKey, unknown> | unknown[] ? T : never;
|
|
9
|
-
declare const _default: <T>(data: Guard<T>) =>
|
|
12
|
+
declare const _default: <T>(data: Guard<T>) => API<T>;
|
|
10
13
|
export default _default;
|
package/build/reactive/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { isArray, isObject, isPromise } from '@esportsplus/utilities';
|
|
2
|
+
import array from './array.js';
|
|
3
|
+
import object from './object.js';
|
|
4
|
+
import promise from './promise.js';
|
|
4
5
|
export default (data) => {
|
|
5
|
-
let value;
|
|
6
6
|
if (isArray(data)) {
|
|
7
|
-
|
|
7
|
+
return array(data);
|
|
8
8
|
}
|
|
9
9
|
else if (isObject(data)) {
|
|
10
|
-
|
|
10
|
+
return object(data);
|
|
11
11
|
}
|
|
12
|
-
else {
|
|
13
|
-
|
|
12
|
+
else if (isPromise(data)) {
|
|
13
|
+
return promise(data);
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(data)}`);
|
|
16
16
|
};
|
|
@@ -5,7 +5,7 @@ type API<T extends Record<PropertyKey, unknown>> = Prettify<{
|
|
|
5
5
|
[K in keyof T]: Infer<T[K]>;
|
|
6
6
|
}> & ReactiveObject<T>;
|
|
7
7
|
declare class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable {
|
|
8
|
-
private
|
|
8
|
+
private disposable;
|
|
9
9
|
constructor(data: T);
|
|
10
10
|
dispose(): void;
|
|
11
11
|
}
|
package/build/reactive/object.js
CHANGED
|
@@ -1,59 +1,69 @@
|
|
|
1
|
-
import { defineProperty, isArray, isFunction, isInstanceOf, isObject } from '@esportsplus/utilities';
|
|
1
|
+
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import array from './array.js';
|
|
3
|
-
import { computed, dispose,
|
|
3
|
+
import { computed, dispose, read, signal } from '../signal.js';
|
|
4
4
|
import { Disposable } from './disposable.js';
|
|
5
|
+
import promise from './promise.js';
|
|
5
6
|
let { set } = signal;
|
|
6
7
|
class ReactiveObject extends Disposable {
|
|
7
|
-
|
|
8
|
+
disposable = {};
|
|
8
9
|
constructor(data) {
|
|
9
10
|
super();
|
|
10
|
-
let
|
|
11
|
+
let disposable = this.disposable, triggers = {};
|
|
11
12
|
for (let key in data) {
|
|
12
13
|
let value = data[key];
|
|
13
14
|
if (isArray(value)) {
|
|
14
|
-
let
|
|
15
|
+
let a = disposable[key] = array(value), t = triggers[key] = signal(false);
|
|
15
16
|
defineProperty(this, key, {
|
|
16
17
|
enumerable: true,
|
|
17
18
|
get() {
|
|
18
19
|
read(t);
|
|
19
|
-
return
|
|
20
|
+
return a;
|
|
20
21
|
},
|
|
21
22
|
set(v) {
|
|
22
23
|
set(t, !!t.value);
|
|
23
|
-
|
|
24
|
+
a = disposable[key] = array(v);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (isAsyncFunction(value)) {
|
|
29
|
+
let p = promise(value);
|
|
30
|
+
defineProperty(this, key, {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get() {
|
|
33
|
+
return p;
|
|
24
34
|
}
|
|
25
35
|
});
|
|
26
36
|
}
|
|
27
37
|
else if (isFunction(value)) {
|
|
28
|
-
let
|
|
38
|
+
let c = disposable[key] = computed(value);
|
|
29
39
|
defineProperty(this, key, {
|
|
30
40
|
enumerable: true,
|
|
31
41
|
get() {
|
|
32
|
-
return read(
|
|
42
|
+
return read(c);
|
|
33
43
|
}
|
|
34
44
|
});
|
|
35
45
|
}
|
|
36
46
|
else if (isObject(value)) {
|
|
37
|
-
let
|
|
47
|
+
let o = disposable[key] = new ReactiveObject(value), t = triggers[key] = signal(false);
|
|
38
48
|
defineProperty(this, key, {
|
|
39
49
|
enumerable: true,
|
|
40
50
|
get() {
|
|
41
51
|
read(t);
|
|
42
|
-
return
|
|
52
|
+
return o;
|
|
43
53
|
},
|
|
44
54
|
set(v) {
|
|
45
55
|
set(t, !!t.value);
|
|
46
|
-
|
|
56
|
+
o = disposable[key] = new ReactiveObject(v);
|
|
47
57
|
}
|
|
48
58
|
});
|
|
49
59
|
}
|
|
50
60
|
else {
|
|
51
|
-
let s =
|
|
61
|
+
let s = signal(value);
|
|
52
62
|
defineProperty(this, key, {
|
|
53
63
|
enumerable: true,
|
|
54
64
|
get() {
|
|
55
65
|
if (s === undefined) {
|
|
56
|
-
s =
|
|
66
|
+
s = signal(value);
|
|
57
67
|
}
|
|
58
68
|
return read(s);
|
|
59
69
|
},
|
|
@@ -65,16 +75,16 @@ class ReactiveObject extends Disposable {
|
|
|
65
75
|
}
|
|
66
76
|
}
|
|
67
77
|
dispose() {
|
|
68
|
-
for (let key in this.
|
|
69
|
-
let value = this.
|
|
78
|
+
for (let key in this.disposable) {
|
|
79
|
+
let value = this.disposable[key];
|
|
70
80
|
if (isInstanceOf(value, Disposable)) {
|
|
71
81
|
value.dispose();
|
|
72
82
|
}
|
|
73
|
-
else
|
|
83
|
+
else {
|
|
74
84
|
dispose(value);
|
|
75
85
|
}
|
|
76
86
|
}
|
|
77
|
-
this.
|
|
87
|
+
this.disposable = {};
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
export default function object(input) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import CustomFunction from '@esportsplus/custom-function';
|
|
2
|
+
declare class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
|
|
3
|
+
private arguments;
|
|
4
|
+
private okay;
|
|
5
|
+
private response;
|
|
6
|
+
stop: boolean | null;
|
|
7
|
+
constructor(fn: (...args: A) => R);
|
|
8
|
+
get data(): Awaited<R> | null;
|
|
9
|
+
get input(): A | null;
|
|
10
|
+
get ok(): boolean | null;
|
|
11
|
+
}
|
|
12
|
+
declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => ReactivePromise<A, R>;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import CustomFunction from '@esportsplus/custom-function';
|
|
2
|
+
import { read, root, signal } from '../signal.js';
|
|
3
|
+
let { set } = signal;
|
|
4
|
+
class ReactivePromise extends CustomFunction {
|
|
5
|
+
arguments;
|
|
6
|
+
okay;
|
|
7
|
+
response;
|
|
8
|
+
stop = null;
|
|
9
|
+
constructor(fn) {
|
|
10
|
+
super((...args) => {
|
|
11
|
+
this.stop = null;
|
|
12
|
+
set(this.arguments, args);
|
|
13
|
+
set(this.okay, null);
|
|
14
|
+
return root(() => {
|
|
15
|
+
return fn(...args)
|
|
16
|
+
.then((value) => {
|
|
17
|
+
if (this.stop === true) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
set(this.response, value);
|
|
21
|
+
set(this.okay, true);
|
|
22
|
+
})
|
|
23
|
+
.catch(() => {
|
|
24
|
+
if (this.stop === true) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
set(this.response, null);
|
|
28
|
+
set(this.okay, false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
this.response = signal(null);
|
|
33
|
+
this.arguments = signal(null);
|
|
34
|
+
this.okay = signal(null);
|
|
35
|
+
}
|
|
36
|
+
get data() {
|
|
37
|
+
return read(this.response);
|
|
38
|
+
}
|
|
39
|
+
get input() {
|
|
40
|
+
return read(this.arguments);
|
|
41
|
+
}
|
|
42
|
+
get ok() {
|
|
43
|
+
return read(this.okay);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export default (fn) => {
|
|
47
|
+
return new ReactivePromise(fn);
|
|
48
|
+
};
|
package/build/signal.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Computed, Signal } from './types.js';
|
|
2
2
|
declare const computed: <T>(fn: Computed<T>["fn"]) => Computed<T>;
|
|
3
|
-
declare const dispose: (el: Computed<
|
|
3
|
+
declare const dispose: <T>(el: Computed<T>) => void;
|
|
4
4
|
declare const isComputed: (value: unknown) => value is Computed<unknown>;
|
|
5
5
|
declare const isReactive: (value: unknown) => value is Computed<unknown> | Signal<unknown>;
|
|
6
6
|
declare const isSignal: (value: unknown) => value is Signal<unknown>;
|
|
@@ -9,7 +9,7 @@ declare const read: <T>(el: Signal<T> | Computed<T>) => T;
|
|
|
9
9
|
declare const root: <T>(fn: () => T) => T;
|
|
10
10
|
declare const signal: {
|
|
11
11
|
<T>(value: T): Signal<T>;
|
|
12
|
-
set(el: Signal<
|
|
12
|
+
set<T>(el: Signal<T>, v: T): void;
|
|
13
13
|
};
|
|
14
14
|
declare const stabilize: () => void;
|
|
15
15
|
export { computed, dispose, isComputed, isReactive, isSignal, oncleanup, read, root, signal, stabilize };
|
package/build/signal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isArray, isObject } from '@esportsplus/utilities';
|
|
2
2
|
import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
3
|
-
let
|
|
3
|
+
let dirtyHeap = new Array(2000), maxDirty = 0, markedHeap = false, minDirty = 0, observer = null;
|
|
4
4
|
function cleanup(node) {
|
|
5
5
|
if (!node.cleanup) {
|
|
6
6
|
return;
|
|
@@ -125,8 +125,8 @@ function recompute(el, del) {
|
|
|
125
125
|
el.prevHeap = el;
|
|
126
126
|
}
|
|
127
127
|
cleanup(el);
|
|
128
|
-
let
|
|
129
|
-
|
|
128
|
+
let o = observer, ok = true, value;
|
|
129
|
+
observer = el;
|
|
130
130
|
el.depsTail = null;
|
|
131
131
|
el.state = STATE_RECOMPUTING;
|
|
132
132
|
try {
|
|
@@ -135,7 +135,7 @@ function recompute(el, del) {
|
|
|
135
135
|
catch (e) {
|
|
136
136
|
ok = false;
|
|
137
137
|
}
|
|
138
|
-
|
|
138
|
+
observer = o;
|
|
139
139
|
el.state = STATE_NONE;
|
|
140
140
|
let depsTail = el.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : el.deps;
|
|
141
141
|
if (toRemove !== null) {
|
|
@@ -212,16 +212,16 @@ const computed = (fn) => {
|
|
|
212
212
|
value: undefined,
|
|
213
213
|
};
|
|
214
214
|
self.prevHeap = self;
|
|
215
|
-
if (
|
|
216
|
-
if (
|
|
217
|
-
self.height =
|
|
215
|
+
if (observer) {
|
|
216
|
+
if (observer.depsTail === null) {
|
|
217
|
+
self.height = observer.height;
|
|
218
218
|
recompute(self, false);
|
|
219
219
|
}
|
|
220
220
|
else {
|
|
221
|
-
self.height =
|
|
221
|
+
self.height = observer.height + 1;
|
|
222
222
|
insertIntoHeap(self);
|
|
223
223
|
}
|
|
224
|
-
link(self,
|
|
224
|
+
link(self, observer);
|
|
225
225
|
}
|
|
226
226
|
else {
|
|
227
227
|
recompute(self, false);
|
|
@@ -247,10 +247,10 @@ const isSignal = (value) => {
|
|
|
247
247
|
return isObject(value) && REACTIVE in value && 'fn' in value === false;
|
|
248
248
|
};
|
|
249
249
|
const oncleanup = (fn) => {
|
|
250
|
-
if (!
|
|
250
|
+
if (!observer) {
|
|
251
251
|
return fn;
|
|
252
252
|
}
|
|
253
|
-
let node =
|
|
253
|
+
let node = observer;
|
|
254
254
|
if (!node.cleanup) {
|
|
255
255
|
node.cleanup = fn;
|
|
256
256
|
}
|
|
@@ -263,12 +263,12 @@ const oncleanup = (fn) => {
|
|
|
263
263
|
return fn;
|
|
264
264
|
};
|
|
265
265
|
const read = (el) => {
|
|
266
|
-
if (
|
|
267
|
-
link(el,
|
|
266
|
+
if (observer) {
|
|
267
|
+
link(el, observer);
|
|
268
268
|
if ('fn' in el) {
|
|
269
269
|
let height = el.height;
|
|
270
|
-
if (height >=
|
|
271
|
-
|
|
270
|
+
if (height >= observer.height) {
|
|
271
|
+
observer.height = height + 1;
|
|
272
272
|
}
|
|
273
273
|
if (height >= minDirty ||
|
|
274
274
|
el.state & (STATE_DIRTY | STATE_CHECK)) {
|
|
@@ -280,10 +280,10 @@ const read = (el) => {
|
|
|
280
280
|
return el.value;
|
|
281
281
|
};
|
|
282
282
|
const root = (fn) => {
|
|
283
|
-
let
|
|
284
|
-
|
|
283
|
+
let o = observer;
|
|
284
|
+
observer = null;
|
|
285
285
|
let value = fn();
|
|
286
|
-
|
|
286
|
+
observer = o;
|
|
287
287
|
return value;
|
|
288
288
|
};
|
|
289
289
|
const signal = (value) => {
|
package/package.json
CHANGED
package/src/reactive/index.ts
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
|
-
import { isArray, isObject } from '@esportsplus/utilities';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { isArray, isObject, isPromise } from '@esportsplus/utilities';
|
|
2
|
+
import array from './array';
|
|
3
|
+
import object from './object';
|
|
4
|
+
import promise from './promise';
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
type
|
|
8
|
-
T extends
|
|
9
|
-
?
|
|
10
|
-
: T extends Record<PropertyKey, unknown>
|
|
11
|
-
? T
|
|
12
|
-
:
|
|
7
|
+
type API<T> =
|
|
8
|
+
T extends (...args: infer A) => Promise<infer R>
|
|
9
|
+
? ReturnType<typeof promise<A, Promise<R>>>
|
|
10
|
+
: T extends Record<PropertyKey, unknown>
|
|
11
|
+
? ReturnType<typeof object<T>>
|
|
12
|
+
: T extends unknown[]
|
|
13
|
+
? ReturnType<typeof array<T>>
|
|
14
|
+
: never;
|
|
13
15
|
|
|
16
|
+
type Guard<T> =
|
|
17
|
+
T extends (...args: unknown[]) => Promise<unknown>
|
|
18
|
+
? T
|
|
19
|
+
: T extends { dispose: any } | { signals: any }
|
|
20
|
+
? { never: '[ dispose, signals ] are reserved keys' }
|
|
21
|
+
: T extends Record<PropertyKey, unknown> | unknown[]
|
|
22
|
+
? T
|
|
23
|
+
: never;
|
|
14
24
|
|
|
15
|
-
export default <T>(data: Guard<T>) => {
|
|
16
|
-
let value;
|
|
17
25
|
|
|
26
|
+
export default <T>(data: Guard<T>): API<T> => {
|
|
18
27
|
if (isArray(data)) {
|
|
19
|
-
|
|
28
|
+
return array(data) as API<T>;
|
|
20
29
|
}
|
|
21
30
|
else if (isObject(data)) {
|
|
22
|
-
|
|
31
|
+
return object(data) as API<T>;
|
|
23
32
|
}
|
|
24
|
-
else {
|
|
25
|
-
|
|
33
|
+
else if (isPromise(data)) {
|
|
34
|
+
return promise(data) as API<T>;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(data)}`);
|
|
29
38
|
};
|
package/src/reactive/object.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { defineProperty, isArray, isFunction, isInstanceOf, isObject, Prettify } from '@esportsplus/utilities';
|
|
1
|
+
import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, isObject, Prettify } from '@esportsplus/utilities';
|
|
2
2
|
import array, { ReactiveArray } from './array';
|
|
3
|
-
import { computed, dispose,
|
|
3
|
+
import { computed, dispose, read, signal } from '~/signal';
|
|
4
4
|
import { Computed, Infer, Signal } from '~/types';
|
|
5
5
|
import { Disposable } from './disposable';
|
|
6
|
+
import promise from './promise';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
|
|
@@ -12,71 +13,81 @@ let { set } = signal;
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable {
|
|
15
|
-
private
|
|
16
|
+
private disposable: Record<
|
|
16
17
|
PropertyKey,
|
|
17
|
-
Computed<any> | ReactiveArray<any> | ReactiveObject<any>
|
|
18
|
+
Computed<any> | ReactiveArray<any> | ReactiveObject<any>
|
|
18
19
|
> = {};
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
constructor(data: T) {
|
|
22
23
|
super();
|
|
23
24
|
|
|
24
|
-
let
|
|
25
|
+
let disposable = this.disposable,
|
|
25
26
|
triggers: Record<string, Signal<boolean>> = {};
|
|
26
27
|
|
|
27
28
|
for (let key in data) {
|
|
28
29
|
let value = data[key];
|
|
29
30
|
|
|
30
31
|
if (isArray(value)) {
|
|
31
|
-
let
|
|
32
|
+
let a = disposable[key] = array(value),
|
|
32
33
|
t = triggers[key] = signal(false);
|
|
33
34
|
|
|
34
35
|
defineProperty(this, key, {
|
|
35
36
|
enumerable: true,
|
|
36
37
|
get() {
|
|
37
38
|
read(t);
|
|
38
|
-
return
|
|
39
|
+
return a;
|
|
39
40
|
},
|
|
40
41
|
set(v: typeof value) {
|
|
41
42
|
set(t, !!t.value);
|
|
42
|
-
|
|
43
|
+
a = disposable[key] = array(v);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (isAsyncFunction(value)) {
|
|
48
|
+
let p = promise(value);
|
|
49
|
+
|
|
50
|
+
defineProperty(this, key, {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get() {
|
|
53
|
+
return p;
|
|
43
54
|
}
|
|
44
55
|
});
|
|
45
56
|
}
|
|
46
57
|
else if (isFunction(value)) {
|
|
47
|
-
let
|
|
58
|
+
let c = disposable[key] = computed(value as Computed<T>['fn']);
|
|
48
59
|
|
|
49
60
|
defineProperty(this, key, {
|
|
50
61
|
enumerable: true,
|
|
51
62
|
get() {
|
|
52
|
-
return read(
|
|
63
|
+
return read(c as Computed<T>);
|
|
53
64
|
}
|
|
54
65
|
});
|
|
55
66
|
}
|
|
56
67
|
else if (isObject(value)) {
|
|
57
|
-
let
|
|
68
|
+
let o = disposable[key] = new ReactiveObject(value),
|
|
58
69
|
t = triggers[key] = signal(false);
|
|
59
70
|
|
|
60
71
|
defineProperty(this, key, {
|
|
61
72
|
enumerable: true,
|
|
62
73
|
get() {
|
|
63
74
|
read(t);
|
|
64
|
-
return
|
|
75
|
+
return o;
|
|
65
76
|
},
|
|
66
77
|
set(v: typeof value) {
|
|
67
78
|
set(t, !!t.value);
|
|
68
|
-
|
|
79
|
+
o = disposable[key] = new ReactiveObject(v);
|
|
69
80
|
}
|
|
70
81
|
});
|
|
71
82
|
}
|
|
72
83
|
else {
|
|
73
|
-
let s =
|
|
84
|
+
let s = signal(value);
|
|
74
85
|
|
|
75
86
|
defineProperty(this, key, {
|
|
76
87
|
enumerable: true,
|
|
77
88
|
get() {
|
|
78
89
|
if (s === undefined) {
|
|
79
|
-
s =
|
|
90
|
+
s = signal(value);
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
return read(s as Signal<typeof value>);
|
|
@@ -91,18 +102,18 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
|
|
|
91
102
|
|
|
92
103
|
|
|
93
104
|
dispose() {
|
|
94
|
-
for (let key in this.
|
|
95
|
-
let value = this.
|
|
105
|
+
for (let key in this.disposable) {
|
|
106
|
+
let value = this.disposable[key];
|
|
96
107
|
|
|
97
108
|
if (isInstanceOf(value, Disposable)) {
|
|
98
109
|
value.dispose();
|
|
99
110
|
}
|
|
100
|
-
else
|
|
111
|
+
else {
|
|
101
112
|
dispose(value);
|
|
102
113
|
}
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
this.
|
|
116
|
+
this.disposable = {};
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
119
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import CustomFunction from '@esportsplus/custom-function';
|
|
2
|
+
import { read, root, signal } from '~/signal';
|
|
3
|
+
import { Signal } from '~/types';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
let { set } = signal;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
|
|
10
|
+
private arguments: Signal<A | null>;
|
|
11
|
+
private okay: Signal<boolean | null>;
|
|
12
|
+
private response: Signal<Awaited<R> | null>;
|
|
13
|
+
|
|
14
|
+
stop: boolean | null = null;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
constructor(fn: (...args: A) => R) {
|
|
18
|
+
super((...args: A) => {
|
|
19
|
+
this.stop = null;
|
|
20
|
+
|
|
21
|
+
set(this.arguments, args);
|
|
22
|
+
set(this.okay, null);
|
|
23
|
+
|
|
24
|
+
return root(() => {
|
|
25
|
+
return fn(...args)
|
|
26
|
+
.then((value) => {
|
|
27
|
+
if (this.stop === true) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
set(this.response, value as Awaited<R>);
|
|
32
|
+
set(this.okay, true);
|
|
33
|
+
})
|
|
34
|
+
.catch(() => {
|
|
35
|
+
if (this.stop === true) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
set(this.response, null);
|
|
40
|
+
set(this.okay, false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.response = signal(null);
|
|
46
|
+
this.arguments = signal(null);
|
|
47
|
+
this.okay = signal(null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
get data() {
|
|
52
|
+
return read(this.response);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get input() {
|
|
56
|
+
return read(this.arguments);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get ok() {
|
|
60
|
+
return read(this.okay);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export default <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => {
|
|
66
|
+
return new ReactivePromise(fn);
|
|
67
|
+
};
|
package/src/signal.ts
CHANGED
|
@@ -3,14 +3,14 @@ import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RE
|
|
|
3
3
|
import { Computed, Link, Signal, } from './types';
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
let
|
|
7
|
-
dirtyHeap: (Computed<unknown> | undefined)[] = new Array(2000),
|
|
6
|
+
let dirtyHeap: (Computed<unknown> | undefined)[] = new Array(2000),
|
|
8
7
|
maxDirty = 0,
|
|
9
8
|
markedHeap = false,
|
|
10
|
-
minDirty = 0
|
|
9
|
+
minDirty = 0,
|
|
10
|
+
observer: Computed<unknown> | null = null;
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
function cleanup(node: Computed<
|
|
13
|
+
function cleanup<T>(node: Computed<T>): void {
|
|
14
14
|
if (!node.cleanup) {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
@@ -27,7 +27,7 @@ function cleanup(node: Computed<unknown>): void {
|
|
|
27
27
|
node.cleanup = null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function deleteFromHeap(n: Computed<
|
|
30
|
+
function deleteFromHeap<T>(n: Computed<T>) {
|
|
31
31
|
let state = n.state;
|
|
32
32
|
|
|
33
33
|
if (!(state & STATE_IN_HEAP)) {
|
|
@@ -60,7 +60,7 @@ function deleteFromHeap(n: Computed<unknown>) {
|
|
|
60
60
|
n.prevHeap = n;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function insertIntoHeap(n: Computed<
|
|
63
|
+
function insertIntoHeap<T>(n: Computed<T>) {
|
|
64
64
|
let state = n.state;
|
|
65
65
|
|
|
66
66
|
if (state & STATE_IN_HEAP) {
|
|
@@ -94,7 +94,7 @@ function insertIntoHeap(n: Computed<unknown>) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L52
|
|
97
|
-
function link(dep: Signal<
|
|
97
|
+
function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
|
|
98
98
|
let prevDep = sub.depsTail;
|
|
99
99
|
|
|
100
100
|
if (prevDep !== null && prevDep.dep === dep) {
|
|
@@ -152,7 +152,7 @@ function markHeap() {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
function markNode(el: Computed<
|
|
155
|
+
function markNode<T>(el: Computed<T>, newState = STATE_DIRTY) {
|
|
156
156
|
let state = el.state;
|
|
157
157
|
|
|
158
158
|
if ((state & (STATE_CHECK | STATE_DIRTY)) >= newState) {
|
|
@@ -166,7 +166,7 @@ function markNode(el: Computed<unknown>, newState = STATE_DIRTY) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
function recompute(el: Computed<
|
|
169
|
+
function recompute<T>(el: Computed<T>, del: boolean) {
|
|
170
170
|
if (del) {
|
|
171
171
|
deleteFromHeap(el);
|
|
172
172
|
}
|
|
@@ -177,11 +177,11 @@ function recompute(el: Computed<unknown>, del: boolean) {
|
|
|
177
177
|
|
|
178
178
|
cleanup(el);
|
|
179
179
|
|
|
180
|
-
let
|
|
180
|
+
let o = observer,
|
|
181
181
|
ok = true,
|
|
182
182
|
value;
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
observer = el;
|
|
185
185
|
el.depsTail = null;
|
|
186
186
|
el.state = STATE_RECOMPUTING;
|
|
187
187
|
|
|
@@ -192,7 +192,7 @@ function recompute(el: Computed<unknown>, del: boolean) {
|
|
|
192
192
|
ok = false;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
observer = o;
|
|
196
196
|
el.state = STATE_NONE;
|
|
197
197
|
|
|
198
198
|
let depsTail = el.depsTail as Link | null,
|
|
@@ -213,7 +213,7 @@ function recompute(el: Computed<unknown>, del: boolean) {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
if (ok && value !== el.value) {
|
|
216
|
-
el.value = value;
|
|
216
|
+
el.value = value as T;
|
|
217
217
|
|
|
218
218
|
for (let s = el.subs; s !== null; s = s.nextSub) {
|
|
219
219
|
let o = s.sub,
|
|
@@ -256,7 +256,7 @@ function unlink(link: Link): Link | null {
|
|
|
256
256
|
return nextDep;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
function update(el: Computed<
|
|
259
|
+
function update<T>(el: Computed<T>): void {
|
|
260
260
|
if (el.state & STATE_CHECK) {
|
|
261
261
|
for (let d = el.deps; d; d = d.nextDep) {
|
|
262
262
|
let dep = d.dep;
|
|
@@ -297,17 +297,17 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
|
297
297
|
|
|
298
298
|
self.prevHeap = self;
|
|
299
299
|
|
|
300
|
-
if (
|
|
301
|
-
if (
|
|
302
|
-
self.height =
|
|
300
|
+
if (observer) {
|
|
301
|
+
if (observer.depsTail === null) {
|
|
302
|
+
self.height = observer.height;
|
|
303
303
|
recompute(self, false);
|
|
304
304
|
}
|
|
305
305
|
else {
|
|
306
|
-
self.height =
|
|
306
|
+
self.height = observer.height + 1;
|
|
307
307
|
insertIntoHeap(self);
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
link(self,
|
|
310
|
+
link(self, observer);
|
|
311
311
|
}
|
|
312
312
|
else {
|
|
313
313
|
recompute(self, false);
|
|
@@ -316,7 +316,7 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
|
|
|
316
316
|
return self;
|
|
317
317
|
};
|
|
318
318
|
|
|
319
|
-
const dispose = (el: Computed<
|
|
319
|
+
const dispose = <T>(el: Computed<T>) => {
|
|
320
320
|
deleteFromHeap(el);
|
|
321
321
|
|
|
322
322
|
let dep = el.deps;
|
|
@@ -343,11 +343,11 @@ const isSignal = (value: unknown): value is Signal<unknown> => {
|
|
|
343
343
|
};
|
|
344
344
|
|
|
345
345
|
const oncleanup = (fn: VoidFunction): typeof fn => {
|
|
346
|
-
if (!
|
|
346
|
+
if (!observer) {
|
|
347
347
|
return fn;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
let node =
|
|
350
|
+
let node = observer;
|
|
351
351
|
|
|
352
352
|
if (!node.cleanup) {
|
|
353
353
|
node.cleanup = fn;
|
|
@@ -363,14 +363,14 @@ const oncleanup = (fn: VoidFunction): typeof fn => {
|
|
|
363
363
|
};
|
|
364
364
|
|
|
365
365
|
const read = <T>(el: Signal<T> | Computed<T>): T => {
|
|
366
|
-
if (
|
|
367
|
-
link(el,
|
|
366
|
+
if (observer) {
|
|
367
|
+
link(el, observer);
|
|
368
368
|
|
|
369
369
|
if ('fn' in el) {
|
|
370
370
|
let height = el.height;
|
|
371
371
|
|
|
372
|
-
if (height >=
|
|
373
|
-
|
|
372
|
+
if (height >= observer.height) {
|
|
373
|
+
observer.height = height + 1;
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
if (
|
|
@@ -387,13 +387,13 @@ const read = <T>(el: Signal<T> | Computed<T>): T => {
|
|
|
387
387
|
};
|
|
388
388
|
|
|
389
389
|
const root = <T>(fn: () => T) => {
|
|
390
|
-
let
|
|
390
|
+
let o = observer;
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
observer = null;
|
|
393
393
|
|
|
394
394
|
let value = fn();
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
observer = o;
|
|
397
397
|
|
|
398
398
|
return value;
|
|
399
399
|
};
|
|
@@ -407,7 +407,7 @@ const signal = <T>(value: T): Signal<T> => {
|
|
|
407
407
|
};
|
|
408
408
|
};
|
|
409
409
|
|
|
410
|
-
signal.set = (el: Signal<
|
|
410
|
+
signal.set = <T>(el: Signal<T>, v: T) => {
|
|
411
411
|
if (el.value === v) {
|
|
412
412
|
return;
|
|
413
413
|
}
|