@bodil/signal 0.3.0 → 0.3.2
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 +100 -17
- package/dist/index.js +94 -23
- package/dist/index.js.map +1 -1
- package/dist/signal.test.js +10 -0
- package/dist/signal.test.js.map +1 -1
- package/package.json +22 -15
- package/src/index.ts +132 -37
- package/src/signal.test.ts +13 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,34 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A signal library built on the [TC39 Signals
|
|
3
|
+
* Proposal](https://github.com/tc39/proposal-signals).
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
1
6
|
import { Signal } from "signal-polyfill";
|
|
2
7
|
import { type Disposifiable } from "@bodil/disposable";
|
|
3
8
|
interface ISignal<A> {
|
|
9
|
+
/**
|
|
10
|
+
* The current value of the signal.
|
|
11
|
+
*
|
|
12
|
+
* When this is read inside a computation function or an effect function,
|
|
13
|
+
* this signal is automatically added to the effect or computed signal as a
|
|
14
|
+
* dependency.
|
|
15
|
+
*/
|
|
4
16
|
readonly value: A;
|
|
5
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Construct a {@link Signal.Computed} signal using a mapping function over
|
|
19
|
+
* the current value of this signal.
|
|
20
|
+
*/
|
|
21
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B>;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to changes to the value of this signal.
|
|
24
|
+
*/
|
|
6
25
|
on(callback: (value: A) => void): Disposable;
|
|
7
26
|
}
|
|
8
|
-
|
|
27
|
+
/**
|
|
28
|
+
* A writable state signal.
|
|
29
|
+
*/
|
|
30
|
+
declare class StateSignal<A> extends Signal.State<A> implements ISignal<A> {
|
|
9
31
|
get value(): A;
|
|
10
32
|
set value(value: A);
|
|
33
|
+
/**
|
|
34
|
+
* Update the current value of this signal using a function.
|
|
35
|
+
*/
|
|
11
36
|
update(fn: (value: A) => A): void;
|
|
12
|
-
|
|
13
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Get a read only version of this signal.
|
|
39
|
+
*/
|
|
40
|
+
readOnly(): SignalGlobal.Computed<A>;
|
|
41
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B>;
|
|
14
42
|
on(callback: (value: A) => void): Disposable;
|
|
15
|
-
static is(v: unknown): v is State<unknown>;
|
|
43
|
+
static is(v: unknown): v is SignalGlobal.State<unknown>;
|
|
16
44
|
}
|
|
17
|
-
|
|
45
|
+
/**
|
|
46
|
+
* A read only signal computed from the values of other signals.
|
|
47
|
+
*/
|
|
48
|
+
declare class ComputedSignal<A> extends Signal.Computed<A> implements ISignal<A> {
|
|
18
49
|
get value(): A;
|
|
19
|
-
map<B>(fn: (value: A) => B): Computed<B>;
|
|
50
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B>;
|
|
20
51
|
on(callback: (value: A) => void): Disposable;
|
|
21
|
-
static is(v: unknown): v is Computed<unknown>;
|
|
52
|
+
static is(v: unknown): v is SignalGlobal.Computed<unknown>;
|
|
22
53
|
}
|
|
23
|
-
type
|
|
24
|
-
declare const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
54
|
+
type SignalGlobal<A> = SignalGlobal.State<A> | SignalGlobal.Computed<A>;
|
|
55
|
+
declare const SignalGlobal: (<A>(value: A, options?: Signal.Options<A>) => SignalGlobal.State<A>) & {
|
|
56
|
+
/**
|
|
57
|
+
* Test whether the given value is a signal.
|
|
58
|
+
*/
|
|
59
|
+
is(v: unknown): v is SignalGlobal<unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Construct a new {@link Signal.Computed} signal using the provided
|
|
62
|
+
* computation function.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const sig1 = Signal(2);
|
|
66
|
+
* const sig2 = Signal(3);
|
|
67
|
+
* const sum = Signal.computed(() => sig1.get() + sig2.get());
|
|
68
|
+
* assert(sum.get() === 5);
|
|
69
|
+
*/
|
|
70
|
+
computed<A>(fn: (this: SignalGlobal.Computed<A>) => A, options?: Signal.Options<A>): SignalGlobal.Computed<A>;
|
|
71
|
+
/**
|
|
72
|
+
* Suscribe to a signal.
|
|
73
|
+
*
|
|
74
|
+
* The provided callback will be called every time the value of the
|
|
75
|
+
* signal changes.
|
|
76
|
+
*/
|
|
77
|
+
subscribe<A>(signal: SignalGlobal<A>, callback: (value: A) => void): Disposable;
|
|
78
|
+
/**
|
|
79
|
+
* Create an effect responding to signal changes.
|
|
80
|
+
*
|
|
81
|
+
* The provided function will be called immediately, and again every
|
|
82
|
+
* time a signal that was read by the function changes.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const sig = Signal("Hello Joe!");
|
|
86
|
+
* effect(() => console.log("Signal value is:", sig.get()));
|
|
87
|
+
* // prints "Signal value is: Hello Joe!"
|
|
88
|
+
* sig.set("Hello Mike!");
|
|
89
|
+
* // prints "Signal value is: Hello Mike!"
|
|
90
|
+
*/
|
|
28
91
|
effect(fn: () => Disposifiable | void): Disposable;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Construct a new {@link Signal.Computed} signal using an async
|
|
94
|
+
* computation function.
|
|
95
|
+
*
|
|
96
|
+
* This returns a promise which will resolve to a
|
|
97
|
+
* {@link Signal.Computed} signal once the promise returned by the
|
|
98
|
+
* computation function resolves, and will update itself whenever
|
|
99
|
+
* subsequent calls to the computation function resolve.
|
|
100
|
+
*
|
|
101
|
+
* The function is provided with an {@link AbortSignal} which any async
|
|
102
|
+
* jobs started from it should abide by. If a signal dependency changes
|
|
103
|
+
* while the job is running, the {@link AbortSignal} will be triggered
|
|
104
|
+
* and the job restarted.
|
|
105
|
+
*/
|
|
106
|
+
asyncComputed<A>(fn: (abort: AbortSignal) => Promise<A>, options?: Signal.Options<A>): Promise<SignalGlobal.Computed<A>>;
|
|
107
|
+
State: typeof StateSignal;
|
|
108
|
+
Computed: typeof ComputedSignal;
|
|
32
109
|
subtle: typeof Signal.subtle;
|
|
33
110
|
};
|
|
34
|
-
|
|
111
|
+
declare namespace SignalGlobal {
|
|
112
|
+
/** @interface */
|
|
113
|
+
type State<A> = StateSignal<A>;
|
|
114
|
+
/** @interface */
|
|
115
|
+
type Computed<A> = ComputedSignal<A>;
|
|
116
|
+
}
|
|
117
|
+
export { SignalGlobal as Signal };
|
package/dist/index.js
CHANGED
|
@@ -1,41 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A signal library built on the [TC39 Signals
|
|
3
|
+
* Proposal](https://github.com/tc39/proposal-signals).
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
1
6
|
import { Signal } from "signal-polyfill";
|
|
2
7
|
import { toDisposable } from "@bodil/disposable";
|
|
3
|
-
import
|
|
4
|
-
|
|
8
|
+
import * as Async from "@bodil/core/async";
|
|
9
|
+
import { Err, Ok } from "@bodil/opt";
|
|
10
|
+
/**
|
|
11
|
+
* A writable state signal.
|
|
12
|
+
*/
|
|
13
|
+
class StateSignal extends Signal.State {
|
|
5
14
|
get value() {
|
|
6
15
|
return this.get();
|
|
7
16
|
}
|
|
8
17
|
set value(value) {
|
|
9
18
|
this.set(value);
|
|
10
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Update the current value of this signal using a function.
|
|
22
|
+
*/
|
|
11
23
|
update(fn) {
|
|
12
24
|
this.set(Signal.subtle.untrack(() => fn(this.get())));
|
|
13
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Get a read only version of this signal.
|
|
28
|
+
*/
|
|
14
29
|
readOnly() {
|
|
15
|
-
return
|
|
30
|
+
return SignalGlobal.computed(() => this.get());
|
|
16
31
|
}
|
|
17
32
|
map(fn) {
|
|
18
|
-
return
|
|
33
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
19
34
|
}
|
|
20
35
|
on(callback) {
|
|
21
|
-
return
|
|
36
|
+
return SignalGlobal.subscribe(this, callback);
|
|
22
37
|
}
|
|
23
38
|
static is(v) {
|
|
24
|
-
return v instanceof
|
|
39
|
+
return v instanceof StateSignal;
|
|
25
40
|
}
|
|
26
41
|
}
|
|
27
|
-
|
|
42
|
+
/**
|
|
43
|
+
* A read only signal computed from the values of other signals.
|
|
44
|
+
*/
|
|
45
|
+
class ComputedSignal extends Signal.Computed {
|
|
28
46
|
get value() {
|
|
29
47
|
return this.get();
|
|
30
48
|
}
|
|
31
49
|
map(fn) {
|
|
32
|
-
return
|
|
50
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
33
51
|
}
|
|
34
52
|
on(callback) {
|
|
35
|
-
return
|
|
53
|
+
return SignalGlobal.subscribe(this, callback);
|
|
36
54
|
}
|
|
37
55
|
static is(v) {
|
|
38
|
-
return v instanceof
|
|
56
|
+
return v instanceof ComputedSignal;
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
let effectNeedsEnqueue = true;
|
|
@@ -52,21 +70,60 @@ function effectProcess() {
|
|
|
52
70
|
}
|
|
53
71
|
effectWatcher.watch();
|
|
54
72
|
}
|
|
55
|
-
const
|
|
56
|
-
|
|
73
|
+
const SignalGlobal = Object.assign(
|
|
74
|
+
/**
|
|
75
|
+
* Construct a new {@link Signal.State} signal containing the provided value.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* const sig = Signal("Hello Joe!");
|
|
79
|
+
*/
|
|
80
|
+
function (value, options) {
|
|
81
|
+
return new SignalGlobal.State(value, options);
|
|
57
82
|
}, {
|
|
83
|
+
/**
|
|
84
|
+
* Test whether the given value is a signal.
|
|
85
|
+
*/
|
|
58
86
|
is(v) {
|
|
59
|
-
return State.is(v) || Computed.is(v);
|
|
87
|
+
return SignalGlobal.State.is(v) || SignalGlobal.Computed.is(v);
|
|
60
88
|
},
|
|
89
|
+
/**
|
|
90
|
+
* Construct a new {@link Signal.Computed} signal using the provided
|
|
91
|
+
* computation function.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const sig1 = Signal(2);
|
|
95
|
+
* const sig2 = Signal(3);
|
|
96
|
+
* const sum = Signal.computed(() => sig1.get() + sig2.get());
|
|
97
|
+
* assert(sum.get() === 5);
|
|
98
|
+
*/
|
|
61
99
|
computed(fn, options) {
|
|
62
|
-
return new Computed(fn, options);
|
|
100
|
+
return new SignalGlobal.Computed(fn, options);
|
|
63
101
|
},
|
|
102
|
+
/**
|
|
103
|
+
* Suscribe to a signal.
|
|
104
|
+
*
|
|
105
|
+
* The provided callback will be called every time the value of the
|
|
106
|
+
* signal changes.
|
|
107
|
+
*/
|
|
64
108
|
subscribe(signal, callback) {
|
|
65
|
-
return
|
|
109
|
+
return SignalGlobal.effect(() => callback(signal.value));
|
|
66
110
|
},
|
|
111
|
+
/**
|
|
112
|
+
* Create an effect responding to signal changes.
|
|
113
|
+
*
|
|
114
|
+
* The provided function will be called immediately, and again every
|
|
115
|
+
* time a signal that was read by the function changes.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* const sig = Signal("Hello Joe!");
|
|
119
|
+
* effect(() => console.log("Signal value is:", sig.get()));
|
|
120
|
+
* // prints "Signal value is: Hello Joe!"
|
|
121
|
+
* sig.set("Hello Mike!");
|
|
122
|
+
* // prints "Signal value is: Hello Mike!"
|
|
123
|
+
*/
|
|
67
124
|
effect(fn) {
|
|
68
125
|
let cleanup;
|
|
69
|
-
const computed = new Computed(() => {
|
|
126
|
+
const computed = new SignalGlobal.Computed(() => {
|
|
70
127
|
if (cleanup !== undefined) {
|
|
71
128
|
cleanup[Symbol.dispose]();
|
|
72
129
|
}
|
|
@@ -82,19 +139,33 @@ const AnySignal = Object.assign(function (value, options) {
|
|
|
82
139
|
}
|
|
83
140
|
});
|
|
84
141
|
},
|
|
142
|
+
/**
|
|
143
|
+
* Construct a new {@link Signal.Computed} signal using an async
|
|
144
|
+
* computation function.
|
|
145
|
+
*
|
|
146
|
+
* This returns a promise which will resolve to a
|
|
147
|
+
* {@link Signal.Computed} signal once the promise returned by the
|
|
148
|
+
* computation function resolves, and will update itself whenever
|
|
149
|
+
* subsequent calls to the computation function resolve.
|
|
150
|
+
*
|
|
151
|
+
* The function is provided with an {@link AbortSignal} which any async
|
|
152
|
+
* jobs started from it should abide by. If a signal dependency changes
|
|
153
|
+
* while the job is running, the {@link AbortSignal} will be triggered
|
|
154
|
+
* and the job restarted.
|
|
155
|
+
*/
|
|
85
156
|
asyncComputed(fn, options) {
|
|
86
157
|
const result = Promise.withResolvers();
|
|
87
|
-
const stream =
|
|
88
|
-
const sig =
|
|
158
|
+
const stream = SignalGlobal.computed(() => Async.abortable(fn));
|
|
159
|
+
const sig = SignalGlobal(Err(new Error()));
|
|
89
160
|
let job = undefined;
|
|
90
161
|
let resolved = false;
|
|
91
162
|
const resolve = () => {
|
|
92
163
|
if (!resolved) {
|
|
93
164
|
resolved = true;
|
|
94
|
-
result.resolve(
|
|
165
|
+
result.resolve(SignalGlobal.computed(() => sig.get().unwrapExact(), options));
|
|
95
166
|
}
|
|
96
167
|
};
|
|
97
|
-
|
|
168
|
+
SignalGlobal.effect(() => {
|
|
98
169
|
if (job !== undefined) {
|
|
99
170
|
job.abort();
|
|
100
171
|
}
|
|
@@ -112,9 +183,9 @@ const AnySignal = Object.assign(function (value, options) {
|
|
|
112
183
|
});
|
|
113
184
|
return result.promise;
|
|
114
185
|
},
|
|
115
|
-
State,
|
|
116
|
-
Computed,
|
|
186
|
+
State: StateSignal,
|
|
187
|
+
Computed: ComputedSignal,
|
|
117
188
|
subtle: Signal.subtle,
|
|
118
189
|
});
|
|
119
|
-
export {
|
|
190
|
+
export { SignalGlobal as Signal };
|
|
120
191
|
//# 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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/signal.test.js
CHANGED
|
@@ -85,4 +85,14 @@ test("isSignal", () => {
|
|
|
85
85
|
expect(Signal.Computed.is(s2)).toBeTruthy();
|
|
86
86
|
expect(Signal.is("wibble")).toBeFalsy();
|
|
87
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);
|
|
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":"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
|
|
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,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC;AAE3B,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;IACtB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAC;IAC3C,UAAU,CAAC,GAAG,EAAE;QACZ,IAAI,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,CAAU,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC,EAAE,CAAC,CAAC,CAAC;IACN,MAAM,IAAI,CAAC,OAAO,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;IAC/B,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/B,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/B,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC;QACD,MAAM,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAE,KAAe,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IACD,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACZ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACZ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE;IAClB,MAAM,EAAE,GAAyB,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IACzC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAE3C,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IACxC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IAE5C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE;IACtB,MAAM,EAAE,GAAyB,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,EAAE,GAA4B,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAEpE,SAAS,GAAG,CAAC,OAAwB;QACjC,yCAAyC;QACzC,kDAAkD;IACtD,CAAC;IACD,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,GAAG,CAAC,EAAE,CAAC,CAAC;AACZ,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bodil/signal",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Signal implementation built on the TC39 Signals Proposal",
|
|
5
|
+
"homepage": "https://codeberg.org/bodil/signal",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
|
-
"url": "git+https://
|
|
8
|
+
"url": "git+https://codeberg.org/bodil/signal.git"
|
|
8
9
|
},
|
|
9
10
|
"license": "EUPL-1.2+",
|
|
10
11
|
"module": "dist/index.js",
|
|
@@ -25,27 +26,33 @@
|
|
|
25
26
|
"access": "public"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
|
-
"@bodil/core": "^0.
|
|
29
|
-
"@bodil/disposable": "^0.1.
|
|
29
|
+
"@bodil/core": "^0.3.0",
|
|
30
|
+
"@bodil/disposable": "^0.1.5",
|
|
31
|
+
"@bodil/opt": "^0.4.1",
|
|
30
32
|
"signal-polyfill": "^0.2.2"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
|
-
"@bodil/opt-vitest": "^1.
|
|
34
|
-
"@eslint/eslintrc": "^3.3.
|
|
35
|
-
"@eslint/js": "^9.
|
|
36
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
37
|
-
"@typescript-eslint/parser": "^8.
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"eslint-config-prettier": "^10.
|
|
40
|
-
"eslint-plugin-jsdoc": "^
|
|
41
|
-
"globals": "^16.
|
|
42
|
-
"
|
|
43
|
-
"
|
|
35
|
+
"@bodil/opt-vitest": "^1.1.0",
|
|
36
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
37
|
+
"@eslint/js": "^9.33.0",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^8.39.1",
|
|
39
|
+
"@typescript-eslint/parser": "^8.39.1",
|
|
40
|
+
"eslint": "^9.33.0",
|
|
41
|
+
"eslint-config-prettier": "^10.1.8",
|
|
42
|
+
"eslint-plugin-jsdoc": "^54.0.0",
|
|
43
|
+
"globals": "^16.3.0",
|
|
44
|
+
"typedoc": "^0.28.10",
|
|
45
|
+
"typedoc-plugin-extras": "^4.0.1",
|
|
46
|
+
"typedoc-plugin-mdn-links": "^5.0.8",
|
|
47
|
+
"typescript": "^5.9.2",
|
|
48
|
+
"vitest": "^3.2.4"
|
|
44
49
|
},
|
|
45
50
|
"scripts": {
|
|
46
51
|
"build": "tsc",
|
|
47
52
|
"test": "vitest --run",
|
|
48
53
|
"lint": "eslint src",
|
|
54
|
+
"doc": "typedoc",
|
|
55
|
+
"doc:readthedocs": "typedoc --out $READTHEDOCS_OUTPUT/html",
|
|
49
56
|
"prepublish": "tsc"
|
|
50
57
|
}
|
|
51
58
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A signal library built on the [TC39 Signals
|
|
3
|
+
* Proposal](https://github.com/tc39/proposal-signals).
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { Signal } from "signal-polyfill";
|
|
2
8
|
import { toDisposable, type Disposifiable } from "@bodil/disposable";
|
|
3
|
-
import
|
|
9
|
+
import * as Async from "@bodil/core/async";
|
|
10
|
+
import { Err, Ok, type Result } from "@bodil/opt";
|
|
4
11
|
|
|
5
12
|
interface ISignal<A> {
|
|
13
|
+
/**
|
|
14
|
+
* The current value of the signal.
|
|
15
|
+
*
|
|
16
|
+
* When this is read inside a computation function or an effect function,
|
|
17
|
+
* this signal is automatically added to the effect or computed signal as a
|
|
18
|
+
* dependency.
|
|
19
|
+
*/
|
|
6
20
|
readonly value: A;
|
|
7
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Construct a {@link Signal.Computed} signal using a mapping function over
|
|
23
|
+
* the current value of this signal.
|
|
24
|
+
*/
|
|
25
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B>;
|
|
26
|
+
/**
|
|
27
|
+
* Subscribe to changes to the value of this signal.
|
|
28
|
+
*/
|
|
8
29
|
on(callback: (value: A) => void): Disposable;
|
|
9
30
|
}
|
|
10
31
|
|
|
11
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A writable state signal.
|
|
34
|
+
*/
|
|
35
|
+
class StateSignal<A> extends Signal.State<A> implements ISignal<A> {
|
|
12
36
|
get value(): A {
|
|
13
37
|
return this.get();
|
|
14
38
|
}
|
|
@@ -17,42 +41,51 @@ class State<A> extends Signal.State<A> implements ISignal<A> {
|
|
|
17
41
|
this.set(value);
|
|
18
42
|
}
|
|
19
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Update the current value of this signal using a function.
|
|
46
|
+
*/
|
|
20
47
|
update(fn: (value: A) => A): void {
|
|
21
48
|
this.set(Signal.subtle.untrack(() => fn(this.get())));
|
|
22
49
|
}
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Get a read only version of this signal.
|
|
53
|
+
*/
|
|
54
|
+
readOnly(): SignalGlobal.Computed<A> {
|
|
55
|
+
return SignalGlobal.computed(() => this.get());
|
|
26
56
|
}
|
|
27
57
|
|
|
28
|
-
map<B>(fn: (value: A) => B): Computed<B> {
|
|
29
|
-
return
|
|
58
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B> {
|
|
59
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
30
60
|
}
|
|
31
61
|
|
|
32
62
|
on(callback: (value: A) => void): Disposable {
|
|
33
|
-
return
|
|
63
|
+
return SignalGlobal.subscribe(this, callback);
|
|
34
64
|
}
|
|
35
65
|
|
|
36
|
-
static is(v: unknown): v is State<unknown> {
|
|
37
|
-
return v instanceof
|
|
66
|
+
static is(v: unknown): v is SignalGlobal.State<unknown> {
|
|
67
|
+
return v instanceof StateSignal;
|
|
38
68
|
}
|
|
39
69
|
}
|
|
40
70
|
|
|
41
|
-
|
|
71
|
+
/**
|
|
72
|
+
* A read only signal computed from the values of other signals.
|
|
73
|
+
*/
|
|
74
|
+
class ComputedSignal<A> extends Signal.Computed<A> implements ISignal<A> {
|
|
42
75
|
get value(): A {
|
|
43
76
|
return this.get();
|
|
44
77
|
}
|
|
45
78
|
|
|
46
|
-
map<B>(fn: (value: A) => B): Computed<B> {
|
|
47
|
-
return
|
|
79
|
+
map<B>(fn: (value: A) => B): SignalGlobal.Computed<B> {
|
|
80
|
+
return SignalGlobal.computed(() => fn(this.get()));
|
|
48
81
|
}
|
|
49
82
|
|
|
50
83
|
on(callback: (value: A) => void): Disposable {
|
|
51
|
-
return
|
|
84
|
+
return SignalGlobal.subscribe(this, callback);
|
|
52
85
|
}
|
|
53
86
|
|
|
54
|
-
static is(v: unknown): v is Computed<unknown> {
|
|
55
|
-
return v instanceof
|
|
87
|
+
static is(v: unknown): v is SignalGlobal.Computed<unknown> {
|
|
88
|
+
return v instanceof ComputedSignal;
|
|
56
89
|
}
|
|
57
90
|
}
|
|
58
91
|
|
|
@@ -72,28 +105,69 @@ function effectProcess(): void {
|
|
|
72
105
|
effectWatcher.watch();
|
|
73
106
|
}
|
|
74
107
|
|
|
75
|
-
type
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
108
|
+
type SignalGlobal<A> = SignalGlobal.State<A> | SignalGlobal.Computed<A>;
|
|
109
|
+
|
|
110
|
+
const SignalGlobal = Object.assign(
|
|
111
|
+
/**
|
|
112
|
+
* Construct a new {@link Signal.State} signal containing the provided value.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const sig = Signal("Hello Joe!");
|
|
116
|
+
*/
|
|
117
|
+
function <A>(value: A, options?: Signal.Options<A>): SignalGlobal.State<A> {
|
|
118
|
+
return new SignalGlobal.State(value, options);
|
|
80
119
|
},
|
|
81
120
|
{
|
|
82
|
-
|
|
83
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Test whether the given value is a signal.
|
|
123
|
+
*/
|
|
124
|
+
is(v: unknown): v is SignalGlobal<unknown> {
|
|
125
|
+
return SignalGlobal.State.is(v) || SignalGlobal.Computed.is(v);
|
|
84
126
|
},
|
|
85
127
|
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Construct a new {@link Signal.Computed} signal using the provided
|
|
130
|
+
* computation function.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* const sig1 = Signal(2);
|
|
134
|
+
* const sig2 = Signal(3);
|
|
135
|
+
* const sum = Signal.computed(() => sig1.get() + sig2.get());
|
|
136
|
+
* assert(sum.get() === 5);
|
|
137
|
+
*/
|
|
138
|
+
computed<A>(
|
|
139
|
+
fn: (this: SignalGlobal.Computed<A>) => A,
|
|
140
|
+
options?: Signal.Options<A>
|
|
141
|
+
): SignalGlobal.Computed<A> {
|
|
142
|
+
return new SignalGlobal.Computed(fn, options);
|
|
88
143
|
},
|
|
89
144
|
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Suscribe to a signal.
|
|
147
|
+
*
|
|
148
|
+
* The provided callback will be called every time the value of the
|
|
149
|
+
* signal changes.
|
|
150
|
+
*/
|
|
151
|
+
subscribe<A>(signal: SignalGlobal<A>, callback: (value: A) => void): Disposable {
|
|
152
|
+
return SignalGlobal.effect(() => callback(signal.value));
|
|
92
153
|
},
|
|
93
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Create an effect responding to signal changes.
|
|
157
|
+
*
|
|
158
|
+
* The provided function will be called immediately, and again every
|
|
159
|
+
* time a signal that was read by the function changes.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* const sig = Signal("Hello Joe!");
|
|
163
|
+
* effect(() => console.log("Signal value is:", sig.get()));
|
|
164
|
+
* // prints "Signal value is: Hello Joe!"
|
|
165
|
+
* sig.set("Hello Mike!");
|
|
166
|
+
* // prints "Signal value is: Hello Mike!"
|
|
167
|
+
*/
|
|
94
168
|
effect(fn: () => Disposifiable | void): Disposable {
|
|
95
169
|
let cleanup: Disposable | undefined;
|
|
96
|
-
const computed = new Computed(() => {
|
|
170
|
+
const computed = new SignalGlobal.Computed(() => {
|
|
97
171
|
if (cleanup !== undefined) {
|
|
98
172
|
cleanup[Symbol.dispose]();
|
|
99
173
|
}
|
|
@@ -110,22 +184,36 @@ const AnySignal = Object.assign(
|
|
|
110
184
|
});
|
|
111
185
|
},
|
|
112
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Construct a new {@link Signal.Computed} signal using an async
|
|
189
|
+
* computation function.
|
|
190
|
+
*
|
|
191
|
+
* This returns a promise which will resolve to a
|
|
192
|
+
* {@link Signal.Computed} signal once the promise returned by the
|
|
193
|
+
* computation function resolves, and will update itself whenever
|
|
194
|
+
* subsequent calls to the computation function resolve.
|
|
195
|
+
*
|
|
196
|
+
* The function is provided with an {@link AbortSignal} which any async
|
|
197
|
+
* jobs started from it should abide by. If a signal dependency changes
|
|
198
|
+
* while the job is running, the {@link AbortSignal} will be triggered
|
|
199
|
+
* and the job restarted.
|
|
200
|
+
*/
|
|
113
201
|
asyncComputed<A>(
|
|
114
202
|
fn: (abort: AbortSignal) => Promise<A>,
|
|
115
203
|
options?: Signal.Options<A>
|
|
116
|
-
): Promise<Computed<A>> {
|
|
117
|
-
const result = Promise.withResolvers<Computed<A>>();
|
|
118
|
-
const stream =
|
|
119
|
-
const sig: State<Result<A, Error>> =
|
|
204
|
+
): Promise<SignalGlobal.Computed<A>> {
|
|
205
|
+
const result = Promise.withResolvers<SignalGlobal.Computed<A>>();
|
|
206
|
+
const stream = SignalGlobal.computed(() => Async.abortable(fn));
|
|
207
|
+
const sig: SignalGlobal.State<Result<A, Error>> = SignalGlobal(Err(new Error()));
|
|
120
208
|
let job: Async.AbortableJob<A> | undefined = undefined;
|
|
121
209
|
let resolved = false;
|
|
122
210
|
const resolve = () => {
|
|
123
211
|
if (!resolved) {
|
|
124
212
|
resolved = true;
|
|
125
|
-
result.resolve(
|
|
213
|
+
result.resolve(SignalGlobal.computed(() => sig.get().unwrapExact(), options));
|
|
126
214
|
}
|
|
127
215
|
};
|
|
128
|
-
|
|
216
|
+
SignalGlobal.effect(() => {
|
|
129
217
|
if (job !== undefined) {
|
|
130
218
|
job.abort();
|
|
131
219
|
}
|
|
@@ -147,10 +235,17 @@ const AnySignal = Object.assign(
|
|
|
147
235
|
return result.promise;
|
|
148
236
|
},
|
|
149
237
|
|
|
150
|
-
State,
|
|
151
|
-
Computed,
|
|
238
|
+
State: StateSignal,
|
|
239
|
+
Computed: ComputedSignal,
|
|
152
240
|
subtle: Signal.subtle,
|
|
153
241
|
}
|
|
154
242
|
);
|
|
155
243
|
|
|
156
|
-
|
|
244
|
+
declare namespace SignalGlobal {
|
|
245
|
+
/** @interface */
|
|
246
|
+
export type State<A> = StateSignal<A>;
|
|
247
|
+
/** @interface */
|
|
248
|
+
export type Computed<A> = ComputedSignal<A>;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export { SignalGlobal as Signal };
|
package/src/signal.test.ts
CHANGED
|
@@ -77,7 +77,7 @@ test("asyncComputed", async () => {
|
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
test("isSignal", () => {
|
|
80
|
-
const s1 = Signal(1);
|
|
80
|
+
const s1: Signal.State<number> = Signal(1);
|
|
81
81
|
expect(Signal.is(s1)).toBeTruthy();
|
|
82
82
|
expect(Signal.State.is(s1)).toBeTruthy();
|
|
83
83
|
expect(Signal.Computed.is(s1)).toBeFalsy();
|
|
@@ -89,3 +89,15 @@ test("isSignal", () => {
|
|
|
89
89
|
|
|
90
90
|
expect(Signal.is("wibble")).toBeFalsy();
|
|
91
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);
|
|
103
|
+
});
|