import { ActionChain, Observable, Settable } from '@zedux/core' import { AtomApi } from '../classes/AtomApi' import { Ecosystem } from '../classes/Ecosystem' import { SelectorCache } from '../classes/Selectors' import { AnyAtomInstance, AnyAtomTemplate, AtomGenerics, AtomGenericsToAtomApiGenerics, AtomInstanceType, AtomParamsType, AtomStateType, } from './atoms' export * from './atoms' // eslint-disable-next-line @typescript-eslint/ban-types export type AnyNonNullishValue = {} export interface AtomConfig { dehydrate?: (state: State) => any flags?: string[] hydrate?: (dehydratedState: unknown) => State manualHydration?: boolean ttl?: number } /** * The AtomGettersBase interface. You probably won't want to use this directly. * Use AtomGetters instead. */ export interface AtomGettersBase { /** * Registers a dynamic graph edge on the resolved atom instance when called * synchronously during atom or AtomSelector evaluation. When called * asynchronously, is just an alias for `ecosystem.get` */ get( template: A, params: AtomParamsType ): AtomStateType get>(template: A): AtomStateType get( template: ParamlessTemplate ): AtomStateType get(instance: I): AtomStateType /** * Registers a static graph edge on the resolved atom instance when called * synchronously during atom or AtomSelector evaluation. When called * asynchronously, is just an alias for `ecosystem.getInstance` */ getInstance( template: A, params: AtomParamsType, edgeInfo?: GraphEdgeInfo ): AtomInstanceType getInstance>( template: A ): AtomInstanceType getInstance( template: ParamlessTemplate ): AtomInstanceType getInstance( instance: I, params?: [], edgeInfo?: GraphEdgeInfo ): I /** * Runs an AtomSelector which receives its own AtomGetters object and can use * those to register its own dynamic and/or static graph edges (when called * synchronously during the AtomSelector's evaluation) * * ```ts * const mySelector = ion('mySelector', ({ select }) => { * // registers a dynamic dependency on myAtom: * const dynamicVal = select(({ get }) => get(myAtom)) * * injectEffect(() => { * // doesn't register anything: * const staticVal = select(({ get }) => get(myAtom)) * }, []) // no need to pass select as a dep; it's a stable reference * }) * ``` * * @see AtomSelector */ select( selectorOrConfigOrCache: Selectable, ...args: Args ): T } /** * AtomGetters are used all throughout Zedux. When called synchronously during * atom or AtomSelector evaluation, they register graph edges. When called * asynchronously, they're just aliases for the corresponding ecosystem method. * * ```ts * const mySelector = ion('mySelector', ({ ecosystem, get }) => { * const dynamicVal = get(myAtom) // registers graph edge * const staticVal = ecosystem.get(myAtom) // doesn't register anything * * injectEffect(() => { * const staticVal2 = get(myAtom) // doesn't register anything * // const staticVal2 = ecosystem.get(myAtom) // same exact thing * }, []) * }) * ``` */ export interface AtomGetters extends AtomGettersBase { /** * A reference to the ecosystem of the current atom instance or AtomSelector. * * The ecosystem itself has `get`, `getInstance`, and `select` methods which * can be used instead of the other AtomGetters to prevent graph dependencies * from being registered. * * ```ts * // the current component will NOT rerender when myAtom changes: * const staticVal = useAtomSelector(({ ecosystem }) => ecosystem.get(myAtom)) * * // the current component will rerender when myAtom changes: * const dynamicVal = useAtomSelector(({ get }) => get(myAtom)) * ``` */ ecosystem: Ecosystem } export type AtomInstanceTtl = number | Promise | Observable export type AtomSelector = ( getters: AtomGetters, ...args: Args ) => T export interface AtomSelectorConfig { argsComparator?: (newArgs: Args, oldArgs: Args) => boolean name?: string resultsComparator?: (newResult: T, oldResult: T) => boolean selector: AtomSelector } export type AtomSelectorOrConfig = | AtomSelector | AtomSelectorConfig export type AtomStateFactory = ( ...params: G['Params'] ) => AtomApi> | G['Store'] | G['State'] export type AtomTuple = [A, AtomParamsType] export type AtomValueOrFactory = | AtomStateFactory | G['Store'] | G['State'] export type Cleanup = () => void export type DependentCallback = ( signal: GraphEdgeSignal, val?: any, reason?: EvaluationReason ) => any export interface DependentEdge { callback?: DependentCallback createdAt: number dependentKey?: string flags: number // calculated from the EdgeFlags isMaterialized?: boolean operation: string prevEdge?: WeakRef task?: () => void // for external edges - so they can unschedule jobs } export interface EcosystemConfig< Context extends Record | undefined = any > { atomDefaults?: { ttl?: number } complexParams?: boolean context?: Context destroyOnUnmount?: boolean flags?: string[] id?: string onReady?: ( ecosystem: Ecosystem, prevContext?: Context ) => MaybeCleanup overrides?: AnyAtomTemplate[] ssr?: boolean } export interface EcosystemGraphNode { dependencies: Map dependents: Map isSelector?: boolean refCount: number weight: number } export type EffectCallback = () => MaybeCleanup | Promise export interface EvaluationReason { action?: ActionChain newState?: State oldState?: State operation: string // e.g. a method like "injectValue" sourceType: EvaluationSourceType sourceId?: string // e.g. a fully-qualified atom instance id like "myAtom-[0]" reasons?: EvaluationReason[] type: EvaluationType } export type EvaluationSourceType = | 'Atom' | 'AtomSelector' | 'External' | 'Injector' | 'Store' export type EvaluationType = | 'cache invalidated' | 'node destroyed' | 'promise changed' | 'state changed' export type ExportsInfusedSetter = Exports & { (settable: Settable, meta?: any): State } export type GraphEdgeInfo = [ // these flags are calculated from EdgeFlags: flags: number, operation: string ] /** * A low-level detail that tells dependents what sort of event is causing the * current update. Promise changes and state updates are lumped together as * 'Update' signals. If you need to distinguish between them, look at the * EvaluationType (the `type` field) in the full reasons list. */ export type GraphEdgeSignal = 'Destroyed' | 'Updated' export interface GraphViewRecursive { [key: string]: GraphViewRecursive } export interface InjectAtomInstanceConfig { operation?: string subscribe?: boolean } export type InjectorDeps = any[] | undefined export type InjectOrUseSelector = Params extends [] ? (selector: (state: State) => D) => D : (params: Params, selector: (state: State) => D) => D export interface InjectPromiseConfig { dataOnly?: boolean initialState?: T runOnInvalidate?: boolean } export interface InjectStoreConfig { hydrate?: boolean subscribe?: boolean } export type IonStateFactory = ( getters: AtomGetters, ...params: G['Params'] ) => AtomApi> | G['Store'] | G['State'] export type LifecycleStatus = 'Active' | 'Destroyed' | 'Initializing' | 'Stale' export type MaybeCleanup = Cleanup | void export interface MutableRefObject { current: T } /** * Many Zedux APIs make the `params` parameter optional if the atom doesn't take * params or has only optional params. */ export type ParamlessTemplate = AtomParamsType extends [AnyNonNullishValue | undefined | null, ...any[]] ? never : A /** * Part of the atom instance can be accessed during initial evaluation. The only * fields that are inaccessible are those that don't exist yet 'cause the * initial evaluation is supposed to create them. */ export type PartialAtomInstance = Omit< AnyAtomInstance, 'api' | 'exports' | 'promise' | 'store' > // from Matt Pocock https://twitter.com/mattpocockuk/status/1622730173446557697 export type Prettify = { [K in keyof T]: T[K] } & AnyNonNullishValue export interface PromiseState { data?: T error?: Error isError: boolean isLoading: boolean isSuccess: boolean status: PromiseStatus } export type PromiseStatus = 'error' | 'loading' | 'success' export type Ref = MutableRefObject export interface RefObject { readonly current: T | null } export type Selectable = | AtomSelector | AtomSelectorConfig | SelectorCache export type StateHookTuple = [ State, ExportsInfusedSetter ]