@amadeus-it-group/tansu
Version:
tansu is a lightweight, push-based framework-agnostic state management library. It borrows the ideas and APIs originally designed and implemented by Svelte stores and extends them with computed and batch.
309 lines (307 loc) • 13.7 kB
TypeScript
/**
* tansu is a lightweight, push-based state management library.
* It borrows the ideas and APIs originally designed and implemented by {@link https://github.com/sveltejs/rfcs/blob/master/text/0002-reactive-stores.md | Svelte stores}.
*
* @packageDocumentation
*/
import { rawStoreSymbol, symbolObservable } from './internal/exposeRawStores';
import { RawStoreWritable } from './internal/storeWritable';
import type { AsyncDeriveFn, AsyncDeriveOptions, OnUseFn, Readable, ReadableSignal, StoreInput, StoreOptions, StoresInput, StoresInputValues, Subscriber, SyncDeriveFn, SyncDeriveOptions, UnsubscribeFunction, UnsubscribeObject, Unsubscriber, Updater, WritableSignal } from './types';
export { batch } from './internal/batch';
export { equal } from './internal/equal';
export { symbolObservable } from './internal/exposeRawStores';
export { untrack } from './internal/untrack';
export type * from './types';
/**
* Returns a wrapper (for the given store) which only exposes the {@link ReadableSignal} interface.
* This converts any {@link StoreInput} to a {@link ReadableSignal} and exposes the store as read-only.
*
* @param store - store to wrap
* @returns A wrapper which only exposes the {@link ReadableSignal} interface.
*/
export declare function asReadable<T>(store: StoreInput<T>): ReadableSignal<T>;
/**
* Returns a wrapper (for the given store) which only exposes the {@link ReadableSignal} interface and
* also adds the given extra properties on the returned object.
*
* @param store - store to wrap
* @param extraProp - extra properties to add on the returned object
* @returns A wrapper which only exposes the {@link ReadableSignal} interface and the given extra properties.
*/
export declare function asReadable<T, U>(store: StoreInput<T>, extraProp: U): ReadableSignal<T> & Omit<U, keyof Readable<T>>;
/**
* Returns a wrapper (for the given store) which only exposes the {@link WritableSignal} interface.
* When the value is changed from the given wrapper, the provided set function is called.
*
* @param store - store to wrap
* @param set - function that will be called when the value is changed from the wrapper
* (through the {@link Writable.set|set} or the {@link Writable.update|update} function).
* If set is not specified, a noop function is used (so the value of the store cannot be changed
* from the returned wrapper).
* @returns A wrapper which only exposes the {@link WritableSignal} interface.
*/
export declare function asWritable<T, W = T>(store: StoreInput<T>, set?: WritableSignal<T, W>['set']): WritableSignal<T, W>;
/**
* Returns a wrapper (for the given store) which only exposes the {@link WritableSignal} interface and
* also adds the given extra properties on the returned object.
*
* @param store - store to wrap
* @param extraProps - object containing the extra properties to add on the returned object,
* and optionally the {@link Writable.set|set} and the {@link Writable.update|update} function of the
* {@link WritableSignal} interface.
* If the set function is not specified, a noop function is used.
*
* If the update function is not specified, a default function that calls set is used.
* @returns A wrapper which only exposes the {@link WritableSignal} interface and the given extra properties.
*/
export declare function asWritable<T, U, W = T>(store: StoreInput<T>, extraProps: U & Partial<Pick<WritableSignal<T, W>, 'set' | 'update'>>): WritableSignal<T, W> & Omit<U, keyof WritableSignal<T, W>>;
/**
* A utility function to get the current value from a given store.
* It works by subscribing to a store, capturing the value (synchronously) and unsubscribing just after.
*
* @param store - a store from which the current value is retrieved.
*
* @example
* ```typescript
* const myStore = writable(1);
* console.log(get(myStore)); // logs 1
* ```
*/
export declare const get: <T>(store: StoreInput<T>) => T;
/**
* Base class that can be extended to easily create a custom {@link Readable} store.
*
* @example
* ```typescript
* class CounterStore extends Store {
* constructor() {
* super(1); // initial value
* }
*
* reset() {
* this.set(0);
* }
*
* increment() {
* this.update(value => value + 1);
* }
* }
*
* const store = new CounterStore(1);
*
* // logs 1 (initial value) upon subscription
* const unsubscribe = store.subscribe((value) => {
* console.log(value);
* });
* store.increment(); // logs 2
* store.reset(); // logs 0
*
* unsubscribe(); // stops notifications and corresponding logging
* ```
*/
export declare abstract class Store<T> implements Readable<T> {
/**
*
* @param value - Initial value of the store
*/
constructor(value: T);
/** @internal */
[rawStoreSymbol]: RawStoreWritable<T>;
/**
* Compares two values and returns true if they are equal.
* It is called when setting a new value to avoid doing anything
* (such as notifying subscribers) if the value did not change.
* The default logic is to return false if `a` is a function or an object,
* or if `a` and `b` are different according to `Object.is`.
* This method can be overridden by subclasses to change the logic.
*
* @remarks
* For backward compatibility, the default implementation calls the
* deprecated {@link Store.notEqual} method and returns the negation
* of its return value.
*
* @param a - First value to compare.
* @param b - Second value to compare.
* @returns true if a and b are considered equal.
*/
protected equal(a: T, b: T): boolean;
/**
* Compares two values and returns true if they are different.
* It is called when setting a new value to avoid doing anything
* (such as notifying subscribers) if the value did not change.
* The default logic is to return true if `a` is a function or an object,
* or if `a` and `b` are different according to `Object.is`.
* This method can be overridden by subclasses to change the logic.
*
* @remarks
* This method is only called by the default implementation of
* {@link Store.equal}, so overriding {@link Store.equal} takes
* precedence over overriding notEqual.
*
* @deprecated Use {@link Store.equal} instead
* @param a - First value to compare.
* @param b - Second value to compare.
* @returns true if a and b are considered different.
*/
protected notEqual(a: T, b: T): boolean;
/**
* Replaces store's state with the provided value.
* Equivalent of {@link Writable.set}, but internal to the store.
*
* @param value - value to be used as the new state of a store.
*/
protected set(value: T): void;
get(): T;
/**
* Updates store's state by using an {@link Updater} function.
* Equivalent of {@link Writable.update}, but internal to the store.
*
* @param updater - a function that takes the current state as an argument and returns the new state.
*/
protected update(updater: Updater<T>): void;
/**
* Function called when the number of subscribers changes from 0 to 1
* (but not called when the number of subscribers changes from 1 to 2, ...).
* If a function is returned, it will be called when the number of subscribers changes from 1 to 0.
*
* @example
*
* ```typescript
* class CustomStore extends Store {
* onUse() {
* console.log('Got the fist subscriber!');
* return () => {
* console.log('All subscribers are gone...');
* };
* }
* }
*
* const store = new CustomStore();
* const unsubscribe1 = store.subscribe(() => {}); // logs 'Got the fist subscriber!'
* const unsubscribe2 = store.subscribe(() => {}); // nothing is logged as we've got one subscriber already
* unsubscribe1(); // nothing is logged as we still have one subscriber
* unsubscribe2(); // logs 'All subscribers are gone...'
* ```
*/
protected onUse?(): Unsubscriber | void;
/**
* Default Implementation of the {@link SubscribableStore.subscribe}, not meant to be overridden.
* @param subscriber - see {@link SubscribableStore.subscribe}
*/
subscribe(subscriber: Subscriber<T>): UnsubscribeFunction & UnsubscribeObject;
[symbolObservable](): this;
}
/**
* A convenience function to create {@link Readable} store instances.
* @param value - Initial value of a readable store.
* @param options - Either an object with {@link StoreOptions | store options}, or directly the onUse function.
*
* The onUse function is a function called when the number of subscribers changes from 0 to 1
* (but not called when the number of subscribers changes from 1 to 2, ...).
*
* If a function is returned, it will be called when the number of subscribers changes from 1 to 0.
*
* @example Sample with an onUse function
* ```typescript
* const clock = readable("00:00", setState => {
* const intervalID = setInterval(() => {
* const date = new Date();
* setState(`${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`);
* }, 1000);
*
* return () => clearInterval(intervalID);
* });
* ```
*/
export declare function readable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): ReadableSignal<T>;
/**
* A convenience function to create {@link Writable} store instances.
* @param value - initial value of a new writable store.
* @param options - Either an object with {@link StoreOptions | store options}, or directly the onUse function.
*
* The onUse function is a function called when the number of subscribers changes from 0 to 1
* (but not called when the number of subscribers changes from 1 to 2, ...).
*
* If a function is returned, it will be called when the number of subscribers changes from 1 to 0.
*
* @example
* ```typescript
* const x = writable(0);
*
* x.update(v => v + 1); // increment
* x.set(0); // reset back to the default value
* ```
*/
export declare function writable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): WritableSignal<T>;
export declare abstract class DerivedStore<T, S extends StoresInput = StoresInput> extends Store<T> {
constructor(stores: S, initialValue: T);
protected abstract derive(values: StoresInputValues<S>): Unsubscriber | void;
}
/**
* A convenience function to create a new store with a state computed from the latest values of dependent stores.
* Each time the state of one of the dependent stores changes, a provided derive function is called to compute a new, derived state.
*
* @param stores - a single store or an array of dependent stores
* @param options - either an object with store options, including a derive function, or the derive function itself directly.
* The derive function is used to compute a new state based on the latest values of dependent stores.
*
* Alternatively, this function can accept a second argument, `set`, to manage asynchronous values.
* If you return a function from the callback, it will be called when the callback runs again, or the last subscriber unsubscribes.
*
* @example synchronous
* ```typescript
* const x$ = writable(2);
* const y$ = writable(3);
* const sum$ = derived([x$, y$], ([x, y]) => x + y);
*
* // will log 5 upon subscription
* sum$.subscribe((value) => {
* console.log(value)
* });
*
* x$.set(3); // will re-evaluate the `([x, y]) => x + y` function and log 6 as this is the new state of the derived store
*
* ```
*
* @example asynchronous
* ```typescript
* const x$ = writable(2);
* const y$ = writable(3);
*
* const sum$ = derived([x$, $y], ([x, y], set) => {
* const timeoutId = setTimeout(() => set(x + y)));
* return () => clearTimeout(timeoutId);
* }, <number>undefined);
*
* // will log undefined (the default value), then 5 asynchronously
* sum$.subscribe((value) => {
* console.log(value)
* });
*
* ```
*/
export declare function derived<T, S extends StoresInput>(stores: S, options: AsyncDeriveFn<T, S> | AsyncDeriveOptions<T, S>, initialValue: T): ReadableSignal<T>;
export declare function derived<T, S extends StoresInput>(stores: S, options: SyncDeriveFn<T, S> | SyncDeriveOptions<T, S>, initialValue?: T): ReadableSignal<T>;
/**
* Creates a store whose value is computed by the provided function.
*
* @remarks
*
* The computation function is first called the first time the store is used.
*
* It can use the value of other stores or observables and the computation function is called again if the value of those dependencies
* changed, as long as the store is still used.
*
* Dependencies are detected automatically as the computation function gets their value either by calling the stores
* as a function (as it is possible with stores implementing {@link ReadableSignal}), or by calling the {@link get} function
* (with a store or any observable). If some calls made by the function should not be tracked as dependencies, it is possible
* to wrap them in a call to {@link untrack}.
*
* Note that dependencies can change between calls of the computation function. Internally, tansu will subscribe to new dependencies
* when they are used and unsubscribe from dependencies that are no longer used after the call of the computation function.
*
* @param fn - computation function that returns the value of the store
* @param options - store option. Allows to define the {@link StoreOptions.equal|equal} function, if needed
* @returns store containing the value returned by the computation function
*/
export declare function computed<T>(fn: () => T, options?: Omit<StoreOptions<T>, 'onUse'>): ReadableSignal<T>;