Loading...
Loading...
Async Svelte stores (svas): fetching, caching, revalidation, persistence. Use when UI presents data from the API.
npx skill4agent add temich/svas svastype Maybe<T, E extends Error = Error> = null | T | E
// null → no data yet (loading / cleared / not fetched)
// T → resolved value
// E → fetch error (errors are values, never thrown)import {
value, values, collection, // stores
ok, ensure, having, awaited, once, // guards & awaiting
combined, sync, Async, // compose & render
type Maybe
} from 'svas'| Option | Meaning |
|---|---|
| fetcher returning |
| ms before re-fetch (default |
| mirror into local storage under this key |
| use |
| keep previous value visible while revalidating (default |
| |
| initial value when nothing is persisted |
persistbindvalue<T>Writable<T | null>const me = value<User>({ get: () => api.me(), persist: 'user' })
const seen = value<number>({ persist: 'seen', default: 0 }) // transient/local-onlyset(v)update(fn)extract(): T | nullsync()values<T>Readable<Maybe<T>>const products = values<Product>({ get: (id) => api.product(id), stale: true, persist: 'products' })
const one = products.get('42') // Readable<Maybe<Product>>, fetches if missing
products.set('42', next) // write by key
products.extract('42') // Product | null, syncget(key, { fetch? })set(key, v, { stash? })reset(key)extract(key)delete(key)clear()permanentstash: truesetreset(key)collection<T>IdentifiableReadable<Maybe<T[]>>{ id: string }valuesget(id)const todos = collection<Todo>({
get: () => api.todos(),
values: values<Todo>(), // enables todos.get(id) / extract(id)
stale: true,
persist: 'todos',
bind: session
})subscribeadd(item)set(item, { add? })addupdate(id, fn, opts?)delete(id)replace(items)get(id)extract(id)valuessync(): thisfetch(): Promise<this>valuesget(id)?.Maybe<T>Errorok($store) // boolean type guard: narrows Maybe<T> → T (excludes null | Error)
ensure(store) // sync read, THROWS if null/Error — use when value must exist now (e.g. unsafe net requests in response to user action)
store.extract(key?) // sync read, T | null (ignores errors) — never throwsif (ok($todos)) $todos.length // typed as T
items.filter((i) => ok(i.account) && !i.account.deleted) // ✅ guard before accessawait having(store) // first non-null value; REJECTS on Error — services needing auth
await awaited(store) // first non-null value; returns Error as a value (never rejects)
await once(store, (v) => v === 'ready') // first value satisfying a condition<Async>Maybe<Async store={todos}>
{#snippet awaited(todos)}
<!-- here todos are T[] -->
{#each todos as t}<Row {t} />{/each}
{/snippet}
{#snippet waiting()}<Spinner />{/snippet} <!-- optional; default loader otherwise -->
{#snippet error(e)}<Err {e} />{/snippet} <!-- optional; default error UI otherwise -->
</Async>storeawaitedwaitingerrorsilentcombinedcombined(...stores)MaybeErrornull<Async store={combined(account, todos)}>
{#snippet awaited([account, todos])}
<!-- here account is U and todos is T[] -->
<Profile {account} />
{#each todos as t}<Row {t} />{/each}
{/snippet}
</Async>sync(store, item, { delete? })collectionvalue_version_deleteddelete: falseevents.on('todos.sync', (todo) => sync(todos, todo))T | Errorinstanceof Errortry/catchok()ensure()extract()?.Maybe<T>$store<Async>gethaving.subscribeMaybe