@persevie/statemanjs
Version:
Proper state manager for JavaScript
206 lines (198 loc) • 8.04 kB
TypeScript
type Transaction<T> = {
number: number;
snapshot: T;
timestamp: number;
};
type TransactionDiff<T> = {
old: T;
new: T;
};
/**
* Interface for a transaction API that stores state history in a chain
* (with a configurable length) and allows for receiving diffs and rolling back changes.
*
* @template T - The type of the state being managed by the transactions.
*/
interface TransactionAPI<T> {
/**
* The total number of transactions that have occurred since the state was initialized.
*/
totalTransactions: number;
/**
* Adds a new transaction to the transaction chain.
*
* @param {T} snapshot - The snapshot of the state to be added as a transaction.
*/
addTransaction(snapshot: T): void;
/**
* Retrieves the last transaction in the transaction chain.
*
* @returns {Transaction<T> | null} The last transaction, or null if there are no transactions.
*/
getLastTransaction(): Transaction<T> | null;
/**
* Retrieves all transactions that have occurred.
*
* @returns {Transaction<T>[]} An array of all transactions.
*/
getAllTransactions(): Transaction<T>[];
/**
* Retrieves a specific transaction by its number in the transaction chain.
*
* @param {number} transactionNumber - The number of the transaction to retrieve.
* @returns {Transaction<T> | null} The transaction with the specified number, or null if it doesn't exist.
*/
getTransactionByNumber(transactionNumber: number): Transaction<T> | null;
/**
* Retrieves the difference between the current state and the last transaction.
*
* @returns {TransactionDiff<T> | null} The difference between the current state and the last transaction, or null if there are no transactions.
*/
getLastDiff(): TransactionDiff<T> | null;
/**
* Retrieves the difference between two specific transactions.
*
* @param {number} transactionA - The number of the first transaction.
* @param {number} transactionB - The number of the second transaction.
* @returns {TransactionDiff<T> | null} The difference between the two specified transactions, or null if the transactions don't exist or there is no difference.
*/
getDiffBetween(transactionA: number, transactionB: number): TransactionDiff<T> | null;
}
interface DebugAPI<T> {
transactionService: TransactionAPI<T>;
}
/**
* A callback function that will be called every time the state changes.
* Can take the updated state as an argument.
*/
type SubscriptionCb<T> = (newState: T) => void;
/**
* A callback function for unsubscribing.
*/
type UnsubscribeCb = () => void;
/** Callback for state updates. */
type UpdateCb<T> = (state: T) => void;
/**
* It may contain additional information about the subscriber,
* as well as a condition for notification, a mark that the subscriber is protected and properties to watch.
*/
type SubscriptionOptions<T> = {
notifyCondition?: (state: T) => boolean;
protect?: boolean;
properties?: Array<string>;
};
type CustomComparator<T> = (a: T, b: T) => boolean;
type DefaultComparator = "none" | "ref" | "shallow" | "custom";
type BaseModifyOptions<T> = {
skipComparison?: boolean;
comparatorOverride?: DefaultComparator;
customComparatorOverride?: CustomComparator<T>;
afterUpdate: () => void;
};
type SetOptions<T> = Omit<BaseModifyOptions<T>, "afterUpdate">;
type UpdateOptions<T> = Omit<BaseModifyOptions<T>, "afterUpdate">;
interface StatemanjsComputedAPI<T> {
/** Get current state */
get(): T;
/**
* The method of subscribing to the status change.
* Accepts a callback function (subscription callback),
* which will be called at each update, and a subscription options object.
* In the options, you can specify information about the subscription,
* as well as specify the condition under which the subscriber will be notified
* and mark the subscriber as protected. All subscribers are unprotected by default.
* Protected subscribers can only be unsubscribed using the unsubscribe method returned by this method.
* Returns the unsubscribe callback function.
*
* @param subscriptionCb A function that runs on every update.
* @param subscriptionOptions Additional information and notification condition.
* @returns Unsubscribe callback function.
*/
subscribe(subscriptionCb: SubscriptionCb<T>, subscriptionOptions?: SubscriptionOptions<T>): UnsubscribeCb;
/** Remove all unprotected subscribers */
unsubscribeAll(): void;
/**
* Returns count of all active subscribers.
* @returns number.
*/
getActiveSubscribersCount(): number;
/**
* Unwrap a proxy object to a regular JavaScript object
* @returns unwrapped state
*/
unwrap(): T;
}
/**
* Public API.
* Interacts with the @see {StatemanjsBaseAPI}.
*/
interface StatemanjsAPI<T> {
/**
* Accepts a new state and compares it with the current one.
* Nothing will happen if the passed value is equal to the current one.
* @param newState New state.
* @returns Status of operation.
*/
set(newState: T, options?: SetOptions<T>): boolean;
/** Get current state */
get(): T;
/**
* The method of subscribing to the status change.
* Accepts a callback function (subscription callback),
* which will be called at each update, and a subscription options object.
* In the options, you can specify information about the subscription,
* as well as specify the condition under which the subscriber will be notified
* and mark the subscriber as protected. All subscribers are unprotected by default.
* Protected subscribers can only be unsubscribed using the unsubscribe method returned by this method.
* Returns the unsubscribe callback function.
*
* @param subscriptionCb A function that runs on every update.
* @param subscriptionOptions Additional information and notification condition.
* @returns Unsubscribe callback function.
*/
subscribe(subscriptionCb: SubscriptionCb<T>, subscriptionOptions?: SubscriptionOptions<T>): UnsubscribeCb;
/** Remove all unprotected subscribers */
unsubscribeAll(): void;
/**
* Returns count of all active subscribers.
* @returns number.
*/
getActiveSubscribersCount(): number;
/**
* Flexible state update.
* @param updateCb Callback for state updates.
*/
update(updateCb: UpdateCb<T>, options?: UpdateOptions<T>): boolean;
/**
* Unwrap a proxy object to a regular JavaScript object
* @returns unwrapped state
*/
unwrap(): T;
/**
* Dispatch an async action
* @param action An async action. It accepts a stateManager object,
* which is used to access the current state.
* @returns Promise.
*/
asyncAction(action: (stateManager: StatemanjsAPI<T>) => Promise<void>): Promise<void>;
/**
* Create a computed state for a state property.
* @param selectorFn A function that returns a value of a state property.
* @returns A computed state.
*/
createSelector<E>(selectorFn: (state: T) => E, subscriptionOptions?: SubscriptionOptions<unknown>): StatemanjsComputedAPI<E>;
/**
* Debug API. Allows you to use additional debugging functionality such as transactions.
* Parameters are set when creating the state.
* @see {DebugAPI}
*/
DEBUG?: DebugAPI<T>;
}
type StatemanjsOptions<T> = {
transactionsLen?: number;
customComparator?: CustomComparator<T>;
defaultComparator?: DefaultComparator;
};
declare function createState<T>(element: T, options?: StatemanjsOptions<T>): StatemanjsAPI<T>;
declare function createComputedState<T>(callback: () => T, deps: StatemanjsAPI<any>[], options?: StatemanjsOptions<T>): StatemanjsComputedAPI<T>;
export { StatemanjsAPI, StatemanjsComputedAPI, StatemanjsOptions, SubscriptionOptions, createComputedState, createState };