UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

175 lines (174 loc) 9.01 kB
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; }