@bodil/signal 0.2.2 → 0.3.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/dist/index.d.ts +21 -14
- package/dist/index.js +76 -72
- package/dist/index.js.map +1 -1
- package/dist/signal.test.js +27 -17
- package/dist/signal.test.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +102 -92
- package/src/signal.test.ts +30 -26
package/dist/index.d.ts
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
import { Signal } from "signal-polyfill";
|
|
2
2
|
import { type Disposifiable } from "@bodil/disposable";
|
|
3
|
-
interface
|
|
3
|
+
interface ISignal<A> {
|
|
4
4
|
readonly value: A;
|
|
5
5
|
map<B>(fn: (value: A) => B): ComputedSignal<B>;
|
|
6
6
|
on(callback: (value: A) => void): Disposable;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
declare class StateSignal<A> extends Signal.State<A> implements ISignal<A> {
|
|
9
9
|
get value(): A;
|
|
10
10
|
set value(value: A);
|
|
11
11
|
update(fn: (value: A) => A): void;
|
|
12
12
|
readOnly(): ComputedSignal<A>;
|
|
13
13
|
map<B>(fn: (value: A) => B): ComputedSignal<B>;
|
|
14
14
|
on(callback: (value: A) => void): Disposable;
|
|
15
|
+
static is(v: unknown): v is StateSignal<unknown>;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
declare class ComputedSignal<A> extends Signal.Computed<A> implements ISignal<A> {
|
|
17
18
|
get value(): A;
|
|
18
19
|
map<B>(fn: (value: A) => B): ComputedSignal<B>;
|
|
19
20
|
on(callback: (value: A) => void): Disposable;
|
|
21
|
+
static is(v: unknown): v is ComputedSignal<unknown>;
|
|
20
22
|
}
|
|
21
|
-
type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
type SignalGlobal<A> = StateSignal<A> | ComputedSignal<A>;
|
|
24
|
+
declare const SignalGlobal: (<A>(value: A, options?: Signal.Options<A>) => SignalGlobal.State<A>) & {
|
|
25
|
+
is(v: unknown): v is SignalGlobal<unknown>;
|
|
26
|
+
computed<A>(fn: (this: ComputedSignal<A>) => A, options?: Signal.Options<A>): SignalGlobal.Computed<A>;
|
|
27
|
+
subscribe<A>(signal: ISignal<A>, callback: (value: A) => void): Disposable;
|
|
28
|
+
effect(fn: () => Disposifiable | void): Disposable;
|
|
29
|
+
asyncComputed<A>(fn: (abort: AbortSignal) => Promise<A>, options?: Signal.Options<A>): Promise<SignalGlobal.Computed<A>>;
|
|
30
|
+
State: typeof StateSignal;
|
|
31
|
+
Computed: typeof ComputedSignal;
|
|
32
|
+
subtle: typeof Signal.subtle;
|
|
33
|
+
};
|
|
34
|
+
declare namespace SignalGlobal {
|
|
35
|
+
type State<A> = StateSignal<A>;
|
|
36
|
+
type Computed<A> = ComputedSignal<A>;
|
|
37
|
+
}
|
|
38
|
+
export { SignalGlobal as Signal };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Signal } from "signal-polyfill";
|
|
2
2
|
import { toDisposable } from "@bodil/disposable";
|
|
3
3
|
import { Err, Ok, Async } from "@bodil/core";
|
|
4
|
-
|
|
4
|
+
class StateSignal extends Signal.State {
|
|
5
5
|
get value() {
|
|
6
6
|
return this.get();
|
|
7
7
|
}
|
|
@@ -12,41 +12,31 @@ export class StateSignal extends Signal.State {
|
|
|
12
12
|
this.set(Signal.subtle.untrack(() => fn(this.get())));
|
|
13
13
|
}
|
|
14
14
|
readOnly() {
|
|
15
|
-
return computed(() => this.get());
|
|
15
|
+
return SignalGlobal.computed(() => this.get());
|
|
16
16
|
}
|
|
17
17
|
map(fn) {
|
|
18
|
-
return computed(() => fn(this.get()));
|
|
18
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
19
19
|
}
|
|
20
20
|
on(callback) {
|
|
21
|
-
return subscribe(this, callback);
|
|
21
|
+
return SignalGlobal.subscribe(this, callback);
|
|
22
|
+
}
|
|
23
|
+
static is(v) {
|
|
24
|
+
return v instanceof StateSignal;
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
|
-
|
|
27
|
+
class ComputedSignal extends Signal.Computed {
|
|
25
28
|
get value() {
|
|
26
29
|
return this.get();
|
|
27
30
|
}
|
|
28
31
|
map(fn) {
|
|
29
|
-
return computed(() => fn(this.get()));
|
|
32
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
30
33
|
}
|
|
31
34
|
on(callback) {
|
|
32
|
-
return subscribe(this, callback);
|
|
35
|
+
return SignalGlobal.subscribe(this, callback);
|
|
36
|
+
}
|
|
37
|
+
static is(v) {
|
|
38
|
+
return v instanceof ComputedSignal;
|
|
33
39
|
}
|
|
34
|
-
}
|
|
35
|
-
export const subtle = Signal.subtle;
|
|
36
|
-
export function isStateSignal(v) {
|
|
37
|
-
return v instanceof StateSignal;
|
|
38
|
-
}
|
|
39
|
-
export function isComputedSignal(v) {
|
|
40
|
-
return v instanceof ComputedSignal;
|
|
41
|
-
}
|
|
42
|
-
export function isSignal(v) {
|
|
43
|
-
return isStateSignal(v) || isComputedSignal(v);
|
|
44
|
-
}
|
|
45
|
-
export function signal(value, options) {
|
|
46
|
-
return new StateSignal(value, options);
|
|
47
|
-
}
|
|
48
|
-
export function computed(fn, options) {
|
|
49
|
-
return new ComputedSignal(fn, options);
|
|
50
40
|
}
|
|
51
41
|
let effectNeedsEnqueue = true;
|
|
52
42
|
const effectWatcher = new Signal.subtle.Watcher(() => {
|
|
@@ -62,55 +52,69 @@ function effectProcess() {
|
|
|
62
52
|
}
|
|
63
53
|
effectWatcher.watch();
|
|
64
54
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
export function subscribe(signal, callback) {
|
|
84
|
-
return effect(() => callback(signal.value));
|
|
85
|
-
}
|
|
86
|
-
export function asyncComputed(fn, options) {
|
|
87
|
-
const result = Promise.withResolvers();
|
|
88
|
-
const stream = computed(() => Async.abortable(fn));
|
|
89
|
-
const sig = signal(Err(new Error()));
|
|
90
|
-
let job = undefined;
|
|
91
|
-
let resolved = false;
|
|
92
|
-
const resolve = () => {
|
|
93
|
-
if (!resolved) {
|
|
94
|
-
resolved = true;
|
|
95
|
-
result.resolve(computed(() => sig.get().unwrapExact(), options));
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
effect(() => {
|
|
99
|
-
if (job !== undefined) {
|
|
100
|
-
job.abort();
|
|
101
|
-
}
|
|
102
|
-
job = stream.get();
|
|
103
|
-
job.result.then((next) => {
|
|
104
|
-
sig.set(Ok(next));
|
|
105
|
-
resolve();
|
|
106
|
-
}, (error) => {
|
|
107
|
-
if (job?.signal.aborted === true) {
|
|
108
|
-
return;
|
|
55
|
+
const SignalGlobal = Object.assign(function (value, options) {
|
|
56
|
+
return new StateSignal(value, options);
|
|
57
|
+
}, {
|
|
58
|
+
is(v) {
|
|
59
|
+
return StateSignal.is(v) || ComputedSignal.is(v);
|
|
60
|
+
},
|
|
61
|
+
computed(fn, options) {
|
|
62
|
+
return new ComputedSignal(fn, options);
|
|
63
|
+
},
|
|
64
|
+
subscribe(signal, callback) {
|
|
65
|
+
return SignalGlobal.effect(() => callback(signal.value));
|
|
66
|
+
},
|
|
67
|
+
effect(fn) {
|
|
68
|
+
let cleanup;
|
|
69
|
+
const computed = new ComputedSignal(() => {
|
|
70
|
+
if (cleanup !== undefined) {
|
|
71
|
+
cleanup[Symbol.dispose]();
|
|
109
72
|
}
|
|
110
|
-
|
|
111
|
-
|
|
73
|
+
const result = fn();
|
|
74
|
+
cleanup = result !== undefined ? toDisposable(result) : undefined;
|
|
112
75
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
76
|
+
effectWatcher.watch(computed);
|
|
77
|
+
computed.get();
|
|
78
|
+
return toDisposable(() => {
|
|
79
|
+
effectWatcher.unwatch(computed);
|
|
80
|
+
if (cleanup !== undefined) {
|
|
81
|
+
cleanup[Symbol.dispose]();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
asyncComputed(fn, options) {
|
|
86
|
+
const result = Promise.withResolvers();
|
|
87
|
+
const stream = SignalGlobal.computed(() => Async.abortable(fn));
|
|
88
|
+
const sig = SignalGlobal(Err(new Error()));
|
|
89
|
+
let job = undefined;
|
|
90
|
+
let resolved = false;
|
|
91
|
+
const resolve = () => {
|
|
92
|
+
if (!resolved) {
|
|
93
|
+
resolved = true;
|
|
94
|
+
result.resolve(SignalGlobal.computed(() => sig.get().unwrapExact(), options));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
SignalGlobal.effect(() => {
|
|
98
|
+
if (job !== undefined) {
|
|
99
|
+
job.abort();
|
|
100
|
+
}
|
|
101
|
+
job = stream.get();
|
|
102
|
+
job.result.then((next) => {
|
|
103
|
+
sig.set(Ok(next));
|
|
104
|
+
resolve();
|
|
105
|
+
}, (error) => {
|
|
106
|
+
if (job?.signal.aborted === true) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
sig.set(Err(error));
|
|
110
|
+
resolve();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
return result.promise;
|
|
114
|
+
},
|
|
115
|
+
State: StateSignal,
|
|
116
|
+
Computed: ComputedSignal,
|
|
117
|
+
subtle: Signal.subtle,
|
|
118
|
+
});
|
|
119
|
+
export { SignalGlobal as Signal };
|
|
116
120
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAsB,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,KAAK,EAAE,MAAM,aAAa,CAAC;AAQ1D,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/signal.test.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { sleep } from "@bodil/core/async";
|
|
2
2
|
import { expect, test } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { Signal } from ".";
|
|
4
4
|
test("Signal", async () => {
|
|
5
|
-
const sig =
|
|
5
|
+
const sig = Signal(0);
|
|
6
6
|
const result = [];
|
|
7
|
-
effect(() => void result.push(sig.value));
|
|
7
|
+
Signal.effect(() => void result.push(sig.value));
|
|
8
8
|
expect(result).toEqual([0]);
|
|
9
9
|
sig.value = 1;
|
|
10
10
|
sig.value = 2;
|
|
@@ -24,8 +24,8 @@ test("Signal", async () => {
|
|
|
24
24
|
});
|
|
25
25
|
test("signal equality", async () => {
|
|
26
26
|
const result = [];
|
|
27
|
-
const sig =
|
|
28
|
-
effect(() => void result.push(sig.value));
|
|
27
|
+
const sig = Signal(0);
|
|
28
|
+
Signal.effect(() => void result.push(sig.value));
|
|
29
29
|
expect(result).toEqual([0]);
|
|
30
30
|
sig.value = 1;
|
|
31
31
|
await sleep(1);
|
|
@@ -50,11 +50,11 @@ test("signal equality", async () => {
|
|
|
50
50
|
expect(result).toEqual([0, 1, 2, 3, 2]);
|
|
51
51
|
});
|
|
52
52
|
test("asyncComputed", async () => {
|
|
53
|
-
const s =
|
|
54
|
-
const c = await asyncComputed(() => Promise.resolve(s.value + 1));
|
|
53
|
+
const s = Signal(1);
|
|
54
|
+
const c = await Signal.asyncComputed(() => Promise.resolve(s.value + 1));
|
|
55
55
|
expect(c.value).toEqual(2);
|
|
56
56
|
try {
|
|
57
|
-
await asyncComputed(() => {
|
|
57
|
+
await Signal.asyncComputed(() => {
|
|
58
58
|
throw new Error("welp!");
|
|
59
59
|
});
|
|
60
60
|
throw new Error("computed error failed to throw");
|
|
@@ -75,14 +75,24 @@ test("asyncComputed", async () => {
|
|
|
75
75
|
expect(c.value).toEqual(4);
|
|
76
76
|
});
|
|
77
77
|
test("isSignal", () => {
|
|
78
|
-
const s1 =
|
|
79
|
-
expect(
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
82
|
-
const s2 = computed(() => 2);
|
|
83
|
-
expect(
|
|
84
|
-
expect(
|
|
85
|
-
expect(
|
|
86
|
-
expect(
|
|
78
|
+
const s1 = Signal(1);
|
|
79
|
+
expect(Signal.is(s1)).toBeTruthy();
|
|
80
|
+
expect(Signal.State.is(s1)).toBeTruthy();
|
|
81
|
+
expect(Signal.Computed.is(s1)).toBeFalsy();
|
|
82
|
+
const s2 = Signal.computed(() => 2);
|
|
83
|
+
expect(Signal.is(s2)).toBeTruthy();
|
|
84
|
+
expect(Signal.State.is(s2)).toBeFalsy();
|
|
85
|
+
expect(Signal.Computed.is(s2)).toBeTruthy();
|
|
86
|
+
expect(Signal.is("wibble")).toBeFalsy();
|
|
87
|
+
});
|
|
88
|
+
test("Signal types", () => {
|
|
89
|
+
const s1 = Signal(1);
|
|
90
|
+
const s2 = Signal.computed(() => s1.get());
|
|
91
|
+
function foo(_signal) {
|
|
92
|
+
// ensure Signal<A> is an accessible type
|
|
93
|
+
// and that it accepts both concrete signal types.
|
|
94
|
+
}
|
|
95
|
+
foo(s1);
|
|
96
|
+
foo(s2);
|
|
87
97
|
});
|
|
88
98
|
//# sourceMappingURL=signal.test.js.map
|
package/dist/signal.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.test.js","sourceRoot":"","sources":["../src/signal.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"signal.test.js","sourceRoot":"","sources":["../src/signal.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bodil/signal",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Signal implementation built on the TC39 Signals Proposal",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/bodil/signal.git"
|
package/src/index.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { Signal } from "signal-polyfill";
|
|
|
2
2
|
import { toDisposable, type Disposifiable } from "@bodil/disposable";
|
|
3
3
|
import { Err, Ok, type Result, Async } from "@bodil/core";
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface ISignal<A> {
|
|
6
6
|
readonly value: A;
|
|
7
7
|
map<B>(fn: (value: A) => B): ComputedSignal<B>;
|
|
8
8
|
on(callback: (value: A) => void): Disposable;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
class StateSignal<A> extends Signal.State<A> implements ISignal<A> {
|
|
12
12
|
get value(): A {
|
|
13
13
|
return this.get();
|
|
14
14
|
}
|
|
@@ -22,59 +22,38 @@ export class StateSignal<A> extends Signal.State<A> implements ReadableSignal<A>
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
readOnly(): ComputedSignal<A> {
|
|
25
|
-
return computed(() => this.get());
|
|
25
|
+
return SignalGlobal.computed(() => this.get());
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
map<B>(fn: (value: A) => B): ComputedSignal<B> {
|
|
29
|
-
return computed(() => fn(this.get()));
|
|
29
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
on(callback: (value: A) => void): Disposable {
|
|
33
|
-
return subscribe(this, callback);
|
|
33
|
+
return SignalGlobal.subscribe(this, callback);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static is(v: unknown): v is StateSignal<unknown> {
|
|
37
|
+
return v instanceof StateSignal;
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
class ComputedSignal<A> extends Signal.Computed<A> implements ISignal<A> {
|
|
38
42
|
get value(): A {
|
|
39
43
|
return this.get();
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
map<B>(fn: (value: A) => B): ComputedSignal<B> {
|
|
43
|
-
return computed(() => fn(this.get()));
|
|
47
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
on(callback: (value: A) => void): Disposable {
|
|
47
|
-
return subscribe(this, callback);
|
|
51
|
+
return SignalGlobal.subscribe(this, callback);
|
|
48
52
|
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
type AnySignal<A> = StateSignal<A> | ComputedSignal<A>;
|
|
52
|
-
|
|
53
|
-
export type { AnySignal as Signal };
|
|
54
|
-
|
|
55
|
-
export const subtle = Signal.subtle;
|
|
56
|
-
|
|
57
|
-
export function isStateSignal(v: unknown): v is StateSignal<unknown> {
|
|
58
|
-
return v instanceof StateSignal;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function isComputedSignal(v: unknown): v is ComputedSignal<unknown> {
|
|
62
|
-
return v instanceof ComputedSignal;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function isSignal(v: unknown): v is AnySignal<unknown> {
|
|
66
|
-
return isStateSignal(v) || isComputedSignal(v);
|
|
67
|
-
}
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function computed<A>(
|
|
74
|
-
fn: (this: ComputedSignal<A>) => A,
|
|
75
|
-
options?: Signal.Options<A>
|
|
76
|
-
): ComputedSignal<A> {
|
|
77
|
-
return new ComputedSignal(fn, options);
|
|
54
|
+
static is(v: unknown): v is ComputedSignal<unknown> {
|
|
55
|
+
return v instanceof ComputedSignal;
|
|
56
|
+
}
|
|
78
57
|
}
|
|
79
58
|
|
|
80
59
|
let effectNeedsEnqueue = true;
|
|
@@ -93,62 +72,93 @@ function effectProcess(): void {
|
|
|
93
72
|
effectWatcher.watch();
|
|
94
73
|
}
|
|
95
74
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
75
|
+
type SignalGlobal<A> = StateSignal<A> | ComputedSignal<A>;
|
|
76
|
+
|
|
77
|
+
const SignalGlobal = Object.assign(
|
|
78
|
+
function <A>(value: A, options?: Signal.Options<A>): SignalGlobal.State<A> {
|
|
79
|
+
return new StateSignal(value, options);
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
is(v: unknown): v is SignalGlobal<unknown> {
|
|
83
|
+
return StateSignal.is(v) || ComputedSignal.is(v);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
computed<A>(
|
|
87
|
+
fn: (this: ComputedSignal<A>) => A,
|
|
88
|
+
options?: Signal.Options<A>
|
|
89
|
+
): SignalGlobal.Computed<A> {
|
|
90
|
+
return new ComputedSignal(fn, options);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
subscribe<A>(signal: ISignal<A>, callback: (value: A) => void): Disposable {
|
|
94
|
+
return SignalGlobal.effect(() => callback(signal.value));
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
effect(fn: () => Disposifiable | void): Disposable {
|
|
98
|
+
let cleanup: Disposable | undefined;
|
|
99
|
+
const computed = new ComputedSignal(() => {
|
|
100
|
+
if (cleanup !== undefined) {
|
|
101
|
+
cleanup[Symbol.dispose]();
|
|
102
|
+
}
|
|
103
|
+
const result = fn();
|
|
104
|
+
cleanup = result !== undefined ? toDisposable(result) : undefined;
|
|
105
|
+
});
|
|
106
|
+
effectWatcher.watch(computed);
|
|
107
|
+
computed.get();
|
|
108
|
+
return toDisposable(() => {
|
|
109
|
+
effectWatcher.unwatch(computed);
|
|
110
|
+
if (cleanup !== undefined) {
|
|
111
|
+
cleanup[Symbol.dispose]();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
asyncComputed<A>(
|
|
117
|
+
fn: (abort: AbortSignal) => Promise<A>,
|
|
118
|
+
options?: Signal.Options<A>
|
|
119
|
+
): Promise<SignalGlobal.Computed<A>> {
|
|
120
|
+
const result = Promise.withResolvers<ComputedSignal<A>>();
|
|
121
|
+
const stream = SignalGlobal.computed(() => Async.abortable(fn));
|
|
122
|
+
const sig: StateSignal<Result<A, Error>> = SignalGlobal(Err(new Error()));
|
|
123
|
+
let job: Async.AbortableJob<A> | undefined = undefined;
|
|
124
|
+
let resolved = false;
|
|
125
|
+
const resolve = () => {
|
|
126
|
+
if (!resolved) {
|
|
127
|
+
resolved = true;
|
|
128
|
+
result.resolve(SignalGlobal.computed(() => sig.get().unwrapExact(), options));
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
SignalGlobal.effect(() => {
|
|
132
|
+
if (job !== undefined) {
|
|
133
|
+
job.abort();
|
|
134
|
+
}
|
|
135
|
+
job = stream.get();
|
|
136
|
+
job.result.then(
|
|
137
|
+
(next) => {
|
|
138
|
+
sig.set(Ok(next));
|
|
139
|
+
resolve();
|
|
140
|
+
},
|
|
141
|
+
(error) => {
|
|
142
|
+
if (job?.signal.aborted === true) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
sig.set(Err(error));
|
|
146
|
+
resolve();
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
return result.promise;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
State: StateSignal,
|
|
154
|
+
Computed: ComputedSignal,
|
|
155
|
+
subtle: Signal.subtle,
|
|
156
|
+
}
|
|
157
|
+
);
|
|
114
158
|
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
declare namespace SignalGlobal {
|
|
160
|
+
export type State<A> = StateSignal<A>;
|
|
161
|
+
export type Computed<A> = ComputedSignal<A>;
|
|
117
162
|
}
|
|
118
163
|
|
|
119
|
-
export
|
|
120
|
-
fn: (abort: AbortSignal) => Promise<A>,
|
|
121
|
-
options?: Signal.Options<A>
|
|
122
|
-
): Promise<ComputedSignal<A>> {
|
|
123
|
-
const result = Promise.withResolvers<ComputedSignal<A>>();
|
|
124
|
-
const stream = computed(() => Async.abortable(fn));
|
|
125
|
-
const sig: StateSignal<Result<A, Error>> = signal(Err(new Error()));
|
|
126
|
-
let job: Async.AbortableJob<A> | undefined = undefined;
|
|
127
|
-
let resolved = false;
|
|
128
|
-
const resolve = () => {
|
|
129
|
-
if (!resolved) {
|
|
130
|
-
resolved = true;
|
|
131
|
-
result.resolve(computed(() => sig.get().unwrapExact(), options));
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
effect(() => {
|
|
135
|
-
if (job !== undefined) {
|
|
136
|
-
job.abort();
|
|
137
|
-
}
|
|
138
|
-
job = stream.get();
|
|
139
|
-
job.result.then(
|
|
140
|
-
(next) => {
|
|
141
|
-
sig.set(Ok(next));
|
|
142
|
-
resolve();
|
|
143
|
-
},
|
|
144
|
-
(error) => {
|
|
145
|
-
if (job?.signal.aborted === true) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
sig.set(Err(error));
|
|
149
|
-
resolve();
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
});
|
|
153
|
-
return result.promise;
|
|
154
|
-
}
|
|
164
|
+
export { SignalGlobal as Signal };
|
package/src/signal.test.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { sleep } from "@bodil/core/async";
|
|
2
|
-
import { expect, test } from "vitest";
|
|
3
|
-
import {
|
|
4
|
-
asyncComputed,
|
|
5
|
-
computed,
|
|
6
|
-
effect,
|
|
7
|
-
isComputedSignal,
|
|
8
|
-
isSignal,
|
|
9
|
-
isStateSignal,
|
|
10
|
-
signal,
|
|
11
|
-
} from ".";
|
|
2
|
+
import { expect, expectTypeOf, test } from "vitest";
|
|
3
|
+
import { Signal } from ".";
|
|
12
4
|
|
|
13
5
|
test("Signal", async () => {
|
|
14
|
-
const sig =
|
|
6
|
+
const sig = Signal(0);
|
|
15
7
|
const result: Array<number> = [];
|
|
16
|
-
effect(() => void result.push(sig.value));
|
|
8
|
+
Signal.effect(() => void result.push(sig.value));
|
|
17
9
|
expect(result).toEqual([0]);
|
|
18
10
|
sig.value = 1;
|
|
19
11
|
sig.value = 2;
|
|
@@ -33,8 +25,8 @@ test("Signal", async () => {
|
|
|
33
25
|
|
|
34
26
|
test("signal equality", async () => {
|
|
35
27
|
const result: Array<number> = [];
|
|
36
|
-
const sig =
|
|
37
|
-
effect(() => void result.push(sig.value));
|
|
28
|
+
const sig = Signal(0);
|
|
29
|
+
Signal.effect(() => void result.push(sig.value));
|
|
38
30
|
expect(result).toEqual([0]);
|
|
39
31
|
sig.value = 1;
|
|
40
32
|
await sleep(1);
|
|
@@ -60,11 +52,11 @@ test("signal equality", async () => {
|
|
|
60
52
|
});
|
|
61
53
|
|
|
62
54
|
test("asyncComputed", async () => {
|
|
63
|
-
const s =
|
|
64
|
-
const c = await asyncComputed(() => Promise.resolve(s.value + 1));
|
|
55
|
+
const s = Signal(1);
|
|
56
|
+
const c = await Signal.asyncComputed(() => Promise.resolve(s.value + 1));
|
|
65
57
|
expect(c.value).toEqual(2);
|
|
66
58
|
try {
|
|
67
|
-
await asyncComputed(() => {
|
|
59
|
+
await Signal.asyncComputed(() => {
|
|
68
60
|
throw new Error("welp!");
|
|
69
61
|
});
|
|
70
62
|
throw new Error("computed error failed to throw");
|
|
@@ -85,15 +77,27 @@ test("asyncComputed", async () => {
|
|
|
85
77
|
});
|
|
86
78
|
|
|
87
79
|
test("isSignal", () => {
|
|
88
|
-
const s1 =
|
|
89
|
-
expect(
|
|
90
|
-
expect(
|
|
91
|
-
expect(
|
|
80
|
+
const s1: Signal.State<number> = Signal(1);
|
|
81
|
+
expect(Signal.is(s1)).toBeTruthy();
|
|
82
|
+
expect(Signal.State.is(s1)).toBeTruthy();
|
|
83
|
+
expect(Signal.Computed.is(s1)).toBeFalsy();
|
|
92
84
|
|
|
93
|
-
const s2 = computed(() => 2);
|
|
94
|
-
expect(
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
85
|
+
const s2 = Signal.computed(() => 2);
|
|
86
|
+
expect(Signal.is(s2)).toBeTruthy();
|
|
87
|
+
expect(Signal.State.is(s2)).toBeFalsy();
|
|
88
|
+
expect(Signal.Computed.is(s2)).toBeTruthy();
|
|
97
89
|
|
|
98
|
-
expect(
|
|
90
|
+
expect(Signal.is("wibble")).toBeFalsy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("Signal types", () => {
|
|
94
|
+
const s1: Signal.State<number> = Signal(1);
|
|
95
|
+
const s2: Signal.Computed<number> = Signal.computed(() => s1.get());
|
|
96
|
+
|
|
97
|
+
function foo(_signal: Signal<unknown>) {
|
|
98
|
+
// ensure Signal<A> is an accessible type
|
|
99
|
+
// and that it accepts both concrete signal types.
|
|
100
|
+
}
|
|
101
|
+
foo(s1);
|
|
102
|
+
foo(s2);
|
|
99
103
|
});
|