@zeix/cause-effect 0.10.1 → 0.12.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 +133 -39
- package/index.d.ts +5 -4
- package/index.js +1 -1
- package/index.ts +5 -4
- package/lib/computed.d.ts +18 -5
- package/lib/computed.ts +111 -43
- package/lib/effect.d.ts +13 -2
- package/lib/effect.ts +28 -9
- package/lib/scheduler.d.ts +40 -0
- package/lib/scheduler.ts +127 -0
- package/lib/signal.d.ts +16 -26
- package/lib/signal.ts +49 -55
- package/lib/state.d.ts +8 -46
- package/lib/state.ts +89 -69
- package/lib/util.d.ts +4 -1
- package/lib/util.ts +18 -1
- package/package.json +5 -2
- package/test/batch.test.ts +99 -0
- package/test/benchmark.test.ts +127 -52
- package/test/computed.test.ts +329 -0
- package/test/effect.test.ts +157 -0
- package/test/state.test.ts +199 -0
- package/test/util/dependency-graph.ts +95 -37
- package/test/util/framework-types.ts +53 -0
- package/test/util/perf-tests.ts +42 -0
- package/test/util/reactive-framework.ts +22 -0
- package/test/cause-effect.test.ts +0 -458
- package/test/util/pseudo-random.ts +0 -45
package/lib/scheduler.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* === Types === */
|
|
2
|
+
|
|
3
|
+
export type EnqueueDedupe = [Element, string]
|
|
4
|
+
|
|
5
|
+
export type Watcher = () => void
|
|
6
|
+
export type Updater = <T>() => T
|
|
7
|
+
|
|
8
|
+
/* === Internal === */
|
|
9
|
+
|
|
10
|
+
// Currently active watcher
|
|
11
|
+
let active: Watcher | undefined
|
|
12
|
+
|
|
13
|
+
// Pending queue for batched change notifications
|
|
14
|
+
const pending = new Set<Watcher>()
|
|
15
|
+
let batchDepth = 0
|
|
16
|
+
|
|
17
|
+
// Map of DOM elements to update functions
|
|
18
|
+
const updateMap = new Map<Element, Map<string, () => void>>()
|
|
19
|
+
let requestId: number | undefined
|
|
20
|
+
|
|
21
|
+
const updateDOM = () => {
|
|
22
|
+
requestId = undefined
|
|
23
|
+
for (const elementMap of updateMap.values()) {
|
|
24
|
+
for (const fn of elementMap.values()) {
|
|
25
|
+
fn()
|
|
26
|
+
}
|
|
27
|
+
elementMap.clear()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const requestTick = () => {
|
|
32
|
+
if (requestId) cancelAnimationFrame(requestId)
|
|
33
|
+
requestId = requestAnimationFrame(updateDOM)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Initial render when the call stack is empty
|
|
37
|
+
queueMicrotask(updateDOM)
|
|
38
|
+
|
|
39
|
+
/* === Exported Functions === */
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Add active watcher to the array of watchers
|
|
43
|
+
*
|
|
44
|
+
* @param {Watcher[]} watchers - watchers of the signal
|
|
45
|
+
*/
|
|
46
|
+
export const subscribe = (watchers: Watcher[]) => {
|
|
47
|
+
// if (!active) console.warn('Calling .get() outside of a reactive context')
|
|
48
|
+
if (active && !watchers.includes(active)) {
|
|
49
|
+
watchers.push(active)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Add watchers to the pending set of change notifications
|
|
55
|
+
*
|
|
56
|
+
* @param {Watcher[]} watchers - watchers of the signal
|
|
57
|
+
*/
|
|
58
|
+
export const notify = (watchers: Watcher[]) => {
|
|
59
|
+
for (const mark of watchers) {
|
|
60
|
+
batchDepth ? pending.add(mark) : mark()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Flush all pending changes to notify watchers
|
|
66
|
+
*/
|
|
67
|
+
export const flush = () => {
|
|
68
|
+
while (pending.size) {
|
|
69
|
+
const watchers = Array.from(pending)
|
|
70
|
+
pending.clear()
|
|
71
|
+
for (const mark of watchers) {
|
|
72
|
+
mark()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Batch multiple changes in a single signal graph and DOM update cycle
|
|
79
|
+
*
|
|
80
|
+
* @param {() => void} fn - function with multiple signal writes to be batched
|
|
81
|
+
*/
|
|
82
|
+
export const batch = (fn: () => void) => {
|
|
83
|
+
batchDepth++
|
|
84
|
+
fn()
|
|
85
|
+
flush()
|
|
86
|
+
batchDepth--
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Run a function in a reactive context
|
|
91
|
+
*
|
|
92
|
+
* @param {() => void} run - function to run the computation or effect
|
|
93
|
+
* @param {Watcher} mark - function to be called when the state changes
|
|
94
|
+
*/
|
|
95
|
+
export const watch = (run: () => void, mark: Watcher): void => {
|
|
96
|
+
const prev = active
|
|
97
|
+
active = mark
|
|
98
|
+
run()
|
|
99
|
+
active = prev
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Enqueue a function to be executed on the next animation frame
|
|
104
|
+
*
|
|
105
|
+
* @param callback
|
|
106
|
+
* @param dedupe
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
export const enqueue = <T>(
|
|
110
|
+
update: Updater,
|
|
111
|
+
dedupe?: EnqueueDedupe
|
|
112
|
+
) => new Promise<T>((resolve, reject) => {
|
|
113
|
+
const wrappedCallback = () => {
|
|
114
|
+
try {
|
|
115
|
+
resolve(update())
|
|
116
|
+
} catch (error) {
|
|
117
|
+
reject(error)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (dedupe) {
|
|
121
|
+
const [el, op] = dedupe
|
|
122
|
+
if (!updateMap.has(el)) updateMap.set(el, new Map())
|
|
123
|
+
const elementMap = updateMap.get(el)!
|
|
124
|
+
elementMap.set(op, wrappedCallback)
|
|
125
|
+
}
|
|
126
|
+
requestTick()
|
|
127
|
+
})
|
package/lib/signal.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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
|
|
4
|
+
type UnknownSignal = Signal<{}>;
|
|
5
|
+
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T);
|
|
6
|
+
type SignalValue<T> = T extends Signal<infer U> ? U : never;
|
|
7
|
+
export declare const UNSET: any;
|
|
6
8
|
/**
|
|
7
9
|
* Check whether a value is a Signal or not
|
|
8
10
|
*
|
|
@@ -19,30 +21,18 @@ declare const isSignal: <T extends {}>(value: any) => value is Signal<T>;
|
|
|
19
21
|
* @param memo
|
|
20
22
|
* @returns {Signal<T>} - converted Signal
|
|
21
23
|
*/
|
|
22
|
-
declare const toSignal: <T extends {}>(value: MaybeSignal<T
|
|
24
|
+
declare const toSignal: <T extends {}>(value: MaybeSignal<T>) => Signal<T>;
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
26
|
+
* Resolve signals and apply callbacks based on the results
|
|
25
27
|
*
|
|
26
|
-
* @
|
|
28
|
+
* @since 0.12.0
|
|
29
|
+
* @param {U} signals - dependency signals
|
|
30
|
+
* @param {Record<string, (...args) => T | Promise<T> | Error | void>} callbacks - ok, nil, err callbacks
|
|
31
|
+
* @returns {T | Promise<T> | Error | void} - result of chosen callback
|
|
27
32
|
*/
|
|
28
|
-
declare const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
declare const notify: (watchers: Watcher[]) => void;
|
|
35
|
-
/**
|
|
36
|
-
* Run a function in a reactive context
|
|
37
|
-
*
|
|
38
|
-
* @param {() => void} run - function to run the computation or effect
|
|
39
|
-
* @param {Watcher} mark - function to be called when the state changes
|
|
40
|
-
*/
|
|
41
|
-
declare const watch: (run: () => void, mark: Watcher) => void;
|
|
42
|
-
/**
|
|
43
|
-
* Batch multiple state changes into a single update
|
|
44
|
-
*
|
|
45
|
-
* @param {() => void} run - function to run the batch of state changes
|
|
46
|
-
*/
|
|
47
|
-
declare const batch: (run: () => void) => void;
|
|
48
|
-
export { type Signal, type MaybeSignal, type Watcher, isSignal, toSignal, subscribe, notify, watch, batch };
|
|
33
|
+
declare const resolveSignals: <T extends {}, U extends UnknownSignal[]>(signals: U, callbacks: {
|
|
34
|
+
ok: (...values: { [K in keyof U]: SignalValue<U[K]>; }) => T | Promise<T> | Error | void;
|
|
35
|
+
nil?: () => T | Promise<T> | Error | void;
|
|
36
|
+
err?: (...errors: Error[]) => T | Promise<T> | Error | void;
|
|
37
|
+
}) => T | Promise<T> | Error | void;
|
|
38
|
+
export { type Signal, type UnknownSignal, type SignalValue, type MaybeSignal, isSignal, toSignal, resolveSignals, };
|
package/lib/signal.ts
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import { type State, isState, state } from "./state"
|
|
2
2
|
import { computed, type Computed, isComputed } from "./computed"
|
|
3
|
-
import { isComputeFunction } from "./util"
|
|
3
|
+
import { isComputeFunction, toError } from "./util"
|
|
4
4
|
|
|
5
5
|
/* === Types === */
|
|
6
6
|
|
|
7
7
|
type Signal<T extends {}> = State<T> | Computed<T>
|
|
8
|
+
type UnknownSignal = Signal<{}>
|
|
9
|
+
type MaybeSignal<T extends {}> = Signal<T> | T | (() => T)
|
|
8
10
|
|
|
9
|
-
type
|
|
11
|
+
type SignalValue<T> = T extends Signal<infer U> ? U : never
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
/* === Constants === */
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Currently active watcher
|
|
16
|
-
let active: () => void | undefined
|
|
17
|
-
|
|
18
|
-
// Batching state
|
|
19
|
-
let batching = false
|
|
20
|
-
|
|
21
|
-
// Pending notifications
|
|
22
|
-
const pending: Watcher[] = []
|
|
15
|
+
export const UNSET: any = Symbol()
|
|
23
16
|
|
|
24
17
|
/* === Exported Functions === */
|
|
25
18
|
|
|
@@ -42,57 +35,58 @@ const isSignal = /*#__PURE__*/ <T extends {}>(value: any): value is Signal<T> =>
|
|
|
42
35
|
* @returns {Signal<T>} - converted Signal
|
|
43
36
|
*/
|
|
44
37
|
const toSignal = /*#__PURE__*/ <T extends {}>(
|
|
45
|
-
value: MaybeSignal<T
|
|
46
|
-
memo: boolean = false
|
|
38
|
+
value: MaybeSignal<T>
|
|
47
39
|
): Signal<T> =>
|
|
48
40
|
isSignal<T>(value) ? value
|
|
49
|
-
: isComputeFunction<T>(value) ? computed(value
|
|
41
|
+
: isComputeFunction<T>(value) ? computed(value)
|
|
50
42
|
: state(value)
|
|
51
43
|
|
|
52
|
-
/**
|
|
53
|
-
* Add notify function of active watchers to the set of watchers
|
|
54
|
-
*
|
|
55
|
-
* @param {Watcher[]} watchers - set of current watchers
|
|
56
|
-
*/
|
|
57
|
-
const subscribe = (watchers: Watcher[]) => {
|
|
58
|
-
if (active && !watchers.includes(active)) watchers.push(active)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Notify all subscribers of the state change or add to the pending set if batching is enabled
|
|
63
|
-
*
|
|
64
|
-
* @param {Watcher[]} watchers
|
|
65
|
-
*/
|
|
66
|
-
const notify = (watchers: Watcher[]) =>
|
|
67
|
-
watchers.forEach(n => batching ? pending.push(n) : n())
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Run a function in a reactive context
|
|
71
|
-
*
|
|
72
|
-
* @param {() => void} run - function to run the computation or effect
|
|
73
|
-
* @param {Watcher} mark - function to be called when the state changes
|
|
74
|
-
*/
|
|
75
|
-
const watch = (run: () => void, mark: Watcher): void => {
|
|
76
|
-
const prev = active
|
|
77
|
-
active = mark
|
|
78
|
-
run()
|
|
79
|
-
active = prev
|
|
80
|
-
}
|
|
81
44
|
|
|
82
45
|
/**
|
|
83
|
-
*
|
|
46
|
+
* Resolve signals and apply callbacks based on the results
|
|
84
47
|
*
|
|
85
|
-
* @
|
|
48
|
+
* @since 0.12.0
|
|
49
|
+
* @param {U} signals - dependency signals
|
|
50
|
+
* @param {Record<string, (...args) => T | Promise<T> | Error | void>} callbacks - ok, nil, err callbacks
|
|
51
|
+
* @returns {T | Promise<T> | Error | void} - result of chosen callback
|
|
86
52
|
*/
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
53
|
+
const resolveSignals = <T extends {}, U extends UnknownSignal[]>(
|
|
54
|
+
signals: U,
|
|
55
|
+
callbacks: {
|
|
56
|
+
ok: (...values: { [K in keyof U]: SignalValue<U[K]> }) => T | Promise<T> | Error | void
|
|
57
|
+
nil?: () => T | Promise<T> | Error | void
|
|
58
|
+
err?: (...errors: Error[]) => T | Promise<T> | Error | void
|
|
59
|
+
}
|
|
60
|
+
): T | Promise<T> | Error | void => {
|
|
61
|
+
const { ok, nil, err } = callbacks
|
|
62
|
+
const values = [] as { [K in keyof U]: SignalValue<U[K]> }
|
|
63
|
+
const errors: Error[] = []
|
|
64
|
+
let hasUnset = false
|
|
65
|
+
|
|
66
|
+
for (const signal of signals) {
|
|
67
|
+
try {
|
|
68
|
+
const value = signal.get()
|
|
69
|
+
if (value === UNSET) hasUnset = true
|
|
70
|
+
values.push(value)
|
|
71
|
+
} catch (e) {
|
|
72
|
+
errors.push(toError(e))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let result: T | Promise<T> | Error | void = undefined
|
|
77
|
+
try {
|
|
78
|
+
if (hasUnset && nil) result = nil()
|
|
79
|
+
else if (errors.length) result = err ? err(...errors) : errors[0]
|
|
80
|
+
else if (!hasUnset) result = ok(...values)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
result = toError(e)
|
|
83
|
+
if (err) result = err(result)
|
|
84
|
+
} finally {
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
93
87
|
}
|
|
94
88
|
|
|
95
89
|
export {
|
|
96
|
-
type Signal, type
|
|
97
|
-
isSignal, toSignal,
|
|
90
|
+
type Signal, type UnknownSignal, type SignalValue, type MaybeSignal,
|
|
91
|
+
isSignal, toSignal, resolveSignals,
|
|
98
92
|
}
|
package/lib/state.d.ts
CHANGED
|
@@ -1,59 +1,21 @@
|
|
|
1
|
-
import { type Computed } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* @since 0.9.0
|
|
7
|
-
* @class State
|
|
8
|
-
*/
|
|
9
|
-
export declare class State<T extends {}> {
|
|
10
|
-
private value;
|
|
11
|
-
private watchers;
|
|
12
|
-
constructor(value: T);
|
|
13
|
-
/**
|
|
14
|
-
* Get the current value of the state
|
|
15
|
-
*
|
|
16
|
-
* @since 0.9.0
|
|
17
|
-
* @method of State<T>
|
|
18
|
-
* @returns {T} - current value of the state
|
|
19
|
-
*/
|
|
1
|
+
import { type Computed } from './computed';
|
|
2
|
+
import { type EffectCallbacks } from './effect';
|
|
3
|
+
export type State<T extends {}> = {
|
|
4
|
+
[Symbol.toStringTag]: 'State';
|
|
20
5
|
get(): T;
|
|
21
|
-
/**
|
|
22
|
-
* Set a new value of the state
|
|
23
|
-
*
|
|
24
|
-
* @since 0.9.0
|
|
25
|
-
* @method of State<T>
|
|
26
|
-
* @param {T} value
|
|
27
|
-
* @returns {void}
|
|
28
|
-
*/
|
|
29
6
|
set(value: T): void;
|
|
30
|
-
/**
|
|
31
|
-
* Update the state with a new value using a function
|
|
32
|
-
*
|
|
33
|
-
* @since 0.10.0
|
|
34
|
-
* @method of State<T>
|
|
35
|
-
* @param {(value: T) => T} fn
|
|
36
|
-
* @returns {void} - updates the state with the result of the function
|
|
37
|
-
*/
|
|
38
7
|
update(fn: (value: T) => T): void;
|
|
39
|
-
/**
|
|
40
|
-
* Create a derived state from an existing state
|
|
41
|
-
*
|
|
42
|
-
* @since 0.9.0
|
|
43
|
-
* @method of State<T>
|
|
44
|
-
* @param {(value: T) => U} fn
|
|
45
|
-
* @returns {Computed<U>} - derived state
|
|
46
|
-
*/
|
|
47
8
|
map<U extends {}>(fn: (value: T) => U): Computed<U>;
|
|
48
|
-
|
|
9
|
+
match: (callbacks: EffectCallbacks<[State<T>]>) => void;
|
|
10
|
+
};
|
|
49
11
|
/**
|
|
50
12
|
* Create a new state signal
|
|
51
13
|
*
|
|
52
14
|
* @since 0.9.0
|
|
53
|
-
* @param {T}
|
|
15
|
+
* @param {T} initialValue - initial value of the state
|
|
54
16
|
* @returns {State<T>} - new state signal
|
|
55
17
|
*/
|
|
56
|
-
export declare const state: <T extends {}>(
|
|
18
|
+
export declare const state: <T extends {}>(v: T) => State<T>;
|
|
57
19
|
/**
|
|
58
20
|
* Check if the provided value is a State instance
|
|
59
21
|
*
|
package/lib/state.ts
CHANGED
|
@@ -1,88 +1,108 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type Computed, computed } from
|
|
1
|
+
import { UNSET } from './signal'
|
|
2
|
+
import { type Computed, computed } from './computed'
|
|
3
|
+
import { isObjectOfType } from './util';
|
|
4
|
+
import { type Watcher, notify, subscribe } from './scheduler'
|
|
5
|
+
import { type EffectCallbacks, effect } from './effect';
|
|
6
|
+
|
|
7
|
+
/* === Types === */
|
|
8
|
+
|
|
9
|
+
export type State<T extends {}> = {
|
|
10
|
+
[Symbol.toStringTag]: 'State';
|
|
11
|
+
get(): T;
|
|
12
|
+
set(value: T): void;
|
|
13
|
+
update(fn: (value: T) => T): void;
|
|
14
|
+
map<U extends {}>(fn: (value: T) => U): Computed<U>;
|
|
15
|
+
match: (callbacks: EffectCallbacks<[State<T>]>) => void
|
|
16
|
+
}
|
|
3
17
|
|
|
4
18
|
/* === Constants === */
|
|
5
19
|
|
|
6
|
-
|
|
20
|
+
const TYPE_STATE = 'State'
|
|
7
21
|
|
|
8
|
-
/* ===
|
|
22
|
+
/* === State Factory === */
|
|
9
23
|
|
|
10
24
|
/**
|
|
11
|
-
*
|
|
25
|
+
* Create a new state signal
|
|
12
26
|
*
|
|
13
27
|
* @since 0.9.0
|
|
14
|
-
* @
|
|
28
|
+
* @param {T} initialValue - initial value of the state
|
|
29
|
+
* @returns {State<T>} - new state signal
|
|
15
30
|
*/
|
|
16
|
-
export
|
|
17
|
-
|
|
31
|
+
export const state = /*#__PURE__*/ <T extends {}>(v: T): State<T> => {
|
|
32
|
+
const watchers: Watcher[] = []
|
|
33
|
+
let value: T = v
|
|
18
34
|
|
|
19
|
-
|
|
35
|
+
const s: State<T> = {
|
|
36
|
+
[Symbol.toStringTag]: TYPE_STATE,
|
|
20
37
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Get the current value of the state
|
|
40
|
+
*
|
|
41
|
+
* @since 0.9.0
|
|
42
|
+
* @method of State<T>
|
|
43
|
+
* @returns {T} - current value of the state
|
|
44
|
+
*/
|
|
45
|
+
get: (): T => {
|
|
46
|
+
subscribe(watchers)
|
|
47
|
+
return value
|
|
48
|
+
},
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Set a new value of the state
|
|
52
|
+
*
|
|
53
|
+
* @since 0.9.0
|
|
54
|
+
* @method of State<T>
|
|
55
|
+
* @param {T} v
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
set: (v: T): void => {
|
|
59
|
+
if (Object.is(value, v)) return
|
|
60
|
+
value = v
|
|
61
|
+
notify(watchers)
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
// Setting to UNSET clears the watchers so the signal can be garbage collected
|
|
64
|
+
if (UNSET === value) watchers.length = 0 // head = tail = undefined
|
|
65
|
+
},
|
|
49
66
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Update the state with a new value using a function
|
|
69
|
+
*
|
|
70
|
+
* @since 0.10.0
|
|
71
|
+
* @method of State<T>
|
|
72
|
+
* @param {(v: T) => T} fn
|
|
73
|
+
* @returns {void} - updates the state with the result of the function
|
|
74
|
+
*/
|
|
75
|
+
update: (fn: (v: T) => T): void => {
|
|
76
|
+
s.set(fn(value))
|
|
77
|
+
},
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a computed signal from the current state signal
|
|
81
|
+
*
|
|
82
|
+
* @since 0.9.0
|
|
83
|
+
* @method of State<T>
|
|
84
|
+
* @param {(v: T) => R} fn
|
|
85
|
+
* @returns {Computed<R>} - computed signal
|
|
86
|
+
*/
|
|
87
|
+
map: <R extends {}>(fn: (v: T) => R): Computed<R> =>
|
|
88
|
+
computed(() => fn(s.get())),
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Case matching for the state signal with effect callbacks
|
|
92
|
+
*
|
|
93
|
+
* @since 0.12.0
|
|
94
|
+
* @method of State<T>
|
|
95
|
+
* @param {EffectCallbacks[<T>]} callbacks
|
|
96
|
+
* @returns {State<T>} - self, for chaining effect callbacks
|
|
97
|
+
*/
|
|
98
|
+
match: (callbacks: EffectCallbacks<[State<T>]>): State<T> => {
|
|
99
|
+
effect(callbacks, s)
|
|
100
|
+
return s
|
|
101
|
+
}
|
|
102
|
+
}
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
*
|
|
80
|
-
* @since 0.9.0
|
|
81
|
-
* @param {T} value - initial value of the state
|
|
82
|
-
* @returns {State<T>} - new state signal
|
|
83
|
-
*/
|
|
84
|
-
export const state = /*#__PURE__*/ <T extends {}>(value: T): State<T> =>
|
|
85
|
-
new State(value)
|
|
104
|
+
return s
|
|
105
|
+
}
|
|
86
106
|
|
|
87
107
|
/**
|
|
88
108
|
* Check if the provided value is a State instance
|
|
@@ -92,4 +112,4 @@ export const state = /*#__PURE__*/ <T extends {}>(value: T): State<T> =>
|
|
|
92
112
|
* @returns {boolean} - true if the value is a State instance, false otherwise
|
|
93
113
|
*/
|
|
94
114
|
export const isState = /*#__PURE__*/ <T extends {}>(value: unknown): value is State<T> =>
|
|
95
|
-
value
|
|
115
|
+
isObjectOfType(value, TYPE_STATE)
|
package/lib/util.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
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
3
|
declare const isComputeFunction: <T>(value: unknown) => value is ((old?: T) => T);
|
|
4
|
+
declare const isObjectOfType: <T>(value: unknown, type: string) => value is T;
|
|
4
5
|
declare const isInstanceOf: <T>(type: new (...args: any[]) => T) => (value: unknown) => value is T;
|
|
5
6
|
declare const isError: (value: unknown) => value is Error;
|
|
6
7
|
declare const isPromise: (value: unknown) => value is Promise<unknown>;
|
|
7
|
-
|
|
8
|
+
declare const toError: (value: unknown) => Error;
|
|
9
|
+
declare const isEquivalentError: (error1: Error, error2: Error | undefined) => boolean;
|
|
10
|
+
export { isFunction, isAsyncFunction, isComputeFunction, isObjectOfType, isInstanceOf, isError, isPromise, toError, isEquivalentError };
|
package/lib/util.ts
CHANGED
|
@@ -9,6 +9,9 @@ const isAsyncFunction = /*#__PURE__*/ <T>(value: unknown): value is (...args: un
|
|
|
9
9
|
const isComputeFunction = /*#__PURE__*/ <T>(value: unknown): value is ((old?: T) => T) =>
|
|
10
10
|
isFunction(value) && value.length < 2
|
|
11
11
|
|
|
12
|
+
const isObjectOfType = <T>(value: unknown, type: string): value is T =>
|
|
13
|
+
Object.prototype.toString.call(value) === `[object ${type}]`
|
|
14
|
+
|
|
12
15
|
const isInstanceOf = /*#__PURE__*/ <T>(type: new (...args: any[]) => T) =>
|
|
13
16
|
(value: unknown): value is T =>
|
|
14
17
|
value instanceof type
|
|
@@ -16,4 +19,18 @@ const isInstanceOf = /*#__PURE__*/ <T>(type: new (...args: any[]) => T) =>
|
|
|
16
19
|
const isError = /*#__PURE__*/ isInstanceOf(Error)
|
|
17
20
|
const isPromise = /*#__PURE__*/ isInstanceOf(Promise)
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
const toError = (value: unknown): Error =>
|
|
23
|
+
isError(value) ? value : new Error(String(value))
|
|
24
|
+
|
|
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
|
+
export {
|
|
34
|
+
isFunction, isAsyncFunction, isComputeFunction,
|
|
35
|
+
isObjectOfType, isInstanceOf, isError, isPromise, toError, isEquivalentError
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeix/cause-effect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"author": "Esther Brunner",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.ts",
|
|
@@ -26,5 +26,8 @@
|
|
|
26
26
|
"test": "bun test"
|
|
27
27
|
},
|
|
28
28
|
"type": "module",
|
|
29
|
-
"types": "index.d.ts"
|
|
29
|
+
"types": "index.d.ts",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"random": "^5.3.0"
|
|
32
|
+
}
|
|
30
33
|
}
|