@zeix/cause-effect 0.12.0 → 0.12.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/README.md +5 -5
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/index.ts +8 -3
- package/lib/computed.d.ts +8 -18
- package/lib/computed.ts +33 -35
- package/lib/effect.d.ts +4 -13
- package/lib/effect.ts +8 -23
- package/lib/signal.d.ts +30 -15
- package/lib/signal.ts +67 -32
- package/lib/state.d.ts +6 -6
- package/lib/state.ts +16 -20
- package/lib/util.d.ts +1 -3
- package/lib/util.ts +3 -14
- package/package.json +4 -6
- package/test/computed.test.ts +2 -2
- package/test/state.test.ts +17 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cause & Effect
|
|
2
2
|
|
|
3
|
-
Version 0.12.
|
|
3
|
+
Version 0.12.1
|
|
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
|
|
|
@@ -108,14 +108,14 @@ document.querySelector('.increment').addEventListener('click', () => {
|
|
|
108
108
|
|
|
109
109
|
Async computed signals are as straight forward as their sync counterparts. Just create the computed signal with an async function.
|
|
110
110
|
|
|
111
|
-
**Caution**:
|
|
111
|
+
**Caution**: Async computed signals will return a Symbol `UNSET` until the Promise is resolved.
|
|
112
112
|
|
|
113
113
|
```js
|
|
114
|
-
import { state,
|
|
114
|
+
import { state, effect } from '@zeix/cause-effect'
|
|
115
115
|
|
|
116
116
|
const entryId = state(42)
|
|
117
|
-
const entryData =
|
|
118
|
-
const response = await fetch(`/api/entry/${
|
|
117
|
+
const entryData = entryId.map(async id => {
|
|
118
|
+
const response = await fetch(`/api/entry/${id}`)
|
|
119
119
|
if (!response.ok) return new Error(`Failed to fetch data: ${response.statusText}`)
|
|
120
120
|
return response.json()
|
|
121
121
|
})
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name Cause & Effect
|
|
3
|
-
* @version 0.12.
|
|
3
|
+
* @version 0.12.1
|
|
4
4
|
* @author Esther Brunner
|
|
5
5
|
*/
|
|
6
|
-
export { type Signal, type MaybeSignal, UNSET, isSignal, toSignal } from './lib/signal';
|
|
6
|
+
export { type Signal, type MaybeSignal, type InferMaybeSignalType, type ComputedCallbacks, type EffectCallbacks, UNSET, isSignal, 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
|
-
export {
|
|
9
|
+
export { effect } from './lib/effect';
|
|
10
10
|
export { type EnqueueDedupe, batch, watch, enqueue } from './lib/scheduler';
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var V=(x)=>typeof x==="function";var R=(x,B)=>Object.prototype.toString.call(x)===`[object ${B}]`,m=(x)=>(B)=>B instanceof x,j=m(Error),g=m(Promise),y=(x)=>j(x)?x:new Error(String(x));var F,D=new Set,w=0,N=new Map,M,p=()=>{M=void 0;for(let x of N.values()){for(let B of x.values())B();x.clear()}},d=()=>{if(M)cancelAnimationFrame(M);M=requestAnimationFrame(p)};queueMicrotask(p);var T=(x)=>{if(F&&!x.includes(F))x.push(F)},C=(x)=>{for(let B of x)w?D.add(B):B()},U=()=>{while(D.size){let x=Array.from(D);D.clear();for(let B of x)B()}},n=(x)=>{w++,x(),U(),w--},O=(x,B)=>{let z=F;F=B,x(),F=z},v=(x,B)=>new Promise((z,$)=>{let L=()=>{try{z(x())}catch(H){$(H)}};if(B){let[H,J]=B;if(!N.has(H))N.set(H,new Map);N.get(H).set(J,L)}d()});function Y(x,...B){let z=()=>O(()=>{let $=_(B,x);if(j($))console.error("Unhandled error in effect:",$)},z);z()}var o="Computed",i=(x,B)=>{if(!B)return!1;return x.name===B.name&&x.message===B.message},I=(x,...B)=>{let z=[],$=Q,L,H=!0,J=!1,X=!1,K=(G)=>{if(!Object.is(G,$))$=G,H=!1,L=void 0,J=!1},W=()=>{J=Q===$,$=Q,L=void 0},Z=(G)=>{let P=y(G);J=i(P,L),$=Q,L=P},A=()=>{if(H=!0,!J)C(z)},h=()=>O(()=>{if(X)throw new Error("Circular dependency detected");J=!0,X=!0;let G=_(B,x);if(g(G))W(),G.then((P)=>{K(P),C(z)}).catch(Z);else if(G==null||Q===G)W();else if(j(G))Z(G);else K(G);X=!1},A),q={[Symbol.toStringTag]:o,get:()=>{if(T(z),U(),H)h();if(L)throw L;return $},map:(G)=>I(G,q),match:(G)=>{return Y(G,q),q}};return q},S=(x)=>R(x,o);var b="State",E=(x)=>{let B=[],z=x,$={[Symbol.toStringTag]:b,get:()=>{return T(B),z},set:(L)=>{if(Object.is(z,L))return;if(z=L,C(B),Q===z)B.length=0},update:(L)=>{$.set(L(z))},map:(L)=>I(L,$),match:(L)=>{return Y(L,$),$}};return $},k=(x)=>R(x,b);var Q=Symbol(),c=(x)=>V(x)&&!x.length||typeof x==="object"&&x!==null&&("ok"in x)&&V(x.ok),f=(x)=>k(x)||S(x),t=(x)=>f(x)?x:c(x)?I(x):E(x),_=(x,B)=>{let{ok:z,nil:$,err:L}=V(B)?{ok:B}:B,H=[],J=[],X=!1;for(let W=0;W<x.length;W++){let Z=x[W];try{let A=f(Z)?Z.get():V(Z)?Z():Z;if(A===Q)X=!0;H[W]=A}catch(A){J.push(y(A))}}let K=void 0;try{if(X&&$)K=$();else if(J.length)K=L?L(...J):J[0];else if(!X)K=z(...H)}catch(W){if(K=y(W),L)K=L(K)}return K};export{O as watch,t as toSignal,E as state,k as isState,f as isSignal,S as isComputed,v as enqueue,Y as effect,I as computed,n as batch,Q as UNSET};
|
package/index.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @name Cause & Effect
|
|
3
|
-
* @version 0.12.
|
|
3
|
+
* @version 0.12.1
|
|
4
4
|
* @author Esther Brunner
|
|
5
5
|
*/
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
type Signal, type MaybeSignal, type InferMaybeSignalType,
|
|
8
|
+
type ComputedCallbacks, type EffectCallbacks,
|
|
9
|
+
UNSET, isSignal, toSignal
|
|
10
|
+
} from './lib/signal'
|
|
11
|
+
|
|
7
12
|
export { type State, state, isState } from './lib/state'
|
|
8
13
|
export { type Computed, computed, isComputed } from './lib/computed'
|
|
9
|
-
export {
|
|
14
|
+
export { effect } from './lib/effect'
|
|
10
15
|
export { type EnqueueDedupe, batch, watch, enqueue } from './lib/scheduler'
|
package/lib/computed.d.ts
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type EffectCallbacks } from './effect';
|
|
3
|
-
export type ComputedOkCallback<T extends {}, U extends UnknownSignal[]> = (...values: {
|
|
4
|
-
[K in keyof U]: SignalValue<U[K]>;
|
|
5
|
-
}) => T | Promise<T>;
|
|
6
|
-
export type ComputedCallbacks<T extends {}, U extends UnknownSignal[]> = {
|
|
7
|
-
ok: (...values: {
|
|
8
|
-
[K in keyof U]: SignalValue<U[K]>;
|
|
9
|
-
}) => T | Promise<T>;
|
|
10
|
-
nil?: () => T | Promise<T>;
|
|
11
|
-
err?: (...errors: Error[]) => T | Promise<T>;
|
|
12
|
-
};
|
|
1
|
+
import { type MaybeSignal, type EffectCallbacks, type ComputedCallbacks } from './signal';
|
|
13
2
|
export type Computed<T extends {}> = {
|
|
14
3
|
[Symbol.toStringTag]: 'Computed';
|
|
15
4
|
get: () => T;
|
|
16
|
-
map: <U extends {}>(
|
|
17
|
-
match: (
|
|
5
|
+
map: <U extends {}>(cb: ComputedCallbacks<U, [Computed<T>]>) => Computed<U>;
|
|
6
|
+
match: (cb: EffectCallbacks<[Computed<T>]>) => void;
|
|
18
7
|
};
|
|
19
8
|
/**
|
|
20
|
-
* Create a derived
|
|
9
|
+
* Create a derived signal from existing signals
|
|
21
10
|
*
|
|
22
11
|
* @since 0.9.0
|
|
23
|
-
* @param {() => T}
|
|
24
|
-
* @
|
|
12
|
+
* @param {() => T} cb - compute callback or object of ok, nil, err callbacks to derive state
|
|
13
|
+
* @param {U} maybeSignals - signals of functions using signals this values depends on
|
|
14
|
+
* @returns {Computed<T>} - Computed signal
|
|
25
15
|
*/
|
|
26
|
-
export declare const computed: <T extends {}, U extends
|
|
16
|
+
export declare const computed: <T extends {}, U extends MaybeSignal<{}>[]>(cb: ComputedCallbacks<T, U>, ...maybeSignals: U) => Computed<T>;
|
|
27
17
|
/**
|
|
28
18
|
* Check if a value is a computed state
|
|
29
19
|
*
|
package/lib/computed.ts
CHANGED
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type MaybeSignal, type EffectCallbacks, type ComputedCallbacks,
|
|
3
|
+
resolve, UNSET
|
|
4
|
+
} from './signal'
|
|
5
|
+
import { isError, isObjectOfType, isPromise, toError } from './util'
|
|
3
6
|
import { type Watcher, flush, notify, subscribe, watch } from './scheduler'
|
|
4
|
-
import {
|
|
7
|
+
import { effect } from './effect'
|
|
5
8
|
|
|
6
9
|
/* === Types === */
|
|
7
10
|
|
|
8
|
-
export type ComputedOkCallback<T extends {}, U extends UnknownSignal[]> = (
|
|
9
|
-
...values: { [K in keyof U]: SignalValue<U[K]> }
|
|
10
|
-
) => T | Promise<T>
|
|
11
|
-
|
|
12
|
-
export type ComputedCallbacks<T extends {}, U extends UnknownSignal[]> = {
|
|
13
|
-
ok: (...values: { [K in keyof U]: SignalValue<U[K]> }) => T | Promise<T>
|
|
14
|
-
nil?: () => T | Promise<T>
|
|
15
|
-
err?: (...errors: Error[]) => T | Promise<T>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
11
|
export type Computed<T extends {}> = {
|
|
19
12
|
[Symbol.toStringTag]: 'Computed'
|
|
20
13
|
get: () => T
|
|
21
|
-
map: <U extends {}>(
|
|
22
|
-
match: (
|
|
14
|
+
map: <U extends {}>(cb: ComputedCallbacks<U, [Computed<T>]>) => Computed<U>
|
|
15
|
+
match: (cb: EffectCallbacks<[Computed<T>]>) => void
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
/* === Constants === */
|
|
26
19
|
|
|
27
20
|
const TYPE_COMPUTED = 'Computed'
|
|
28
21
|
|
|
22
|
+
/* === Private Functions === */
|
|
23
|
+
|
|
24
|
+
const isEquivalentError = /*#__PURE__*/ (
|
|
25
|
+
error1: Error,
|
|
26
|
+
error2: Error | undefined
|
|
27
|
+
): boolean => {
|
|
28
|
+
if (!error2) return false
|
|
29
|
+
return error1.name === error2.name && error1.message === error2.message
|
|
30
|
+
}
|
|
31
|
+
|
|
29
32
|
/* === Computed Factory === */
|
|
30
33
|
|
|
31
34
|
/**
|
|
32
|
-
* Create a derived
|
|
35
|
+
* Create a derived signal from existing signals
|
|
33
36
|
*
|
|
34
37
|
* @since 0.9.0
|
|
35
|
-
* @param {() => T}
|
|
36
|
-
* @
|
|
38
|
+
* @param {() => T} cb - compute callback or object of ok, nil, err callbacks to derive state
|
|
39
|
+
* @param {U} maybeSignals - signals of functions using signals this values depends on
|
|
40
|
+
* @returns {Computed<T>} - Computed signal
|
|
37
41
|
*/
|
|
38
|
-
export const computed = <T extends {}, U extends
|
|
39
|
-
|
|
40
|
-
...
|
|
42
|
+
export const computed = <T extends {}, U extends MaybeSignal<{}>[]>(
|
|
43
|
+
cb: ComputedCallbacks<T, U>,
|
|
44
|
+
...maybeSignals: U
|
|
41
45
|
): Computed<T> => {
|
|
42
|
-
const callbacks = isFunction(callbacksOrFn)
|
|
43
|
-
? { ok: callbacksOrFn }
|
|
44
|
-
: callbacksOrFn
|
|
45
46
|
const watchers: Watcher[] = []
|
|
46
47
|
let value: T = UNSET
|
|
47
48
|
let error: Error | undefined
|
|
@@ -81,7 +82,7 @@ export const computed = <T extends {}, U extends UnknownSignal[]>(
|
|
|
81
82
|
if (computing) throw new Error('Circular dependency detected')
|
|
82
83
|
unchanged = true
|
|
83
84
|
computing = true
|
|
84
|
-
const result =
|
|
85
|
+
const result = resolve(maybeSignals, cb)
|
|
85
86
|
if (isPromise(result)) {
|
|
86
87
|
nil() // sync
|
|
87
88
|
result.then(v => {
|
|
@@ -101,7 +102,6 @@ export const computed = <T extends {}, U extends UnknownSignal[]>(
|
|
|
101
102
|
* Get the current value of the computed
|
|
102
103
|
*
|
|
103
104
|
* @since 0.9.0
|
|
104
|
-
* @method of Computed<T>
|
|
105
105
|
* @returns {T} - current value of the computed
|
|
106
106
|
*/
|
|
107
107
|
get: (): T => {
|
|
@@ -116,23 +116,21 @@ export const computed = <T extends {}, U extends UnknownSignal[]>(
|
|
|
116
116
|
* Create a computed signal from the current computed signal
|
|
117
117
|
*
|
|
118
118
|
* @since 0.9.0
|
|
119
|
-
* @
|
|
120
|
-
* @
|
|
121
|
-
* @returns {Computed<R>} - computed signal
|
|
119
|
+
* @param {ComputedCallbacks<U, [Computed<T>]>} cb - compute callback or object of ok, nil, err callbacks to map this value to new computed
|
|
120
|
+
* @returns {Computed<U>} - computed signal
|
|
122
121
|
*/
|
|
123
|
-
map: <
|
|
124
|
-
computed(
|
|
122
|
+
map: <U extends {}>(cb: ComputedCallbacks<U, [Computed<T>]>): Computed<U> =>
|
|
123
|
+
computed(cb, c),
|
|
125
124
|
|
|
126
125
|
/**
|
|
127
126
|
* Case matching for the computed signal with effect callbacks
|
|
128
127
|
*
|
|
129
128
|
* @since 0.12.0
|
|
130
|
-
* @
|
|
131
|
-
* @param {EffectCallbacks[<T>]} callbacks
|
|
129
|
+
* @param {EffectCallbacks[Computed<T>]} cb - effect callback or object of ok, nil, err callbacks to be executed when the computed changes
|
|
132
130
|
* @returns {Computed<T>} - self, for chaining effect callbacks
|
|
133
131
|
*/
|
|
134
|
-
match: (
|
|
135
|
-
effect(
|
|
132
|
+
match: (cb: EffectCallbacks<[Computed<T>]>): Computed<T> => {
|
|
133
|
+
effect(cb, c)
|
|
136
134
|
return c
|
|
137
135
|
}
|
|
138
136
|
}
|
package/lib/effect.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export type EffectOkCallback<T extends UnknownSignal[]> = (...values: {
|
|
3
|
-
[K in keyof T]: SignalValue<T[K]>;
|
|
4
|
-
}) => void;
|
|
5
|
-
export type EffectCallbacks<T extends UnknownSignal[]> = {
|
|
6
|
-
ok: (...values: {
|
|
7
|
-
[K in keyof T]: SignalValue<T[K]>;
|
|
8
|
-
}) => void;
|
|
9
|
-
nil?: () => void;
|
|
10
|
-
err?: (...errors: Error[]) => void;
|
|
11
|
-
};
|
|
1
|
+
import { type EffectCallbacks, type MaybeSignal } from './signal';
|
|
12
2
|
/**
|
|
13
3
|
* Define what happens when a reactive state changes
|
|
14
4
|
*
|
|
15
5
|
* @since 0.1.0
|
|
16
|
-
* @param {() => void}
|
|
6
|
+
* @param {() => void} cb - effect callback or object of ok, nil, err callbacks to be executed when a state changes
|
|
7
|
+
* @param {U} maybeSignals - signals of functions using signals that should trigger the effect
|
|
17
8
|
*/
|
|
18
|
-
export declare function effect<
|
|
9
|
+
export declare function effect<U extends MaybeSignal<{}>[]>(cb: EffectCallbacks<U>, ...maybeSignals: U): void;
|
package/lib/effect.ts
CHANGED
|
@@ -1,38 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
import { isError
|
|
2
|
+
import { type EffectCallbacks, type MaybeSignal, resolve } from './signal'
|
|
3
|
+
import { isError } from './util'
|
|
4
4
|
import { watch } from './scheduler'
|
|
5
5
|
|
|
6
|
-
/* === Types === */
|
|
7
|
-
|
|
8
|
-
export type EffectOkCallback<T extends UnknownSignal[]> = (
|
|
9
|
-
...values: { [K in keyof T]: SignalValue<T[K]> }
|
|
10
|
-
) => void
|
|
11
|
-
|
|
12
|
-
export type EffectCallbacks<T extends UnknownSignal[]> = {
|
|
13
|
-
ok: (...values: { [K in keyof T]: SignalValue<T[K]> }) => void
|
|
14
|
-
nil?: () => void
|
|
15
|
-
err?: (...errors: Error[]) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
6
|
/* === Exported Function === */
|
|
19
7
|
|
|
20
8
|
/**
|
|
21
9
|
* Define what happens when a reactive state changes
|
|
22
10
|
*
|
|
23
11
|
* @since 0.1.0
|
|
24
|
-
* @param {() => void}
|
|
12
|
+
* @param {() => void} cb - effect callback or object of ok, nil, err callbacks to be executed when a state changes
|
|
13
|
+
* @param {U} maybeSignals - signals of functions using signals that should trigger the effect
|
|
25
14
|
*/
|
|
26
|
-
export function effect<
|
|
27
|
-
|
|
28
|
-
...
|
|
15
|
+
export function effect<U extends MaybeSignal<{}>[]>(
|
|
16
|
+
cb: EffectCallbacks<U>,
|
|
17
|
+
...maybeSignals: U
|
|
29
18
|
): void {
|
|
30
|
-
const callbacks = isFunction(callbacksOrFn)
|
|
31
|
-
? { ok: callbacksOrFn }
|
|
32
|
-
: callbacksOrFn
|
|
33
|
-
|
|
34
19
|
const run = () => watch(() => {
|
|
35
|
-
const result =
|
|
20
|
+
const result = resolve(maybeSignals, cb)
|
|
36
21
|
if (isError(result))
|
|
37
22
|
console.error('Unhandled error in effect:', result)
|
|
38
23
|
}, run)
|
package/lib/signal.d.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { type State } from "./state";
|
|
2
2
|
import { type Computed } from "./computed";
|
|
3
3
|
type Signal<T extends {}> = State<T> | Computed<T>;
|
|
4
|
-
type
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
|
|
4
|
+
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T | Promise<T>);
|
|
5
|
+
type InferMaybeSignalType<T> = T extends Signal<infer U> ? U : T extends (() => infer U) ? U : T;
|
|
6
|
+
type OkCallback<T, U extends MaybeSignal<{}>[]> = (...values: {
|
|
7
|
+
[K in keyof U]: InferMaybeSignalType<U[K]>;
|
|
8
|
+
}) => T | Promise<T> | Error;
|
|
9
|
+
type NilCallback<T> = () => T | Promise<T> | Error;
|
|
10
|
+
type ErrCallback<T> = (...errors: Error[]) => T | Promise<T> | Error;
|
|
11
|
+
type ComputedCallbacks<T extends {}, U extends MaybeSignal<{}>[]> = OkCallback<T, U> | {
|
|
12
|
+
ok: OkCallback<T, U>;
|
|
13
|
+
nil?: NilCallback<T>;
|
|
14
|
+
err?: ErrCallback<T>;
|
|
15
|
+
};
|
|
16
|
+
type EffectCallbacks<U extends MaybeSignal<{}>[]> = OkCallback<void, U> | {
|
|
17
|
+
ok: OkCallback<void, U>;
|
|
18
|
+
nil?: NilCallback<void>;
|
|
19
|
+
err?: ErrCallback<void>;
|
|
20
|
+
};
|
|
21
|
+
type CallbackReturnType<T> = T | Promise<T> | Error | void;
|
|
22
|
+
declare const UNSET: any;
|
|
8
23
|
/**
|
|
9
24
|
* Check whether a value is a Signal or not
|
|
10
25
|
*
|
|
@@ -21,18 +36,18 @@ declare const isSignal: <T extends {}>(value: any) => value is Signal<T>;
|
|
|
21
36
|
* @param memo
|
|
22
37
|
* @returns {Signal<T>} - converted Signal
|
|
23
38
|
*/
|
|
24
|
-
declare const toSignal: <T extends {}>(value: MaybeSignal<T>) => Signal<T>;
|
|
39
|
+
declare const toSignal: <T extends {}>(value: MaybeSignal<T> | ComputedCallbacks<T, []>) => Signal<T>;
|
|
25
40
|
/**
|
|
26
|
-
* Resolve signals and apply callbacks based on the results
|
|
41
|
+
* Resolve signals or functions using signals and apply callbacks based on the results
|
|
27
42
|
*
|
|
28
43
|
* @since 0.12.0
|
|
29
|
-
* @param {U}
|
|
30
|
-
* @param {Record<string, (...args) =>
|
|
31
|
-
* @returns {
|
|
44
|
+
* @param {U} maybeSignals - dependency signals (or functions using signals)
|
|
45
|
+
* @param {Record<string, (...args) => CallbackReturnType<T>} cb - object of ok, nil, err callbacks or just ok callback
|
|
46
|
+
* @returns {CallbackReturnType<T>} - result of chosen callback
|
|
32
47
|
*/
|
|
33
|
-
declare const
|
|
34
|
-
ok:
|
|
35
|
-
nil?:
|
|
36
|
-
err?:
|
|
37
|
-
}) =>
|
|
38
|
-
export { type Signal, type
|
|
48
|
+
declare const resolve: <T, U extends MaybeSignal<{}>[]>(maybeSignals: U, cb: OkCallback<T | Promise<T>, U> | {
|
|
49
|
+
ok: OkCallback<T | Promise<T>, U>;
|
|
50
|
+
nil?: NilCallback<T>;
|
|
51
|
+
err?: ErrCallback<T>;
|
|
52
|
+
}) => CallbackReturnType<T>;
|
|
53
|
+
export { type Signal, type MaybeSignal, type InferMaybeSignalType, type EffectCallbacks, type ComputedCallbacks, type CallbackReturnType, UNSET, isSignal, toSignal, resolve, };
|
package/lib/signal.ts
CHANGED
|
@@ -1,18 +1,44 @@
|
|
|
1
1
|
import { type State, isState, state } from "./state"
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { type Computed, computed, isComputed } from "./computed"
|
|
3
|
+
import { isFunction, toError } from "./util"
|
|
4
4
|
|
|
5
5
|
/* === Types === */
|
|
6
6
|
|
|
7
7
|
type Signal<T extends {}> = State<T> | Computed<T>
|
|
8
|
-
type
|
|
9
|
-
type
|
|
8
|
+
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T | Promise<T>)
|
|
9
|
+
type InferMaybeSignalType<T> = T extends Signal<infer U> ? U :
|
|
10
|
+
T extends (() => infer U) ? U :
|
|
11
|
+
T
|
|
10
12
|
|
|
11
|
-
type
|
|
13
|
+
type OkCallback<T, U extends MaybeSignal<{}>[]> = (...values: {
|
|
14
|
+
[K in keyof U]: InferMaybeSignalType<U[K]>
|
|
15
|
+
}) => T | Promise<T> | Error
|
|
16
|
+
type NilCallback<T> = () => T | Promise<T> | Error
|
|
17
|
+
type ErrCallback<T> = (...errors: Error[]) => T | Promise<T> | Error
|
|
18
|
+
|
|
19
|
+
type ComputedCallbacks<T extends {}, U extends MaybeSignal<{}>[]> = OkCallback<T, U> | {
|
|
20
|
+
ok: OkCallback<T, U>,
|
|
21
|
+
nil?: NilCallback<T>,
|
|
22
|
+
err?: ErrCallback<T>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type EffectCallbacks<U extends MaybeSignal<{}>[]> = OkCallback<void, U> | {
|
|
26
|
+
ok: OkCallback<void, U>,
|
|
27
|
+
nil?: NilCallback<void>,
|
|
28
|
+
err?: ErrCallback<void>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type CallbackReturnType<T> = T | Promise<T> | Error | void
|
|
12
32
|
|
|
13
33
|
/* === Constants === */
|
|
14
34
|
|
|
15
|
-
|
|
35
|
+
const UNSET: any = Symbol()
|
|
36
|
+
|
|
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))
|
|
16
42
|
|
|
17
43
|
/* === Exported Functions === */
|
|
18
44
|
|
|
@@ -35,58 +61,67 @@ const isSignal = /*#__PURE__*/ <T extends {}>(value: any): value is Signal<T> =>
|
|
|
35
61
|
* @returns {Signal<T>} - converted Signal
|
|
36
62
|
*/
|
|
37
63
|
const toSignal = /*#__PURE__*/ <T extends {}>(
|
|
38
|
-
value: MaybeSignal<T>
|
|
64
|
+
value: MaybeSignal<T> | ComputedCallbacks<T, []>
|
|
39
65
|
): Signal<T> =>
|
|
40
66
|
isSignal<T>(value) ? value
|
|
41
|
-
:
|
|
42
|
-
: state(value)
|
|
67
|
+
: isComputedCallbacks<T>(value) ? computed(value)
|
|
68
|
+
: state(value as T)
|
|
43
69
|
|
|
44
70
|
|
|
45
71
|
/**
|
|
46
|
-
* Resolve signals and apply callbacks based on the results
|
|
72
|
+
* Resolve signals or functions using signals and apply callbacks based on the results
|
|
47
73
|
*
|
|
48
74
|
* @since 0.12.0
|
|
49
|
-
* @param {U}
|
|
50
|
-
* @param {Record<string, (...args) =>
|
|
51
|
-
* @returns {
|
|
75
|
+
* @param {U} maybeSignals - dependency signals (or functions using signals)
|
|
76
|
+
* @param {Record<string, (...args) => CallbackReturnType<T>} cb - object of ok, nil, err callbacks or just ok callback
|
|
77
|
+
* @returns {CallbackReturnType<T>} - result of chosen callback
|
|
52
78
|
*/
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ok:
|
|
57
|
-
nil?:
|
|
58
|
-
err?:
|
|
79
|
+
const resolve = <T, U extends MaybeSignal<{}>[]>(
|
|
80
|
+
maybeSignals: U,
|
|
81
|
+
cb: OkCallback<T | Promise<T>, U> | {
|
|
82
|
+
ok: OkCallback<T | Promise<T>, U>
|
|
83
|
+
nil?: NilCallback<T>
|
|
84
|
+
err?: ErrCallback<T>
|
|
85
|
+
}
|
|
86
|
+
): CallbackReturnType<T> => {
|
|
87
|
+
const { ok, nil, err } = isFunction(cb)
|
|
88
|
+
? { ok: cb }
|
|
89
|
+
: cb as {
|
|
90
|
+
ok: OkCallback<T | Promise<T>, U>
|
|
91
|
+
nil?: NilCallback<T>
|
|
92
|
+
err?: ErrCallback<T>
|
|
93
|
+
}
|
|
94
|
+
const values = [] as {
|
|
95
|
+
[K in keyof U]: InferMaybeSignalType<U[K]>
|
|
59
96
|
}
|
|
60
|
-
): T | Promise<T> | Error | void => {
|
|
61
|
-
const { ok, nil, err } = callbacks
|
|
62
|
-
const values = [] as { [K in keyof U]: SignalValue<U[K]> }
|
|
63
97
|
const errors: Error[] = []
|
|
64
98
|
let hasUnset = false
|
|
65
99
|
|
|
66
|
-
for (
|
|
100
|
+
for (let i = 0; i < maybeSignals.length; i++) {
|
|
101
|
+
const s = maybeSignals[i]
|
|
67
102
|
try {
|
|
68
|
-
const value =
|
|
103
|
+
const value = isSignal(s) ? s.get() : isFunction(s) ? s() : s
|
|
69
104
|
if (value === UNSET) hasUnset = true
|
|
70
|
-
values
|
|
105
|
+
values[i] = value as InferMaybeSignalType<typeof s>
|
|
71
106
|
} catch (e) {
|
|
72
107
|
errors.push(toError(e))
|
|
73
108
|
}
|
|
74
109
|
}
|
|
75
110
|
|
|
76
|
-
let result:
|
|
111
|
+
let result: CallbackReturnType<T> = undefined
|
|
77
112
|
try {
|
|
78
113
|
if (hasUnset && nil) result = nil()
|
|
79
114
|
else if (errors.length) result = err ? err(...errors) : errors[0]
|
|
80
|
-
else if (!hasUnset) result = ok(...values)
|
|
115
|
+
else if (!hasUnset) result = ok(...values) as CallbackReturnType<T>
|
|
81
116
|
} catch (e) {
|
|
82
117
|
result = toError(e)
|
|
83
118
|
if (err) result = err(result)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
}
|
|
119
|
+
}
|
|
120
|
+
return result
|
|
87
121
|
}
|
|
88
122
|
|
|
89
123
|
export {
|
|
90
|
-
type Signal, type
|
|
91
|
-
|
|
124
|
+
type Signal, type MaybeSignal, type InferMaybeSignalType,
|
|
125
|
+
type EffectCallbacks, type ComputedCallbacks, type CallbackReturnType,
|
|
126
|
+
UNSET, isSignal, toSignal, resolve,
|
|
92
127
|
}
|
package/lib/state.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { type ComputedCallbacks, type EffectCallbacks } from './signal';
|
|
1
2
|
import { type Computed } from './computed';
|
|
2
|
-
import { type EffectCallbacks } from './effect';
|
|
3
3
|
export type State<T extends {}> = {
|
|
4
4
|
[Symbol.toStringTag]: 'State';
|
|
5
5
|
get(): T;
|
|
6
|
-
set(
|
|
7
|
-
update(fn: (
|
|
8
|
-
map<U extends {}>(
|
|
9
|
-
match: (
|
|
6
|
+
set(v: T): void;
|
|
7
|
+
update(fn: (v: T) => T): void;
|
|
8
|
+
map<U extends {}>(cb: ComputedCallbacks<U, [State<T>]>): Computed<U>;
|
|
9
|
+
match: (cb: EffectCallbacks<[State<T>]>) => void;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* Create a new state signal
|
|
@@ -15,7 +15,7 @@ export type State<T extends {}> = {
|
|
|
15
15
|
* @param {T} initialValue - initial value of the state
|
|
16
16
|
* @returns {State<T>} - new state signal
|
|
17
17
|
*/
|
|
18
|
-
export declare const state: <T extends {}>(
|
|
18
|
+
export declare const state: <T extends {}>(initialValue: T) => State<T>;
|
|
19
19
|
/**
|
|
20
20
|
* Check if the provided value is a State instance
|
|
21
21
|
*
|
package/lib/state.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { UNSET } from './signal'
|
|
1
|
+
import { UNSET, type ComputedCallbacks, type EffectCallbacks } from './signal'
|
|
2
2
|
import { type Computed, computed } from './computed'
|
|
3
3
|
import { isObjectOfType } from './util';
|
|
4
4
|
import { type Watcher, notify, subscribe } from './scheduler'
|
|
5
|
-
import {
|
|
5
|
+
import { effect } from './effect';
|
|
6
6
|
|
|
7
7
|
/* === Types === */
|
|
8
8
|
|
|
9
9
|
export type State<T extends {}> = {
|
|
10
10
|
[Symbol.toStringTag]: 'State';
|
|
11
11
|
get(): T;
|
|
12
|
-
set(
|
|
13
|
-
update(fn: (
|
|
14
|
-
map<U extends {}>(
|
|
15
|
-
match: (
|
|
12
|
+
set(v: T): void;
|
|
13
|
+
update(fn: (v: T) => T): void;
|
|
14
|
+
map<U extends {}>(cb: ComputedCallbacks<U, [State<T>]>): Computed<U>;
|
|
15
|
+
match: (cb: EffectCallbacks<[State<T>]>) => void
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/* === Constants === */
|
|
@@ -28,9 +28,9 @@ const TYPE_STATE = 'State'
|
|
|
28
28
|
* @param {T} initialValue - initial value of the state
|
|
29
29
|
* @returns {State<T>} - new state signal
|
|
30
30
|
*/
|
|
31
|
-
export const state = /*#__PURE__*/ <T extends {}>(
|
|
31
|
+
export const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
|
|
32
32
|
const watchers: Watcher[] = []
|
|
33
|
-
let value: T =
|
|
33
|
+
let value: T = initialValue
|
|
34
34
|
|
|
35
35
|
const s: State<T> = {
|
|
36
36
|
[Symbol.toStringTag]: TYPE_STATE,
|
|
@@ -39,7 +39,6 @@ export const state = /*#__PURE__*/ <T extends {}>(v: T): State<T> => {
|
|
|
39
39
|
* Get the current value of the state
|
|
40
40
|
*
|
|
41
41
|
* @since 0.9.0
|
|
42
|
-
* @method of State<T>
|
|
43
42
|
* @returns {T} - current value of the state
|
|
44
43
|
*/
|
|
45
44
|
get: (): T => {
|
|
@@ -51,7 +50,6 @@ export const state = /*#__PURE__*/ <T extends {}>(v: T): State<T> => {
|
|
|
51
50
|
* Set a new value of the state
|
|
52
51
|
*
|
|
53
52
|
* @since 0.9.0
|
|
54
|
-
* @method of State<T>
|
|
55
53
|
* @param {T} v
|
|
56
54
|
* @returns {void}
|
|
57
55
|
*/
|
|
@@ -68,8 +66,7 @@ export const state = /*#__PURE__*/ <T extends {}>(v: T): State<T> => {
|
|
|
68
66
|
* Update the state with a new value using a function
|
|
69
67
|
*
|
|
70
68
|
* @since 0.10.0
|
|
71
|
-
* @
|
|
72
|
-
* @param {(v: T) => T} fn
|
|
69
|
+
* @param {(v: T) => T} fn - function to update the state
|
|
73
70
|
* @returns {void} - updates the state with the result of the function
|
|
74
71
|
*/
|
|
75
72
|
update: (fn: (v: T) => T): void => {
|
|
@@ -80,23 +77,22 @@ export const state = /*#__PURE__*/ <T extends {}>(v: T): State<T> => {
|
|
|
80
77
|
* Create a computed signal from the current state signal
|
|
81
78
|
*
|
|
82
79
|
* @since 0.9.0
|
|
83
|
-
* @
|
|
84
|
-
* @
|
|
85
|
-
* @returns {Computed<R>} - computed signal
|
|
80
|
+
* @param {ComputedCallbacks<U, [State<T>]>} cb - compute callback or object of ok, nil, err callbacks to map this value to new computed
|
|
81
|
+
* @returns {Computed<U>} - computed signal
|
|
86
82
|
*/
|
|
87
|
-
map: <
|
|
88
|
-
computed(
|
|
83
|
+
map: <U extends {}>(cb: ComputedCallbacks<U, [State<T>]>): Computed<U> =>
|
|
84
|
+
computed(cb, s),
|
|
89
85
|
|
|
90
86
|
/**
|
|
91
87
|
* Case matching for the state signal with effect callbacks
|
|
92
88
|
*
|
|
93
89
|
* @since 0.12.0
|
|
94
90
|
* @method of State<T>
|
|
95
|
-
* @param {EffectCallbacks[<T>]} callbacks
|
|
91
|
+
* @param {EffectCallbacks<[State<T>]>} cb - effect callback or object of ok, nil, err callbacks to be executed when the state changes
|
|
96
92
|
* @returns {State<T>} - self, for chaining effect callbacks
|
|
97
93
|
*/
|
|
98
|
-
match: (
|
|
99
|
-
effect(
|
|
94
|
+
match: (cb: EffectCallbacks<[State<T>]>): State<T> => {
|
|
95
|
+
effect(cb, s)
|
|
100
96
|
return s
|
|
101
97
|
}
|
|
102
98
|
}
|
package/lib/util.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
declare const isFunction: <T>(value: unknown) => value is (...args: unknown[]) => T;
|
|
2
2
|
declare const isAsyncFunction: <T>(value: unknown) => value is (...args: unknown[]) => Promise<T> | PromiseLike<T>;
|
|
3
|
-
declare const isComputeFunction: <T>(value: unknown) => value is ((old?: T) => T);
|
|
4
3
|
declare const isObjectOfType: <T>(value: unknown, type: string) => value is T;
|
|
5
4
|
declare const isInstanceOf: <T>(type: new (...args: any[]) => T) => (value: unknown) => value is T;
|
|
6
5
|
declare const isError: (value: unknown) => value is Error;
|
|
7
6
|
declare const isPromise: (value: unknown) => value is Promise<unknown>;
|
|
8
7
|
declare const toError: (value: unknown) => Error;
|
|
9
|
-
|
|
10
|
-
export { isFunction, isAsyncFunction, isComputeFunction, isObjectOfType, isInstanceOf, isError, isPromise, toError, isEquivalentError };
|
|
8
|
+
export { isFunction, isAsyncFunction, isObjectOfType, isInstanceOf, isError, isPromise, toError };
|
package/lib/util.ts
CHANGED
|
@@ -6,10 +6,7 @@ const isFunction = /*#__PURE__*/ <T>(value: unknown): value is (...args: unknown
|
|
|
6
6
|
const isAsyncFunction = /*#__PURE__*/ <T>(value: unknown): value is (...args: unknown[]) => Promise<T> | PromiseLike<T> =>
|
|
7
7
|
isFunction(value) && /^async\s+/.test(value.toString())
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
isFunction(value) && value.length < 2
|
|
11
|
-
|
|
12
|
-
const isObjectOfType = <T>(value: unknown, type: string): value is T =>
|
|
9
|
+
const isObjectOfType = /*#__PURE__*/ <T>(value: unknown, type: string): value is T =>
|
|
13
10
|
Object.prototype.toString.call(value) === `[object ${type}]`
|
|
14
11
|
|
|
15
12
|
const isInstanceOf = /*#__PURE__*/ <T>(type: new (...args: any[]) => T) =>
|
|
@@ -22,15 +19,7 @@ const isPromise = /*#__PURE__*/ isInstanceOf(Promise)
|
|
|
22
19
|
const toError = (value: unknown): Error =>
|
|
23
20
|
isError(value) ? value : new Error(String(value))
|
|
24
21
|
|
|
25
|
-
const isEquivalentError = /*#__PURE__*/ (
|
|
26
|
-
error1: Error,
|
|
27
|
-
error2: Error | undefined
|
|
28
|
-
): boolean => {
|
|
29
|
-
if (!error2) return false
|
|
30
|
-
return error1.name === error2.name && error1.message === error2.message
|
|
31
|
-
}
|
|
32
|
-
|
|
33
22
|
export {
|
|
34
|
-
isFunction, isAsyncFunction,
|
|
35
|
-
isObjectOfType, isInstanceOf, isError, isPromise, toError
|
|
23
|
+
isFunction, isAsyncFunction,
|
|
24
|
+
isObjectOfType, isInstanceOf, isError, isPromise, toError
|
|
36
25
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeix/cause-effect",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"author": "Esther Brunner",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.ts",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"@types/bun": "latest"
|
|
8
|
+
"@types/bun": "latest",
|
|
9
|
+
"random": "^5.3.0"
|
|
9
10
|
},
|
|
10
11
|
"peerDependencies": {
|
|
11
12
|
"typescript": "^5.6.3"
|
|
@@ -26,8 +27,5 @@
|
|
|
26
27
|
"test": "bun test"
|
|
27
28
|
},
|
|
28
29
|
"type": "module",
|
|
29
|
-
"types": "index.d.ts"
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"random": "^5.3.0"
|
|
32
|
-
}
|
|
30
|
+
"types": "index.d.ts"
|
|
33
31
|
}
|
package/test/computed.test.ts
CHANGED
|
@@ -308,7 +308,7 @@ describe('Computed', function () {
|
|
|
308
308
|
return 0
|
|
309
309
|
},
|
|
310
310
|
err: (_e) => { // error path
|
|
311
|
-
console.error('Error:', _e);
|
|
311
|
+
// console.error('Error:', _e);
|
|
312
312
|
errCount++;
|
|
313
313
|
return -1
|
|
314
314
|
},
|
|
@@ -318,7 +318,7 @@ describe('Computed', function () {
|
|
|
318
318
|
toggleState.set(!!(i % 2));
|
|
319
319
|
await wait(10);
|
|
320
320
|
result = complexComputed.get();
|
|
321
|
-
console.log(`i: ${i}, result: ${result}`);
|
|
321
|
+
// console.log(`i: ${i}, result: ${result}`);
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
expect(nilCount).toBeGreaterThanOrEqual(4);
|
package/test/state.test.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test'
|
|
2
2
|
import { state, isComputed, UNSET } from '../'
|
|
3
3
|
|
|
4
|
+
/* === Utility Functions === */
|
|
5
|
+
|
|
6
|
+
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
7
|
+
|
|
4
8
|
/* === Tests === */
|
|
5
9
|
|
|
6
10
|
describe('State', function () {
|
|
@@ -166,6 +170,18 @@ describe('State', function () {
|
|
|
166
170
|
expect(double.get()).toBe(84);
|
|
167
171
|
});
|
|
168
172
|
|
|
173
|
+
test('should return a computed signal for an async function', async function() {
|
|
174
|
+
const cause = state(42);
|
|
175
|
+
const asyncDouble = cause.map(async v => {
|
|
176
|
+
await wait(100);
|
|
177
|
+
return v * 2;
|
|
178
|
+
});
|
|
179
|
+
expect(isComputed(asyncDouble)).toBe(true);
|
|
180
|
+
expect(asyncDouble.get()).toBe(UNSET);
|
|
181
|
+
await wait(110);
|
|
182
|
+
expect(asyncDouble.get()).toBe(84);
|
|
183
|
+
});
|
|
184
|
+
|
|
169
185
|
});
|
|
170
186
|
|
|
171
187
|
describe('Match method', function () {
|
|
@@ -174,7 +190,7 @@ describe('State', function () {
|
|
|
174
190
|
const cause = state(42);
|
|
175
191
|
let okCount = 0;
|
|
176
192
|
let nilCount = 0;
|
|
177
|
-
let result
|
|
193
|
+
let result = 0;
|
|
178
194
|
cause.match({
|
|
179
195
|
ok: v => {
|
|
180
196
|
result = v;
|