UNPKG

@tanstack/store

Version:

Framework agnostic type-safe store w/ reactive framework adapters

1 lines 12.1 kB
{"version":3,"file":"atom.cjs","names":["ReactiveFlags"],"sources":["../src/atom.ts"],"sourcesContent":["import { ReactiveFlags, createReactiveSystem } from './alien'\n\nimport type { ReactiveNode } from './alien'\nimport type {\n Atom,\n AtomOptions,\n Observer,\n ReadonlyAtom,\n Subscription,\n} from './types'\n\nexport function toObserver<T>(\n nextHandler?: Observer<T> | ((value: T) => void),\n errorHandler?: (error: any) => void,\n completionHandler?: () => void,\n): Observer<T> {\n const isObserver = typeof nextHandler === 'object'\n const self = isObserver ? nextHandler : undefined\n\n return {\n next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),\n error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),\n complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(\n self,\n ),\n }\n}\n\ninterface InternalAtom<T> extends ReactiveNode {\n _snapshot: T\n _update: (getValue?: T | ((snapshot: T) => T)) => boolean\n get: () => T\n subscribe: (observerOrFn: Observer<T> | ((value: T) => void)) => Subscription\n}\n\nconst queuedEffects: Array<Effect | undefined> = []\nlet cycle = 0\nconst { link, unlink, propagate, checkDirty, shallowPropagate } =\n createReactiveSystem({\n update(atom: InternalAtom<any>): boolean {\n return atom._update()\n },\n // eslint-disable-next-line no-shadow\n notify(effect: Effect): void {\n queuedEffects[queuedEffectsLength++] = effect\n effect.flags &= ~ReactiveFlags.Watching\n },\n unwatched(atom: InternalAtom<any>): void {\n if (atom.depsTail !== undefined) {\n atom.depsTail = undefined\n atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty\n purgeDeps(atom)\n }\n },\n })\n\nlet notifyIndex = 0\nlet queuedEffectsLength = 0\nlet activeSub: ReactiveNode | undefined\nlet batchDepth = 0\n\nexport function batch(fn: () => void) {\n try {\n ++batchDepth\n fn()\n } finally {\n if (!--batchDepth) {\n flush()\n }\n }\n}\n\nfunction purgeDeps(sub: ReactiveNode) {\n const depsTail = sub.depsTail\n let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps\n while (dep !== undefined) {\n dep = unlink(dep, sub)\n }\n}\n\nexport function flush(): void {\n if (batchDepth > 0) {\n return\n }\n while (notifyIndex < queuedEffectsLength) {\n // eslint-disable-next-line no-shadow\n const effect = queuedEffects[notifyIndex]!\n queuedEffects[notifyIndex++] = undefined\n effect.notify()\n }\n notifyIndex = 0\n queuedEffectsLength = 0\n}\n\ntype AsyncAtomState<TData, TError = unknown> =\n | { status: 'pending' }\n | { status: 'done'; data: TData }\n | { status: 'error'; error: TError }\n\nexport function createAsyncAtom<T>(\n getValue: () => Promise<T>,\n options?: AtomOptions<AsyncAtomState<T>>,\n): ReadonlyAtom<AsyncAtomState<T>> {\n const ref: { current?: InternalAtom<AsyncAtomState<T>> } = {}\n const atom = createAtom<AsyncAtomState<T>>(() => {\n getValue().then(\n (data) => {\n const internalAtom = ref.current!\n if (internalAtom._update({ status: 'done', data })) {\n const subs = internalAtom.subs\n if (subs !== undefined) {\n propagate(subs)\n shallowPropagate(subs)\n flush()\n }\n }\n },\n (error) => {\n const internalAtom = ref.current!\n if (internalAtom._update({ status: 'error', error })) {\n const subs = internalAtom.subs\n if (subs !== undefined) {\n propagate(subs)\n shallowPropagate(subs)\n flush()\n }\n }\n },\n )\n\n return { status: 'pending' }\n }, options)\n ref.current = atom as unknown as InternalAtom<AsyncAtomState<T>>\n\n return atom\n}\n\nexport function createAtom<T>(\n getValue: (prev?: NoInfer<T>) => T,\n options?: AtomOptions<T>,\n): ReadonlyAtom<T>\nexport function createAtom<T>(\n initialValue: T,\n options?: AtomOptions<T>,\n): Atom<T>\nexport function createAtom<T>(\n valueOrFn: T | ((prev?: T) => T),\n options?: AtomOptions<T>,\n): Atom<T> | ReadonlyAtom<T> {\n const isComputed = typeof valueOrFn === 'function'\n const getter = valueOrFn as (prev?: T) => T\n\n // Create plain object atom\n const atom: InternalAtom<T> = {\n _snapshot: isComputed ? undefined! : valueOrFn,\n\n subs: undefined,\n subsTail: undefined,\n deps: undefined,\n depsTail: undefined,\n flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,\n\n get(): T {\n if (activeSub !== undefined) {\n link(atom, activeSub, cycle)\n }\n return atom._snapshot\n },\n\n subscribe(observerOrFn: Observer<T> | ((value: T) => void)) {\n const obs = toObserver(observerOrFn)\n const observed = { current: false }\n const e = effect(() => {\n atom.get()\n if (!observed.current) {\n observed.current = true\n } else {\n obs.next?.(atom._snapshot)\n }\n })\n\n return {\n unsubscribe: () => {\n e.stop()\n },\n }\n },\n _update(getValue?: T | ((snapshot: T) => T)): boolean {\n const prevSub = activeSub\n const compare = options?.compare ?? Object.is\n if (isComputed) {\n activeSub = atom\n ++cycle\n atom.depsTail = undefined\n } else if (getValue === undefined) {\n // Mutable atoms can be marked dirty by the reactive graph, but they should\n // never be recomputed without an explicit value/updater.\n return false\n }\n if (isComputed) {\n atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck\n }\n try {\n const oldValue = atom._snapshot\n const newValue =\n typeof getValue === 'function'\n ? (getValue as (snapshot: T) => T)(oldValue)\n : getValue === undefined && isComputed\n ? getter(oldValue)\n : getValue!\n if (oldValue === undefined || !compare(oldValue, newValue)) {\n atom._snapshot = newValue\n return true\n }\n return false\n } finally {\n activeSub = prevSub\n if (isComputed) {\n atom.flags &= ~ReactiveFlags.RecursedCheck\n }\n purgeDeps(atom)\n }\n },\n }\n\n if (isComputed) {\n atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty\n atom.get = function (): T {\n const flags = atom.flags\n if (\n flags & ReactiveFlags.Dirty ||\n (flags & ReactiveFlags.Pending && checkDirty(atom.deps!, atom))\n ) {\n if (atom._update()) {\n const subs = atom.subs\n if (subs !== undefined) {\n shallowPropagate(subs)\n }\n }\n } else if (flags & ReactiveFlags.Pending) {\n atom.flags = flags & ~ReactiveFlags.Pending\n }\n if (activeSub !== undefined) {\n link(atom, activeSub, cycle)\n }\n return atom._snapshot\n }\n } else {\n ;(atom as unknown as Atom<T>).set = function (\n // eslint-disable-next-line no-shadow\n valueOrFn: T | ((prev: T) => T),\n ): void {\n if (atom._update(valueOrFn)) {\n const subs = atom.subs\n if (subs !== undefined) {\n propagate(subs)\n shallowPropagate(subs)\n flush()\n }\n }\n }\n }\n\n return atom as unknown as Atom<T> | ReadonlyAtom<T>\n}\n\ninterface Effect extends ReactiveNode {\n notify: () => void\n stop: () => void\n}\n\nfunction effect<T>(fn: () => T): Effect {\n const run = (): T => {\n const prevSub = activeSub\n activeSub = effectObj\n ++cycle\n effectObj.depsTail = undefined\n effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck\n try {\n return fn()\n } finally {\n activeSub = prevSub\n effectObj.flags &= ~ReactiveFlags.RecursedCheck\n purgeDeps(effectObj)\n }\n }\n const effectObj: Effect = {\n deps: undefined,\n depsTail: undefined,\n subs: undefined,\n subsTail: undefined,\n flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,\n\n notify(): void {\n const flags = this.flags\n if (\n flags & ReactiveFlags.Dirty ||\n (flags & ReactiveFlags.Pending && checkDirty(this.deps!, this))\n ) {\n run()\n } else {\n this.flags = ReactiveFlags.Watching\n }\n },\n\n stop(): void {\n this.flags = ReactiveFlags.None\n this.depsTail = undefined\n purgeDeps(this)\n },\n }\n\n run()\n\n return effectObj\n}\n"],"mappings":"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"}