@zeix/cause-effect 0.12.2 → 0.12.4
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 +44 -2
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/index.ts +3 -3
- package/lib/computed.d.ts +2 -2
- package/lib/computed.ts +2 -2
- package/lib/effect.d.ts +2 -2
- package/lib/effect.ts +2 -2
- package/lib/scheduler.d.ts +6 -7
- package/lib/scheduler.ts +27 -25
- package/lib/signal.d.ts +16 -8
- package/lib/signal.ts +23 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cause & Effect
|
|
2
2
|
|
|
3
|
-
Version 0.12.
|
|
3
|
+
Version 0.12.4
|
|
4
4
|
|
|
5
5
|
**Cause & Effect** is a lightweight, reactive state management library for JavaScript applications. It uses the concept of signals to create a predictable and efficient data flow in your app.
|
|
6
6
|
|
|
@@ -54,7 +54,7 @@ npm install @zeix/cause-effect
|
|
|
54
54
|
bun add @zeix/cause-effect
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
##
|
|
57
|
+
## Usage
|
|
58
58
|
|
|
59
59
|
### Single State Signal
|
|
60
60
|
|
|
@@ -214,3 +214,45 @@ This example showcases several powerful features of Cause & Effect:
|
|
|
214
214
|
5. **Flexibility and Integration**: Seamlessly integrates with DOM manipulation and event listeners, fitting into any JavaScript application or framework.
|
|
215
215
|
|
|
216
216
|
These principles enable developers to create complex, reactive applications with clear data flow, efficient updates, and robust error handling, while promoting code reuse and modularity.
|
|
217
|
+
|
|
218
|
+
### Effects and DOM Updates
|
|
219
|
+
|
|
220
|
+
The `enqueue()` function allows you to schedule DOM updates to be executed on the next animation frame. This function returns a `Promise`, which makes it easy to detect when updates are applied or if they fail.
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
import { enqueue } from '@zeix/cause-effect'
|
|
224
|
+
|
|
225
|
+
// Schedule a DOM update
|
|
226
|
+
enqueue(() => {
|
|
227
|
+
document.getElementById('myElement').textContent = 'Updated content'
|
|
228
|
+
})
|
|
229
|
+
.then(() => console.log('Update applied successfully'))
|
|
230
|
+
.catch(error => console.error('Update failed:', error))
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
You can also use the deduplication feature to ensure that only the latest update for a specific element and operation is applied:
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
import { state, effect, enqueue } from '@zeix/cause-effect'
|
|
237
|
+
|
|
238
|
+
// Define a signal and update it in an event handler
|
|
239
|
+
const name = state('')
|
|
240
|
+
document.querySelector('input[name="name"]').addEventListener('input', e => {
|
|
241
|
+
state.set(e.target.value) // Triggers an update on every keystroke
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// Define an effect to react to signal changes
|
|
245
|
+
effect(text => {
|
|
246
|
+
const nameSpan = document.querySelector('.greeting .name')
|
|
247
|
+
enqueue(() => {
|
|
248
|
+
nameSpan.textContent = text
|
|
249
|
+
return text
|
|
250
|
+
}, [nameSpan, 'setName']) // For deduplication
|
|
251
|
+
.then(result => console.log(`Name was updated to ${result}`))
|
|
252
|
+
.catch(error => console.error('Failed to update name:', error))
|
|
253
|
+
}, name)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
In this example, as the user types in the input field only 'Jane' will be applied to the DOM. 'J', 'Ja', 'Jan' were superseded by more recent updates and deduplicated (if typing was fast enough).
|
|
257
|
+
|
|
258
|
+
When multiple `enqueue` calls are made with the same deduplication key before the next animation frame, only the last call will be executed. Previous calls are superseded and their Promises will not be resolved or rejected. This "last-write-wins" behavior ensures that only the most recent update is applied, which is typically desirable for UI updates and state changes.
|
package/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name Cause & Effect
|
|
3
|
-
* @version 0.12.
|
|
3
|
+
* @version 0.12.4
|
|
4
4
|
* @author Esther Brunner
|
|
5
5
|
*/
|
|
6
|
-
export { type Signal, type MaybeSignal, type
|
|
6
|
+
export { type Signal, type MaybeSignal, type InferSignalType, type ComputedCallbacks, type EffectCallbacks, UNSET, isSignal, isComputedCallbacks, toSignal } from './lib/signal';
|
|
7
7
|
export { type State, state, isState } from './lib/state';
|
|
8
8
|
export { type Computed, computed, isComputed } from './lib/computed';
|
|
9
9
|
export { effect } from './lib/effect';
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var q=(x)=>typeof x==="function";var M=(x,B)=>Object.prototype.toString.call(x)===`[object ${B}]`,f=(x)=>(B)=>B instanceof x,I=f(Error),m=f(Promise),V=(x)=>I(x)?x:new Error(String(x));var Z,R=new Set,T=0,w=new Map,N,g=()=>{N=void 0;let x=Array.from(w.values());w.clear();for(let B of x)B()},v=()=>{if(N)cancelAnimationFrame(N);N=requestAnimationFrame(g)};queueMicrotask(g);var D=(x)=>{if(Z&&!x.includes(Z))x.push(Z)},j=(x)=>{for(let B of x)if(T)R.add(B);else B()},S=()=>{while(R.size){let x=Array.from(R);R.clear();for(let B of x)B()}},n=(x)=>{T++;try{x()}finally{S(),T--}},C=(x,B)=>{let z=Z;Z=B;try{x()}finally{Z=z}},i=(x,B)=>new Promise((z,$)=>{let L=()=>{try{z(x())}catch(Q){$(Q)}};if(B)w.set(B,L);v()});function O(x,...B){let z=!1,$=()=>C(()=>{if(z)throw new Error("Circular dependency in effect detected");z=!0;let L=_(B,x);if(I(L))console.error("Unhandled error in effect:",L);z=!1},$);$()}var p="Computed",c=(x,B)=>{if(!B)return!1;return x.name===B.name&&x.message===B.message},A=(x,...B)=>{let z=[],$=J,L,Q=!0,G=!1,W=!1,H=(F)=>{if(!Object.is(F,$))$=F,Q=!1,L=void 0,G=!1},K=()=>{G=J===$,$=J,L=void 0},Y=(F)=>{let P=V(F);G=c(P,L),$=J,L=P},X=()=>{if(Q=!0,!G)j(z)},d=()=>C(()=>{if(W)throw new Error("Circular dependency in computed detected");G=!0,W=!0;let F=_(B,x);if(m(F))K(),F.then((P)=>{H(P),j(z)}).catch(Y);else if(F==null||J===F)K();else if(I(F))Y(F);else H(F);W=!1},X),y={[Symbol.toStringTag]:p,get:()=>{if(D(z),S(),Q)d();if(L)throw L;return $},map:(F)=>A(F,y),match:(F)=>{return O(F,y),y}};return y},U=(x)=>M(x,p);var b="State",k=(x)=>{let B=[],z=x,$={[Symbol.toStringTag]:b,get:()=>{return D(B),z},set:(L)=>{if(Object.is(z,L))return;if(z=L,j(B),J===z)B.length=0},update:(L)=>{$.set(L(z))},map:(L)=>A(L,$),match:(L)=>{return O(L,$),$}};return $},E=(x)=>M(x,b);var J=Symbol(),h=(x)=>E(x)||U(x),o=(x)=>q(x)&&!x.length||typeof x==="object"&&x!==null&&("ok"in x)&&q(x.ok),t=(x)=>h(x)?x:o(x)?A(x):k(x),_=(x,B)=>{let{ok:z,nil:$,err:L}=q(B)?{ok:B}:B,Q=[],G=[],W=!1;for(let K=0;K<x.length;K++){let Y=x[K];try{let X=Y.get();if(X===J)W=!0;Q[K]=X}catch(X){G.push(V(X))}}let H=void 0;try{if(W&&$)H=$();else if(G.length)H=L?L(...G):G[0];else if(!W)H=z(...Q)}catch(K){if(H=V(K),L)H=L(H)}return H};export{C as watch,t as toSignal,k as state,E as isState,h as isSignal,o as isComputedCallbacks,U as isComputed,i as enqueue,O as effect,A as computed,n as batch,J as UNSET};
|
package/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name Cause & Effect
|
|
3
|
-
* @version 0.12.
|
|
3
|
+
* @version 0.12.4
|
|
4
4
|
* @author Esther Brunner
|
|
5
5
|
*/
|
|
6
6
|
export {
|
|
7
|
-
type Signal, type MaybeSignal, type
|
|
7
|
+
type Signal, type MaybeSignal, type InferSignalType,
|
|
8
8
|
type ComputedCallbacks, type EffectCallbacks,
|
|
9
|
-
UNSET, isSignal, toSignal
|
|
9
|
+
UNSET, isSignal, isComputedCallbacks, toSignal
|
|
10
10
|
} from './lib/signal'
|
|
11
11
|
|
|
12
12
|
export { type State, state, isState } from './lib/state'
|
package/lib/computed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Signal, type EffectCallbacks, type ComputedCallbacks } from './signal';
|
|
2
2
|
export type Computed<T extends {}> = {
|
|
3
3
|
[Symbol.toStringTag]: 'Computed';
|
|
4
4
|
get: () => T;
|
|
@@ -13,7 +13,7 @@ export type Computed<T extends {}> = {
|
|
|
13
13
|
* @param {U} maybeSignals - signals of functions using signals this values depends on
|
|
14
14
|
* @returns {Computed<T>} - Computed signal
|
|
15
15
|
*/
|
|
16
|
-
export declare const computed: <T extends {}, U extends
|
|
16
|
+
export declare const computed: <T extends {}, U extends Signal<{}>[]>(cb: ComputedCallbacks<T, U>, ...maybeSignals: U) => Computed<T>;
|
|
17
17
|
/**
|
|
18
18
|
* Check if a value is a computed state
|
|
19
19
|
*
|
package/lib/computed.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
2
|
+
type Signal, type EffectCallbacks, type ComputedCallbacks,
|
|
3
3
|
resolve, UNSET
|
|
4
4
|
} from './signal'
|
|
5
5
|
import { isError, isObjectOfType, isPromise, toError } from './util'
|
|
@@ -39,7 +39,7 @@ const isEquivalentError = /*#__PURE__*/ (
|
|
|
39
39
|
* @param {U} maybeSignals - signals of functions using signals this values depends on
|
|
40
40
|
* @returns {Computed<T>} - Computed signal
|
|
41
41
|
*/
|
|
42
|
-
export const computed = <T extends {}, U extends
|
|
42
|
+
export const computed = <T extends {}, U extends Signal<{}>[]>(
|
|
43
43
|
cb: ComputedCallbacks<T, U>,
|
|
44
44
|
...maybeSignals: U
|
|
45
45
|
): Computed<T> => {
|
package/lib/effect.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EffectCallbacks, type
|
|
1
|
+
import { type EffectCallbacks, type Signal } from './signal';
|
|
2
2
|
/**
|
|
3
3
|
* Define what happens when a reactive state changes
|
|
4
4
|
*
|
|
@@ -6,4 +6,4 @@ import { type EffectCallbacks, type MaybeSignal } from './signal';
|
|
|
6
6
|
* @param {() => void} cb - effect callback or object of ok, nil, err callbacks to be executed when a state changes
|
|
7
7
|
* @param {U} maybeSignals - signals of functions using signals that should trigger the effect
|
|
8
8
|
*/
|
|
9
|
-
export declare function effect<U extends
|
|
9
|
+
export declare function effect<U extends Signal<{}>[]>(cb: EffectCallbacks<U>, ...maybeSignals: U): void;
|
package/lib/effect.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { type EffectCallbacks, type
|
|
2
|
+
import { type EffectCallbacks, type Signal, resolve } from './signal'
|
|
3
3
|
import { isError } from './util'
|
|
4
4
|
import { watch } from './scheduler'
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ import { watch } from './scheduler'
|
|
|
12
12
|
* @param {() => void} cb - effect callback or object of ok, nil, err callbacks to be executed when a state changes
|
|
13
13
|
* @param {U} maybeSignals - signals of functions using signals that should trigger the effect
|
|
14
14
|
*/
|
|
15
|
-
export function effect<U extends
|
|
15
|
+
export function effect<U extends Signal<{}>[]>(
|
|
16
16
|
cb: EffectCallbacks<U>,
|
|
17
17
|
...maybeSignals: U
|
|
18
18
|
): void {
|
package/lib/scheduler.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type EnqueueDedupe = [Element, string];
|
|
2
2
|
export type Watcher = () => void;
|
|
3
|
-
export type Updater = <T>() => T;
|
|
3
|
+
export type Updater = <T>() => T | boolean | void;
|
|
4
4
|
/**
|
|
5
5
|
* Add active watcher to the array of watchers
|
|
6
6
|
*
|
|
@@ -27,14 +27,13 @@ export declare const batch: (fn: () => void) => void;
|
|
|
27
27
|
* Run a function in a reactive context
|
|
28
28
|
*
|
|
29
29
|
* @param {() => void} run - function to run the computation or effect
|
|
30
|
-
* @param {Watcher} mark - function to be called when the state changes
|
|
30
|
+
* @param {Watcher} mark - function to be called when the state changes or undefined for temporary unwatching while inserting auto-hydrating DOM nodes that might read signals (e.g., web components)
|
|
31
31
|
*/
|
|
32
|
-
export declare const watch: (run: () => void, mark
|
|
32
|
+
export declare const watch: (run: () => void, mark?: Watcher) => void;
|
|
33
33
|
/**
|
|
34
34
|
* Enqueue a function to be executed on the next animation frame
|
|
35
35
|
*
|
|
36
|
-
* @param
|
|
37
|
-
* @param dedupe
|
|
38
|
-
* @returns
|
|
36
|
+
* @param {Updater} fn - function to be executed on the next animation frame; can return updated value <T>, success <boolean> or void
|
|
37
|
+
* @param {EnqueueDedupe} dedupe - [element, operation] pair for deduplication
|
|
39
38
|
*/
|
|
40
|
-
export declare const enqueue: <T>(
|
|
39
|
+
export declare const enqueue: <T>(fn: Updater, dedupe?: EnqueueDedupe) => Promise<boolean | void | T>;
|
package/lib/scheduler.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
export type EnqueueDedupe = [Element, string]
|
|
4
4
|
|
|
5
5
|
export type Watcher = () => void
|
|
6
|
-
export type Updater = <T>() => T
|
|
6
|
+
export type Updater = <T>() => T | boolean | void
|
|
7
7
|
|
|
8
8
|
/* === Internal === */
|
|
9
9
|
|
|
@@ -15,16 +15,15 @@ const pending = new Set<Watcher>()
|
|
|
15
15
|
let batchDepth = 0
|
|
16
16
|
|
|
17
17
|
// Map of DOM elements to update functions
|
|
18
|
-
const updateMap = new Map<
|
|
18
|
+
const updateMap = new Map<EnqueueDedupe, () => void>()
|
|
19
19
|
let requestId: number | undefined
|
|
20
20
|
|
|
21
21
|
const updateDOM = () => {
|
|
22
22
|
requestId = undefined
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
elementMap.clear()
|
|
23
|
+
const updates = Array.from(updateMap.values())
|
|
24
|
+
updateMap.clear()
|
|
25
|
+
for (const fn of updates) {
|
|
26
|
+
fn()
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -57,7 +56,8 @@ export const subscribe = (watchers: Watcher[]) => {
|
|
|
57
56
|
*/
|
|
58
57
|
export const notify = (watchers: Watcher[]) => {
|
|
59
58
|
for (const mark of watchers) {
|
|
60
|
-
batchDepth
|
|
59
|
+
if (batchDepth) pending.add(mark)
|
|
60
|
+
else mark()
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -81,47 +81,49 @@ export const flush = () => {
|
|
|
81
81
|
*/
|
|
82
82
|
export const batch = (fn: () => void) => {
|
|
83
83
|
batchDepth++
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
try {
|
|
85
|
+
fn()
|
|
86
|
+
} finally {
|
|
87
|
+
flush()
|
|
88
|
+
batchDepth--
|
|
89
|
+
}
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
/**
|
|
90
93
|
* Run a function in a reactive context
|
|
91
94
|
*
|
|
92
95
|
* @param {() => void} run - function to run the computation or effect
|
|
93
|
-
* @param {Watcher} mark - function to be called when the state changes
|
|
96
|
+
* @param {Watcher} mark - function to be called when the state changes or undefined for temporary unwatching while inserting auto-hydrating DOM nodes that might read signals (e.g., web components)
|
|
94
97
|
*/
|
|
95
|
-
export const watch = (run: () => void, mark
|
|
98
|
+
export const watch = (run: () => void, mark?: Watcher): void => {
|
|
96
99
|
const prev = active
|
|
97
100
|
active = mark
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
try {
|
|
102
|
+
run()
|
|
103
|
+
} finally {
|
|
104
|
+
active = prev
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
/**
|
|
103
109
|
* Enqueue a function to be executed on the next animation frame
|
|
104
110
|
*
|
|
105
|
-
* @param
|
|
106
|
-
* @param dedupe
|
|
107
|
-
* @returns
|
|
111
|
+
* @param {Updater} fn - function to be executed on the next animation frame; can return updated value <T>, success <boolean> or void
|
|
112
|
+
* @param {EnqueueDedupe} dedupe - [element, operation] pair for deduplication
|
|
108
113
|
*/
|
|
109
114
|
export const enqueue = <T>(
|
|
110
|
-
|
|
115
|
+
fn: Updater,
|
|
111
116
|
dedupe?: EnqueueDedupe
|
|
112
|
-
) => new Promise<T>((resolve, reject) => {
|
|
117
|
+
) => new Promise<T | boolean | void>((resolve, reject) => {
|
|
113
118
|
const wrappedCallback = () => {
|
|
114
119
|
try {
|
|
115
|
-
resolve(
|
|
120
|
+
resolve(fn())
|
|
116
121
|
} catch (error) {
|
|
117
122
|
reject(error)
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
if (dedupe) {
|
|
121
|
-
|
|
122
|
-
if (!updateMap.has(el)) updateMap.set(el, new Map())
|
|
123
|
-
const elementMap = updateMap.get(el)!
|
|
124
|
-
elementMap.set(op, wrappedCallback)
|
|
126
|
+
updateMap.set(dedupe, wrappedCallback)
|
|
125
127
|
}
|
|
126
128
|
requestTick()
|
|
127
129
|
})
|
package/lib/signal.d.ts
CHANGED
|
@@ -2,18 +2,18 @@ import { type State } from "./state";
|
|
|
2
2
|
import { type Computed } from "./computed";
|
|
3
3
|
type Signal<T extends {}> = State<T> | Computed<T>;
|
|
4
4
|
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T | Promise<T>);
|
|
5
|
-
type
|
|
6
|
-
type OkCallback<T, U extends
|
|
7
|
-
[K in keyof U]:
|
|
5
|
+
type InferSignalType<T> = T extends Signal<infer U> ? U : never;
|
|
6
|
+
type OkCallback<T, U extends Signal<{}>[]> = (...values: {
|
|
7
|
+
[K in keyof U]: InferSignalType<U[K]>;
|
|
8
8
|
}) => T | Promise<T> | Error;
|
|
9
9
|
type NilCallback<T> = () => T | Promise<T> | Error;
|
|
10
10
|
type ErrCallback<T> = (...errors: Error[]) => T | Promise<T> | Error;
|
|
11
|
-
type ComputedCallbacks<T extends {}, U extends
|
|
11
|
+
type ComputedCallbacks<T extends {}, U extends Signal<{}>[]> = OkCallback<T, U> | {
|
|
12
12
|
ok: OkCallback<T, U>;
|
|
13
13
|
nil?: NilCallback<T>;
|
|
14
14
|
err?: ErrCallback<T>;
|
|
15
15
|
};
|
|
16
|
-
type EffectCallbacks<U extends
|
|
16
|
+
type EffectCallbacks<U extends Signal<{}>[]> = OkCallback<void, U> | {
|
|
17
17
|
ok: OkCallback<void, U>;
|
|
18
18
|
nil?: NilCallback<void>;
|
|
19
19
|
err?: ErrCallback<void>;
|
|
@@ -24,10 +24,18 @@ declare const UNSET: any;
|
|
|
24
24
|
* Check whether a value is a Signal or not
|
|
25
25
|
*
|
|
26
26
|
* @since 0.9.0
|
|
27
|
-
* @param {
|
|
27
|
+
* @param {unknown} value - value to check
|
|
28
28
|
* @returns {boolean} - true if value is a Signal, false otherwise
|
|
29
29
|
*/
|
|
30
30
|
declare const isSignal: <T extends {}>(value: any) => value is Signal<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if the provided value is a callback or callbacks object of { ok, nil?, err? } that may be used as input for toSignal() to derive a computed state
|
|
33
|
+
*
|
|
34
|
+
* @since 0.12.4
|
|
35
|
+
* @param {unknown} value - value to check
|
|
36
|
+
* @returns {boolean} - true if value is a callback or callbacks object, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
declare const isComputedCallbacks: <T extends {}>(value: unknown) => value is ComputedCallbacks<T, []>;
|
|
31
39
|
/**
|
|
32
40
|
* Convert a value to a Signal if it's not already a Signal
|
|
33
41
|
*
|
|
@@ -45,9 +53,9 @@ declare const toSignal: <T extends {}>(value: MaybeSignal<T> | ComputedCallbacks
|
|
|
45
53
|
* @param {Record<string, (...args) => CallbackReturnType<T>} cb - object of ok, nil, err callbacks or just ok callback
|
|
46
54
|
* @returns {CallbackReturnType<T>} - result of chosen callback
|
|
47
55
|
*/
|
|
48
|
-
declare const resolve: <T, U extends
|
|
56
|
+
declare const resolve: <T, U extends Signal<{}>[]>(maybeSignals: U, cb: OkCallback<T | Promise<T>, U> | {
|
|
49
57
|
ok: OkCallback<T | Promise<T>, U>;
|
|
50
58
|
nil?: NilCallback<T>;
|
|
51
59
|
err?: ErrCallback<T>;
|
|
52
60
|
}) => CallbackReturnType<T>;
|
|
53
|
-
export { type Signal, type MaybeSignal, type
|
|
61
|
+
export { type Signal, type MaybeSignal, type InferSignalType, type EffectCallbacks, type ComputedCallbacks, type CallbackReturnType, UNSET, isSignal, isComputedCallbacks, toSignal, resolve, };
|
package/lib/signal.ts
CHANGED
|
@@ -6,23 +6,21 @@ import { isFunction, toError } from "./util"
|
|
|
6
6
|
|
|
7
7
|
type Signal<T extends {}> = State<T> | Computed<T>
|
|
8
8
|
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T | Promise<T>)
|
|
9
|
-
type
|
|
10
|
-
T extends (() => infer U) ? U :
|
|
11
|
-
T
|
|
9
|
+
type InferSignalType<T> = T extends Signal<infer U> ? U : never
|
|
12
10
|
|
|
13
|
-
type OkCallback<T, U extends
|
|
14
|
-
[K in keyof U]:
|
|
11
|
+
type OkCallback<T, U extends Signal<{}>[]> = (...values: {
|
|
12
|
+
[K in keyof U]: InferSignalType<U[K]>
|
|
15
13
|
}) => T | Promise<T> | Error
|
|
16
14
|
type NilCallback<T> = () => T | Promise<T> | Error
|
|
17
15
|
type ErrCallback<T> = (...errors: Error[]) => T | Promise<T> | Error
|
|
18
16
|
|
|
19
|
-
type ComputedCallbacks<T extends {}, U extends
|
|
17
|
+
type ComputedCallbacks<T extends {}, U extends Signal<{}>[]> = OkCallback<T, U> | {
|
|
20
18
|
ok: OkCallback<T, U>,
|
|
21
19
|
nil?: NilCallback<T>,
|
|
22
20
|
err?: ErrCallback<T>
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
type EffectCallbacks<U extends
|
|
23
|
+
type EffectCallbacks<U extends Signal<{}>[]> = OkCallback<void, U> | {
|
|
26
24
|
ok: OkCallback<void, U>,
|
|
27
25
|
nil?: NilCallback<void>,
|
|
28
26
|
err?: ErrCallback<void>
|
|
@@ -34,24 +32,29 @@ type CallbackReturnType<T> = T | Promise<T> | Error | void
|
|
|
34
32
|
|
|
35
33
|
const UNSET: any = Symbol()
|
|
36
34
|
|
|
37
|
-
/* === Private Functions === */
|
|
38
|
-
|
|
39
|
-
const isComputedCallbacks = /*#__PURE__*/ <T extends {}>(value: unknown): value is ComputedCallbacks<T, []> =>
|
|
40
|
-
(isFunction(value) && !value.length)
|
|
41
|
-
|| (typeof value === 'object' && value !== null && 'ok' in value && isFunction(value.ok))
|
|
42
|
-
|
|
43
35
|
/* === Exported Functions === */
|
|
44
36
|
|
|
45
37
|
/**
|
|
46
38
|
* Check whether a value is a Signal or not
|
|
47
39
|
*
|
|
48
40
|
* @since 0.9.0
|
|
49
|
-
* @param {
|
|
41
|
+
* @param {unknown} value - value to check
|
|
50
42
|
* @returns {boolean} - true if value is a Signal, false otherwise
|
|
51
43
|
*/
|
|
52
44
|
const isSignal = /*#__PURE__*/ <T extends {}>(value: any): value is Signal<T> =>
|
|
53
45
|
isState(value) || isComputed(value)
|
|
54
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Check if the provided value is a callback or callbacks object of { ok, nil?, err? } that may be used as input for toSignal() to derive a computed state
|
|
49
|
+
*
|
|
50
|
+
* @since 0.12.4
|
|
51
|
+
* @param {unknown} value - value to check
|
|
52
|
+
* @returns {boolean} - true if value is a callback or callbacks object, false otherwise
|
|
53
|
+
*/
|
|
54
|
+
const isComputedCallbacks = /*#__PURE__*/ <T extends {}>(value: unknown): value is ComputedCallbacks<T, []> =>
|
|
55
|
+
(isFunction(value) && !value.length)
|
|
56
|
+
|| (typeof value === 'object' && value !== null && 'ok' in value && isFunction(value.ok))
|
|
57
|
+
|
|
55
58
|
/**
|
|
56
59
|
* Convert a value to a Signal if it's not already a Signal
|
|
57
60
|
*
|
|
@@ -76,7 +79,7 @@ const toSignal = /*#__PURE__*/ <T extends {}>(
|
|
|
76
79
|
* @param {Record<string, (...args) => CallbackReturnType<T>} cb - object of ok, nil, err callbacks or just ok callback
|
|
77
80
|
* @returns {CallbackReturnType<T>} - result of chosen callback
|
|
78
81
|
*/
|
|
79
|
-
const resolve = <T, U extends
|
|
82
|
+
const resolve = <T, U extends Signal<{}>[]>(
|
|
80
83
|
maybeSignals: U,
|
|
81
84
|
cb: OkCallback<T | Promise<T>, U> | {
|
|
82
85
|
ok: OkCallback<T | Promise<T>, U>
|
|
@@ -92,7 +95,7 @@ const resolve = <T, U extends MaybeSignal<{}>[]>(
|
|
|
92
95
|
err?: ErrCallback<T>
|
|
93
96
|
}
|
|
94
97
|
const values = [] as {
|
|
95
|
-
[K in keyof U]:
|
|
98
|
+
[K in keyof U]: InferSignalType<U[K]>
|
|
96
99
|
}
|
|
97
100
|
const errors: Error[] = []
|
|
98
101
|
let hasUnset = false
|
|
@@ -100,9 +103,9 @@ const resolve = <T, U extends MaybeSignal<{}>[]>(
|
|
|
100
103
|
for (let i = 0; i < maybeSignals.length; i++) {
|
|
101
104
|
const s = maybeSignals[i]
|
|
102
105
|
try {
|
|
103
|
-
const value =
|
|
106
|
+
const value = s.get()
|
|
104
107
|
if (value === UNSET) hasUnset = true
|
|
105
|
-
values[i] = value as
|
|
108
|
+
values[i] = value as InferSignalType<typeof s>
|
|
106
109
|
} catch (e) {
|
|
107
110
|
errors.push(toError(e))
|
|
108
111
|
}
|
|
@@ -121,7 +124,7 @@ const resolve = <T, U extends MaybeSignal<{}>[]>(
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
export {
|
|
124
|
-
type Signal, type MaybeSignal, type
|
|
127
|
+
type Signal, type MaybeSignal, type InferSignalType,
|
|
125
128
|
type EffectCallbacks, type ComputedCallbacks, type CallbackReturnType,
|
|
126
|
-
UNSET, isSignal, toSignal, resolve,
|
|
129
|
+
UNSET, isSignal, isComputedCallbacks, toSignal, resolve,
|
|
127
130
|
}
|