shelving
Version:
Toolkit for using data in JavaScript.
175 lines (174 loc) • 9.01 kB
TypeScript
import { DeferredSequence } from "../sequence/DeferredSequence.js";
import { NONE, SKIP } from "../util/constants.js";
import type { AnyCaller, Arguments, Callback, ErrorCallback, ValueCallback } from "../util/function.js";
import { type PossibleStarter, type StopCallback } from "../util/start.js";
/** Any `Store` instance. */
export type AnyStore = Store<any, any>;
/** Values that a store natively knows how to process as inputs. */
export type StoreInput<I> = I | typeof SKIP | typeof NONE;
/** Values that a store natively knows how to process as inputs. */
export type AsyncStoreInput<I> = StoreInput<I> | PromiseLike<StoreInput<I>>;
/** Internal storage value for a store. */
export type StoreInternal<O> = O | typeof NONE;
/** Callback that sets a store's value (possibly asynchronously). */
export type StoreCallback<I, A extends Arguments = []> = (...args: A) => AsyncStoreInput<I>;
/** Reducer that receives a store's current value and sets the stores next value (possibly asynchronously). */
export type StoreReducer<I, O, A extends Arguments = []> = (value: O, ...args: A) => AsyncStoreInput<I>;
/**
* Store that retains its most recent value and is async-iterable to allow values to be observed.
* - Current value can be read at `store.value` and `store.data`
* - Stores also send their most-recent value to any new subscribers immediately when a new subscriber is added.
* - Stores can also be in a loading store where they do not have a current value.
*
* @param initial The initial value for this store, a `Promise` that resolves to the initial value, a source `Subscribable` to subscribe to, or another `Store` instance to take the initial value from and subscribe to.
* - To set this store to be loading, use the `NONE` constant or a `Promise` value.
* - To set this store to an explicit value, use that value or another `Store` instance with a value.
*
* @param T The "main type" for this store.
* - Indicates what values `this.value` will return.
* - Methods that set values like `this.call()` and `this.await()` can also accept these values.
* @param TT The "input type" for this store.
* - Indicates what additional input types `this.value` can convert to `T`
* - Defaults to `T` (no conversion).
* - Override conversion by overriding `this._convert(v: TT): T`
* - Warning: With no override, default behaviour is to just assert TT is T (unsafe).
*/
export declare class Store<T, TT = T> implements AsyncIterable<T, void, void>, AsyncDisposable {
/** Deferred sequence this store uses to issue values as they change. */
readonly next: DeferredSequence<T, void, void>;
/**
* Snapshot returns either the current reason or the current value (or `NONE` if reason is unset).
*/
get snapshot(): unknown;
/**
* Store is considered to be "loading" if it has no value or error.
* - Calling `this.value` will throw `this.reason` if there's an error reason set, or a `Promise` if there's no value set.
* - Calling `this.loading` is a way to check if this store has a value without triggering those throws.
*/
get loading(): boolean;
/**
* Set the value of this store.
* - Sets any sync values.
* - Awaits any async values.
* - Setting value the `NONE` symbol indicates the store has no value so should be in a "loading" state.
* - Setting value to `SKIP` indicates the value should be silently ignored (sometimes it's helpful to have a way to skip a write entirely).
* - Setting value to the same as the existing value
* - If this store has any pending `await()` calls they are aborted and their results are silently discarded.
*/
set value(input: AsyncStoreInput<TT>);
/** Write a synchronous value to this store. */
write(input: StoreInput<TT>): void;
/**
* Convert input type to internal storage type.
* - Override in subclasses to change conversion behaviour.
* - Warning: With no override, default behaviour is to just assert TT is T (unsafe).
*/
protected _convert(input: TT, _caller?: AnyCaller): T;
/** Compare two values for this store and return whether they are equal. */
protected _equal(a: T, b: T): boolean;
/** Internal storage for current value. */
private _value;
/**
* Get the current value of this store.
*
* @throws {Promise} if this store currently is in a "loading" state (resolves when a value is set).
* @throws {unknown} if this store currently has an error.
*/
get value(): T;
/**
* Called to read values. Can be used to override get behaviour.
* - Override in subclasses to change getting behaviour.
* - Note: doesn't throw `reason` if there is one!
*/
read(): StoreInternal<T>;
/**
* Time (in milliseconds) this store was last updated with a new value.
* - Will be `undefined` if the value is still loading.
*/
get time(): number | undefined;
private _time;
/**
* How old this store's value is (in milliseconds).
* - Will be `Infinity` if the value is still loading (to simplify downstream calculations).
*
* @example if (store.age > MINUTE) refreshStore(store);
*/
get age(): number;
/**
* Whether this store is stale based on a `maxAge` value in milliseconds.
*
* @param maxAge The maximum age for the stale check.
* - `0` zero means "always refresh" (this is the default).
* - `Infinity` means "refresh only if store is still in a loading state"
* - Any other value may or may not be stale based on `this.age`
*/
stale(maxAge: number): boolean;
/** Current error of this store, or `undefined` if there is no error. */
get reason(): unknown;
set reason(reason: unknown);
private _reason;
/**
* Set a starter for this store to allow a function to execute when this store has subscribers or not.
*
* @todo DH: Change this significantly. Not happy with how it's settable like this. It should be set in `constructor()`?
* - Also would love some internal hooks
*/
protected set starter(start: PossibleStarter<[this]>);
private _starter;
/** Store is initiated with a value, or `NONE` to put it in a "loading" state. */
constructor(value: StoreInternal<T>);
/** Set the value of this store as values are pulled from a sequence. */
through(sequence: AsyncIterable<TT>): AsyncIterable<TT>;
/**
* Call a callback and save the returned value to this store.
* - If the callback returns an async value, it is awaited and values/errors will be saved.
*/
call<A extends Arguments>(callback: StoreCallback<TT, A>, ...args: A): Promise<boolean> | boolean;
/**
* Send the current value to a callback and save the returned value to this store.
* - If the callback returns an async value, it is awaited and values/errors will be saved.
*/
reduce<A extends Arguments>(reducer: StoreReducer<TT, T, A>, ...args: A): Promise<boolean> | boolean;
/**
* Run a callback and ignore any returned value.
* - If the callback returns an async value, it is awaited and errors will be saved.
*/
run<A extends Arguments>(callback: (...args: A) => void, ...args: A): Promise<boolean> | boolean;
/**
* Send the current value to a callback and ignore any returned value.
* - If the callback returns an async value, it is awaited and errors will be saved.
*/
send<A extends Arguments>(callback: (value: T, ...args: A) => void, ...args: A): Promise<boolean> | boolean;
/**
* Await an async value and save it to this store.
* - Saves the resolved value.
* - If it rejects saves the rejection as `reason`.
* - Silently discarded if a newer value is set.
* - Silently discarded if `await()` is called again.
* - Silently discarded if `abort()` is called.
*
* @param pending The pending value to await.
*
* @returns {true} If the callback returned a value and it was set.
* @returns {false} If the callback threw.
* @returns {Promise<true>} If the callback returned a promise and it resolved.
* @returns {Promise<false>} If the callback returned a promise and it rejected, or `abort()` was called before it resolved.
*
* @throws {never} Never throws — safe to call without handling the return value.
*/
await(pending: PromiseLike<StoreInput<TT>>): Promise<boolean>;
private _pendingValue;
/**
* Abort any current pending `await()` call.
* - The pending call's result will be silently discarded and its error will not be stored.
*/
abort(): void;
[Symbol.asyncIterator](): AsyncIterator<T, void, void>;
private _iterating;
[Symbol.asyncDispose](): Promise<void>;
/**
* Subscribe to this store with handlers.
* - Returns a `StopCallback` to stop the subscription.
*/
subscribe(onNext?: ValueCallback<T>, onError?: ErrorCallback, onReturn?: Callback): StopCallback;
}