@xmachines/play-signals 2.0.0 → 2.1.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/README.md +15 -17
- package/dist/index.d.ts +24 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -21
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +44 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/dist/watch-signal.d.ts +12 -12
- package/dist/watch-signal.js +13 -13
- package/dist/watch-signal.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @xmachines/play-signals
|
|
2
2
|
|
|
3
|
-
TC39 Signals polyfill for XMachines
|
|
3
|
+
TC39 Signals polyfill for XMachines. It gives the Play Architecture fine-grained reactive state primitives. The primitives propagate state without a glitch and without a subscription.
|
|
4
4
|
|
|
5
|
-
[](https://opensource.org/licenses/MIT) [.
|
|
5
|
+
[](https://opensource.org/licenses/MIT) [](https://www.npmjs.com/package/@xmachines/play-signals)
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
10
8
|
|
|
@@ -14,9 +12,9 @@ pnpm add @xmachines/play-signals
|
|
|
14
12
|
|
|
15
13
|
## Overview
|
|
16
14
|
|
|
17
|
-
This package wraps the [`signal-polyfill`](https://github.com/nicolo-ribaudo/tc39-proposal-signals-polyfill) reference implementation of the [TC39 Signals proposal](https://github.com/tc39/proposal-signals) (Stage 1). It re-exports the
|
|
15
|
+
This package wraps the [`signal-polyfill`](https://github.com/nicolo-ribaudo/tc39-proposal-signals-polyfill) reference implementation of the [TC39 Signals proposal](https://github.com/tc39/proposal-signals) (Stage 1). It re-exports the complete `Signal` namespace, and it adds the memory-safe `watchSignal` utility. The wrapper keeps the rest of the code away from a Stage 1 API that can still change.
|
|
18
16
|
|
|
19
|
-
**
|
|
17
|
+
**Import every signal in the XMachines ecosystem from this package**, not from `signal-polyfill`. One import point keeps each polyfill update and each API change in one place.
|
|
20
18
|
|
|
21
19
|
## Usage
|
|
22
20
|
|
|
@@ -46,11 +44,11 @@ console.log(doubled.get()); // 10 (recomputed because dependency changed)
|
|
|
46
44
|
console.log(doubled.get()); // 10 (memoized — no recomputation)
|
|
47
45
|
```
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
A computation tracks each signal that it reads. A dynamic branch is safe: the computation keeps only the signals of the _current_ execution path as its dependencies.
|
|
50
48
|
|
|
51
49
|
### `watchSignal` — memory-safe one-shot effect
|
|
52
50
|
|
|
53
|
-
Use `watchSignal` to subscribe to a `Signal.State` or `Signal.Computed
|
|
51
|
+
Use `watchSignal` to subscribe to a `Signal.State` or to a `Signal.Computed`. The callback receives the value after each change. `watchSignal` groups the updates of one synchronous batch into a single microtask.
|
|
54
52
|
|
|
55
53
|
```typescript
|
|
56
54
|
import { Signal, watchSignal } from "@xmachines/play-signals";
|
|
@@ -69,11 +67,11 @@ count.set(3); // → logs "count changed: 3" once
|
|
|
69
67
|
cleanup();
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
The
|
|
70
|
+
The cleanup function is idempotent. A second call is safe, and it does not throw.
|
|
73
71
|
|
|
74
72
|
### `Signal.subtle.Watcher` — low-level multi-signal observation
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
Advanced code, such as a framework integration, can use the complete `Signal.subtle.Watcher` API:
|
|
77
75
|
|
|
78
76
|
```typescript
|
|
79
77
|
import { Signal } from "@xmachines/play-signals";
|
|
@@ -97,7 +95,7 @@ count.set(5); // schedules microtask notification
|
|
|
97
95
|
|
|
98
96
|
### Custom equality
|
|
99
97
|
|
|
100
|
-
Both `Signal.State` and `Signal.Computed` accept an `equals` option
|
|
98
|
+
Both `Signal.State` and `Signal.Computed` accept an `equals` option. The option controls when a signal notifies its dependents:
|
|
101
99
|
|
|
102
100
|
```typescript
|
|
103
101
|
import { Signal } from "@xmachines/play-signals";
|
|
@@ -108,7 +106,7 @@ const options: SignalOptions<{ name: string; age: number }> = {
|
|
|
108
106
|
};
|
|
109
107
|
|
|
110
108
|
const person = new Signal.State({ name: "Alice", age: 30 }, options);
|
|
111
|
-
//
|
|
109
|
+
// A structurally identical value does not notify the dependents
|
|
112
110
|
person.set({ name: "Alice", age: 30 });
|
|
113
111
|
```
|
|
114
112
|
|
|
@@ -117,13 +115,13 @@ person.set({ name: "Alice", age: 30 });
|
|
|
117
115
|
| Export | Kind | Description |
|
|
118
116
|
| ------------------------------ | --------- | ------------------------------------------------------------------------------------------------------ |
|
|
119
117
|
| `Signal` | namespace | Full TC39 Signals namespace (`State`, `Computed`, `subtle.Watcher`) re-exported from `signal-polyfill` |
|
|
120
|
-
| `watchSignal(signal, onValue)` | function |
|
|
118
|
+
| `watchSignal(signal, onValue)` | function | The memory-safe subscription helper. It returns a cleanup function |
|
|
121
119
|
| `SignalState<T>` | interface | Shape of `Signal.State<T>` (`.get()`, `.set()`) |
|
|
122
120
|
| `SignalComputed<T>` | interface | Shape of `Signal.Computed<T>` (`.get()`) |
|
|
123
121
|
| `SignalWatcher` | interface | Shape of `Signal.subtle.Watcher` (`.watch()`, `.unwatch()`, `.getPending()`) |
|
|
124
|
-
| `SignalOptions<T>` | interface |
|
|
125
|
-
| `ComputedOptions<T>` | interface |
|
|
126
|
-
| `WatcherNotify` | type |
|
|
122
|
+
| `SignalOptions<T>` | interface | The options object for the `Signal.State` constructor (`equals?`) |
|
|
123
|
+
| `ComputedOptions<T>` | interface | The options object for the `Signal.Computed` constructor (`equals?`) |
|
|
124
|
+
| `WatcherNotify` | type | The callback signature of the `Signal.subtle.Watcher` notify function |
|
|
127
125
|
|
|
128
126
|
## Testing
|
|
129
127
|
|
|
@@ -150,7 +148,7 @@ pnpm run test:coverage
|
|
|
150
148
|
## Requirements
|
|
151
149
|
|
|
152
150
|
- **Node.js** `>= 22.0.0`
|
|
153
|
-
- **TypeScript** `5.7+` (for
|
|
151
|
+
- **TypeScript** `5.7+` (for a consumer that uses TypeScript)
|
|
154
152
|
|
|
155
153
|
## License
|
|
156
154
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TC39 Signals
|
|
2
|
+
* The TC39 Signals polyfill of the XMachines Play Architecture
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This package gives you the fine-grained reactive state primitives of the TC39
|
|
5
|
+
* Signals proposal (Stage 1). It keeps the TC39 polyfill in one place, and it
|
|
6
|
+
* therefore protects the code from a change of the Stage 1 API.
|
|
6
7
|
*
|
|
7
|
-
* **Architectural
|
|
8
|
-
* the reactive primitives that
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* **Architectural context:** the package implements **Signal-Only Reactivity
|
|
9
|
+
* (INV-05)**. It gives the reactive primitives that carry the communication from
|
|
10
|
+
* the Actor to the infrastructure, without a subscription and without an event
|
|
11
|
+
* emitter. Every propagation of state in the Play Architecture uses a TC39 Signal,
|
|
12
|
+
* which tracks each dependency and updates without a glitch.
|
|
11
13
|
*
|
|
12
14
|
* @packageDocumentation
|
|
13
15
|
* @module @xmachines/play-signals
|
|
14
16
|
*
|
|
15
17
|
* @example
|
|
16
|
-
*
|
|
18
|
+
* The basic use of a signal
|
|
17
19
|
* ```typescript
|
|
18
20
|
* import { Signal } from "@xmachines/play-signals";
|
|
19
21
|
*
|
|
20
|
-
* // Create state signal
|
|
22
|
+
* // Create a state signal
|
|
21
23
|
* const count = new Signal.State(0);
|
|
22
24
|
*
|
|
23
|
-
* // Create computed signal
|
|
25
|
+
* // Create a computed signal
|
|
24
26
|
* const doubled = new Signal.Computed(() => count.get() * 2);
|
|
25
27
|
*
|
|
26
|
-
* // Observe changes
|
|
28
|
+
* // Observe the changes
|
|
27
29
|
* const watcher = new Signal.subtle.Watcher(() => {
|
|
28
30
|
* console.log('Count:', count.get(), 'Doubled:', doubled.get());
|
|
29
31
|
* });
|
|
@@ -32,18 +34,20 @@
|
|
|
32
34
|
* count.set(5); // Logs: Count: 5 Doubled: 10
|
|
33
35
|
* ```
|
|
34
36
|
*
|
|
35
|
-
* @see [Play RFC](../../docs/rfc/play.md) -
|
|
37
|
+
* @see [Play RFC](../../docs/rfc/play.md) - invariant INV-05
|
|
36
38
|
* @see {@link https://github.com/tc39/proposal-signals | TC39 Signals Proposal}
|
|
37
39
|
*
|
|
38
40
|
* @remarks
|
|
39
|
-
* **Stage 1
|
|
40
|
-
* uses the official `signal-polyfill` reference implementation
|
|
41
|
-
*
|
|
42
|
-
* this package
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
41
|
+
* **Stage 1 status:** TC39 Signals is at Stage 1 in the TC39 process now. This
|
|
42
|
+
* package uses the official `signal-polyfill` reference implementation. The code
|
|
43
|
+
* therefore stays separate from an API that can change while the proposal
|
|
44
|
+
* develops. Import every signal through this package, and the separation stays
|
|
45
|
+
* complete.
|
|
46
|
+
*
|
|
47
|
+
* **The reason for the separation:** this dedicated package re-exports the
|
|
48
|
+
* polyfill. Therefore one place holds each new polyfill version and each change of
|
|
49
|
+
* the API, and no consuming package changes. This architectural decision protects
|
|
50
|
+
* the code from a change of the Stage 1 API.
|
|
47
51
|
*/
|
|
48
52
|
export { Signal } from "signal-polyfill";
|
|
49
53
|
export { watchSignal } from "./watch-signal.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,YAAY,EACX,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,eAAe,EACf,aAAa,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TC39 Signals
|
|
2
|
+
* The TC39 Signals polyfill of the XMachines Play Architecture
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This package gives you the fine-grained reactive state primitives of the TC39
|
|
5
|
+
* Signals proposal (Stage 1). It keeps the TC39 polyfill in one place, and it
|
|
6
|
+
* therefore protects the code from a change of the Stage 1 API.
|
|
6
7
|
*
|
|
7
|
-
* **Architectural
|
|
8
|
-
* the reactive primitives that
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* **Architectural context:** the package implements **Signal-Only Reactivity
|
|
9
|
+
* (INV-05)**. It gives the reactive primitives that carry the communication from
|
|
10
|
+
* the Actor to the infrastructure, without a subscription and without an event
|
|
11
|
+
* emitter. Every propagation of state in the Play Architecture uses a TC39 Signal,
|
|
12
|
+
* which tracks each dependency and updates without a glitch.
|
|
11
13
|
*
|
|
12
14
|
* @packageDocumentation
|
|
13
15
|
* @module @xmachines/play-signals
|
|
14
16
|
*
|
|
15
17
|
* @example
|
|
16
|
-
*
|
|
18
|
+
* The basic use of a signal
|
|
17
19
|
* ```typescript
|
|
18
20
|
* import { Signal } from "@xmachines/play-signals";
|
|
19
21
|
*
|
|
20
|
-
* // Create state signal
|
|
22
|
+
* // Create a state signal
|
|
21
23
|
* const count = new Signal.State(0);
|
|
22
24
|
*
|
|
23
|
-
* // Create computed signal
|
|
25
|
+
* // Create a computed signal
|
|
24
26
|
* const doubled = new Signal.Computed(() => count.get() * 2);
|
|
25
27
|
*
|
|
26
|
-
* // Observe changes
|
|
28
|
+
* // Observe the changes
|
|
27
29
|
* const watcher = new Signal.subtle.Watcher(() => {
|
|
28
30
|
* console.log('Count:', count.get(), 'Doubled:', doubled.get());
|
|
29
31
|
* });
|
|
@@ -32,20 +34,22 @@
|
|
|
32
34
|
* count.set(5); // Logs: Count: 5 Doubled: 10
|
|
33
35
|
* ```
|
|
34
36
|
*
|
|
35
|
-
* @see [Play RFC](../../docs/rfc/play.md) -
|
|
37
|
+
* @see [Play RFC](../../docs/rfc/play.md) - invariant INV-05
|
|
36
38
|
* @see {@link https://github.com/tc39/proposal-signals | TC39 Signals Proposal}
|
|
37
39
|
*
|
|
38
40
|
* @remarks
|
|
39
|
-
* **Stage 1
|
|
40
|
-
* uses the official `signal-polyfill` reference implementation
|
|
41
|
-
*
|
|
42
|
-
* this package
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
41
|
+
* **Stage 1 status:** TC39 Signals is at Stage 1 in the TC39 process now. This
|
|
42
|
+
* package uses the official `signal-polyfill` reference implementation. The code
|
|
43
|
+
* therefore stays separate from an API that can change while the proposal
|
|
44
|
+
* develops. Import every signal through this package, and the separation stays
|
|
45
|
+
* complete.
|
|
46
|
+
*
|
|
47
|
+
* **The reason for the separation:** this dedicated package re-exports the
|
|
48
|
+
* polyfill. Therefore one place holds each new polyfill version and each change of
|
|
49
|
+
* the API, and no consuming package changes. This architectural decision protects
|
|
50
|
+
* the code from a change of the Stage 1 API.
|
|
47
51
|
*/
|
|
48
|
-
// Re-export complete Signal namespace
|
|
52
|
+
// Re-export the complete Signal namespace of the official polyfill
|
|
49
53
|
export { Signal } from "signal-polyfill";
|
|
50
54
|
export { watchSignal } from "./watch-signal.js";
|
|
51
55
|
//# 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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,mEAAmE;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The type definitions of the TC39 Signals API
|
|
3
3
|
*
|
|
4
|
-
* These types
|
|
5
|
-
* Note: Signal is Stage 1
|
|
4
|
+
* These types follow the signal-polyfill implementation and the TC39 proposal.
|
|
5
|
+
* Note: Signal is at Stage 1, and a later version of the specification can change
|
|
6
|
+
* the API.
|
|
6
7
|
*
|
|
7
8
|
* @see {@link https://github.com/tc39/proposal-signals | TC39 Signals Proposal}
|
|
8
9
|
*/
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* The options of a Signal.State
|
|
11
12
|
*
|
|
12
|
-
* @param equals -
|
|
13
|
+
* @param equals - The optional equality function of your own. It decides if the value changed
|
|
13
14
|
*
|
|
14
15
|
* @example
|
|
15
16
|
* ```typescript
|
|
@@ -24,19 +25,19 @@
|
|
|
24
25
|
*/
|
|
25
26
|
export interface SignalOptions<T> {
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
-
* @param a -
|
|
29
|
-
* @param b -
|
|
30
|
-
* @returns true
|
|
28
|
+
* The equality function of your own. It decides if the value changed
|
|
29
|
+
* @param a - The previous value
|
|
30
|
+
* @param b - The new value
|
|
31
|
+
* @returns true when the two values are equal, and no notification is necessary
|
|
31
32
|
*/
|
|
32
33
|
equals?: (a: T, b: T) => boolean;
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* The writable state signal. It holds one reactive value
|
|
36
37
|
*
|
|
37
|
-
* Signal.State is the
|
|
38
|
-
*
|
|
39
|
-
*
|
|
38
|
+
* Signal.State is the base primitive of a reactive state. A `get()` call inside a
|
|
39
|
+
* computed signal or inside a watcher tracks the state as a dependency. A `set()`
|
|
40
|
+
* call notifies every computation and every watcher that depends on the state.
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
43
|
* ```typescript
|
|
@@ -50,22 +51,22 @@ export interface SignalOptions<T> {
|
|
|
50
51
|
*/
|
|
51
52
|
export interface SignalState<T> {
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
+
* Reads the current value, and tracks the signal as a dependency
|
|
54
55
|
*
|
|
55
|
-
* @returns
|
|
56
|
+
* @returns The current value of the signal
|
|
56
57
|
*/
|
|
57
58
|
get(): T;
|
|
58
59
|
/**
|
|
59
|
-
*
|
|
60
|
+
* Writes a new value, and notifies the watchers when the value changed
|
|
60
61
|
*
|
|
61
|
-
* @param value -
|
|
62
|
+
* @param value - The new value
|
|
62
63
|
*/
|
|
63
64
|
set(value: T): void;
|
|
64
65
|
}
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
+
* The options of a Signal.Computed
|
|
67
68
|
*
|
|
68
|
-
* @param equals -
|
|
69
|
+
* @param equals - The optional equality function of your own, for the memoization
|
|
69
70
|
*
|
|
70
71
|
* @example
|
|
71
72
|
* ```typescript
|
|
@@ -79,16 +80,16 @@ export interface SignalState<T> {
|
|
|
79
80
|
*/
|
|
80
81
|
export interface ComputedOptions<T> {
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
+
* The equality function of your own, for the memoization
|
|
83
84
|
*/
|
|
84
85
|
equals?: (a: T, b: T) => boolean;
|
|
85
86
|
}
|
|
86
87
|
/**
|
|
87
|
-
*
|
|
88
|
+
* The computed signal. It evaluates late, and it memoizes the result
|
|
88
89
|
*
|
|
89
|
-
* Signal.Computed
|
|
90
|
-
*
|
|
91
|
-
*
|
|
90
|
+
* Signal.Computed tracks each dependency when it runs its callback. It memoizes the
|
|
91
|
+
* result, and it runs the callback again only after a dependency changes. It
|
|
92
|
+
* therefore tracks each dependency for you, and you manage no subscription.
|
|
92
93
|
*
|
|
93
94
|
* @example
|
|
94
95
|
* ```typescript
|
|
@@ -99,23 +100,24 @@ export interface ComputedOptions<T> {
|
|
|
99
100
|
*
|
|
100
101
|
* console.log(doubled.get()); // 0
|
|
101
102
|
* count.set(5);
|
|
102
|
-
* console.log(doubled.get()); // 10 (
|
|
103
|
-
* console.log(doubled.get()); // 10 (
|
|
103
|
+
* console.log(doubled.get()); // 10 (the signal computed it again)
|
|
104
|
+
* console.log(doubled.get()); // 10 (from the memory, with no new computation)
|
|
104
105
|
* ```
|
|
105
106
|
*/
|
|
106
107
|
export interface SignalComputed<T> {
|
|
107
108
|
/**
|
|
108
|
-
*
|
|
109
|
+
* Reads the computed value. It computes the value again only after a dependency changed
|
|
109
110
|
*
|
|
110
|
-
* @returns
|
|
111
|
+
* @returns The value from the current dependencies
|
|
111
112
|
*/
|
|
112
113
|
get(): T;
|
|
113
114
|
}
|
|
114
115
|
/**
|
|
115
|
-
*
|
|
116
|
+
* The notification callback of a Signal.subtle.Watcher
|
|
116
117
|
*
|
|
117
|
-
*
|
|
118
|
-
* rapid updates
|
|
118
|
+
* The watcher calls it after a signal that it watches changes. Use the microtask
|
|
119
|
+
* batching pattern to group rapid updates. The README of signal-polyfill gives the
|
|
120
|
+
* best practice.
|
|
119
121
|
*
|
|
120
122
|
* @example
|
|
121
123
|
* ```typescript
|
|
@@ -124,7 +126,7 @@ export interface SignalComputed<T> {
|
|
|
124
126
|
* const notify: WatcherNotify = () => {
|
|
125
127
|
* queueMicrotask(() => {
|
|
126
128
|
* const pending = watcher.getPending();
|
|
127
|
-
* // Process
|
|
129
|
+
* // Process the signal changes that wait
|
|
128
130
|
* });
|
|
129
131
|
* };
|
|
130
132
|
* const watcher = new Signal.subtle.Watcher(notify);
|
|
@@ -132,10 +134,10 @@ export interface SignalComputed<T> {
|
|
|
132
134
|
*/
|
|
133
135
|
export type WatcherNotify = () => void;
|
|
134
136
|
/**
|
|
135
|
-
*
|
|
137
|
+
* The watcher. It observes each signal change and schedules an effect
|
|
136
138
|
*
|
|
137
|
-
* Signal.subtle.Watcher
|
|
138
|
-
*
|
|
139
|
+
* Signal.subtle.Watcher observes more than one signal, and it groups the updates.
|
|
140
|
+
* It is the low-level primitive, and a framework uses it to make a reactive effect.
|
|
139
141
|
*
|
|
140
142
|
* @example
|
|
141
143
|
* ```typescript
|
|
@@ -154,26 +156,26 @@ export type WatcherNotify = () => void;
|
|
|
154
156
|
* watcher.watch(count);
|
|
155
157
|
* watcher.watch(doubled);
|
|
156
158
|
*
|
|
157
|
-
* count.set(5); //
|
|
159
|
+
* count.set(5); // The watcher schedules the notification in a microtask
|
|
158
160
|
* ```
|
|
159
161
|
*/
|
|
160
162
|
export interface SignalWatcher {
|
|
161
163
|
/**
|
|
162
|
-
*
|
|
164
|
+
* Starts the watch of one signal
|
|
163
165
|
*
|
|
164
|
-
* @param signal -
|
|
166
|
+
* @param signal - The signal to observe: a State or a Computed
|
|
165
167
|
*/
|
|
166
168
|
watch(signal: SignalState<unknown> | SignalComputed<unknown>): void;
|
|
167
169
|
/**
|
|
168
|
-
*
|
|
170
|
+
* Stops the watch of one signal
|
|
169
171
|
*
|
|
170
|
-
* @param signal -
|
|
172
|
+
* @param signal - The signal to leave
|
|
171
173
|
*/
|
|
172
174
|
unwatch(signal: SignalState<unknown> | SignalComputed<unknown>): void;
|
|
173
175
|
/**
|
|
174
|
-
*
|
|
176
|
+
* Returns the signals that changed after the last check
|
|
175
177
|
*
|
|
176
|
-
* @returns
|
|
178
|
+
* @returns The array of the signals with an update that waits
|
|
177
179
|
*/
|
|
178
180
|
getPending(): Array<SignalState<unknown> | SignalComputed<unknown>>;
|
|
179
181
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B;;;;OAIG;IACH,GAAG,IAAI,CAAC,CAAC;IAET;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC;;;;OAIG;IACH,GAAG,IAAI,CAAC,CAAC;CACT;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAEpE;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAEtE;;;;OAIG;IACH,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;CACpE"}
|
package/dist/types.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The type definitions of the TC39 Signals API
|
|
3
3
|
*
|
|
4
|
-
* These types
|
|
5
|
-
* Note: Signal is Stage 1
|
|
4
|
+
* These types follow the signal-polyfill implementation and the TC39 proposal.
|
|
5
|
+
* Note: Signal is at Stage 1, and a later version of the specification can change
|
|
6
|
+
* the API.
|
|
6
7
|
*
|
|
7
8
|
* @see {@link https://github.com/tc39/proposal-signals | TC39 Signals Proposal}
|
|
8
9
|
*/
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
package/dist/watch-signal.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { Signal } from "signal-polyfill";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Subscribes to one signal, with the canonical one-shot watcher lifecycle.
|
|
4
4
|
*
|
|
5
|
-
* The callback runs from a
|
|
6
|
-
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
5
|
+
* The callback runs from a microtask in the queue, after the watcher drains the
|
|
6
|
+
* notifications that wait. The watcher then arms itself again, so that it misses no
|
|
7
|
+
* later update. The cleanup function is idempotent, because it accepts a watcher
|
|
8
|
+
* that is detached already.
|
|
9
9
|
*
|
|
10
10
|
* **Memory safety (Phase 29):**
|
|
11
|
-
* - `disposed` flag
|
|
12
|
-
*
|
|
13
|
-
* - `needsEnqueue` guard
|
|
14
|
-
*
|
|
11
|
+
* - The `disposed` flag stops a callback after the cleanup: the microtask returns at
|
|
12
|
+
* once when the cleanup runs before the microtask fires.
|
|
13
|
+
* - The `needsEnqueue` guard removes the duplicates of rapid synchronous signal
|
|
14
|
+
* changes: one batch of synchronous changes queues one microtask only.
|
|
15
15
|
*
|
|
16
|
-
* @param signal -
|
|
17
|
-
* @param onValue -
|
|
18
|
-
* @returns
|
|
16
|
+
* @param signal - The `Signal.State` or `Signal.Computed` to subscribe to.
|
|
17
|
+
* @param onValue - The watcher calls it with the current signal value after each change.
|
|
18
|
+
* @returns The cleanup function. It removes the watcher.
|
|
19
19
|
*/
|
|
20
20
|
export declare function watchSignal<T>(signal: Signal.State<T> | Signal.Computed<T>, onValue: (value: T) => void): () => void;
|
|
21
21
|
//# sourceMappingURL=watch-signal.d.ts.map
|
package/dist/watch-signal.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { Signal } from "signal-polyfill";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Subscribes to one signal, with the canonical one-shot watcher lifecycle.
|
|
4
4
|
*
|
|
5
|
-
* The callback runs from a
|
|
6
|
-
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
5
|
+
* The callback runs from a microtask in the queue, after the watcher drains the
|
|
6
|
+
* notifications that wait. The watcher then arms itself again, so that it misses no
|
|
7
|
+
* later update. The cleanup function is idempotent, because it accepts a watcher
|
|
8
|
+
* that is detached already.
|
|
9
9
|
*
|
|
10
10
|
* **Memory safety (Phase 29):**
|
|
11
|
-
* - `disposed` flag
|
|
12
|
-
*
|
|
13
|
-
* - `needsEnqueue` guard
|
|
14
|
-
*
|
|
11
|
+
* - The `disposed` flag stops a callback after the cleanup: the microtask returns at
|
|
12
|
+
* once when the cleanup runs before the microtask fires.
|
|
13
|
+
* - The `needsEnqueue` guard removes the duplicates of rapid synchronous signal
|
|
14
|
+
* changes: one batch of synchronous changes queues one microtask only.
|
|
15
15
|
*
|
|
16
|
-
* @param signal -
|
|
17
|
-
* @param onValue -
|
|
18
|
-
* @returns
|
|
16
|
+
* @param signal - The `Signal.State` or `Signal.Computed` to subscribe to.
|
|
17
|
+
* @param onValue - The watcher calls it with the current signal value after each change.
|
|
18
|
+
* @returns The cleanup function. It removes the watcher.
|
|
19
19
|
*/
|
|
20
20
|
export function watchSignal(signal, onValue) {
|
|
21
21
|
let disposed = false;
|
|
@@ -40,7 +40,7 @@ export function watchSignal(signal, onValue) {
|
|
|
40
40
|
watcher.unwatch(signal);
|
|
41
41
|
}
|
|
42
42
|
catch {
|
|
43
|
-
// Ignore detached watcher
|
|
43
|
+
// Ignore an error of a detached watcher. The cleanup therefore stays idempotent.
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
}
|
package/dist/watch-signal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch-signal.js","sourceRoot":"","sources":["../src/watch-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CAC1B,MAA4C,EAC5C,OAA2B;IAE3B,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,YAAY,GAAG,IAAI,CAAC;IAExB,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QAC9C,IAAI,QAAQ,IAAI,CAAC,YAAY;YAAE,OAAO;QACtC,YAAY,GAAG,KAAK,CAAC;QACrB,cAAc,CAAC,GAAG,EAAE;YACnB,IAAI,QAAQ;gBAAE,OAAO;YACrB,YAAY,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtB,OAAO,GAAG,EAAE;QACX,QAAQ,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC;YACJ,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACR,
|
|
1
|
+
{"version":3,"file":"watch-signal.js","sourceRoot":"","sources":["../src/watch-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CAC1B,MAA4C,EAC5C,OAA2B;IAE3B,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,YAAY,GAAG,IAAI,CAAC;IAExB,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QAC9C,IAAI,QAAQ,IAAI,CAAC,YAAY;YAAE,OAAO;QACtC,YAAY,GAAG,KAAK,CAAC;QACrB,cAAc,CAAC,GAAG,EAAE;YACnB,IAAI,QAAQ;gBAAE,OAAO;YACrB,YAAY,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtB,OAAO,GAAG,EAAE;QACX,QAAQ,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC;YACJ,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACR,iFAAiF;QAClF,CAAC;IACF,CAAC,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-signals",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TC39 Signals polyfill for XMachines - Fine-grained reactive state primitives",
|
|
6
6
|
"keywords": [
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"build": "vite build && tsc --build",
|
|
42
42
|
"clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__ node_modules/.svelte2tsx-* node_modules/.vite*",
|
|
43
43
|
"test": "vitest",
|
|
44
|
+
"test:coverage": "vitest run --coverage",
|
|
44
45
|
"lint": "oxlint .",
|
|
45
46
|
"lint:fix": "oxlint --fix .",
|
|
46
47
|
"format": "oxfmt .",
|