reago
Version:
Reago is a declarative, atomic state management library for JavaScript and TypeScript
349 lines (322 loc) • 15.3 kB
TypeScript
type Atom<Result, FamilyArgs extends AtomFamilyArgs = [], ActionArgs extends AtomActionArgs = never> = (FunctionalAtom<Result, FamilyArgs, ActionArgs> | (Result extends Promise<infer InnerResult> ? GenerativeAtom<InnerResult, FamilyArgs, ActionArgs> : never));
type AnyAtom<Result = unknown, FamilyArgs extends AtomFamilyArgs = AtomFamilyArgs, ActionArgs extends AtomActionArgs = AtomActionArgs> = (AnyFunctionalAtom<Result, FamilyArgs, ActionArgs> | (Result extends Promise<infer InnerResult> ? AnyGenerativeAtom<InnerResult, FamilyArgs, ActionArgs> : never));
type FunctionalAtom<ImplResult, FamilyArgs extends AtomFamilyArgs = [], ActionArgs extends AtomActionArgs = never> = (...args: FamilyArgs) => ImplResult extends AtomGenerator<any> ? never : ImplResult;
type GenerativeAtom<ImplResult, FamilyArgs extends AtomFamilyArgs = [], ActionArgs extends AtomActionArgs = never> = (...args: FamilyArgs) => AtomGenerator<ImplResult>;
type AnyFunctionalAtom<ImplResult = unknown, FamilyArgs extends AtomFamilyArgs = AtomFamilyArgs, ActionArgs extends AtomActionArgs = AtomActionArgs> = {
bivarianceHack(...args: FamilyArgs): ImplResult extends AtomGenerator<any> ? never : ImplResult;
}['bivarianceHack'];
type AnyGenerativeAtom<ImplResult = unknown, FamilyArgs extends AtomFamilyArgs = AtomFamilyArgs, ActionArgs extends AtomActionArgs = AtomActionArgs> = {
bivarianceHack(...args: FamilyArgs): AtomGenerator<ImplResult>;
}['bivarianceHack'];
type AtomFamilyArg = string | number | boolean | null | undefined;
type AtomFamilyArgs = AtomFamilyArg[];
type AtomActionArg = any;
type AtomActionArgs = AtomActionArg[];
type AtomGenerator<Result> = Generator<PromiseLike<unknown>, // accepted type in yield
Result, // return type
unknown>;
type AtomResultOf<T> = (T extends GenerativeAtom<infer IR, infer FA, infer AA> ? Promise<IR> : T extends FunctionalAtom<infer IR, infer FA, infer AA> ? IR : never);
type AtomImplResultOf<T> = (T extends GenerativeAtom<infer IR, infer FA, infer AA> ? IR : T extends FunctionalAtom<infer IR, infer FA, infer AA> ? IR : never);
type AtomFamilyArgsOf<T> = (T extends GenerativeAtom<infer IR, infer FA, infer AA> ? FA : T extends FunctionalAtom<infer IR, infer FA, infer AA> ? FA : never);
type AtomActionArgsOf<T> = (T extends GenerativeAtom<infer IR, infer FA, infer AA> ? AA : T extends FunctionalAtom<infer IR, infer FA, infer AA> ? AA : never);
type AtomDispatcher<T extends AnyAtom> = (...args: AtomActionArgsOf<T>) => void;
declare const METADATA: unique symbol;
declare const NO_VALUE: unique symbol;
declare const FUNCTIONAL_ATOM: unique symbol;
declare const GENERATIVE_ATOM: unique symbol;
declare const PENDING: unique symbol;
declare const RESOLVED: unique symbol;
declare const REJECTED: unique symbol;
declare const LOADED: unique symbol;
declare const UNLOADED: unique symbol;
declare const COMPUTED: unique symbol;
declare const COMPUTING: unique symbol;
declare const OUTDATED: unique symbol;
declare const FRESH: unique symbol;
declare const STALE: unique symbol;
declare const MOUNTED_DIRECTLY: unique symbol;
declare const MOUNTED_TRANSITIVELY: unique symbol;
declare const UNMOUNTED: unique symbol;
declare class IterableWeakSet<T extends WeakKey> {
#private;
add(value: T): this;
delete(value: T): boolean;
some(callback: (value: T) => boolean): boolean;
[Symbol.iterator](): Generator<T, void, unknown>;
}
interface AtomHook {
(...args: any[]): any;
onSkip?: {
bivarianceHack(frame: AtomHookFrame): void;
}['bivarianceHack'];
}
interface AtomHookFrame {
readonly hook: AtomHook;
}
interface AtomActionFrame extends AtomHookFrame {
handler: AtomAction;
dependencies: unknown[];
}
type AtomAction = (...args: AtomActionArg[]) => void;
declare function atomAction(handler: AtomAction, dependencies: unknown[]): void;
interface AtomComputationEffectFrame extends AtomHookFrame {
setup?: AtomComputationEffect;
cleanup?: AtomComputationEffectCleanup;
dependencies?: unknown[];
}
type AtomComputationEffect = () => (void | AtomComputationEffectCleanup);
interface AtomComputationEffectCleanup {
(): void;
[METADATA]?: {
token: WeakKey;
};
}
declare function atomComputationEffect(setup: AtomComputationEffect, dependencies?: unknown[]): void;
interface AtomMountEffectFrame extends AtomHookFrame {
status: typeof UNLOADED | typeof LOADED;
setup: AtomMountEffect;
cleanup?: AtomMountEffectCleanup;
dependencies: unknown[];
}
type AtomMountEffect = () => (void | AtomMountEffectCleanup);
type AtomMountEffectCleanup = () => void;
declare function atomMountEffect(setup: AtomMountEffect, dependencies: unknown[]): void;
interface Computation<T extends AnyAtom> {
/**
* Computation mode - functional or generative.
*/
mode: typeof FUNCTIONAL_ATOM | typeof GENERATIVE_ATOM;
/**
* Value of the atom or NO_VALUE if atom is not computed yet.
*/
result: AtomImplResultOf<T> | typeof NO_VALUE;
/**
* Error thrown during the computation or NO_VALUE if there was no error.
*/
error: unknown | typeof NO_VALUE;
/**
* Promise that tracks the state of an async computation.
*
* It's present only if the computation turned out to be async. Note that
* only generative atoms can compute asynchronously, but not all of them will.
*/
promise?: Promise<void>;
/**
* Abort controller for the computation.
*/
abortController: AbortController;
/**
* Pointer to the current stack frame, for hooks.
*/
pointer: number;
/**
* Set of atom instance dependencies.
*
* These are atom instances that were referenced during this computation.
*/
dependencies: Set<AtomInstance<AnyAtom>>;
}
type SwappablePromise<Type> = Promise<Type> & {
[METADATA]: (PendingSwappablePromise<Type> | ResolvedSwappablePromise | RejectedSwappablePromise);
};
interface PendingSwappablePromise<Type> {
status: typeof PENDING;
promise: PromiseLike<Type> | null;
setPromise: (p: PromiseLike<Type> | null) => void;
}
interface ResolvedSwappablePromise {
status: typeof RESOLVED;
}
interface RejectedSwappablePromise {
status: typeof REJECTED;
}
interface AtomInstance<T extends AnyAtom> {
/**
* Reference to the atom this instance is derived from.
*/
readonly atom: T;
/**
* Atom arguments for the fn call.
*
* An array of arguments passed to the atom for computation, used to implement
* atom families.
*/
readonly args: AtomFamilyArgsOf<T>;
/**
* Current status of the atom instance.
*
* - `COMPUTED` - computed and seemingly up to date (see `freshness`)
* - `COMPUTING` - atom is generative and computation is still running
* - `OUTDATED` - outdated or not computed at all
*
* Note that in case of functional atoms the `COMPUTING` state can't be observed
* outside since everything runs synchronously.
*/
status: typeof COMPUTED | typeof COMPUTING | typeof OUTDATED;
/**
* Current freshness of the atom instance.
*
* - `FRESH` - computation result is known to be up to date
* - `STALE` - something in the dependency subgraph requires recomputing
*
* An atom instance that is `COMPUTED` might depend directly or indirectly
* on other instances that are marked as `OUTDATED`. We don't know yet if
* their values are going to change, but if they do, they could potentially
* trigger a recomputation of this instance as well.
*/
freshness: typeof FRESH | typeof STALE;
/**
* Current mount state of the atom instance.
*/
mount: typeof MOUNTED_DIRECTLY | typeof MOUNTED_TRANSITIVELY | typeof UNMOUNTED;
/**
* Promise that resolves/rejects with the computation result.
*
* The promise is created for generative atoms only.
*/
promise?: SwappablePromise<AtomImplResultOf<T>>;
/**
* Reference to the active computation.
*
* This prop is `undefined` only if an atom instance hasn't been computed before.
*/
computation?: Computation<T>;
/**
* Collection of attached watchers.
*
* They should be notified of any value changes of this atom instance.
*/
watchers: Set<AtomWatcher<T>>;
/**
* Collection of atom instance dependencies.
*
* These are atoms that this atom directly depends on.
*/
dependencies: Set<AtomInstance<AnyAtom>>;
/**
* Collection of atom instance dependants.
*
* These are atoms that directly depend on the value of this atom and need
* to be notified when the value here changes.
*/
dependants: IterableWeakSet<AtomInstance<AnyAtom>>;
/**
* Internal stack for hooks.
*
* The stack maintains internal state of each hook call, in their order of
* invocation. The n-th element of the stack corresponds to the state of
* the n-th hook call within an atom.
*/
stack: AtomHookFrame[];
/**
* Internal stack for hooks, restricted to `atomAction` hook type.
*
* Contains the same data as `stack.filter(o => o.hook = atomAction)`.
* Used to improve performance of `dispatch()` calls.
*/
stackAction: AtomActionFrame[];
/**
* Internal stack for hooks, restricted to `atomComputationEffect` hook type.
*
* Contains the same data as `stack.filter(o => o.hook = atomComputationEffect)`.
* Used to improve performance of computation effects handling.
*/
stackComputationEffect: AtomComputationEffectFrame[];
/**
* Internal stack for hooks, restricted to `atomMountEffect` hook type.
*
* Contains the same data as `stack.filter(o => o.hook = atomMountEffect)`.
* Used to improve performance of mount effects handling.
*/
stackMountEffect: AtomMountEffectFrame[];
/**
* Flag indicating that the stack is frozen.
*
* If true, no new hooks should be added to the stack. The stack is frozen
* after first successful computation, helping to catch any developer mistakes
* that could result in inconsistent hook calls across computations.
*/
freezeStack: boolean;
}
interface AtomFamily<T extends AnyAtom> {
readonly atom: T;
readonly instanceMap: Map<string, AtomInstance<T>>;
}
declare class Store$1 {
#private;
constructor(supervisor: Supervisor);
read<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomResultOf<T>;
watch<T extends AnyAtom>(atom: T, ...args: [...AtomFamilyArgsOf<T>, AtomListener<T>]): AtomWatcher<T>;
dispatch<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomDispatcher<T>;
invalidate<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): void;
}
declare class Supervisor {
#private;
readonly store: Store$1;
constructor();
getOrCreateFamily<T extends AnyAtom>(atom: T): AtomFamily<T>;
getInstance<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomInstance<T> | null;
getOrCreateInstance<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomInstance<T>;
readInstance<T extends AnyAtom>(instance: AtomInstance<T>): AtomResultOf<T>;
watchInstance<T extends AnyAtom>(instance: AtomInstance<T>, listener: AtomListener<T>): AtomWatcher<T>;
dispatchInstance<T extends AnyAtom>(instance: AtomInstance<T>, ...args: AtomActionArgsOf<T>): void;
syncInstance<T extends AnyAtom>(instance: AtomInstance<T>): void;
mountInstance<T extends AnyAtom>(instance: AtomInstance<T>, mode?: typeof MOUNTED_DIRECTLY | typeof MOUNTED_TRANSITIVELY): void;
unmountInstance<T extends AnyAtom>(instance: AtomInstance<T>): void;
invalidateInstance<T extends AnyAtom>(instance: AtomInstance<T>): void;
flush(): void;
requestAsyncFlush(): void;
}
type AtomWatcher<T extends AnyAtom = AnyAtom> = {
[METADATA]: {
readonly supervisor: Supervisor;
readonly instance: AtomInstance<T>;
readonly listener: AtomListener<T>;
} | undefined;
readonly clear: () => void;
readonly [Symbol.dispose]: () => void;
};
type AtomListener<T extends AnyAtom = AnyAtom> = () => any;
type Store = Store$1;
declare function getDefaultStore(): Store;
declare function createStore(): Store;
declare function read<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomResultOf<T>;
declare function watch<T extends AnyAtom>(atom: T, ...args: [...AtomFamilyArgsOf<T>, AtomListener<T>]): AtomWatcher<T>;
declare function dispatch<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): AtomDispatcher<T>;
declare function invalidate<T extends AnyAtom>(atom: T, ...args: AtomFamilyArgsOf<T>): void;
type DeasyncAtom<T extends AnyAtom> = FunctionalAtom<DeasyncState<Awaited<AtomResultOf<T>>>, AtomFamilyArgsOf<T>, never>;
type DeasyncState<ResultType = unknown, ErrorType = unknown> = (PendingDeasyncState | ResolvedDeasyncState<ResultType> | RejectedDeasyncState<ErrorType>);
interface PendingDeasyncState {
status: 'pending';
result?: undefined;
error?: undefined;
}
interface ResolvedDeasyncState<ResultType> {
status: 'resolved';
result: ResultType;
error?: undefined;
}
interface RejectedDeasyncState<ErrorType> {
status: 'rejected';
result?: undefined;
error: ErrorType;
}
declare function deasync<T>(promise: PromiseLike<T>): DeasyncState<Awaited<T>>;
declare function deasync<T extends AnyAtom>(atom: T): DeasyncAtom<T>;
declare function deasync<T>(value: T): ResolvedDeasyncState<T>;
declare function atomAbortSignal(): AbortSignal;
declare function atomMemo<Value>(calculateValue: () => Value, dependencies: unknown[]): Value;
type AtomReducer<Value, ActionArgs extends any[] = any[]> = [Value, AtomReducerDispatcher<ActionArgs>];
type AtomReducerReducer<Value, ActionArgs extends any[]> = (prevState: Value, ...args: ActionArgs) => Value;
type AtomReducerDispatcher<ActionArgs extends any[]> = (...args: ActionArgs) => void;
declare function atomReducer<Value, ActionArgs extends any[] = any[]>(reducer: AtomReducerReducer<Value, ActionArgs>, initialArg: Value, init?: (initialArg: Value) => Value): AtomReducer<Value, ActionArgs>;
interface AtomRef<Value> {
current: Value;
}
declare function atomRef<Value>(initialValue: Value): AtomRef<Value>;
type AtomState<Value> = [Value, AtomStateSetter<Value>];
type AtomStateSetter<Value> = (nextState: AtomStateSetterNextState<Value>) => void;
type AtomStateSetterNextState<Value> = Value | ((prevState: Value) => Value);
declare function atomState<Value>(initialState: Value | (() => Value)): AtomState<Value>;
declare function atomStore(): Store;
export { type AnyAtom, type Atom, type AtomAction, type AtomActionArgsOf, type AtomComputationEffect, type AtomComputationEffectCleanup, type AtomFamilyArgsOf, type AtomListener, type AtomMountEffect, type AtomMountEffectCleanup, type AtomReducer, type AtomReducerDispatcher, type AtomReducerReducer, type AtomRef, type AtomResultOf, type AtomState, type AtomStateSetter, type AtomWatcher, type DeasyncAtom, type DeasyncState, type Store, atomAbortSignal, atomAction, atomComputationEffect, atomMemo, atomMountEffect, atomReducer, atomRef, atomState, atomStore, createStore, deasync, dispatch, getDefaultStore, invalidate, read, watch };