shelving/storemodule

Observable value containers for reactive state. A Store<T> holds a single current value, broadcasts changes to all active consumers, and integrates with React Suspense out of the box. Stores suppress duplicate emissions using deep equality, so consumers only see genuine changes.

Concepts

Loading state — a store starts in a loading state represented internally by the NONE sentinel. Reading Store.value while loading throws a Promise (the store's internal DeferredSequence), which React Suspense catches and waits on. Reading Store.loading is safe and never throws.

Error state — setting Store.reason puts the store in an error state. Subsequent reads of .value throw that reason, which React error boundaries can catch.

Async iterationStore<T> implements AsyncIterable<T>. Iterating with for await...of first emits the current value (if one exists), then emits each subsequent value as it changes. The iterator blocks between values using the store's internal DeferredSequence.

Duplicate suppression — deep equality is checked before emitting. Setting the same value twice only triggers one emission.

Startersstore.starter accepts a function that runs when the store has at least one active iterator and stops when none remain. Use this to wire up external subscriptions (e.g. a database realtime feed) that should only be active while something is listening.

Subclasses

ClassDescription
DataStore<T>Adds DataStore.data, DataStore.update(), DataStore.get(), DataStore.set() helpers for object values.
OptionalDataStore<T>Like DataStore but the value may be undefined; OptionalDataStore.exists and OptionalDataStore.require() handle the absent case.
ArrayStore<T>Stores an array; adds ArrayStore.first, ArrayStore.last, ArrayStore.count, ArrayStore.add(), ArrayStore.delete(), ArrayStore.toggle().
DictionaryStore<T>Stores a string-keyed object; adds DictionaryStore.get(), DictionaryStore.set(), DictionaryStore.delete(), DictionaryStore.update().
BooleanStoreStores a boolean; adds BooleanStore.toggle().
PathStoreStores an absolute path; adds PathStore.isActive() / PathStore.isProud() route helpers.
URLStoreStores a URL; adds query-param helpers and URLStore.isActive() / URLStore.isProud().
BusyStore<T>Adds a BusyStore.busy boolean store that is true while awaiting a value.
FetchStore<T>Fetches its value from a callback; adds FetchStore.refresh() / FetchStore.invalidate().
PayloadFetchStore<P, R>A FetchStore driven by a PayloadFetchStore.payload store — changing the payload re-fetches.

ItemStore and QueryStore in the shelving/db module extend FetchStore directly, adding database-aware fetch and subscription logic.

Usage

Every store shares the same core: set .value, read it back (or for await it), and consumers see the change. The base Store page covers the full lifecycle; each subclass page covers its own helpers.

As an integration example, Store.through() bridges any AsyncIterable source into a store — it sets the store's value for each item yielded and re-yields it:

ts
import { Store, NONE } from "shelving/store";

const store = new Store<number>(NONE);

async function connect(stream: AsyncIterable<number>) {
  for await (const _ of store.through(stream)) {
    // store.value is updated on each iteration; any consumers re-render
  }
}

Classes

Go

Storeclass

Store that retains its most recent value and is async-iterable to allow values to be observed.

new Store<T, TT>(value: StoreInternal<T>)
Go

FetchStoreclass

Store that fetches its values from a remote source on demand.

new FetchStore<T, TT>(value: T | typeof NONE, callback?: FetchCallback<TT>)
Go

DataStoreclass

Store a data object, with helpers to read and update individual props.

new DataStore<T>()
Go

PayloadFetchStoreclass

Store that fetches its values from a remote source by sending it a payload.

new PayloadFetchStore<P, R>(payload: P | typeof NONE, value: R | typeof NONE, callback?: PayloadFetchCallback<P, R>, debounce = 0)
Go

BusyStoreclass

Store that tracks its busy status via a separate this.busy store.

new BusyStore<T, TT>()

Types

Go

AnyStoretype

Any Store instance.

Store<any, any>
Go

StoreInputtype

Synchronous values that a store natively knows how to process as inputs.

I | typeof SKIP | typeof NONE
Go

AsyncStoreInputtype

Synchronous or asynchronous values that a store natively knows how to process as inputs.

StoreInput<I> | PromiseLike<StoreInput<I>>
Go

StoreInternaltype

Internal storage value for a store.

O | typeof NONE
Go

StoreCallbacktype

Callback that sets a store's value (possibly asynchronously).

(...args: A) => AsyncStoreInput<I>
Go

StoreReducertype

Reducer that receives a store's current value and returns the store's next value (possibly asynchronously).

(value: O, ...args: A) => AsyncStoreInput<I>
Go

FetchCallbacktype

Callback that fetches the next value for a FetchStore.

(signal: AbortSignal) => StoreInput<T> | PromiseLike<StoreInput<T>>
Go

PayloadFetchCallbacktype

Callback that fetches the next value for a PayloadFetchStore from a payload.

(payload: P, signal: AbortSignal) => AsyncStoreInput<R>

Constants

Go

ALWAYS_REFRESHconstant

maxAge of zero passed to refresh() means "always refresh this value" (this is the default).

Go

AVOID_REFRESHconstant

maxAge of Infinity passed to refresh() means "only refresh if this value is not loaded or invalid".