solid-state-tools 1.5.1 → 1.6.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 +25 -8
- package/dist/index.js +142 -101
- package/dist/types/index.d.ts +347 -0
- package/package.json +11 -7
- package/dist/index.d.ts +0 -126
package/README.md
CHANGED
|
@@ -6,16 +6,17 @@ Solid State Tools is a collection of simple utilities for managing [Solid JS](ht
|
|
|
6
6
|
|
|
7
7
|
All features are intended to compliment Solid JS's existing state system, building upon the existing foundation.
|
|
8
8
|
|
|
9
|
-
The package is small and only
|
|
9
|
+
The package is small and the only production dependency is Solid JS.
|
|
10
10
|
|
|
11
11
|
# Usage
|
|
12
12
|
|
|
13
|
-
| Utility
|
|
14
|
-
|
|
|
15
|
-
| [atom](#atoms-atom)
|
|
16
|
-
| [createPair](#pairs-createpair)
|
|
17
|
-
| [asig](#atomic-signals-asig)
|
|
18
|
-
| [apair](#atomic-pairs-apair)
|
|
13
|
+
| Utility | Summary |
|
|
14
|
+
| ---------------------------------------- | ------------------------------------------------- |
|
|
15
|
+
| [atom](#atoms-atom) | Combines a getter setter pair into one function. |
|
|
16
|
+
| [createPair](#pairs-createpair) | Creates a signal from a getter setter pair. |
|
|
17
|
+
| [asig](#atomic-signals-asig) | Shorthand for `atom(createSignal(...))` |
|
|
18
|
+
| [apair](#atomic-pairs-apair) | Shorthand for `atom(createCouple(...))` |
|
|
19
|
+
| [createBlinker](#blinkers-createblinker) | Creates a reactive boolean for flashing elements. |
|
|
19
20
|
|
|
20
21
|
Read the sections below for a breakdown of each utility.
|
|
21
22
|
|
|
@@ -180,9 +181,25 @@ const double = apair(() => count() * 2, (double) => count(double / 2));
|
|
|
180
181
|
const double = atom(createPair(() => count() * 2, (double) => count(double / 2)));
|
|
181
182
|
```
|
|
182
183
|
|
|
184
|
+
## Blinkers (`createBlinker`)
|
|
185
|
+
|
|
186
|
+
A blinker is a reactive boolean that tracks some state. When the state updates, the blinker temporarily flips to true.
|
|
187
|
+
|
|
188
|
+
This is primarily useful for flashing an element on the page when a signal changes to draw the user's attention to it.
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
const [ count, setCount ] = createSignal(0);
|
|
192
|
+
const countBlinked = createBlinker(count, 200);
|
|
193
|
+
|
|
194
|
+
// Flashes orange for 200ms when count changes.
|
|
195
|
+
<span style={ { "color": countBlinked() ? "orange" : undefined } }>
|
|
196
|
+
{ count() }
|
|
197
|
+
</span>
|
|
198
|
+
```
|
|
199
|
+
|
|
183
200
|
# Conclusion
|
|
184
201
|
|
|
185
202
|
Perhaps you can see the power of the above primitives.
|
|
186
|
-
|
|
203
|
+
Specifically, how they work together to reduce boilerplate.
|
|
187
204
|
|
|
188
205
|
More utilities for this library are in the works and are coming soon.
|
package/dist/index.js
CHANGED
|
@@ -1,106 +1,147 @@
|
|
|
1
|
-
import { createMemo, createSignal, untrack } from "solid-js";
|
|
2
|
-
import { isDev } from "solid-js/web";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* // Read
|
|
20
|
-
* count();
|
|
21
|
-
*
|
|
22
|
-
* // Write
|
|
23
|
-
* count(100);
|
|
24
|
-
* count(x => x + 1);
|
|
25
|
-
* count(count() + 1);
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function atom(signal) {
|
|
29
|
-
if (isDev) {
|
|
30
|
-
// Assert that the input is valid.
|
|
31
|
-
// For production, these checks are skipped for performance.
|
|
32
|
-
if (!Array.isArray(signal)) {
|
|
33
|
-
throw new Error(`expected a getter setter pair as an array, but got ${typeof signal}`);
|
|
34
|
-
}
|
|
35
|
-
if (typeof signal[0] !== "function") {
|
|
36
|
-
throw new Error(`expected a getter function, but got ${typeof signal[0]}`);
|
|
37
|
-
}
|
|
38
|
-
if (typeof signal[1] !== "function") {
|
|
39
|
-
throw new Error(`expected a setter function, but got ${typeof signal[1]}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const [getter, setter] = signal;
|
|
43
|
-
return (...args) => (args.length === 0) ? getter() : setter(...args);
|
|
1
|
+
import { createMemo as E, createSignal as s, createComputed as w, on as $, getOwner as L, createSelector as O, onCleanup as x, batch as m, getListener as k, untrack as a, runWithOwner as C, createRoot as F } from "solid-js";
|
|
2
|
+
import { isDev as g } from "solid-js/web";
|
|
3
|
+
function h(t) {
|
|
4
|
+
if (g) {
|
|
5
|
+
if (!Array.isArray(t))
|
|
6
|
+
throw new Error(`expected signal to be an array, but got ${typeof t}`);
|
|
7
|
+
if (typeof t[0] != "function")
|
|
8
|
+
throw new Error(`expected getter to be a function, but got ${typeof t[0]}`);
|
|
9
|
+
if (typeof t[1] != "function")
|
|
10
|
+
throw new Error(`expected setter to be a function, but got ${typeof t[1]}`);
|
|
11
|
+
}
|
|
12
|
+
const [e, r] = t;
|
|
13
|
+
return Object.assign(
|
|
14
|
+
(...o) => o.length === 0 ? e() : r(...o),
|
|
15
|
+
e,
|
|
16
|
+
r
|
|
17
|
+
);
|
|
44
18
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
19
|
+
function j(t, e, r) {
|
|
20
|
+
if (g) {
|
|
21
|
+
if (typeof t != "function")
|
|
22
|
+
throw new Error(`expected getter to be a function, but got ${typeof t}`);
|
|
23
|
+
if (typeof e != "function")
|
|
24
|
+
throw new Error(`expected setter to be a function, but got ${typeof e}`);
|
|
25
|
+
}
|
|
26
|
+
const o = r?.memoized === !1 ? t : E(t);
|
|
27
|
+
return [o, ((c) => {
|
|
28
|
+
const u = typeof c == "function" ? c(a(o)) : c;
|
|
29
|
+
return e(u), u;
|
|
30
|
+
})];
|
|
31
|
+
}
|
|
32
|
+
function R(t, e = 500) {
|
|
33
|
+
if (g) {
|
|
34
|
+
if (typeof t != "function")
|
|
35
|
+
throw new Error(`expected subject to be a function, but got ${typeof t}`);
|
|
36
|
+
if (typeof e != "number")
|
|
37
|
+
throw new Error(`expected duration to be a number, but got ${typeof e}`);
|
|
38
|
+
}
|
|
39
|
+
const [r, o] = s(!1);
|
|
40
|
+
let n;
|
|
41
|
+
return w($(t, () => {
|
|
42
|
+
n !== void 0 ? clearTimeout(n) : o(!0), n = setTimeout(() => {
|
|
43
|
+
o(!1), n = void 0;
|
|
44
|
+
}, e);
|
|
45
|
+
}, { defer: !0 })), r;
|
|
46
|
+
}
|
|
47
|
+
function q(t, e) {
|
|
48
|
+
const [r, o] = s(e?.initial), n = () => t((c) => o(() => c), r);
|
|
49
|
+
if (e?.late) {
|
|
50
|
+
const c = L();
|
|
51
|
+
let u = () => {
|
|
52
|
+
u = void 0, C(c, n);
|
|
53
|
+
};
|
|
54
|
+
return () => (u?.(), r());
|
|
55
|
+
}
|
|
56
|
+
return n(), r;
|
|
57
|
+
}
|
|
58
|
+
function T(t, e) {
|
|
59
|
+
const [r, o] = s(), [n, c] = s(e?.initial), [u, l] = s("unresolved"), p = O(u);
|
|
60
|
+
let d = !1;
|
|
61
|
+
const b = q((y, v) => {
|
|
62
|
+
w(() => {
|
|
63
|
+
let i = !1;
|
|
64
|
+
x(() => {
|
|
65
|
+
i = !0, y(void 0);
|
|
66
|
+
}), l(d ? "refreshing" : "pending");
|
|
67
|
+
try {
|
|
68
|
+
t((f) => {
|
|
69
|
+
i || m(() => {
|
|
70
|
+
l("ready"), c(() => f), d = !0, y(f);
|
|
71
|
+
});
|
|
72
|
+
}, v);
|
|
73
|
+
} catch (f) {
|
|
74
|
+
m(() => {
|
|
75
|
+
l("errored"), o(f);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
79
78
|
});
|
|
80
|
-
|
|
79
|
+
}, e);
|
|
80
|
+
return Object.assign(b, {
|
|
81
|
+
error: r,
|
|
82
|
+
latest: n,
|
|
83
|
+
state: u,
|
|
84
|
+
is: p
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function S(t, e) {
|
|
88
|
+
let r = 0, o;
|
|
89
|
+
const n = () => {
|
|
90
|
+
r === 0 && (o = a(e)), r++;
|
|
91
|
+
}, c = () => queueMicrotask(() => {
|
|
92
|
+
r--, r === 0 && (o?.(), o = void 0);
|
|
93
|
+
});
|
|
94
|
+
return () => (k() && (n(), x(c)), t());
|
|
95
|
+
}
|
|
96
|
+
function z(t, e) {
|
|
97
|
+
const [r, o] = s(a(t), e);
|
|
98
|
+
return S(r, () => F((n) => (w(() => {
|
|
99
|
+
o(t);
|
|
100
|
+
}), n)));
|
|
101
|
+
}
|
|
102
|
+
function B(t, e) {
|
|
103
|
+
const [r, o] = s(e?.initial);
|
|
104
|
+
let n = !!e?.initial;
|
|
105
|
+
const [c, u] = s(), [l, p] = s(!1), d = E(() => r() !== c()), b = (i) => m(() => {
|
|
106
|
+
const f = t.reconcile ? t.reconcile(i, a(r), a(c)) : i;
|
|
107
|
+
return n = !0, o(() => f), u(() => f), f;
|
|
108
|
+
}), y = h([
|
|
109
|
+
S(r, () => {
|
|
110
|
+
const i = t.sub(b);
|
|
111
|
+
return p(!0), () => {
|
|
112
|
+
p(!1), i();
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
(...i) => (n = !0, o(...i))
|
|
116
|
+
]);
|
|
117
|
+
return Object.assign(y, {
|
|
118
|
+
async pull() {
|
|
119
|
+
const i = await a(t.get);
|
|
120
|
+
return b(i);
|
|
121
|
+
},
|
|
122
|
+
async push() {
|
|
123
|
+
n && await t.set(a(r));
|
|
124
|
+
},
|
|
125
|
+
cache: c,
|
|
126
|
+
subscribed: l,
|
|
127
|
+
detached: d
|
|
128
|
+
});
|
|
81
129
|
}
|
|
82
|
-
|
|
83
|
-
|
|
130
|
+
function D(t, e) {
|
|
131
|
+
return h(s(t, e));
|
|
84
132
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
* Create an atomic getter setter pair. Short for `atom(createPair(...))`.
|
|
88
|
-
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atomic-pairs-apair)
|
|
89
|
-
*
|
|
90
|
-
* @see {@link Accessor} (input), {@link Writer} (input), {@link PairOptions} (input), {@link Asig} (output)
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```
|
|
94
|
-
* const count = asig(0);
|
|
95
|
-
* const double = apair(() => count() * 2, (x) => count(x / 2));
|
|
96
|
-
*
|
|
97
|
-
* count(10);
|
|
98
|
-
* console.log(count(), double()); // 10 20
|
|
99
|
-
*
|
|
100
|
-
* double(100);
|
|
101
|
-
* console.log(count(), double()); // 50 100
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
export function apair(getter, setter, options) {
|
|
105
|
-
return atom(createPair(getter, setter, options));
|
|
133
|
+
function P(t, e, r) {
|
|
134
|
+
return h(j(t, e, r));
|
|
106
135
|
}
|
|
136
|
+
export {
|
|
137
|
+
P as apair,
|
|
138
|
+
D as asig,
|
|
139
|
+
h as atom,
|
|
140
|
+
R as createBlinker,
|
|
141
|
+
T as createFetched,
|
|
142
|
+
j as createPair,
|
|
143
|
+
q as createSpool,
|
|
144
|
+
B as createSubscription,
|
|
145
|
+
z as derive,
|
|
146
|
+
S as quantum
|
|
147
|
+
};
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { Accessor, Setter, Signal, SignalOptions } from 'solid-js';
|
|
2
|
+
/**
|
|
3
|
+
* A function that updates a state to the given value.
|
|
4
|
+
*
|
|
5
|
+
* @see {@link Setter} (related type)
|
|
6
|
+
*/
|
|
7
|
+
export type Update<T> = (value: T) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Any tuple of two functions where the first accepts no arguments and the second accepts any amount.
|
|
10
|
+
*
|
|
11
|
+
* Used as the source of an {@link Atom}.
|
|
12
|
+
*
|
|
13
|
+
* @see {@link Atom}, {@link atom}
|
|
14
|
+
*/
|
|
15
|
+
export type SignalLike = readonly [
|
|
16
|
+
getter: () => any,
|
|
17
|
+
setter: (...args: any) => any
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* An {@link Atom} is a polymorphic function that calls one of two
|
|
21
|
+
* functions depending on the number of arguments it has.
|
|
22
|
+
*
|
|
23
|
+
* If called with zero arguments, the first function is called.
|
|
24
|
+
* Otherwise, the second function is called with all arguments forwarded to it.
|
|
25
|
+
*
|
|
26
|
+
* @see {@link SignalLike}, {@link atom} (constructor)
|
|
27
|
+
*/
|
|
28
|
+
export type Atom<T extends SignalLike = SignalLike> = T[0] & T[1];
|
|
29
|
+
/**
|
|
30
|
+
* Combine a getter and setter function pair into one.
|
|
31
|
+
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atoms-atom)
|
|
32
|
+
*
|
|
33
|
+
* Accepts a tuple of two functions.
|
|
34
|
+
* The first is the getter, the second is the setter.
|
|
35
|
+
* A new function is returned that, when called with zero arguments, calls the getter.
|
|
36
|
+
* Otherwise, it calls the setter and forwards all of the arguments.
|
|
37
|
+
*
|
|
38
|
+
* @see {@link SignalLike} (input), {@link Atom} (output)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const count: Atom<Signal<T>> = atom(createSignal(0));
|
|
43
|
+
*
|
|
44
|
+
* // Read
|
|
45
|
+
* count();
|
|
46
|
+
*
|
|
47
|
+
* // Write
|
|
48
|
+
* count(100);
|
|
49
|
+
* count(x => x + 1);
|
|
50
|
+
* count(count() + 1);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function atom<const T extends SignalLike>(signal: T): Atom<T>;
|
|
54
|
+
export interface PairOptions {
|
|
55
|
+
/**
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
memoized: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a signal from a getter setter pair.
|
|
62
|
+
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#pairs-createpair)
|
|
63
|
+
*
|
|
64
|
+
* **The getter is immediately invoked for memoization.**
|
|
65
|
+
*
|
|
66
|
+
* @see {@link Accessor} (input), {@link Update} (input), {@link PairOptions} (input), {@link Signal} (output)
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const [ count, setCount ] = createSignal(0);
|
|
71
|
+
* const [ double, setDouble ] = createPair(() => count() * 2, (x) => setCount(x / 2));
|
|
72
|
+
*
|
|
73
|
+
* setDouble(x => x + 2);
|
|
74
|
+
* console.log(double(), count()); // 2 1
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createPair<T>(getter: Accessor<T>, setter: Update<T>, options?: PairOptions): Signal<T>;
|
|
78
|
+
/**
|
|
79
|
+
* Create a reactive boolean that temporarily flips to true when the subject changes value.
|
|
80
|
+
*
|
|
81
|
+
* Useful for flashing an element when a signal changes value.
|
|
82
|
+
* Perhaps to draw the user's attention to it.
|
|
83
|
+
*
|
|
84
|
+
* @param subject The subject to track for changes.
|
|
85
|
+
* @param duration The duration in milliseconds to "blink" before resetting to false. Default: 500.
|
|
86
|
+
*
|
|
87
|
+
* @see {@link Accessor}
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* const [ count, setCount ] = createSignal(0);
|
|
92
|
+
* const countBlinked = createBlinker(count, 200);
|
|
93
|
+
*
|
|
94
|
+
* <span style={ { "color": countBlinked() ? "orange" : undefined } }>
|
|
95
|
+
* { count() }
|
|
96
|
+
* </span>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function createBlinker(subject: Accessor<unknown>, duration?: number): Accessor<boolean>;
|
|
100
|
+
/**
|
|
101
|
+
* A function that will periodically call the given update function as needed.
|
|
102
|
+
* The current value (stateful) is also available to the consumer for incremental changes.
|
|
103
|
+
*
|
|
104
|
+
* @see {@link Update}, {@link Accessor}
|
|
105
|
+
*/
|
|
106
|
+
export type Winch<T, Initial extends T | undefined> = (update: Update<T>, value: Accessor<Initial>) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Options for creation of a spool signal.
|
|
109
|
+
*
|
|
110
|
+
* @see {@link createSpool}
|
|
111
|
+
*/
|
|
112
|
+
export interface SpoolOptions<T> {
|
|
113
|
+
/**
|
|
114
|
+
* The initial value of the spool signal.
|
|
115
|
+
* @default undefined
|
|
116
|
+
*/
|
|
117
|
+
initial?: T;
|
|
118
|
+
/**
|
|
119
|
+
* If set to true, the winch will not be invoked until the spool signal is read for the first time.
|
|
120
|
+
* @default false
|
|
121
|
+
*/
|
|
122
|
+
late?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a signal from a winch.
|
|
126
|
+
*
|
|
127
|
+
* This is essentially an alternative way of writing a signal declaration.
|
|
128
|
+
*
|
|
129
|
+
* Note that the winch is not tracked and therefore does not automatically rerun when state changes.
|
|
130
|
+
* This behavior can be implemented in the winch function itself, if desired.
|
|
131
|
+
* Use {@link createFetched} for a reactive version.
|
|
132
|
+
*
|
|
133
|
+
* @see {@link Winch} (input), {@link SpoolOptions} (input), {@link Accessor} (output)
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```tsx
|
|
137
|
+
* // A timer that updates each second.
|
|
138
|
+
* const time: Accessor<number> = createSpool((setTime, curr) => {
|
|
139
|
+
* // Start timer.
|
|
140
|
+
* const interval = setInterval(() => void setTime(curr() + 1), 1000);
|
|
141
|
+
*
|
|
142
|
+
* // Dispose of timer when signal is destroyed.
|
|
143
|
+
* onCleanup(() => void clearInterval(interval));
|
|
144
|
+
* }, {
|
|
145
|
+
* initial: 0,
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* <div>
|
|
149
|
+
* { time() }
|
|
150
|
+
* </div>
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export declare function createSpool<T>(winch: Winch<T, T>, options: SpoolOptions<T> & {
|
|
154
|
+
initial: T;
|
|
155
|
+
}): Accessor<T>;
|
|
156
|
+
export declare function createSpool<T>(winch: Winch<T, T | undefined>, options?: SpoolOptions<T>): Accessor<T | undefined>;
|
|
157
|
+
/**
|
|
158
|
+
* A synchronous reactive state driven by a winch.
|
|
159
|
+
*
|
|
160
|
+
* @see {@link Accessor}, {@link Winch}, {@link createFetched} (constructor)
|
|
161
|
+
*/
|
|
162
|
+
export type Fetched<T> = Accessor<T> & FetchedMembers<T>;
|
|
163
|
+
export interface FetchedMembers<T> {
|
|
164
|
+
error: Accessor<unknown>;
|
|
165
|
+
latest: Accessor<T>;
|
|
166
|
+
state: Accessor<FetchedState>;
|
|
167
|
+
is: (key: FetchedState) => boolean;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Represents the current state of a fetched signal.
|
|
171
|
+
*
|
|
172
|
+
* Its identical to {@link Resource.state} in meaning.
|
|
173
|
+
* See the [table of definitions](https://docs.solidjs.com/reference/basic-reactivity/create-resource#resource) for that.
|
|
174
|
+
*
|
|
175
|
+
* @see {@link Fetched.state}
|
|
176
|
+
*/
|
|
177
|
+
export type FetchedState = "unresolved" | "pending" | "ready" | "refreshing" | "errored";
|
|
178
|
+
/**
|
|
179
|
+
* Create a signal that's driven by a tracked winch.
|
|
180
|
+
*
|
|
181
|
+
* The winch is tracked and therefore is rerun when its state changes.
|
|
182
|
+
*
|
|
183
|
+
* If the winch is rerun, the old winch can no longer mutate the state.
|
|
184
|
+
* This prevents race conditions where the old winch attempts to write _after_ the new winch resolved.
|
|
185
|
+
*
|
|
186
|
+
* @see {@link Winch} (input), {@link SpoolOptions} (input), {@link Fetched} (output)
|
|
187
|
+
*/
|
|
188
|
+
export declare function createFetched<T>(winch: Winch<T, T>, options: SpoolOptions<T> & {
|
|
189
|
+
initial: T;
|
|
190
|
+
}): Fetched<T>;
|
|
191
|
+
export declare function createFetched<T>(winch: Winch<T, T | undefined>, options?: SpoolOptions<T>): Fetched<T | undefined>;
|
|
192
|
+
export type QuantumAccessor<T> = Accessor<T>;
|
|
193
|
+
/**
|
|
194
|
+
* Create a quantum accessor.
|
|
195
|
+
*
|
|
196
|
+
* A quantum accessor counts its listeners and executes `track` if it is used at least once.
|
|
197
|
+
* When it is no longer used, `track`'s disposal function is invoked.
|
|
198
|
+
*
|
|
199
|
+
* Quantum accessors have standalone utility, but their primary purpose was to create and destroy subscriptions on the fly.
|
|
200
|
+
* Thus, {@link createSubscription} and {@link derive} replace most situations where {@link quantum} would be invoked directly.
|
|
201
|
+
*
|
|
202
|
+
* Note that quantum accessors count listeners ambiguously.
|
|
203
|
+
* Thus, some primitives must be substituted with a quantum-compatible one to preserve the 'quantum' behavior.
|
|
204
|
+
* Using a quantum accessor in these primitives (e.g. {@link createMemo}) is not _wrong_, but it may have unintended side-effects.
|
|
205
|
+
* Use {@link derive} in place of {@link createMemo} for quantum accessors.
|
|
206
|
+
*
|
|
207
|
+
* Technically, {@link QuantumAccessor} is just an alias of {@link Accessor} and can be used anywhere {@link Accessor} is allowed.
|
|
208
|
+
* However, it is useful to mark it this way due to the special handling (mentioned above) that's sometimes needed.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```ts
|
|
212
|
+
* const [ source ] = createSignal(0);
|
|
213
|
+
* const count = quantum(source, () => {
|
|
214
|
+
* console.log("count is now observed");
|
|
215
|
+
* return () => {
|
|
216
|
+
* console.log("count is no longer observed");
|
|
217
|
+
* };
|
|
218
|
+
* });
|
|
219
|
+
*
|
|
220
|
+
* createRoot((dispose) => {
|
|
221
|
+
* count(); // nothing happens because `createRoot` doesn't track signals
|
|
222
|
+
*
|
|
223
|
+
* createComputed(() => {
|
|
224
|
+
* count(); // prints "count is now observed" because `createComputed` started tracking `count`
|
|
225
|
+
* count(); // nothing happens because count is already tracked at least once
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* dispose(); // prints "count is no longer observed" because the `createComputed` is destroyed and `count` isn't tracked by anything else
|
|
229
|
+
* });
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export declare function quantum<T>(source: Accessor<T>, track: () => () => void): QuantumAccessor<T>;
|
|
233
|
+
/**
|
|
234
|
+
* Derive a new quantum accessor from an existing one while preserving the quantum behavior.
|
|
235
|
+
*
|
|
236
|
+
* Essentially, this is a quantum-compatible version of {@link createMemo}.
|
|
237
|
+
*
|
|
238
|
+
* This provides no advantage over {@link createMemo} for traditional accessors.
|
|
239
|
+
*
|
|
240
|
+
* @see {@link quantum}, {@link QuantumAccessor}
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* const count: QuantumAccessor<number> = ...;
|
|
245
|
+
* const double = derive(() => count() * 2);
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export declare function derive<T>(source: QuantumAccessor<T>, options?: SignalOptions<T>): QuantumAccessor<T>;
|
|
249
|
+
/**
|
|
250
|
+
* Handler for a subscription.
|
|
251
|
+
*
|
|
252
|
+
* @see {@link Subscription}, {@link createSubscription}
|
|
253
|
+
*/
|
|
254
|
+
export type Subscribable<T, Initial extends T | undefined> = {
|
|
255
|
+
get: () => Promise<T>;
|
|
256
|
+
set: (value: T) => Promise<void>;
|
|
257
|
+
sub: (update: Update<T>) => () => void;
|
|
258
|
+
reconcile?: (remote: T, local: Initial, cache: T | undefined) => T;
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* A state driven by a remote subscription.
|
|
262
|
+
*
|
|
263
|
+
* @see {@link createSubscription} (constructor), {@link QuantumAccessor} (component type)
|
|
264
|
+
*/
|
|
265
|
+
export type Subscription<T> = QuantumAccessor<T> & Setter<T> & SubscriptionMembers<T>;
|
|
266
|
+
export type SubscriptionMembers<T> = {
|
|
267
|
+
/**
|
|
268
|
+
* The last known remote value.
|
|
269
|
+
*
|
|
270
|
+
* It changes when a new remote value is pushed or pulled to this subscribable.
|
|
271
|
+
*/
|
|
272
|
+
cache: Accessor<T | undefined>;
|
|
273
|
+
/**
|
|
274
|
+
* Fetch the remote value and reconcile it with the current value (local) and cache.
|
|
275
|
+
*/
|
|
276
|
+
pull: () => Promise<T>;
|
|
277
|
+
/**
|
|
278
|
+
* Push local to the remote.
|
|
279
|
+
*/
|
|
280
|
+
push: () => Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* True if subscribed to the remote.
|
|
283
|
+
*/
|
|
284
|
+
subscribed: Accessor<boolean>;
|
|
285
|
+
/**
|
|
286
|
+
* True if the current value (local) is different from the last known remote value (cache).
|
|
287
|
+
*/
|
|
288
|
+
detached: Accessor<boolean>;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Options for creation of a subscription.
|
|
292
|
+
*
|
|
293
|
+
* @see {@link createSubscription}, {@link Subscription}
|
|
294
|
+
*/
|
|
295
|
+
export interface SubscriptionOptions<T> {
|
|
296
|
+
initial?: T;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Create a new subscription from a subscribable (handler).
|
|
300
|
+
*
|
|
301
|
+
* Quantum signals are used for dynamic subscription through observation.
|
|
302
|
+
*/
|
|
303
|
+
export declare function createSubscription<T>(handler: Subscribable<T, T | undefined>, options?: SubscriptionOptions<T>): Subscription<T | undefined>;
|
|
304
|
+
/**
|
|
305
|
+
* An atomic signal. A signal where the getter and setter are combined into one function.
|
|
306
|
+
*
|
|
307
|
+
* @see {@link Atom}, {@link asig} (constructor)
|
|
308
|
+
*/
|
|
309
|
+
export type Asig<T> = Atom<Signal<T>>;
|
|
310
|
+
/**
|
|
311
|
+
* Create an atomic signal. Short for `atom(createSignal(...))`.
|
|
312
|
+
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atomic-signals-asig)
|
|
313
|
+
*
|
|
314
|
+
* @see {@link Asig} (output), {@link Atom}, {@link atom}
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* const count = asig(0);
|
|
319
|
+
*
|
|
320
|
+
* count(10);
|
|
321
|
+
* console.log(count()); // 10
|
|
322
|
+
*
|
|
323
|
+
* count(x => x + 10);
|
|
324
|
+
* console.log(count()); // 20
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
export declare function asig<T>(value: T, options?: SignalOptions<T>): Asig<T>;
|
|
328
|
+
export declare function asig<T>(): Asig<T | undefined>;
|
|
329
|
+
/**
|
|
330
|
+
* Create an atomic getter setter pair. Short for `atom(createPair(...))`.
|
|
331
|
+
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atomic-pairs-apair)
|
|
332
|
+
*
|
|
333
|
+
* @see {@link Accessor} (input), {@link Update} (input), {@link PairOptions} (input), {@link Asig} (output)
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* const count = asig(0);
|
|
338
|
+
* const double = apair(() => count() * 2, (x) => count(x / 2));
|
|
339
|
+
*
|
|
340
|
+
* count(10);
|
|
341
|
+
* console.log(count(), double()); // 10 20
|
|
342
|
+
*
|
|
343
|
+
* double(100);
|
|
344
|
+
* console.log(count(), double()); // 50 100
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
export declare function apair<T>(getter: Accessor<T>, setter: Update<T>, options?: PairOptions): Asig<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-state-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A collection of Solid JS state utilities",
|
|
5
5
|
"author": "Reed Syllas <reedsyllas@gmail.com>",
|
|
6
6
|
"license": "ISC",
|
|
@@ -17,24 +17,28 @@
|
|
|
17
17
|
],
|
|
18
18
|
"exports": {
|
|
19
19
|
"import": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"types": "./dist/types/index.d.ts"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
|
-
"
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/types/index.d.ts",
|
|
27
27
|
"files": [
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^24.10.0",
|
|
32
|
-
"typescript": "^5.9.3"
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vite": "^7.3.1",
|
|
34
|
+
"vite-plugin-dts": "^4.5.4",
|
|
35
|
+
"vite-plugin-solid": "^2.11.10"
|
|
33
36
|
},
|
|
34
37
|
"peerDependencies": {
|
|
35
38
|
"solid-js": "^1.9.10"
|
|
36
39
|
},
|
|
37
40
|
"scripts": {
|
|
38
|
-
"
|
|
41
|
+
"dev": "vite dev",
|
|
42
|
+
"build": "vite build"
|
|
39
43
|
}
|
|
40
44
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { type Accessor, type Signal, type SignalOptions } from "solid-js";
|
|
2
|
-
/**
|
|
3
|
-
* @description
|
|
4
|
-
* Any tuple of two functions where the first accepts no arguments and the second accepts any amount.
|
|
5
|
-
*
|
|
6
|
-
* Used as the source of an {@link Atom}.
|
|
7
|
-
*
|
|
8
|
-
* @see {@link Atom}, {@link atom}
|
|
9
|
-
*/
|
|
10
|
-
export type SignalLike = readonly [() => any, (...args: any) => any];
|
|
11
|
-
/**
|
|
12
|
-
* @description
|
|
13
|
-
* An {@link Atom} is a polymorphic function that calls one of two
|
|
14
|
-
* functions depending on the number of arguments it has.
|
|
15
|
-
*
|
|
16
|
-
* If called with zero arguments, the first function is called.
|
|
17
|
-
* Otherwise, the second function is called with all arguments forwarded to it.
|
|
18
|
-
*
|
|
19
|
-
* @see {@link SignalLike}, {@link atom} (constructor)
|
|
20
|
-
*/
|
|
21
|
-
export type Atom<T extends SignalLike = SignalLike> = T[0] & T[1];
|
|
22
|
-
/**
|
|
23
|
-
* @description
|
|
24
|
-
* Combine a getter and setter function pair into one.
|
|
25
|
-
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atoms-atom)
|
|
26
|
-
*
|
|
27
|
-
* Accepts a tuple of two functions.
|
|
28
|
-
* The first is the getter, the second is the setter.
|
|
29
|
-
* A new function is returned that, when called with zero arguments, calls the getter.
|
|
30
|
-
* Otherwise, it calls the setter and forwards all of the arguments.
|
|
31
|
-
*
|
|
32
|
-
* @see {@link SignalLike} (input), {@link Atom} (output)
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```
|
|
36
|
-
* const count: Atom<Signal<T>> = atom(createSignal(0));
|
|
37
|
-
*
|
|
38
|
-
* // Read
|
|
39
|
-
* count();
|
|
40
|
-
*
|
|
41
|
-
* // Write
|
|
42
|
-
* count(100);
|
|
43
|
-
* count(x => x + 1);
|
|
44
|
-
* count(count() + 1);
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export declare function atom<const T extends SignalLike>(signal: T): Atom<T>;
|
|
48
|
-
/**
|
|
49
|
-
* @description
|
|
50
|
-
* Similar to a signal setter, except it doesn't accept a mapping function nor return a result.
|
|
51
|
-
*
|
|
52
|
-
* @see {@link createPair} (for example usage)
|
|
53
|
-
*/
|
|
54
|
-
export type Writer<T> = (value: T) => void;
|
|
55
|
-
export interface PairOptions {
|
|
56
|
-
/**
|
|
57
|
-
* @default true
|
|
58
|
-
*/
|
|
59
|
-
memoized: boolean;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* @description
|
|
63
|
-
* Create a signal from a getter setter pair.
|
|
64
|
-
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#pairs-createpair)
|
|
65
|
-
*
|
|
66
|
-
* **The getter is immediately invoked for memoization.**
|
|
67
|
-
*
|
|
68
|
-
* @see {@link Accessor} (input), {@link Writer} (input), {@link PairOptions} (input), {@link Signal} (output)
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```
|
|
72
|
-
* const [ count, setCount ] = createSignal(0);
|
|
73
|
-
* const [ double, setDouble ] = createPair(() => count() * 2, (x) => setCount(x / 2));
|
|
74
|
-
*
|
|
75
|
-
* setDouble(x => x + 2);
|
|
76
|
-
* console.log(double(), count()); // 2 1
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
|
-
export declare function createPair<T>(getter: Accessor<T>, setter: Writer<T>, options?: PairOptions): Signal<T>;
|
|
80
|
-
/**
|
|
81
|
-
* @description
|
|
82
|
-
* An atomic signal. A signal where the getter and setter are combined into one function.
|
|
83
|
-
*
|
|
84
|
-
* @see {@link Atom}, {@link asig} (constructor)
|
|
85
|
-
*/
|
|
86
|
-
export type Asig<T> = Atom<Signal<T>>;
|
|
87
|
-
/**
|
|
88
|
-
* @description
|
|
89
|
-
* Create an atomic signal. Short for `atom(createSignal(...))`.
|
|
90
|
-
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atomic-signals-asig)
|
|
91
|
-
*
|
|
92
|
-
* @see {@link Asig} (output), {@link Atom}, {@link atom}
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```
|
|
96
|
-
* const count = asig(0);
|
|
97
|
-
*
|
|
98
|
-
* count(10);
|
|
99
|
-
* console.log(count()); // 10
|
|
100
|
-
*
|
|
101
|
-
* count(x => x + 10);
|
|
102
|
-
* console.log(count()); // 20
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
export declare function asig<T>(value: T, options?: SignalOptions<T>): Asig<T>;
|
|
106
|
-
export declare function asig<T>(): Asig<T | undefined>;
|
|
107
|
-
/**
|
|
108
|
-
* @description
|
|
109
|
-
* Create an atomic getter setter pair. Short for `atom(createPair(...))`.
|
|
110
|
-
* [See documentation.](https://github.com/ReedSyllas/solid-state-tools#atomic-pairs-apair)
|
|
111
|
-
*
|
|
112
|
-
* @see {@link Accessor} (input), {@link Writer} (input), {@link PairOptions} (input), {@link Asig} (output)
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```
|
|
116
|
-
* const count = asig(0);
|
|
117
|
-
* const double = apair(() => count() * 2, (x) => count(x / 2));
|
|
118
|
-
*
|
|
119
|
-
* count(10);
|
|
120
|
-
* console.log(count(), double()); // 10 20
|
|
121
|
-
*
|
|
122
|
-
* double(100);
|
|
123
|
-
* console.log(count(), double()); // 50 100
|
|
124
|
-
* ```
|
|
125
|
-
*/
|
|
126
|
-
export declare function apair<T>(getter: Accessor<T>, setter: Writer<T>, options?: PairOptions): Asig<T>;
|