effect-atom-svelte 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +91 -2
- package/dist/Hooks.svelte.d.ts +396 -0
- package/dist/Hooks.svelte.js +1149 -0
- package/dist/HydrationBoundary.svelte +127 -0
- package/dist/HydrationBoundary.svelte.d.ts +36 -0
- package/dist/RegistryContext.d.ts +117 -0
- package/dist/RegistryContext.js +85 -0
- package/dist/RegistryProvider.svelte +60 -0
- package/dist/RegistryProvider.svelte.d.ts +31 -0
- package/dist/ScopedAtom.d.ts +73 -0
- package/dist/ScopedAtom.js +55 -0
- package/dist/SvelteKit.d.ts +78 -0
- package/dist/SvelteKit.js +90 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/internal/hydration.d.ts +26 -0
- package/dist/internal/hydration.js +57 -0
- package/package.json +72 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jarred Norris
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# effect-atom-svelte
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Svelte 5 bindings for Effect Atom (`effect/reactivity`): hooks with a reactive `current`, async atoms you can `await` in markup, and server rendering with hydration. The API follows `@effect/atom-react`.
|
|
4
|
+
|
|
5
|
+
This is a community project by Jarred Norris. It is not part of Effect, and the Effect team neither makes nor endorses it. Most of its code and docs were written with the help of AI (Claude Opus 5.5); its behavior is covered by tests in Chromium, Firefox and WebKit.
|
|
6
|
+
|
|
7
|
+
**Documentation: [atom.jarrednorris.dev](https://atom.jarrednorris.dev)** · [API reference](https://atom.jarrednorris.dev/reference) · [GitHub](https://github.com/jarrednorrisdev/effect-atom-svelte)
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- `effect` 4.0.x. The peer range is `~4.0.0`, not `^4.0.0`: the bindings use parts of the atom registry that aren't public API, which a minor release of `effect` can change.
|
|
12
|
+
- Svelte 5.57.2 or later. `experimental.async` is needed for `useAtomSuspense`, `useAtomResult` and server rendering.
|
|
13
|
+
- SvelteKit is optional. The docs site and its tests run on SvelteKit 3. On SvelteKit 2, the error hooks in `effect-atom-svelte/sveltekit` work with less detail.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install effect-atom-svelte "effect@~4.0.0"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Turn on Svelte's async mode. In SvelteKit 3, pass it to `sveltekit()` in `vite.config.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts
|
|
25
|
+
sveltekit({
|
|
26
|
+
compilerOptions: { experimental: { async: true } },
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then put a registry around your app, in the root layout:
|
|
31
|
+
|
|
32
|
+
```svelte
|
|
33
|
+
<!-- src/routes/+layout.svelte -->
|
|
34
|
+
<script lang="ts">
|
|
35
|
+
import { RegistryProvider } from "effect-atom-svelte";
|
|
36
|
+
|
|
37
|
+
const { children } = $props();
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<RegistryProvider>{@render children()}</RegistryProvider>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
On the server, the provider creates a registry for each request, so visitors never see each other's state. Without one, the server throws `No AtomRegistry in context`. [Installation](https://atom.jarrednorris.dev/installation) covers the registry options and apps without SvelteKit.
|
|
44
|
+
|
|
45
|
+
## Example
|
|
46
|
+
|
|
47
|
+
```svelte
|
|
48
|
+
<script module lang="ts">
|
|
49
|
+
import { Effect } from "effect";
|
|
50
|
+
import { Atom } from "effect/reactivity";
|
|
51
|
+
|
|
52
|
+
const countAtom = Atom.make(0);
|
|
53
|
+
const doubledAtom = Atom.make((get) => get(countAtom) * 2);
|
|
54
|
+
const greetingAtom = Atom.make(
|
|
55
|
+
Effect.succeed("Hello from an Effect").pipe(Effect.delay("1 second"))
|
|
56
|
+
);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<script lang="ts">
|
|
60
|
+
import { useAtom, useAtomSuspense, useAtomValue } from "effect-atom-svelte";
|
|
61
|
+
|
|
62
|
+
const count = useAtom(countAtom);
|
|
63
|
+
const doubled = useAtomValue(doubledAtom);
|
|
64
|
+
const greeting = useAtomSuspense(greetingAtom);
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<button onclick={() => (count.current += 1)}>{count.current} × 2 = {doubled.current}</button>
|
|
68
|
+
|
|
69
|
+
<svelte:boundary>
|
|
70
|
+
<p>{await greeting.current}</p>
|
|
71
|
+
{#snippet pending()}<p>Loading…</p>{/snippet}
|
|
72
|
+
{#snippet failed()}<p>Something went wrong</p>{/snippet}
|
|
73
|
+
</svelte:boundary>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Hooks return an object whose `current` you read, assign or `bind:` to. Don't destructure `current`: `const { current } = useAtomValue(atom)` reads once and never updates. Coming from `@effect/atom-react`? See [Migrating from atom-react](https://atom.jarrednorris.dev/migrating-from-react).
|
|
77
|
+
|
|
78
|
+
## Learn more
|
|
79
|
+
|
|
80
|
+
- [Your first atom](https://atom.jarrednorris.dev/first-atom), [Reading and writing](https://atom.jarrednorris.dev/reading-and-writing) and [Async atoms](https://atom.jarrednorris.dev/async-atoms)
|
|
81
|
+
- [Server rendering](https://atom.jarrednorris.dev/server-rendering) and [Hydration](https://atom.jarrednorris.dev/hydration)
|
|
82
|
+
- [Effect RPC](https://atom.jarrednorris.dev/rpc) and [HttpApi](https://atom.jarrednorris.dev/http)
|
|
83
|
+
- [API reference](https://atom.jarrednorris.dev/reference) and [Troubleshooting](https://atom.jarrednorris.dev/troubleshooting)
|
|
84
|
+
- [Every export at a glance, and more recipes](https://github.com/jarrednorrisdev/effect-atom-svelte#api-at-a-glance), in the repository's README
|
|
85
|
+
|
|
86
|
+
## Versioning
|
|
87
|
+
|
|
88
|
+
The package is at 0.x, so a minor release can change the API. Every change is listed in the [changelog](https://github.com/jarrednorrisdev/effect-atom-svelte/blob/main/packages/effect-atom-svelte/CHANGELOG.md).
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
[MIT](https://github.com/jarrednorrisdev/effect-atom-svelte/blob/main/LICENSE)
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks that read, write and await atoms from Svelte 5 components.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
import { Exit } from "effect";
|
|
7
|
+
import { AsyncResult, Atom } from "effect/reactivity";
|
|
8
|
+
import type { AtomRef } from "effect/reactivity";
|
|
9
|
+
/**
|
|
10
|
+
* An atom, or a getter so the hook follows the atom when reactive state picks a different one.
|
|
11
|
+
*
|
|
12
|
+
* @stability unstable
|
|
13
|
+
* @since 0.1.0
|
|
14
|
+
* @category models
|
|
15
|
+
*/
|
|
16
|
+
export type AtomInput<A> = A | (() => A);
|
|
17
|
+
/**
|
|
18
|
+
* A reactive read-only value. Read `.current` in markup, `$derived` or `$effect` to track it.
|
|
19
|
+
*
|
|
20
|
+
* @stability unstable
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
* @category models
|
|
23
|
+
*/
|
|
24
|
+
export interface AtomValue<A> {
|
|
25
|
+
readonly current: A;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A reactive value that can also be assigned, so `bind:value={state.current}` works.
|
|
29
|
+
*
|
|
30
|
+
* @stability unstable
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
* @category models
|
|
33
|
+
*/
|
|
34
|
+
export interface AtomState<R, W = R> {
|
|
35
|
+
get current(): R;
|
|
36
|
+
set current(value: W);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How a setter from `useAtomSet` reports back: not at all, with the result, or with the `Exit`.
|
|
40
|
+
*
|
|
41
|
+
* @stability unstable
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
* @category models
|
|
44
|
+
*/
|
|
45
|
+
export type WriteMode = "value" | "promise" | "promiseExit";
|
|
46
|
+
/**
|
|
47
|
+
* Options for a promise-mode setter call.
|
|
48
|
+
*
|
|
49
|
+
* @stability unstable
|
|
50
|
+
* @since 0.1.0
|
|
51
|
+
* @category models
|
|
52
|
+
*/
|
|
53
|
+
export interface WriteOptions {
|
|
54
|
+
readonly signal?: AbortSignal | undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Reads an atom, optionally through a transform. The atom stays mounted while something reactive
|
|
58
|
+
* reads `.current`, and unmounted when nothing does. The transform runs again only when the atom,
|
|
59
|
+
* or state the transform reads, changes, so a transform that builds an object returns the same
|
|
60
|
+
* object until then. A read outside a reactive context, such as in an event handler, runs it again.
|
|
61
|
+
*
|
|
62
|
+
* **Example** (Reading an atom through a transform)
|
|
63
|
+
*
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { useAtomValue } from "effect-atom-svelte";
|
|
66
|
+
* import { countAtom } from "./atoms.ts";
|
|
67
|
+
*
|
|
68
|
+
* const doubled = useAtomValue(countAtom, (count) => count * 2);
|
|
69
|
+
* // In the markup: {doubled.current}
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @stability unstable
|
|
73
|
+
* @since 0.1.0
|
|
74
|
+
* @category hooks
|
|
75
|
+
*/
|
|
76
|
+
export declare function useAtomValue<A>(input: AtomInput<Atom.Atom<A>>): AtomValue<A>;
|
|
77
|
+
export declare function useAtomValue<A, B>(input: AtomInput<Atom.Atom<A>>, f: (value: A) => B): AtomValue<B>;
|
|
78
|
+
/**
|
|
79
|
+
* Reads and writes a writable atom through `.current`, so `bind:value={state.current}` works.
|
|
80
|
+
*
|
|
81
|
+
* **Example** (Binding an input to an atom)
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { useAtom } from "effect-atom-svelte";
|
|
85
|
+
* import { nameAtom } from "./atoms.ts";
|
|
86
|
+
*
|
|
87
|
+
* const name = useAtom(nameAtom);
|
|
88
|
+
* // In the markup: <input bind:value={name.current} />
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @stability unstable
|
|
92
|
+
* @since 0.1.0
|
|
93
|
+
* @category hooks
|
|
94
|
+
*/
|
|
95
|
+
export declare const useAtom: <R, W>(input: AtomInput<Atom.Writable<R, W>>) => AtomState<R, W>;
|
|
96
|
+
/**
|
|
97
|
+
* Keeps an atom alive for as long as this component is mounted, even when no component reads
|
|
98
|
+
* it. Use it in a component that outlives the readers, such as a layout, so the atom keeps its
|
|
99
|
+
* value while they come and go. Mounting an atom that hasn't been computed also computes it.
|
|
100
|
+
*
|
|
101
|
+
* **Example** (Keeping a connection open while a component is mounted)
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { useAtomMount } from "effect-atom-svelte";
|
|
105
|
+
* import { socketAtom } from "./atoms.ts";
|
|
106
|
+
*
|
|
107
|
+
* // Not disposed while this component lives, even though nothing reads it.
|
|
108
|
+
* useAtomMount(socketAtom);
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @stability unstable
|
|
112
|
+
* @since 0.1.0
|
|
113
|
+
* @category hooks
|
|
114
|
+
*/
|
|
115
|
+
export declare const useAtomMount: (input: AtomInput<Atom.Atom<unknown>>) => void;
|
|
116
|
+
/**
|
|
117
|
+
* Returns a setter. The atom is mounted for the component's lifetime, so an `Atom.fn` keeps its
|
|
118
|
+
* state between calls and is not disposed between set and read.
|
|
119
|
+
*
|
|
120
|
+
* In `value` mode a function passed to the setter is an updater, called with the current value. To
|
|
121
|
+
* store a function in an atom, wrap it: `set(() => handler)`.
|
|
122
|
+
*
|
|
123
|
+
* In `promise` and `promiseExit` modes the setter waits for the atom's next settled result. The
|
|
124
|
+
* wait holds the atom, so a call still in flight keeps it running after the component is
|
|
125
|
+
* destroyed, until it settles. The call's `signal` cancels only the wait: an `Atom.fn` already
|
|
126
|
+
* running keeps going (send `Atom.Interrupt` to stop it). A signal that is already aborted settles
|
|
127
|
+
* the call as interrupted without writing, as `fetch` does. Calls on one `Atom.fn` share its single
|
|
128
|
+
* result, so a new call supersedes one in flight and every pending call resolves with the latest
|
|
129
|
+
* call's result. `Atom.Reset` has no result to wait for, so these modes leave it out of their
|
|
130
|
+
* types and reject it; reset with a `value` mode setter.
|
|
131
|
+
*
|
|
132
|
+
* **Example** (Updating an atom from its current value)
|
|
133
|
+
*
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { useAtomSet } from "effect-atom-svelte";
|
|
136
|
+
* import { countAtom } from "./atoms.ts";
|
|
137
|
+
*
|
|
138
|
+
* const setCount = useAtomSet(countAtom);
|
|
139
|
+
* // In the markup: <button onclick={() => setCount((n) => n + 1)}>+</button>
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @stability unstable
|
|
143
|
+
* @since 0.1.0
|
|
144
|
+
* @category hooks
|
|
145
|
+
*/
|
|
146
|
+
export declare function useAtomSet<R, W>(input: AtomInput<Atom.Writable<R, W>>, options?: {
|
|
147
|
+
readonly mode?: "value" | undefined;
|
|
148
|
+
}): (value: W | ((current: R) => W)) => void;
|
|
149
|
+
export declare function useAtomSet<A, E, W>(input: AtomInput<Atom.Writable<AsyncResult.AsyncResult<A, E>, W>>, options: {
|
|
150
|
+
readonly mode: "promise";
|
|
151
|
+
}): (value: Exclude<W, Atom.Reset>, options?: WriteOptions) => Promise<A>;
|
|
152
|
+
export declare function useAtomSet<A, E, W>(input: AtomInput<Atom.Writable<AsyncResult.AsyncResult<A, E>, W>>, options: {
|
|
153
|
+
readonly mode: "promiseExit";
|
|
154
|
+
}): (value: Exclude<W, Atom.Reset>, options?: WriteOptions) => Promise<Exit.Exit<A, E>>;
|
|
155
|
+
export declare function useAtomSet<A, E, W>(input: AtomInput<Atom.Writable<AsyncResult.AsyncResult<A, E>, W>>, options?: {
|
|
156
|
+
readonly mode?: WriteMode | undefined;
|
|
157
|
+
}): (value: W, options?: WriteOptions) => undefined | Promise<A> | Promise<Exit.Exit<A, E>>;
|
|
158
|
+
/**
|
|
159
|
+
* Returns a function that runs the atom again. The atom is mounted so the refresh is not lost.
|
|
160
|
+
*
|
|
161
|
+
* **Example** (Running an atom again on click)
|
|
162
|
+
*
|
|
163
|
+
* ```ts
|
|
164
|
+
* import { useAtomRefresh } from "effect-atom-svelte";
|
|
165
|
+
* import { dieAtom } from "./atoms.ts";
|
|
166
|
+
*
|
|
167
|
+
* const roll = useAtomRefresh(dieAtom);
|
|
168
|
+
* // In the markup: <button onclick={roll}>Roll</button>
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @stability unstable
|
|
172
|
+
* @since 0.1.0
|
|
173
|
+
* @category hooks
|
|
174
|
+
*/
|
|
175
|
+
export declare const useAtomRefresh: (input: AtomInput<Atom.Atom<unknown>>) => (() => void);
|
|
176
|
+
/**
|
|
177
|
+
* Calls `f` on every change while the component lives, and with the current value first when
|
|
178
|
+
* `immediate` is set. The atom is computed once the component mounts, and never on the server, so a
|
|
179
|
+
* derived or effect atom that nothing else reads still runs and reports its changes. A change raised while another component
|
|
180
|
+
* is reading an atom reaches `f` on a microtask, so `f` can write `$state` (Svelte forbids that
|
|
181
|
+
* during a read). Changes after it wait their turn, so `f` sees every change in order; any other
|
|
182
|
+
* change reaches it synchronously. Nothing reaches `f` once the component is destroyed, not even a
|
|
183
|
+
* change still waiting for its microtask.
|
|
184
|
+
*
|
|
185
|
+
* **Example** (Saving every change)
|
|
186
|
+
*
|
|
187
|
+
* ```ts
|
|
188
|
+
* import { useAtomSubscribe } from "effect-atom-svelte";
|
|
189
|
+
* import { draftAtom } from "./atoms.ts";
|
|
190
|
+
*
|
|
191
|
+
* useAtomSubscribe(draftAtom, (draft) => localStorage.setItem("draft", draft));
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* @stability unstable
|
|
195
|
+
* @since 0.1.0
|
|
196
|
+
* @category hooks
|
|
197
|
+
*/
|
|
198
|
+
export declare const useAtomSubscribe: <A>(input: AtomInput<Atom.Atom<A>>, f: (value: A) => void, options?: {
|
|
199
|
+
readonly immediate?: boolean | undefined;
|
|
200
|
+
}) => void;
|
|
201
|
+
/**
|
|
202
|
+
* Sets starting values as `AtomRegistry.make({ initialValues })` would: on the atom that receives it
|
|
203
|
+
* (a wrapper such as `withRefresh` passes it to its source), kept as the value of the atom's first
|
|
204
|
+
* build, which still reads and follows the atom's sources. The atoms are not computed here: each
|
|
205
|
+
* keeps its value, uncomputed, while this component lives, and the first component to read it
|
|
206
|
+
* computes it from there, so a value set in a layout is still there when a page reads it later, and
|
|
207
|
+
* an atom that can only compute in the browser can start from a value on the server.
|
|
208
|
+
*
|
|
209
|
+
* A value applies once while the atom is held, so a component mounted later, or a second
|
|
210
|
+
* component with its own value, does not overwrite a value the atom has moved on from. Once nothing
|
|
211
|
+
* holds the atom and the registry disposes it, the next component to start it applies its value
|
|
212
|
+
* again. On the server a value lasts one render, so each request against a shared registry applies
|
|
213
|
+
* its own; requests rendering at the same time share the registry's atoms, and with them the value
|
|
214
|
+
* the first one applied.
|
|
215
|
+
*
|
|
216
|
+
* **Example** (Starting an atom from a prop)
|
|
217
|
+
*
|
|
218
|
+
* ```ts
|
|
219
|
+
* import { untrack } from "svelte";
|
|
220
|
+
* import { useAtomInitialValues, useAtomValue } from "effect-atom-svelte";
|
|
221
|
+
* import { countAtom } from "./atoms.ts";
|
|
222
|
+
*
|
|
223
|
+
* const { start } = $props();
|
|
224
|
+
* // Only the first value counts, so untrack says a later change to the prop is not followed.
|
|
225
|
+
* useAtomInitialValues([[countAtom, untrack(() => start)]]);
|
|
226
|
+
* const count = useAtomValue(countAtom);
|
|
227
|
+
* ```
|
|
228
|
+
*
|
|
229
|
+
* @stability unstable
|
|
230
|
+
* @since 0.1.0
|
|
231
|
+
* @category hooks
|
|
232
|
+
*/
|
|
233
|
+
export declare const useAtomInitialValues: (initialValues: Iterable<readonly [Atom.Atom<unknown>, unknown]>) => void;
|
|
234
|
+
/**
|
|
235
|
+
* Reads an `AtomRef`, following it when the getter returns a different ref. For one property of a
|
|
236
|
+
* ref, use `useAtomRefPropValue`.
|
|
237
|
+
*
|
|
238
|
+
* A ref has no registry. On the server, one created at module level is shared by every request,
|
|
239
|
+
* so don't write a visitor's data to it there: see
|
|
240
|
+
* https://atom.jarrednorris.dev/server-rendering#module-state-is-shared-between-visitors
|
|
241
|
+
*
|
|
242
|
+
* **Example** (Reading a ref)
|
|
243
|
+
*
|
|
244
|
+
* ```ts
|
|
245
|
+
* import { useAtomRef } from "effect-atom-svelte";
|
|
246
|
+
* import { profile } from "./refs.ts";
|
|
247
|
+
*
|
|
248
|
+
* const current = useAtomRef(profile);
|
|
249
|
+
* // In the markup: {current.current.name}
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* @stability unstable
|
|
253
|
+
* @since 0.1.0
|
|
254
|
+
* @category hooks
|
|
255
|
+
*/
|
|
256
|
+
export declare const useAtomRef: <A>(input: AtomInput<AtomRef.ReadonlyRef<A>>) => AtomValue<A>;
|
|
257
|
+
/**
|
|
258
|
+
* Returns `ref.prop(prop)`, the `AtomRef` for one property of an `AtomRef`. It takes the ref
|
|
259
|
+
* itself, not a getter, and does not follow later changes to `ref` or `prop`; to read a property
|
|
260
|
+
* of a ref picked by a getter, use `useAtomRefPropValue`.
|
|
261
|
+
*
|
|
262
|
+
* **Example** (Writing one property of a ref)
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* import { useAtomRefProp } from "effect-atom-svelte";
|
|
266
|
+
* import { profile } from "./refs.ts";
|
|
267
|
+
*
|
|
268
|
+
* const name = useAtomRefProp(profile, "name");
|
|
269
|
+
* const rename = (next: string) => name.set(next); // profile.value.name changes too
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* @stability unstable
|
|
273
|
+
* @since 0.1.0
|
|
274
|
+
* @category hooks
|
|
275
|
+
*/
|
|
276
|
+
export declare const useAtomRefProp: <A, K extends keyof A>(ref: AtomRef.AtomRef<A>, prop: K) => AtomRef.AtomRef<A[K]>;
|
|
277
|
+
/**
|
|
278
|
+
* Reads one property of an `AtomRef`, following the getter to a different ref. `prop` is taken
|
|
279
|
+
* once, when the component starts, and is not followed.
|
|
280
|
+
*
|
|
281
|
+
* **Example** (Reading one property of a ref)
|
|
282
|
+
*
|
|
283
|
+
* ```ts
|
|
284
|
+
* import { useAtomRefPropValue } from "effect-atom-svelte";
|
|
285
|
+
* import { profile } from "./refs.ts";
|
|
286
|
+
*
|
|
287
|
+
* const name = useAtomRefPropValue(profile, "name");
|
|
288
|
+
* // In the markup: {name.current}
|
|
289
|
+
* ```
|
|
290
|
+
*
|
|
291
|
+
* @stability unstable
|
|
292
|
+
* @since 0.1.0
|
|
293
|
+
* @category hooks
|
|
294
|
+
*/
|
|
295
|
+
export declare const useAtomRefPropValue: <A, K extends keyof A>(input: AtomInput<AtomRef.AtomRef<A>>, prop: K) => AtomValue<A[K]>;
|
|
296
|
+
/**
|
|
297
|
+
* Options for `useAtomResult`.
|
|
298
|
+
*
|
|
299
|
+
* @stability unstable
|
|
300
|
+
* @since 0.1.0
|
|
301
|
+
* @category models
|
|
302
|
+
*/
|
|
303
|
+
export interface ResultOptions {
|
|
304
|
+
/** Treat a refreshing result as pending, so the first await also waits for the refresh. */
|
|
305
|
+
readonly suspendOnWaiting?: boolean | undefined;
|
|
306
|
+
/**
|
|
307
|
+
* Run a server-rendered atom again once the page has hydrated. Overrides `RegistryProvider`'s
|
|
308
|
+
* `revalidateOnHydrate`, which defaults to `false`.
|
|
309
|
+
*/
|
|
310
|
+
readonly revalidateOnHydrate?: boolean | undefined;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Awaits an async atom's first result, then returns a live handle to its `AsyncResult`. Use it as
|
|
314
|
+
* a top-level `await` in a component script; server rendering waits for it, and with a
|
|
315
|
+
* serialization key the browser starts from the server's result instead of running the atom
|
|
316
|
+
* again. With a getter, only the first atom is awaited: when the getter picks another atom the
|
|
317
|
+
* handle follows it, starting from that atom's current result (often `Initial`), and the
|
|
318
|
+
* component's await does not run again. On the server, an atom with a `withServerValue` override
|
|
319
|
+
* reads as that value and is never computed. An atom that has not started, whose result is
|
|
320
|
+
* `Initial` with nothing running, as an `Atom.fn` nothing has called, keeps the await pending in the
|
|
321
|
+
* browser until something writes it; on the server, where nothing will during the render, the
|
|
322
|
+
* await rejects.
|
|
323
|
+
*
|
|
324
|
+
* **Example** (Awaiting an atom picked by a prop)
|
|
325
|
+
*
|
|
326
|
+
* ```ts
|
|
327
|
+
* import { useAtomResult } from "effect-atom-svelte";
|
|
328
|
+
* import { todoAtom } from "./atoms.ts";
|
|
329
|
+
*
|
|
330
|
+
* const { id } = $props();
|
|
331
|
+
* const todo = await useAtomResult(() => todoAtom(id));
|
|
332
|
+
* // todo.current is the AsyncResult, a Success or a Failure by now
|
|
333
|
+
* ```
|
|
334
|
+
*
|
|
335
|
+
* @stability unstable
|
|
336
|
+
* @since 0.1.0
|
|
337
|
+
* @category async
|
|
338
|
+
*/
|
|
339
|
+
export declare const useAtomResult: <A, E>(input: AtomInput<Atom.Atom<AsyncResult.AsyncResult<A, E>>>, options?: ResultOptions) => Promise<AtomValue<AsyncResult.AsyncResult<A, E>>>;
|
|
340
|
+
/**
|
|
341
|
+
* Options for `useAtomSuspense`.
|
|
342
|
+
*
|
|
343
|
+
* @stability unstable
|
|
344
|
+
* @since 0.1.0
|
|
345
|
+
* @category models
|
|
346
|
+
*/
|
|
347
|
+
export interface SuspenseOptions {
|
|
348
|
+
/**
|
|
349
|
+
* Treat a refreshing result as pending, so `await` waits for the refreshed value instead of
|
|
350
|
+
* resolving with the current one. The boundary keeps showing its content meanwhile; use
|
|
351
|
+
* `$effect.pending()` to show progress.
|
|
352
|
+
*/
|
|
353
|
+
readonly suspendOnWaiting?: boolean | undefined;
|
|
354
|
+
/** Resolve with the Success or Failure result instead of the value, rather than rejecting. */
|
|
355
|
+
readonly includeFailure?: boolean | undefined;
|
|
356
|
+
/**
|
|
357
|
+
* Run a server-rendered atom again once the page has hydrated. Overrides `RegistryProvider`'s
|
|
358
|
+
* `revalidateOnHydrate`, which defaults to `false`.
|
|
359
|
+
*/
|
|
360
|
+
readonly revalidateOnHydrate?: boolean | undefined;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Exposes an async atom as a promise for `await` in markup or `$derived(await ...)`. The promise is
|
|
364
|
+
* stable while the result is unchanged, and a new one is issued when the result changes, so Svelte
|
|
365
|
+
* re-runs dependents only on real updates. Unchanged means the same atom and the same result
|
|
366
|
+
* object: a refresh issues a new promise even if its value is equal, and so does a read after
|
|
367
|
+
* every reader of a pending promise went away, as that wait was interrupted. Failures reject with the squashed cause, or resolve with
|
|
368
|
+
* the `Failure` when `includeFailure` is set. On the server, an atom with a `withServerValue`
|
|
369
|
+
* override resolves from that value and is never computed; if the value is `Initial`, the promise
|
|
370
|
+
* rejects, so read it inside a `<svelte:boundary>` with a `pending` snippet. An atom that has not
|
|
371
|
+
* started, whose result is `Initial` with nothing running, as an `Atom.fn` nothing has called,
|
|
372
|
+
* stays pending in the browser until something writes it, and rejects on the server.
|
|
373
|
+
*
|
|
374
|
+
* **Example** (Awaiting an atom in the markup)
|
|
375
|
+
*
|
|
376
|
+
* ```ts
|
|
377
|
+
* import { useAtomSuspense } from "effect-atom-svelte";
|
|
378
|
+
* import { todosAtom } from "./atoms.ts";
|
|
379
|
+
*
|
|
380
|
+
* const todos = useAtomSuspense(todosAtom);
|
|
381
|
+
* // In the markup, inside a <svelte:boundary>: {#each await todos.current as todo}
|
|
382
|
+
* ```
|
|
383
|
+
*
|
|
384
|
+
* @stability unstable
|
|
385
|
+
* @since 0.1.0
|
|
386
|
+
* @category async
|
|
387
|
+
*/
|
|
388
|
+
export declare function useAtomSuspense<A, E>(input: AtomInput<Atom.Atom<AsyncResult.AsyncResult<A, E>>>, options?: SuspenseOptions & {
|
|
389
|
+
readonly includeFailure?: false | undefined;
|
|
390
|
+
}): AtomValue<Promise<A>>;
|
|
391
|
+
export declare function useAtomSuspense<A, E>(input: AtomInput<Atom.Atom<AsyncResult.AsyncResult<A, E>>>, options: SuspenseOptions & {
|
|
392
|
+
readonly includeFailure: true;
|
|
393
|
+
}): AtomValue<Promise<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>;
|
|
394
|
+
export declare function useAtomSuspense<A, E>(input: AtomInput<Atom.Atom<AsyncResult.AsyncResult<A, E>>>, options?: SuspenseOptions & {
|
|
395
|
+
readonly includeFailure?: boolean | undefined;
|
|
396
|
+
}): AtomValue<Promise<A | AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>;
|