From ba8ef0514a4faa7ee42e1681decb262f71ba5b98 Mon Sep 17 00:00:00 2001 From: daishi Date: Wed, 1 Apr 2026 23:07:03 +0100 Subject: [PATCH 01/24] add BB_ prefix --- src/vanilla/internals.ts | 403 ++++++++++++++++++++------------------- 1 file changed, 206 insertions(+), 197 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index a3bac1a448..44763269d9 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -376,30 +376,30 @@ const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = (store, atom) => { const buildingBlocks = getInternalBuildingBlocks(store) - const atomStateMap = buildingBlocks[0] - const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const BB_atomStateMap = buildingBlocks[0] + const BB_storeHooks = buildingBlocks[6] + const BB_atomOnInit = buildingBlocks[9] if (import.meta.env?.MODE !== 'production' && !atom) { throw new Error('Atom is undefined or null') } - let atomState = atomStateMap.get(atom) + let atomState = BB_atomStateMap.get(atom) if (!atomState) { atomState = { d: new Map(), p: new Set(), n: 0 } - atomStateMap.set(atom, atomState) - storeHooks.i?.(atom) - atomOnInit?.(store, atom) + BB_atomStateMap.set(atom, atomState) + BB_storeHooks.i?.(atom) + BB_atomOnInit?.(store, atom) } return atomState as never } const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const changedAtoms = buildingBlocks[3] - const mountCallbacks = buildingBlocks[4] - const unmountCallbacks = buildingBlocks[5] - const storeHooks = buildingBlocks[6] - const recomputeInvalidatedAtoms = buildingBlocks[13] + const BB_mountedMap = buildingBlocks[1] + const BB_changedAtoms = buildingBlocks[3] + const BB_mountCallbacks = buildingBlocks[4] + const BB_unmountCallbacks = buildingBlocks[5] + const BB_storeHooks = buildingBlocks[6] + const BB_recomputeInvalidatedAtoms = buildingBlocks[13] const errors: unknown[] = [] const call = (fn: () => void) => { try { @@ -409,22 +409,26 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => { } } do { - if (storeHooks.f) { - call(storeHooks.f) + if (BB_storeHooks.f) { + call(BB_storeHooks.f) } const callbacks = new Set<() => void>() const add = callbacks.add.bind(callbacks) - changedAtoms.forEach((atom) => mountedMap.get(atom)?.l.forEach(add)) - changedAtoms.clear() - unmountCallbacks.forEach(add) - unmountCallbacks.clear() - mountCallbacks.forEach(add) - mountCallbacks.clear() + BB_changedAtoms.forEach((atom) => BB_mountedMap.get(atom)?.l.forEach(add)) + BB_changedAtoms.clear() + BB_unmountCallbacks.forEach(add) + BB_unmountCallbacks.clear() + BB_mountCallbacks.forEach(add) + BB_mountCallbacks.clear() callbacks.forEach(call) - if (changedAtoms.size) { - recomputeInvalidatedAtoms(store) + if (BB_changedAtoms.size) { + BB_recomputeInvalidatedAtoms(store) } - } while (changedAtoms.size || unmountCallbacks.size || mountCallbacks.size) + } while ( + BB_changedAtoms.size || + BB_unmountCallbacks.size || + BB_mountCallbacks.size + ) if (errors.length) { throw new AggregateError(errors) } @@ -434,12 +438,12 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( store, ) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const invalidatedAtoms = buildingBlocks[2] - const changedAtoms = buildingBlocks[3] - const ensureAtomState = buildingBlocks[11] - const readAtomState = buildingBlocks[14] - const mountDependencies = buildingBlocks[17] + const BB_mountedMap = buildingBlocks[1] + const BB_invalidatedAtoms = buildingBlocks[2] + const BB_changedAtoms = buildingBlocks[3] + const BB_ensureAtomState = buildingBlocks[11] + const BB_readAtomState = buildingBlocks[14] + const BB_mountDependencies = buildingBlocks[17] // Step 1: traverse the dependency graph to build the topologically sorted atom list // We don't bother to check for cycles, which simplifies the algorithm. // This is a topological sort via depth-first search, slightly modified from @@ -450,10 +454,10 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const visited = new WeakSet() // Visit the root atom. This is the only atom in the dependency graph // without incoming edges, which is one reason we can simplify the algorithm - const stack: AnyAtom[] = Array.from(changedAtoms) + const stack: AnyAtom[] = Array.from(BB_changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -463,11 +467,11 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // The algorithm calls for pushing onto the front of the list. For // performance, we will simply push onto the end, and then will iterate in // reverse order later. - if (invalidatedAtoms.get(a) === aState.n) { + if (BB_invalidatedAtoms.get(a) === aState.n) { topSortedReversed.push([a, aState]) } else if ( import.meta.env?.MODE !== 'production' && - invalidatedAtoms.has(a) + BB_invalidatedAtoms.has(a) ) { throw new Error('[Bug] invalidated atom exists') } @@ -478,7 +482,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } visiting.add(a) // Push unvisited dependents onto the stack - for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { + for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { if (!visiting.has(d)) { stack.push(d) } @@ -490,17 +494,17 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const [a, aState] = topSortedReversed[i]! let hasChangedDeps = false for (const dep of aState.d.keys()) { - if (dep !== a && changedAtoms.has(dep)) { + if (dep !== a && BB_changedAtoms.has(dep)) { hasChangedDeps = true break } } if (hasChangedDeps) { - invalidatedAtoms.set(a, aState.n) - readAtomState(store, a) - mountDependencies(store, a) + BB_invalidatedAtoms.set(a, aState.n) + BB_readAtomState(store, a) + BB_mountDependencies(store, a) } - invalidatedAtoms.delete(a) + BB_invalidatedAtoms.delete(a) } } @@ -509,29 +513,30 @@ const storeMutationSet = new WeakSet() const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const invalidatedAtoms = buildingBlocks[2] - const changedAtoms = buildingBlocks[3] - const storeHooks = buildingBlocks[6] - const atomRead = buildingBlocks[7] - const ensureAtomState = buildingBlocks[11] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const readAtomState = buildingBlocks[14] - const writeAtomState = buildingBlocks[16] - const mountDependencies = buildingBlocks[17] - const setAtomStateValueOrPromise = buildingBlocks[20] - const registerAbortHandler = buildingBlocks[26] - const storeEpochHolder = buildingBlocks[28] - const atomState = ensureAtomState(store, atom) - const storeEpochNumber = storeEpochHolder[0] + const BB_mountedMap = buildingBlocks[1] + const BB_invalidatedAtoms = buildingBlocks[2] + const BB_changedAtoms = buildingBlocks[3] + const BB_storeHooks = buildingBlocks[6] + const BB_atomRead = buildingBlocks[7] + const BB_ensureAtomState = buildingBlocks[11] + const BB_flushCallbacks = buildingBlocks[12] + const BB_recomputeInvalidatedAtoms = buildingBlocks[13] + const BB_readAtomState = buildingBlocks[14] + const BB_writeAtomState = buildingBlocks[16] + const BB_mountDependencies = buildingBlocks[17] + const BB_setAtomStateValueOrPromise = buildingBlocks[20] + const BB_registerAbortHandler = buildingBlocks[26] + const BB_storeEpochHolder = buildingBlocks[28] + const atomState = BB_ensureAtomState(store, atom) + const storeEpochNumber = BB_storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { if ( // If the atom is mounted, we can use cached atom state, // because it should have been updated by dependencies. // We can't use the cache if the atom is invalidated. - (mountedMap.has(atom) && invalidatedAtoms.get(atom) !== atomState.n) || + (BB_mountedMap.has(atom) && + BB_invalidatedAtoms.get(atom) !== atomState.n) || // If atom is not mounted, we can use cached atom state, // only if store hasn't been mutated. atomState.m === storeEpochNumber @@ -543,7 +548,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (readAtomState(store, a).n !== n) { + if (BB_readAtomState(store, a).n !== n) { hasChangedDeps = true break } @@ -565,22 +570,22 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { } } const mountDependenciesIfAsync = () => { - if (mountedMap.has(atom)) { + if (BB_mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it - const shouldRecompute = !changedAtoms.size - mountDependencies(store, atom) + const shouldRecompute = !BB_changedAtoms.size + BB_mountDependencies(store, atom) if (shouldRecompute) { - recomputeInvalidatedAtoms(store) - flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store) + BB_flushCallbacks(store) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - setAtomStateValueOrPromise(store, a, a.init) + BB_setAtomStateValueOrPromise(store, a, a.init) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -589,7 +594,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { return returnAtomValue(aState) } // a !== atom - const aState = readAtomState(store, a) + const aState = BB_readAtomState(store, a) try { return returnAtomValue(aState) } finally { @@ -598,8 +603,8 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { if (isPromiseLike(atomState.v)) { addPendingPromiseToDependency(atom, atomState.v, aState) } - if (mountedMap.has(atom)) { - mountedMap.get(a)?.t.add(atom) + if (BB_mountedMap.has(atom)) { + BB_mountedMap.get(a)?.t.add(atom) } if (!isSync) { mountDependenciesIfAsync() @@ -635,10 +640,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { } if (!isSync) { try { - return writeAtomState(store, atom, ...args) + return BB_writeAtomState(store, atom, ...args) } finally { - recomputeInvalidatedAtoms(store) - flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store) + BB_flushCallbacks(store) } } } @@ -647,20 +652,20 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { }, } const prevEpochNumber = atomState.n - const prevInvalidated = invalidatedAtoms.get(atom) === prevEpochNumber + const prevInvalidated = BB_invalidatedAtoms.get(atom) === prevEpochNumber try { if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(store) } - const valueOrPromise = atomRead(store, atom, getter, options as never) + const valueOrPromise = BB_atomRead(store, atom, getter, options as never) if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) { console.warn( 'Detected store mutation during atom read. This is not supported.', ) } - setAtomStateValueOrPromise(store, atom, valueOrPromise) + BB_setAtomStateValueOrPromise(store, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { - registerAbortHandler(store, valueOrPromise, () => controller?.abort()) + BB_registerAbortHandler(store, valueOrPromise, () => controller?.abort()) const settle = () => { pruneDependencies() mountDependenciesIfAsync() @@ -669,7 +674,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { } else { pruneDependencies() } - storeHooks.r?.(atom) + BB_storeHooks.r?.(atom) atomState.m = storeEpochNumber return atomState } catch (error) { @@ -681,9 +686,9 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { } finally { isSync = false if (atomState.n !== prevEpochNumber && prevInvalidated) { - invalidatedAtoms.set(atom, atomState.n) - changedAtoms.add(atom) - storeHooks.c?.(atom) + BB_invalidatedAtoms.set(atom, atomState.n) + BB_changedAtoms.add(atom) + BB_storeHooks.c?.(atom) } } } @@ -693,17 +698,17 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( atom, ) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const invalidatedAtoms = buildingBlocks[2] - const ensureAtomState = buildingBlocks[11] + const BB_mountedMap = buildingBlocks[1] + const BB_invalidatedAtoms = buildingBlocks[2] + const BB_ensureAtomState = buildingBlocks[11] const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = ensureAtomState(store, a) - for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { - const dState = ensureAtomState(store, d) - if (invalidatedAtoms.get(d) !== dState.n) { - invalidatedAtoms.set(d, dState.n) + const aState = BB_ensureAtomState(store, a) + for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { + const dState = BB_ensureAtomState(store, d) + if (BB_invalidatedAtoms.get(d) !== dState.n) { + BB_invalidatedAtoms.set(d, dState.n) stack.push(d) } } @@ -716,26 +721,26 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( ...args ) => { const buildingBlocks = getInternalBuildingBlocks(store) - const changedAtoms = buildingBlocks[3] - const storeHooks = buildingBlocks[6] - const atomWrite = buildingBlocks[8] - const ensureAtomState = buildingBlocks[11] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const readAtomState = buildingBlocks[14] - const invalidateDependents = buildingBlocks[15] - const writeAtomState = buildingBlocks[16] - const mountDependencies = buildingBlocks[17] - const setAtomStateValueOrPromise = buildingBlocks[20] - const storeEpochHolder = buildingBlocks[28] + const BB_changedAtoms = buildingBlocks[3] + const BB_storeHooks = buildingBlocks[6] + const BB_atomWrite = buildingBlocks[8] + const BB_ensureAtomState = buildingBlocks[11] + const BB_flushCallbacks = buildingBlocks[12] + const BB_recomputeInvalidatedAtoms = buildingBlocks[13] + const BB_readAtomState = buildingBlocks[14] + const BB_invalidateDependents = buildingBlocks[15] + const BB_writeAtomState = buildingBlocks[16] + const BB_mountDependencies = buildingBlocks[17] + const BB_setAtomStateValueOrPromise = buildingBlocks[20] + const BB_storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(readAtomState(store, a)) + returnAtomValue(BB_readAtomState(store, a)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -747,27 +752,27 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const prevEpochNumber = aState.n const v = args[0] as V - setAtomStateValueOrPromise(store, a, v) - mountDependencies(store, a) + BB_setAtomStateValueOrPromise(store, a, v) + BB_mountDependencies(store, a) if (prevEpochNumber !== aState.n) { - ++storeEpochHolder[0] - changedAtoms.add(a) - invalidateDependents(store, a) - storeHooks.c?.(a) + ++BB_storeEpochHolder[0] + BB_changedAtoms.add(a) + BB_invalidateDependents(store, a) + BB_storeHooks.c?.(a) } return undefined as R } else { - return writeAtomState(store, a, ...args) + return BB_writeAtomState(store, a, ...args) } } finally { if (!isSync) { - recomputeInvalidatedAtoms(store) - flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store) + BB_flushCallbacks(store) } } } try { - return atomWrite(store, atom, getter, setter, ...args) + return BB_atomWrite(store, atom, getter, setter, ...args) } finally { isSync = false } @@ -775,33 +780,33 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const changedAtoms = buildingBlocks[3] - const storeHooks = buildingBlocks[6] - const ensureAtomState = buildingBlocks[11] - const invalidateDependents = buildingBlocks[15] - const mountAtom = buildingBlocks[18] - const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(store, atom) - const mounted = mountedMap.get(atom) + const BB_mountedMap = buildingBlocks[1] + const BB_changedAtoms = buildingBlocks[3] + const BB_storeHooks = buildingBlocks[6] + const BB_ensureAtomState = buildingBlocks[11] + const BB_invalidateDependents = buildingBlocks[15] + const BB_mountAtom = buildingBlocks[18] + const BB_unmountAtom = buildingBlocks[19] + const atomState = BB_ensureAtomState(store, atom) + const mounted = BB_mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = ensureAtomState(store, a) - const aMounted = mountAtom(store, a) + const aState = BB_ensureAtomState(store, a) + const aMounted = BB_mountAtom(store, a) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { - changedAtoms.add(a) - invalidateDependents(store, a) - storeHooks.c?.(a) + BB_changedAtoms.add(a) + BB_invalidateDependents(store, a) + BB_storeHooks.c?.(a) } } } for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = unmountAtom(store, a) + const aMounted = BB_unmountAtom(store, a) aMounted?.t.delete(atom) } } @@ -810,24 +815,24 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => { const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const mountCallbacks = buildingBlocks[4] - const storeHooks = buildingBlocks[6] - const atomOnMount = buildingBlocks[10] - const ensureAtomState = buildingBlocks[11] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const readAtomState = buildingBlocks[14] - const writeAtomState = buildingBlocks[16] - const mountAtom = buildingBlocks[18] - const atomState = ensureAtomState(store, atom) - let mounted = mountedMap.get(atom) + const BB_mountedMap = buildingBlocks[1] + const BB_mountCallbacks = buildingBlocks[4] + const BB_storeHooks = buildingBlocks[6] + const BB_atomOnMount = buildingBlocks[10] + const BB_ensureAtomState = buildingBlocks[11] + const BB_flushCallbacks = buildingBlocks[12] + const BB_recomputeInvalidatedAtoms = buildingBlocks[13] + const BB_readAtomState = buildingBlocks[14] + const BB_writeAtomState = buildingBlocks[16] + const BB_mountAtom = buildingBlocks[18] + const atomState = BB_ensureAtomState(store, atom) + let mounted = BB_mountedMap.get(atom) if (!mounted) { // recompute atom state - readAtomState(store, atom) + BB_readAtomState(store, atom) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = mountAtom(store, a) + const aMounted = BB_mountAtom(store, a) aMounted.t.add(atom) } // mount self @@ -836,22 +841,22 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { d: new Set(atomState.d.keys()), t: new Set(), } - mountedMap.set(atom, mounted) + BB_mountedMap.set(atom, mounted) if (isActuallyWritableAtom(atom)) { const processOnMount = () => { let isSync = true const setAtom = (...args: unknown[]) => { try { - return writeAtomState(store, atom, ...args) + return BB_writeAtomState(store, atom, ...args) } finally { if (!isSync) { - recomputeInvalidatedAtoms(store) - flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store) + BB_flushCallbacks(store) } } } try { - const onUnmount = atomOnMount(store, atom, setAtom) + const onUnmount = BB_atomOnMount(store, atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -866,28 +871,28 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { isSync = false } } - mountCallbacks.add(processOnMount) + BB_mountCallbacks.add(processOnMount) } - storeHooks.m?.(atom) + BB_storeHooks.m?.(atom) } return mounted } const BUILDING_BLOCK_unmountAtom: UnmountAtom = (store, atom) => { const buildingBlocks = getInternalBuildingBlocks(store) - const mountedMap = buildingBlocks[1] - const unmountCallbacks = buildingBlocks[5] - const storeHooks = buildingBlocks[6] - const ensureAtomState = buildingBlocks[11] - const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(store, atom) - let mounted = mountedMap.get(atom) + const BB_mountedMap = buildingBlocks[1] + const BB_unmountCallbacks = buildingBlocks[5] + const BB_storeHooks = buildingBlocks[6] + const BB_ensureAtomState = buildingBlocks[11] + const BB_unmountAtom = buildingBlocks[19] + const atomState = BB_ensureAtomState(store, atom) + let mounted = BB_mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted } let isDependent = false for (const a of mounted.t) { - if (mountedMap.get(a)?.d.has(atom)) { + if (BB_mountedMap.get(a)?.d.has(atom)) { isDependent = true break } @@ -895,16 +900,16 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = (store, atom) => { if (!isDependent) { // unmount self if (mounted.u) { - unmountCallbacks.add(mounted.u) + BB_unmountCallbacks.add(mounted.u) } mounted = undefined - mountedMap.delete(atom) + BB_mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = unmountAtom(store, a) + const aMounted = BB_unmountAtom(store, a) aMounted?.t.delete(atom) } - storeHooks.u?.(atom) + BB_storeHooks.u?.(atom) return undefined } return mounted @@ -916,9 +921,9 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( valueOrPromise, ) => { const buildingBlocks = getInternalBuildingBlocks(store) - const ensureAtomState = buildingBlocks[11] - const abortPromise = buildingBlocks[27] - const atomState = ensureAtomState(store, atom) + const BB_ensureAtomState = buildingBlocks[11] + const BB_abortPromise = buildingBlocks[27] + const atomState = BB_ensureAtomState(store, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -926,7 +931,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - ensureAtomState(store, a), + BB_ensureAtomState(store, a), ) } } @@ -935,46 +940,47 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( if (!hasPrevValue || !Object.is(prevValue, atomState.v)) { ++atomState.n if (isPromiseLike(prevValue)) { - abortPromise(store, prevValue) + BB_abortPromise(store, prevValue) } } } const BUILDING_BLOCK_storeGet: StoreGet = (store, atom) => { - const readAtomState = getInternalBuildingBlocks(store)[14] - return returnAtomValue(readAtomState(store, atom)) as any + const buildingBlocks = getInternalBuildingBlocks(store) + const BB_readAtomState = buildingBlocks[14] + return returnAtomValue(BB_readAtomState(store, atom)) as any } const BUILDING_BLOCK_storeSet: StoreSet = (store, atom, ...args) => { const buildingBlocks = getInternalBuildingBlocks(store) - const changedAtoms = buildingBlocks[3] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const writeAtomState = buildingBlocks[16] - const prevChangedAtomsSize = changedAtoms.size + const BB_changedAtoms = buildingBlocks[3] + const BB_flushCallbacks = buildingBlocks[12] + const BB_recomputeInvalidatedAtoms = buildingBlocks[13] + const BB_writeAtomState = buildingBlocks[16] + const prevChangedAtomsSize = BB_changedAtoms.size try { - return writeAtomState(store, atom, ...args) as any + return BB_writeAtomState(store, atom, ...args) as any } finally { - if (changedAtoms.size !== prevChangedAtomsSize) { - recomputeInvalidatedAtoms(store) - flushCallbacks(store) + if (BB_changedAtoms.size !== prevChangedAtomsSize) { + BB_recomputeInvalidatedAtoms(store) + BB_flushCallbacks(store) } } } const BUILDING_BLOCK_storeSub: StoreSub = (store, atom, listener) => { const buildingBlocks = getInternalBuildingBlocks(store) - const flushCallbacks = buildingBlocks[12] - const mountAtom = buildingBlocks[18] - const unmountAtom = buildingBlocks[19] - const mounted = mountAtom(store, atom) + const BB_flushCallbacks = buildingBlocks[12] + const BB_mountAtom = buildingBlocks[18] + const BB_unmountAtom = buildingBlocks[19] + const mounted = BB_mountAtom(store, atom) const listeners = mounted.l listeners.add(listener) - flushCallbacks(store) + BB_flushCallbacks(store) return () => { listeners.delete(listener) - unmountAtom(store, atom) - flushCallbacks(store) + BB_unmountAtom(store, atom) + BB_flushCallbacks(store) } } @@ -984,12 +990,12 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( abortHandler, ) => { const buildingBlocks = getInternalBuildingBlocks(store) - const abortHandlersMap = buildingBlocks[25] - let abortHandlers = abortHandlersMap.get(promise) + const BB_abortHandlersMap = buildingBlocks[25] + let abortHandlers = BB_abortHandlersMap.get(promise) if (!abortHandlers) { abortHandlers = new Set() - abortHandlersMap.set(promise, abortHandlers) - const cleanup = () => abortHandlersMap.delete(promise) + BB_abortHandlersMap.set(promise, abortHandlers) + const cleanup = () => BB_abortHandlersMap.delete(promise) promise.then(cleanup, cleanup) } abortHandlers.add(abortHandler) @@ -997,8 +1003,8 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( const BUILDING_BLOCK_abortPromise: AbortPromise = (store, promise) => { const buildingBlocks = getInternalBuildingBlocks(store) - const abortHandlersMap = buildingBlocks[25] - const abortHandlers = abortHandlersMap.get(promise) + const BB_abortHandlersMap = buildingBlocks[25] + const abortHandlers = BB_abortHandlersMap.get(promise) abortHandlers?.forEach((fn) => fn()) } @@ -1016,9 +1022,9 @@ const getInternalBuildingBlocks = (store: Store): Readonly => { function getBuildingBlocks(store: Store): Readonly { const buildingBlocks = getInternalBuildingBlocks(store) - const enhanceBuildingBlocks = buildingBlocks[24] - if (enhanceBuildingBlocks) { - return enhanceBuildingBlocks(buildingBlocks) + const BB_enhanceBuildingBlocks = buildingBlocks[24] + if (BB_enhanceBuildingBlocks) { + return BB_enhanceBuildingBlocks(buildingBlocks) } return buildingBlocks } @@ -1026,16 +1032,19 @@ function getBuildingBlocks(store: Store): Readonly { function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { - const storeGet = getInternalBuildingBlocks(store)[21] - return storeGet(store, atom) + const buildingBlocks = getInternalBuildingBlocks(store) + const BB_storeGet = buildingBlocks[21] + return BB_storeGet(store, atom) }, set(atom, ...args) { - const storeSet = getInternalBuildingBlocks(store)[22] - return storeSet(store, atom, ...args) + const buildingBlocks = getInternalBuildingBlocks(store) + const BB_storeSet = buildingBlocks[22] + return BB_storeSet(store, atom, ...args) }, sub(atom, listener) { - const storeSub = getInternalBuildingBlocks(store)[23] - return storeSub(store, atom, listener) + const buildingBlocks = getInternalBuildingBlocks(store) + const BB_storeSub = buildingBlocks[23] + return BB_storeSub(store, atom, listener) }, } as Store From 2ca3f5eb288600234e66b3087f51863b61247b90 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 00:05:43 +0100 Subject: [PATCH 02/24] pass building blocks around --- src/vanilla/internals.ts | 258 ++++++++++++++++++++++++--------------- 1 file changed, 159 insertions(+), 99 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 44763269d9..bfb227e280 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -102,40 +102,66 @@ type AtomOnMount = ( type EnsureAtomState = ( store: Store, atom: Atom, + ...buildingBlocks: BuildingBlocks ) => AtomState -type FlushCallbacks = (store: Store) => void -type RecomputeInvalidatedAtoms = (store: Store) => void +type FlushCallbacks = (store: Store, ...buildingBlocks: BuildingBlocks) => void +type RecomputeInvalidatedAtoms = ( + store: Store, + ...buildingBlocks: BuildingBlocks +) => void type ReadAtomState = ( store: Store, atom: Atom, + ...buildingBlocks: BuildingBlocks ) => AtomState -type InvalidateDependents = (store: Store, atom: AnyAtom) => void +type InvalidateDependents = ( + store: Store, + atom: AnyAtom, + ...buildingBlocks: BuildingBlocks +) => void type WriteAtomState = ( store: Store, atom: WritableAtom, - ...args: Args + args: Args, + ...buildingBlocks: BuildingBlocks ) => Result -type MountDependencies = (store: Store, atom: AnyAtom) => void -type MountAtom = (store: Store, atom: Atom) => Mounted +type MountDependencies = ( + store: Store, + atom: AnyAtom, + ...buildingBlocks: BuildingBlocks +) => void +type MountAtom = ( + store: Store, + atom: Atom, + ...buildingBlocks: BuildingBlocks +) => Mounted type UnmountAtom = ( store: Store, atom: Atom, + ...buildingBlocks: BuildingBlocks ) => Mounted | undefined type SetAtomStateValueOrPromise = ( store: Store, atom: Atom, valueOrPromise: Value, + ...buildingBlocks: BuildingBlocks ) => void -type StoreGet = (store: Store, atom: Atom) => Value +type StoreGet = ( + store: Store, + atom: Atom, + ...buildingBlocks: BuildingBlocks +) => Value type StoreSet = ( store: Store, atom: WritableAtom, - ...args: Args + args: Args, + ...buildingBlocks: BuildingBlocks ) => Result type StoreSub = ( store: Store, atom: AnyAtom, listener: () => void, + ...buildingBlocks: BuildingBlocks ) => () => void type EnhanceBuildingBlocks = ( buildingBlocks: Readonly, @@ -145,8 +171,12 @@ type RegisterAbortHandler = ( store: Store, promise: PromiseLike, abortHandler: () => void, + ...buildingBlocks: BuildingBlocks +) => void +type AbortPromise = ( + promise: PromiseLike, + ...buildingBlocks: BuildingBlocks ) => void -type AbortPromise = (store: Store, promise: PromiseLike) => void type StoreEpochHolder = [n: EpochNumber] type Store = { @@ -374,8 +404,11 @@ const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => atom.onMount?.(setAtom) -const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( + store, + atom, + ...buildingBlocks +) => { const BB_atomStateMap = buildingBlocks[0] const BB_storeHooks = buildingBlocks[6] const BB_atomOnInit = buildingBlocks[9] @@ -392,8 +425,10 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = (store, atom) => { return atomState as never } -const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( + store, + ...buildingBlocks +) => { const BB_mountedMap = buildingBlocks[1] const BB_changedAtoms = buildingBlocks[3] const BB_mountCallbacks = buildingBlocks[4] @@ -422,7 +457,7 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => { BB_mountCallbacks.clear() callbacks.forEach(call) if (BB_changedAtoms.size) { - BB_recomputeInvalidatedAtoms(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) } } while ( BB_changedAtoms.size || @@ -436,8 +471,8 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => { const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( store, + ...buildingBlocks ) => { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] const BB_changedAtoms = buildingBlocks[3] @@ -457,7 +492,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const stack: AnyAtom[] = Array.from(BB_changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = BB_ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a, ...buildingBlocks) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -501,8 +536,8 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } if (hasChangedDeps) { BB_invalidatedAtoms.set(a, aState.n) - BB_readAtomState(store, a) - BB_mountDependencies(store, a) + BB_readAtomState(store, a, ...buildingBlocks) + BB_mountDependencies(store, a, ...buildingBlocks) } BB_invalidatedAtoms.delete(a) } @@ -511,8 +546,11 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // Dev only const storeMutationSet = new WeakSet() -const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_readAtomState: ReadAtomState = ( + store, + atom, + ...buildingBlocks +) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] const BB_changedAtoms = buildingBlocks[3] @@ -527,7 +565,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { const BB_setAtomStateValueOrPromise = buildingBlocks[20] const BB_registerAbortHandler = buildingBlocks[26] const BB_storeEpochHolder = buildingBlocks[28] - const atomState = BB_ensureAtomState(store, atom) + const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) const storeEpochNumber = BB_storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -548,7 +586,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (BB_readAtomState(store, a).n !== n) { + if (BB_readAtomState(store, a, ...buildingBlocks).n !== n) { hasChangedDeps = true break } @@ -573,19 +611,19 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { if (BB_mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it const shouldRecompute = !BB_changedAtoms.size - BB_mountDependencies(store, atom) + BB_mountDependencies(store, atom, ...buildingBlocks) if (shouldRecompute) { - BB_recomputeInvalidatedAtoms(store) - BB_flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = BB_ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a, ...buildingBlocks) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - BB_setAtomStateValueOrPromise(store, a, a.init) + BB_setAtomStateValueOrPromise(store, a, a.init, ...buildingBlocks) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -594,7 +632,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { return returnAtomValue(aState) } // a !== atom - const aState = BB_readAtomState(store, a) + const aState = BB_readAtomState(store, a, ...buildingBlocks) try { return returnAtomValue(aState) } finally { @@ -640,10 +678,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { } if (!isSync) { try { - return BB_writeAtomState(store, atom, ...args) + return BB_writeAtomState(store, atom, args, ...buildingBlocks) } finally { - BB_recomputeInvalidatedAtoms(store) - BB_flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } } @@ -663,9 +701,19 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { 'Detected store mutation during atom read. This is not supported.', ) } - BB_setAtomStateValueOrPromise(store, atom, valueOrPromise) + BB_setAtomStateValueOrPromise( + store, + atom, + valueOrPromise, + ...buildingBlocks, + ) if (isPromiseLike(valueOrPromise)) { - BB_registerAbortHandler(store, valueOrPromise, () => controller?.abort()) + BB_registerAbortHandler( + store, + valueOrPromise, + () => controller?.abort(), + ...buildingBlocks, + ) const settle = () => { pruneDependencies() mountDependenciesIfAsync() @@ -696,17 +744,17 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => { const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( store, atom, + ...buildingBlocks ) => { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] const BB_ensureAtomState = buildingBlocks[11] const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = BB_ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a, ...buildingBlocks) for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { - const dState = BB_ensureAtomState(store, d) + const dState = BB_ensureAtomState(store, d, ...buildingBlocks) if (BB_invalidatedAtoms.get(d) !== dState.n) { BB_invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -718,9 +766,9 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( store, atom, - ...args + args, + ...buildingBlocks ) => { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_changedAtoms = buildingBlocks[3] const BB_storeHooks = buildingBlocks[6] const BB_atomWrite = buildingBlocks[8] @@ -735,12 +783,12 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BB_storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(BB_readAtomState(store, a)) + returnAtomValue(BB_readAtomState(store, a, ...buildingBlocks)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = BB_ensureAtomState(store, a) + const aState = BB_ensureAtomState(store, a, ...buildingBlocks) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -752,22 +800,22 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const prevEpochNumber = aState.n const v = args[0] as V - BB_setAtomStateValueOrPromise(store, a, v) - BB_mountDependencies(store, a) + BB_setAtomStateValueOrPromise(store, a, v, ...buildingBlocks) + BB_mountDependencies(store, a, ...buildingBlocks) if (prevEpochNumber !== aState.n) { ++BB_storeEpochHolder[0] BB_changedAtoms.add(a) - BB_invalidateDependents(store, a) + BB_invalidateDependents(store, a, ...buildingBlocks) BB_storeHooks.c?.(a) } return undefined as R } else { - return BB_writeAtomState(store, a, ...args) + return BB_writeAtomState(store, a, args, ...buildingBlocks) } } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(store) - BB_flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } } @@ -778,8 +826,11 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } } -const BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_mountDependencies: MountDependencies = ( + store, + atom, + ...buildingBlocks +) => { const BB_mountedMap = buildingBlocks[1] const BB_changedAtoms = buildingBlocks[3] const BB_storeHooks = buildingBlocks[6] @@ -787,18 +838,18 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => { const BB_invalidateDependents = buildingBlocks[15] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(store, atom) + const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) const mounted = BB_mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = BB_ensureAtomState(store, a) - const aMounted = BB_mountAtom(store, a) + const aState = BB_ensureAtomState(store, a, ...buildingBlocks) + const aMounted = BB_mountAtom(store, a, ...buildingBlocks) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { BB_changedAtoms.add(a) - BB_invalidateDependents(store, a) + BB_invalidateDependents(store, a, ...buildingBlocks) BB_storeHooks.c?.(a) } } @@ -806,15 +857,18 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => { for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = BB_unmountAtom(store, a) + const aMounted = BB_unmountAtom(store, a, ...buildingBlocks) aMounted?.t.delete(atom) } } } } -const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_mountAtom: MountAtom = ( + store, + atom, + ...buildingBlocks +) => { const BB_mountedMap = buildingBlocks[1] const BB_mountCallbacks = buildingBlocks[4] const BB_storeHooks = buildingBlocks[6] @@ -825,14 +879,14 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { const BB_readAtomState = buildingBlocks[14] const BB_writeAtomState = buildingBlocks[16] const BB_mountAtom = buildingBlocks[18] - const atomState = BB_ensureAtomState(store, atom) + const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) let mounted = BB_mountedMap.get(atom) if (!mounted) { // recompute atom state - BB_readAtomState(store, atom) + BB_readAtomState(store, atom, ...buildingBlocks) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = BB_mountAtom(store, a) + const aMounted = BB_mountAtom(store, a, ...buildingBlocks) aMounted.t.add(atom) } // mount self @@ -847,11 +901,11 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { let isSync = true const setAtom = (...args: unknown[]) => { try { - return BB_writeAtomState(store, atom, ...args) + return BB_writeAtomState(store, atom, args, ...buildingBlocks) } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(store) - BB_flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } } @@ -878,14 +932,17 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => { return mounted } -const BUILDING_BLOCK_unmountAtom: UnmountAtom = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( + store, + atom, + ...buildingBlocks +) => { const BB_mountedMap = buildingBlocks[1] const BB_unmountCallbacks = buildingBlocks[5] const BB_storeHooks = buildingBlocks[6] const BB_ensureAtomState = buildingBlocks[11] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(store, atom) + const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) let mounted = BB_mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -906,7 +963,7 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = (store, atom) => { BB_mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = BB_unmountAtom(store, a) + const aMounted = BB_unmountAtom(store, a, ...buildingBlocks) aMounted?.t.delete(atom) } BB_storeHooks.u?.(atom) @@ -919,11 +976,11 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( store, atom, valueOrPromise, + ...buildingBlocks ) => { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_ensureAtomState = buildingBlocks[11] const BB_abortPromise = buildingBlocks[27] - const atomState = BB_ensureAtomState(store, atom) + const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -931,7 +988,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - BB_ensureAtomState(store, a), + BB_ensureAtomState(store, a, ...buildingBlocks), ) } } @@ -940,56 +997,65 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( if (!hasPrevValue || !Object.is(prevValue, atomState.v)) { ++atomState.n if (isPromiseLike(prevValue)) { - BB_abortPromise(store, prevValue) + BB_abortPromise(prevValue, ...buildingBlocks) } } } -const BUILDING_BLOCK_storeGet: StoreGet = (store, atom) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_storeGet: StoreGet = (store, atom, ...buildingBlocks) => { const BB_readAtomState = buildingBlocks[14] - return returnAtomValue(BB_readAtomState(store, atom)) as any + return returnAtomValue( + BB_readAtomState(store, atom, ...buildingBlocks), + ) as any } -const BUILDING_BLOCK_storeSet: StoreSet = (store, atom, ...args) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_storeSet: StoreSet = ( + store, + atom, + args, + ...buildingBlocks +) => { const BB_changedAtoms = buildingBlocks[3] const BB_flushCallbacks = buildingBlocks[12] const BB_recomputeInvalidatedAtoms = buildingBlocks[13] const BB_writeAtomState = buildingBlocks[16] const prevChangedAtomsSize = BB_changedAtoms.size try { - return BB_writeAtomState(store, atom, ...args) as any + return BB_writeAtomState(store, atom, args, ...buildingBlocks) as any } finally { if (BB_changedAtoms.size !== prevChangedAtomsSize) { - BB_recomputeInvalidatedAtoms(store) - BB_flushCallbacks(store) + BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } } -const BUILDING_BLOCK_storeSub: StoreSub = (store, atom, listener) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_storeSub: StoreSub = ( + store, + atom, + listener, + ...buildingBlocks +) => { const BB_flushCallbacks = buildingBlocks[12] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const mounted = BB_mountAtom(store, atom) + const mounted = BB_mountAtom(store, atom, ...buildingBlocks) const listeners = mounted.l listeners.add(listener) - BB_flushCallbacks(store) + BB_flushCallbacks(store, ...buildingBlocks) return () => { listeners.delete(listener) - BB_unmountAtom(store, atom) - BB_flushCallbacks(store) + BB_unmountAtom(store, atom, ...buildingBlocks) + BB_flushCallbacks(store, ...buildingBlocks) } } const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( - store, + _store, promise, abortHandler, + ...buildingBlocks ) => { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_abortHandlersMap = buildingBlocks[25] let abortHandlers = BB_abortHandlersMap.get(promise) if (!abortHandlers) { @@ -1001,8 +1067,10 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( abortHandlers.add(abortHandler) } -const BUILDING_BLOCK_abortPromise: AbortPromise = (store, promise) => { - const buildingBlocks = getInternalBuildingBlocks(store) +const BUILDING_BLOCK_abortPromise: AbortPromise = ( + promise, + ...buildingBlocks +) => { const BB_abortHandlersMap = buildingBlocks[25] const abortHandlers = BB_abortHandlersMap.get(promise) abortHandlers?.forEach((fn) => fn()) @@ -1010,18 +1078,13 @@ const BUILDING_BLOCK_abortPromise: AbortPromise = (store, promise) => { const buildingBlockMap = new WeakMap>() -const getInternalBuildingBlocks = (store: Store): Readonly => { +function getBuildingBlocks(store: Store): Readonly { const buildingBlocks = buildingBlockMap.get(store)! if (import.meta.env?.MODE !== 'production' && !buildingBlocks) { throw new Error( 'Store must be created by buildStore to read its building blocks', ) } - return buildingBlocks -} - -function getBuildingBlocks(store: Store): Readonly { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_enhanceBuildingBlocks = buildingBlocks[24] if (BB_enhanceBuildingBlocks) { return BB_enhanceBuildingBlocks(buildingBlocks) @@ -1032,19 +1095,16 @@ function getBuildingBlocks(store: Store): Readonly { function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_storeGet = buildingBlocks[21] - return BB_storeGet(store, atom) + return BB_storeGet(store, atom, ...buildingBlocks) }, set(atom, ...args) { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_storeSet = buildingBlocks[22] - return BB_storeSet(store, atom, ...args) + return BB_storeSet(store, atom, args, ...buildingBlocks) }, sub(atom, listener) { - const buildingBlocks = getInternalBuildingBlocks(store) const BB_storeSub = buildingBlocks[23] - return BB_storeSub(store, atom, listener) + return BB_storeSub(store, atom, listener, ...buildingBlocks) }, } as Store From ca6a88bc744a9235c715607caa70037538c951b7 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 00:10:42 +0100 Subject: [PATCH 03/24] fix react --- src/react/useAtomValue.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react/useAtomValue.ts b/src/react/useAtomValue.ts index 3e651573f4..aa91d432b3 100644 --- a/src/react/useAtomValue.ts +++ b/src/react/useAtomValue.ts @@ -85,7 +85,7 @@ const createContinuablePromise = ( continuablePromiseMap.set(nextValue, continuablePromise!) curr = nextValue nextValue.then(onFulfilled(nextValue), onRejected(nextValue)) - registerAbortHandler(store, nextValue, onAbort) + registerAbortHandler(store, nextValue, onAbort, ...buildingBlocks) } else { resolve(nextValue) } @@ -94,7 +94,7 @@ const createContinuablePromise = ( } } promise.then(onFulfilled(promise), onRejected(promise)) - registerAbortHandler(store, promise, onAbort) + registerAbortHandler(store, promise, onAbort, ...buildingBlocks) }) continuablePromiseMap.set(promise, continuablePromise) } From 7a4888b3aa1106446aba34b3fc909fcd121e6a6f Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 17:29:15 +0100 Subject: [PATCH 04/24] bb at first --- src/vanilla/internals.ts | 187 ++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 102 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index bfb227e280..d473c9b849 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -100,82 +100,82 @@ type AtomOnMount = ( ) => OnUnmount | void type EnsureAtomState = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, - ...buildingBlocks: BuildingBlocks ) => AtomState -type FlushCallbacks = (store: Store, ...buildingBlocks: BuildingBlocks) => void +type FlushCallbacks = (buildingBlocks: BuildingBlocks, store: Store) => void type RecomputeInvalidatedAtoms = ( + buildingBlocks: BuildingBlocks, store: Store, - ...buildingBlocks: BuildingBlocks ) => void type ReadAtomState = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, - ...buildingBlocks: BuildingBlocks ) => AtomState type InvalidateDependents = ( + buildingBlocks: BuildingBlocks, store: Store, atom: AnyAtom, - ...buildingBlocks: BuildingBlocks ) => void type WriteAtomState = ( + buildingBlocks: BuildingBlocks, store: Store, atom: WritableAtom, args: Args, - ...buildingBlocks: BuildingBlocks ) => Result type MountDependencies = ( + buildingBlocks: BuildingBlocks, store: Store, atom: AnyAtom, - ...buildingBlocks: BuildingBlocks ) => void type MountAtom = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, - ...buildingBlocks: BuildingBlocks ) => Mounted type UnmountAtom = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, - ...buildingBlocks: BuildingBlocks ) => Mounted | undefined type SetAtomStateValueOrPromise = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, valueOrPromise: Value, - ...buildingBlocks: BuildingBlocks ) => void type StoreGet = ( + buildingBlocks: BuildingBlocks, store: Store, atom: Atom, - ...buildingBlocks: BuildingBlocks ) => Value type StoreSet = ( + buildingBlocks: BuildingBlocks, store: Store, atom: WritableAtom, args: Args, - ...buildingBlocks: BuildingBlocks ) => Result type StoreSub = ( + buildingBlocks: BuildingBlocks, store: Store, atom: AnyAtom, listener: () => void, - ...buildingBlocks: BuildingBlocks ) => () => void type EnhanceBuildingBlocks = ( buildingBlocks: Readonly, ) => Readonly type AbortHandlersMap = WeakMapLike, Set<() => void>> type RegisterAbortHandler = ( + buildingBlocks: BuildingBlocks, store: Store, promise: PromiseLike, abortHandler: () => void, - ...buildingBlocks: BuildingBlocks ) => void type AbortPromise = ( + buildingBlocks: BuildingBlocks, promise: PromiseLike, - ...buildingBlocks: BuildingBlocks ) => void type StoreEpochHolder = [n: EpochNumber] @@ -405,9 +405,9 @@ const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => atom.onMount?.(setAtom) const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( + buildingBlocks, store, atom, - ...buildingBlocks ) => { const BB_atomStateMap = buildingBlocks[0] const BB_storeHooks = buildingBlocks[6] @@ -426,8 +426,8 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( } const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( + buildingBlocks, store, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_changedAtoms = buildingBlocks[3] @@ -457,7 +457,7 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( BB_mountCallbacks.clear() callbacks.forEach(call) if (BB_changedAtoms.size) { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) } } while ( BB_changedAtoms.size || @@ -470,8 +470,8 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( } const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( + buildingBlocks, store, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] @@ -492,7 +492,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const stack: AnyAtom[] = Array.from(BB_changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = BB_ensureAtomState(store, a, ...buildingBlocks) + const aState = BB_ensureAtomState(buildingBlocks, store, a) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -536,8 +536,8 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } if (hasChangedDeps) { BB_invalidatedAtoms.set(a, aState.n) - BB_readAtomState(store, a, ...buildingBlocks) - BB_mountDependencies(store, a, ...buildingBlocks) + BB_readAtomState(buildingBlocks, store, a) + BB_mountDependencies(buildingBlocks, store, a) } BB_invalidatedAtoms.delete(a) } @@ -547,9 +547,9 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const storeMutationSet = new WeakSet() const BUILDING_BLOCK_readAtomState: ReadAtomState = ( + buildingBlocks, store, atom, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] @@ -565,7 +565,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const BB_setAtomStateValueOrPromise = buildingBlocks[20] const BB_registerAbortHandler = buildingBlocks[26] const BB_storeEpochHolder = buildingBlocks[28] - const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) + const atomState = BB_ensureAtomState(buildingBlocks, store, atom) const storeEpochNumber = BB_storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -586,7 +586,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (BB_readAtomState(store, a, ...buildingBlocks).n !== n) { + if (BB_readAtomState(buildingBlocks, store, a).n !== n) { hasChangedDeps = true break } @@ -611,19 +611,19 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( if (BB_mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it const shouldRecompute = !BB_changedAtoms.size - BB_mountDependencies(store, atom, ...buildingBlocks) + BB_mountDependencies(buildingBlocks, store, atom) if (shouldRecompute) { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks, store) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = BB_ensureAtomState(store, a, ...buildingBlocks) + const aState = BB_ensureAtomState(buildingBlocks, store, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - BB_setAtomStateValueOrPromise(store, a, a.init, ...buildingBlocks) + BB_setAtomStateValueOrPromise(buildingBlocks, store, a, a.init) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -632,7 +632,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( return returnAtomValue(aState) } // a !== atom - const aState = BB_readAtomState(store, a, ...buildingBlocks) + const aState = BB_readAtomState(buildingBlocks, store, a) try { return returnAtomValue(aState) } finally { @@ -678,10 +678,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } if (!isSync) { try { - return BB_writeAtomState(store, atom, args, ...buildingBlocks) + return BB_writeAtomState(buildingBlocks, store, atom, args) } finally { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks, store) } } } @@ -701,18 +701,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( 'Detected store mutation during atom read. This is not supported.', ) } - BB_setAtomStateValueOrPromise( - store, - atom, - valueOrPromise, - ...buildingBlocks, - ) + BB_setAtomStateValueOrPromise(buildingBlocks, store, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { - BB_registerAbortHandler( - store, - valueOrPromise, - () => controller?.abort(), - ...buildingBlocks, + BB_registerAbortHandler(buildingBlocks, store, valueOrPromise, () => + controller?.abort(), ) const settle = () => { pruneDependencies() @@ -742,9 +734,9 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( + buildingBlocks, store, atom, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] @@ -752,9 +744,9 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = BB_ensureAtomState(store, a, ...buildingBlocks) + const aState = BB_ensureAtomState(buildingBlocks, store, a) for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { - const dState = BB_ensureAtomState(store, d, ...buildingBlocks) + const dState = BB_ensureAtomState(buildingBlocks, store, d) if (BB_invalidatedAtoms.get(d) !== dState.n) { BB_invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -764,10 +756,10 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( } const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( + buildingBlocks, store, atom, args, - ...buildingBlocks ) => { const BB_changedAtoms = buildingBlocks[3] const BB_storeHooks = buildingBlocks[6] @@ -783,12 +775,12 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BB_storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(BB_readAtomState(store, a, ...buildingBlocks)) + returnAtomValue(BB_readAtomState(buildingBlocks, store, a)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = BB_ensureAtomState(store, a, ...buildingBlocks) + const aState = BB_ensureAtomState(buildingBlocks, store, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -800,22 +792,22 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const prevEpochNumber = aState.n const v = args[0] as V - BB_setAtomStateValueOrPromise(store, a, v, ...buildingBlocks) - BB_mountDependencies(store, a, ...buildingBlocks) + BB_setAtomStateValueOrPromise(buildingBlocks, store, a, v) + BB_mountDependencies(buildingBlocks, store, a) if (prevEpochNumber !== aState.n) { ++BB_storeEpochHolder[0] BB_changedAtoms.add(a) - BB_invalidateDependents(store, a, ...buildingBlocks) + BB_invalidateDependents(buildingBlocks, store, a) BB_storeHooks.c?.(a) } return undefined as R } else { - return BB_writeAtomState(store, a, args, ...buildingBlocks) + return BB_writeAtomState(buildingBlocks, store, a, args) } } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks, store) } } } @@ -827,9 +819,9 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const BUILDING_BLOCK_mountDependencies: MountDependencies = ( + buildingBlocks, store, atom, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_changedAtoms = buildingBlocks[3] @@ -838,18 +830,18 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( const BB_invalidateDependents = buildingBlocks[15] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) + const atomState = BB_ensureAtomState(buildingBlocks, store, atom) const mounted = BB_mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = BB_ensureAtomState(store, a, ...buildingBlocks) - const aMounted = BB_mountAtom(store, a, ...buildingBlocks) + const aState = BB_ensureAtomState(buildingBlocks, store, a) + const aMounted = BB_mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { BB_changedAtoms.add(a) - BB_invalidateDependents(store, a, ...buildingBlocks) + BB_invalidateDependents(buildingBlocks, store, a) BB_storeHooks.c?.(a) } } @@ -857,18 +849,14 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = BB_unmountAtom(store, a, ...buildingBlocks) + const aMounted = BB_unmountAtom(buildingBlocks, store, a) aMounted?.t.delete(atom) } } } } -const BUILDING_BLOCK_mountAtom: MountAtom = ( - store, - atom, - ...buildingBlocks -) => { +const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { const BB_mountedMap = buildingBlocks[1] const BB_mountCallbacks = buildingBlocks[4] const BB_storeHooks = buildingBlocks[6] @@ -879,14 +867,14 @@ const BUILDING_BLOCK_mountAtom: MountAtom = ( const BB_readAtomState = buildingBlocks[14] const BB_writeAtomState = buildingBlocks[16] const BB_mountAtom = buildingBlocks[18] - const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) + const atomState = BB_ensureAtomState(buildingBlocks, store, atom) let mounted = BB_mountedMap.get(atom) if (!mounted) { // recompute atom state - BB_readAtomState(store, atom, ...buildingBlocks) + BB_readAtomState(buildingBlocks, store, atom) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = BB_mountAtom(store, a, ...buildingBlocks) + const aMounted = BB_mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) } // mount self @@ -901,11 +889,11 @@ const BUILDING_BLOCK_mountAtom: MountAtom = ( let isSync = true const setAtom = (...args: unknown[]) => { try { - return BB_writeAtomState(store, atom, args, ...buildingBlocks) + return BB_writeAtomState(buildingBlocks, store, atom, args) } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks, store) } } } @@ -933,16 +921,16 @@ const BUILDING_BLOCK_mountAtom: MountAtom = ( } const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( + buildingBlocks, store, atom, - ...buildingBlocks ) => { const BB_mountedMap = buildingBlocks[1] const BB_unmountCallbacks = buildingBlocks[5] const BB_storeHooks = buildingBlocks[6] const BB_ensureAtomState = buildingBlocks[11] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) + const atomState = BB_ensureAtomState(buildingBlocks, store, atom) let mounted = BB_mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -963,7 +951,7 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( BB_mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = BB_unmountAtom(store, a, ...buildingBlocks) + const aMounted = BB_unmountAtom(buildingBlocks, store, a) aMounted?.t.delete(atom) } BB_storeHooks.u?.(atom) @@ -973,14 +961,14 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( } const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( + buildingBlocks, store, atom, valueOrPromise, - ...buildingBlocks ) => { const BB_ensureAtomState = buildingBlocks[11] const BB_abortPromise = buildingBlocks[27] - const atomState = BB_ensureAtomState(store, atom, ...buildingBlocks) + const atomState = BB_ensureAtomState(buildingBlocks, store, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -988,7 +976,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - BB_ensureAtomState(store, a, ...buildingBlocks), + BB_ensureAtomState(buildingBlocks, store, a), ) } } @@ -997,23 +985,21 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( if (!hasPrevValue || !Object.is(prevValue, atomState.v)) { ++atomState.n if (isPromiseLike(prevValue)) { - BB_abortPromise(prevValue, ...buildingBlocks) + BB_abortPromise(buildingBlocks, prevValue) } } } -const BUILDING_BLOCK_storeGet: StoreGet = (store, atom, ...buildingBlocks) => { +const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, store, atom) => { const BB_readAtomState = buildingBlocks[14] - return returnAtomValue( - BB_readAtomState(store, atom, ...buildingBlocks), - ) as any + return returnAtomValue(BB_readAtomState(buildingBlocks, store, atom)) as any } const BUILDING_BLOCK_storeSet: StoreSet = ( + buildingBlocks, store, atom, args, - ...buildingBlocks ) => { const BB_changedAtoms = buildingBlocks[3] const BB_flushCallbacks = buildingBlocks[12] @@ -1021,40 +1007,40 @@ const BUILDING_BLOCK_storeSet: StoreSet = ( const BB_writeAtomState = buildingBlocks[16] const prevChangedAtomsSize = BB_changedAtoms.size try { - return BB_writeAtomState(store, atom, args, ...buildingBlocks) as any + return BB_writeAtomState(buildingBlocks, store, atom, args) as any } finally { if (BB_changedAtoms.size !== prevChangedAtomsSize) { - BB_recomputeInvalidatedAtoms(store, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks, store) } } } const BUILDING_BLOCK_storeSub: StoreSub = ( + buildingBlocks, store, atom, listener, - ...buildingBlocks ) => { const BB_flushCallbacks = buildingBlocks[12] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const mounted = BB_mountAtom(store, atom, ...buildingBlocks) + const mounted = BB_mountAtom(buildingBlocks, store, atom) const listeners = mounted.l listeners.add(listener) - BB_flushCallbacks(store, ...buildingBlocks) + BB_flushCallbacks(buildingBlocks, store) return () => { listeners.delete(listener) - BB_unmountAtom(store, atom, ...buildingBlocks) - BB_flushCallbacks(store, ...buildingBlocks) + BB_unmountAtom(buildingBlocks, store, atom) + BB_flushCallbacks(buildingBlocks, store) } } const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( + buildingBlocks, _store, promise, abortHandler, - ...buildingBlocks ) => { const BB_abortHandlersMap = buildingBlocks[25] let abortHandlers = BB_abortHandlersMap.get(promise) @@ -1067,10 +1053,7 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( abortHandlers.add(abortHandler) } -const BUILDING_BLOCK_abortPromise: AbortPromise = ( - promise, - ...buildingBlocks -) => { +const BUILDING_BLOCK_abortPromise: AbortPromise = (buildingBlocks, promise) => { const BB_abortHandlersMap = buildingBlocks[25] const abortHandlers = BB_abortHandlersMap.get(promise) abortHandlers?.forEach((fn) => fn()) @@ -1096,15 +1079,15 @@ function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { const BB_storeGet = buildingBlocks[21] - return BB_storeGet(store, atom, ...buildingBlocks) + return BB_storeGet(buildingBlocks, store, atom) }, set(atom, ...args) { const BB_storeSet = buildingBlocks[22] - return BB_storeSet(store, atom, args, ...buildingBlocks) + return BB_storeSet(buildingBlocks, store, atom, args) }, sub(atom, listener) { const BB_storeSub = buildingBlocks[23] - return BB_storeSub(store, atom, listener, ...buildingBlocks) + return BB_storeSub(buildingBlocks, store, atom, listener) }, } as Store From 183d3d4da8e95a55c6d4241cd42f4f751af74baf Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 18:13:27 +0100 Subject: [PATCH 05/24] do not pass store around --- src/vanilla/internals.ts | 216 +++++++++++++++------------------------ 1 file changed, 81 insertions(+), 135 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index d473c9b849..cde51ed8ba 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -83,93 +83,73 @@ type ChangedAtoms = SetLike type Callbacks = SetLike<() => void> type AtomRead = ( - store: Store, atom: Atom, ...params: Parameters['read']> ) => Value type AtomWrite = ( - store: Store, atom: WritableAtom, ...params: Parameters['write']> ) => Result -type AtomOnInit = (store: Store, atom: Atom) => void +type AtomOnInit = (atom: Atom) => void type AtomOnMount = ( - store: Store, atom: WritableAtom, setAtom: (...args: Args) => Result, ) => OnUnmount | void type EnsureAtomState = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, ) => AtomState -type FlushCallbacks = (buildingBlocks: BuildingBlocks, store: Store) => void -type RecomputeInvalidatedAtoms = ( - buildingBlocks: BuildingBlocks, - store: Store, -) => void +type FlushCallbacks = (buildingBlocks: BuildingBlocks) => void +type RecomputeInvalidatedAtoms = (buildingBlocks: BuildingBlocks) => void type ReadAtomState = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, ) => AtomState type InvalidateDependents = ( buildingBlocks: BuildingBlocks, - store: Store, atom: AnyAtom, ) => void type WriteAtomState = ( buildingBlocks: BuildingBlocks, - store: Store, atom: WritableAtom, args: Args, ) => Result -type MountDependencies = ( - buildingBlocks: BuildingBlocks, - store: Store, - atom: AnyAtom, -) => void +type MountDependencies = (buildingBlocks: BuildingBlocks, atom: AnyAtom) => void type MountAtom = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, ) => Mounted type UnmountAtom = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, ) => Mounted | undefined type SetAtomStateValueOrPromise = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, valueOrPromise: Value, ) => void type StoreGet = ( buildingBlocks: BuildingBlocks, - store: Store, atom: Atom, ) => Value type StoreSet = ( buildingBlocks: BuildingBlocks, - store: Store, atom: WritableAtom, args: Args, ) => Result type StoreSub = ( buildingBlocks: BuildingBlocks, - store: Store, atom: AnyAtom, listener: () => void, ) => () => void type EnhanceBuildingBlocks = ( buildingBlocks: Readonly, + store: Store, ) => Readonly type AbortHandlersMap = WeakMapLike, Set<() => void>> type RegisterAbortHandler = ( buildingBlocks: BuildingBlocks, - store: Store, promise: PromiseLike, abortHandler: () => void, ) => void @@ -395,18 +375,8 @@ function initializeStoreHooks(storeHooks: StoreHooks): Required { // Main functions // -const BUILDING_BLOCK_atomRead: AtomRead = (_store, atom, ...params) => - atom.read(...params) -const BUILDING_BLOCK_atomWrite: AtomWrite = (_store, atom, ...params) => - atom.write(...params) -const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => - atom.INTERNAL_onInit?.(store) -const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => - atom.onMount?.(setAtom) - const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( buildingBlocks, - store, atom, ) => { const BB_atomStateMap = buildingBlocks[0] @@ -420,15 +390,12 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( atomState = { d: new Map(), p: new Set(), n: 0 } BB_atomStateMap.set(atom, atomState) BB_storeHooks.i?.(atom) - BB_atomOnInit?.(store, atom) + BB_atomOnInit?.(atom) } return atomState as never } -const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( - buildingBlocks, - store, -) => { +const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { const BB_mountedMap = buildingBlocks[1] const BB_changedAtoms = buildingBlocks[3] const BB_mountCallbacks = buildingBlocks[4] @@ -457,7 +424,7 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( BB_mountCallbacks.clear() callbacks.forEach(call) if (BB_changedAtoms.size) { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) } } while ( BB_changedAtoms.size || @@ -471,7 +438,6 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( buildingBlocks, - store, ) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] @@ -492,7 +458,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const stack: AnyAtom[] = Array.from(BB_changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = BB_ensureAtomState(buildingBlocks, store, a) + const aState = BB_ensureAtomState(buildingBlocks, a) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -536,21 +502,17 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } if (hasChangedDeps) { BB_invalidatedAtoms.set(a, aState.n) - BB_readAtomState(buildingBlocks, store, a) - BB_mountDependencies(buildingBlocks, store, a) + BB_readAtomState(buildingBlocks, a) + BB_mountDependencies(buildingBlocks, a) } BB_invalidatedAtoms.delete(a) } } // Dev only -const storeMutationSet = new WeakSet() +const storeMutationSet = new WeakSet() -const BUILDING_BLOCK_readAtomState: ReadAtomState = ( - buildingBlocks, - store, - atom, -) => { +const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { const BB_mountedMap = buildingBlocks[1] const BB_invalidatedAtoms = buildingBlocks[2] const BB_changedAtoms = buildingBlocks[3] @@ -565,7 +527,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const BB_setAtomStateValueOrPromise = buildingBlocks[20] const BB_registerAbortHandler = buildingBlocks[26] const BB_storeEpochHolder = buildingBlocks[28] - const atomState = BB_ensureAtomState(buildingBlocks, store, atom) + const atomState = BB_ensureAtomState(buildingBlocks, atom) const storeEpochNumber = BB_storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -586,7 +548,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (BB_readAtomState(buildingBlocks, store, a).n !== n) { + if (BB_readAtomState(buildingBlocks, a).n !== n) { hasChangedDeps = true break } @@ -611,19 +573,19 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( if (BB_mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it const shouldRecompute = !BB_changedAtoms.size - BB_mountDependencies(buildingBlocks, store, atom) + BB_mountDependencies(buildingBlocks, atom) if (shouldRecompute) { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) - BB_flushCallbacks(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) + BB_flushCallbacks(buildingBlocks) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = BB_ensureAtomState(buildingBlocks, store, a) + const aState = BB_ensureAtomState(buildingBlocks, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - BB_setAtomStateValueOrPromise(buildingBlocks, store, a, a.init) + BB_setAtomStateValueOrPromise(buildingBlocks, a, a.init) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -632,7 +594,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( return returnAtomValue(aState) } // a !== atom - const aState = BB_readAtomState(buildingBlocks, store, a) + const aState = BB_readAtomState(buildingBlocks, a) try { return returnAtomValue(aState) } finally { @@ -678,10 +640,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } if (!isSync) { try { - return BB_writeAtomState(buildingBlocks, store, atom, args) + return BB_writeAtomState(buildingBlocks, atom, args) } finally { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) - BB_flushCallbacks(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) + BB_flushCallbacks(buildingBlocks) } } } @@ -693,17 +655,20 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const prevInvalidated = BB_invalidatedAtoms.get(atom) === prevEpochNumber try { if (import.meta.env?.MODE !== 'production') { - storeMutationSet.delete(store) + storeMutationSet.delete(buildingBlocks) } - const valueOrPromise = BB_atomRead(store, atom, getter, options as never) - if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) { + const valueOrPromise = BB_atomRead(atom, getter, options as never) + if ( + import.meta.env?.MODE !== 'production' && + storeMutationSet.has(buildingBlocks) + ) { console.warn( 'Detected store mutation during atom read. This is not supported.', ) } - BB_setAtomStateValueOrPromise(buildingBlocks, store, atom, valueOrPromise) + BB_setAtomStateValueOrPromise(buildingBlocks, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { - BB_registerAbortHandler(buildingBlocks, store, valueOrPromise, () => + BB_registerAbortHandler(buildingBlocks, valueOrPromise, () => controller?.abort(), ) const settle = () => { @@ -735,7 +700,6 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( buildingBlocks, - store, atom, ) => { const BB_mountedMap = buildingBlocks[1] @@ -744,9 +708,9 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = BB_ensureAtomState(buildingBlocks, store, a) + const aState = BB_ensureAtomState(buildingBlocks, a) for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { - const dState = BB_ensureAtomState(buildingBlocks, store, d) + const dState = BB_ensureAtomState(buildingBlocks, d) if (BB_invalidatedAtoms.get(d) !== dState.n) { BB_invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -757,7 +721,6 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( buildingBlocks, - store, atom, args, ) => { @@ -775,12 +738,12 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BB_storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(BB_readAtomState(buildingBlocks, store, a)) + returnAtomValue(BB_readAtomState(buildingBlocks, a)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = BB_ensureAtomState(buildingBlocks, store, a) + const aState = BB_ensureAtomState(buildingBlocks, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -788,31 +751,31 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( throw new Error('atom not writable') } if (import.meta.env?.MODE !== 'production') { - storeMutationSet.add(store) + storeMutationSet.add(buildingBlocks) } const prevEpochNumber = aState.n const v = args[0] as V - BB_setAtomStateValueOrPromise(buildingBlocks, store, a, v) - BB_mountDependencies(buildingBlocks, store, a) + BB_setAtomStateValueOrPromise(buildingBlocks, a, v) + BB_mountDependencies(buildingBlocks, a) if (prevEpochNumber !== aState.n) { ++BB_storeEpochHolder[0] BB_changedAtoms.add(a) - BB_invalidateDependents(buildingBlocks, store, a) + BB_invalidateDependents(buildingBlocks, a) BB_storeHooks.c?.(a) } return undefined as R } else { - return BB_writeAtomState(buildingBlocks, store, a, args) + return BB_writeAtomState(buildingBlocks, a, args) } } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) - BB_flushCallbacks(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) + BB_flushCallbacks(buildingBlocks) } } } try { - return BB_atomWrite(store, atom, getter, setter, ...args) + return BB_atomWrite(atom, getter, setter, ...args) } finally { isSync = false } @@ -820,7 +783,6 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BUILDING_BLOCK_mountDependencies: MountDependencies = ( buildingBlocks, - store, atom, ) => { const BB_mountedMap = buildingBlocks[1] @@ -830,18 +792,18 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( const BB_invalidateDependents = buildingBlocks[15] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(buildingBlocks, store, atom) + const atomState = BB_ensureAtomState(buildingBlocks, atom) const mounted = BB_mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = BB_ensureAtomState(buildingBlocks, store, a) - const aMounted = BB_mountAtom(buildingBlocks, store, a) + const aState = BB_ensureAtomState(buildingBlocks, a) + const aMounted = BB_mountAtom(buildingBlocks, a) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { BB_changedAtoms.add(a) - BB_invalidateDependents(buildingBlocks, store, a) + BB_invalidateDependents(buildingBlocks, a) BB_storeHooks.c?.(a) } } @@ -849,14 +811,14 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = BB_unmountAtom(buildingBlocks, store, a) + const aMounted = BB_unmountAtom(buildingBlocks, a) aMounted?.t.delete(atom) } } } } -const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { +const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { const BB_mountedMap = buildingBlocks[1] const BB_mountCallbacks = buildingBlocks[4] const BB_storeHooks = buildingBlocks[6] @@ -867,14 +829,14 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { const BB_readAtomState = buildingBlocks[14] const BB_writeAtomState = buildingBlocks[16] const BB_mountAtom = buildingBlocks[18] - const atomState = BB_ensureAtomState(buildingBlocks, store, atom) + const atomState = BB_ensureAtomState(buildingBlocks, atom) let mounted = BB_mountedMap.get(atom) if (!mounted) { // recompute atom state - BB_readAtomState(buildingBlocks, store, atom) + BB_readAtomState(buildingBlocks, atom) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = BB_mountAtom(buildingBlocks, store, a) + const aMounted = BB_mountAtom(buildingBlocks, a) aMounted.t.add(atom) } // mount self @@ -889,16 +851,16 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { let isSync = true const setAtom = (...args: unknown[]) => { try { - return BB_writeAtomState(buildingBlocks, store, atom, args) + return BB_writeAtomState(buildingBlocks, atom, args) } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) - BB_flushCallbacks(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) + BB_flushCallbacks(buildingBlocks) } } } try { - const onUnmount = BB_atomOnMount(store, atom, setAtom) + const onUnmount = BB_atomOnMount(atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -920,17 +882,13 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { return mounted } -const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( - buildingBlocks, - store, - atom, -) => { +const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { const BB_mountedMap = buildingBlocks[1] const BB_unmountCallbacks = buildingBlocks[5] const BB_storeHooks = buildingBlocks[6] const BB_ensureAtomState = buildingBlocks[11] const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(buildingBlocks, store, atom) + const atomState = BB_ensureAtomState(buildingBlocks, atom) let mounted = BB_mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -951,7 +909,7 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( BB_mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = BB_unmountAtom(buildingBlocks, store, a) + const aMounted = BB_unmountAtom(buildingBlocks, a) aMounted?.t.delete(atom) } BB_storeHooks.u?.(atom) @@ -962,13 +920,12 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( buildingBlocks, - store, atom, valueOrPromise, ) => { const BB_ensureAtomState = buildingBlocks[11] const BB_abortPromise = buildingBlocks[27] - const atomState = BB_ensureAtomState(buildingBlocks, store, atom) + const atomState = BB_ensureAtomState(buildingBlocks, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -976,7 +933,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - BB_ensureAtomState(buildingBlocks, store, a), + BB_ensureAtomState(buildingBlocks, a), ) } } @@ -990,55 +947,44 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( } } -const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, store, atom) => { +const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, atom) => { const BB_readAtomState = buildingBlocks[14] - return returnAtomValue(BB_readAtomState(buildingBlocks, store, atom)) as any + return returnAtomValue(BB_readAtomState(buildingBlocks, atom)) as any } -const BUILDING_BLOCK_storeSet: StoreSet = ( - buildingBlocks, - store, - atom, - args, -) => { +const BUILDING_BLOCK_storeSet: StoreSet = (buildingBlocks, atom, args) => { const BB_changedAtoms = buildingBlocks[3] const BB_flushCallbacks = buildingBlocks[12] const BB_recomputeInvalidatedAtoms = buildingBlocks[13] const BB_writeAtomState = buildingBlocks[16] const prevChangedAtomsSize = BB_changedAtoms.size try { - return BB_writeAtomState(buildingBlocks, store, atom, args) as any + return BB_writeAtomState(buildingBlocks, atom, args) as any } finally { if (BB_changedAtoms.size !== prevChangedAtomsSize) { - BB_recomputeInvalidatedAtoms(buildingBlocks, store) - BB_flushCallbacks(buildingBlocks, store) + BB_recomputeInvalidatedAtoms(buildingBlocks) + BB_flushCallbacks(buildingBlocks) } } } -const BUILDING_BLOCK_storeSub: StoreSub = ( - buildingBlocks, - store, - atom, - listener, -) => { +const BUILDING_BLOCK_storeSub: StoreSub = (buildingBlocks, atom, listener) => { const BB_flushCallbacks = buildingBlocks[12] const BB_mountAtom = buildingBlocks[18] const BB_unmountAtom = buildingBlocks[19] - const mounted = BB_mountAtom(buildingBlocks, store, atom) + const mounted = BB_mountAtom(buildingBlocks, atom) const listeners = mounted.l listeners.add(listener) - BB_flushCallbacks(buildingBlocks, store) + BB_flushCallbacks(buildingBlocks) return () => { listeners.delete(listener) - BB_unmountAtom(buildingBlocks, store, atom) - BB_flushCallbacks(buildingBlocks, store) + BB_unmountAtom(buildingBlocks, atom) + BB_flushCallbacks(buildingBlocks) } } const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( buildingBlocks, - _store, promise, abortHandler, ) => { @@ -1070,7 +1016,7 @@ function getBuildingBlocks(store: Store): Readonly { } const BB_enhanceBuildingBlocks = buildingBlocks[24] if (BB_enhanceBuildingBlocks) { - return BB_enhanceBuildingBlocks(buildingBlocks) + return BB_enhanceBuildingBlocks(buildingBlocks, store) } return buildingBlocks } @@ -1079,15 +1025,15 @@ function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { const BB_storeGet = buildingBlocks[21] - return BB_storeGet(buildingBlocks, store, atom) + return BB_storeGet(buildingBlocks, atom) }, set(atom, ...args) { const BB_storeSet = buildingBlocks[22] - return BB_storeSet(buildingBlocks, store, atom, args) + return BB_storeSet(buildingBlocks, atom, args) }, sub(atom, listener) { const BB_storeSub = buildingBlocks[23] - return BB_storeSub(buildingBlocks, store, atom, listener) + return BB_storeSub(buildingBlocks, atom, listener) }, } as Store @@ -1102,10 +1048,10 @@ function buildStore(...buildArgs: Partial): Store { new Set(), // unmountCallbacks {}, // storeHooks // atom interceptors - BUILDING_BLOCK_atomRead, - BUILDING_BLOCK_atomWrite, - BUILDING_BLOCK_atomOnInit, - BUILDING_BLOCK_atomOnMount, + (atom, ...params) => atom.read(...params), + (atom, ...params) => atom.write(...params), + (atom) => atom.INTERNAL_onInit?.(store), + (atom, setAtom) => atom.onMount?.(setAtom), // building-block functions BUILDING_BLOCK_ensureAtomState, BUILDING_BLOCK_flushCallbacks, From b4d845c341586ec59c9431d4740070e6ed62aaf3 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 23:41:50 +0100 Subject: [PATCH 06/24] fix lint --- src/react/useAtomValue.ts | 4 ++-- src/vanilla/internals.ts | 41 ++++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/react/useAtomValue.ts b/src/react/useAtomValue.ts index aa91d432b3..13dd556d7b 100644 --- a/src/react/useAtomValue.ts +++ b/src/react/useAtomValue.ts @@ -85,7 +85,7 @@ const createContinuablePromise = ( continuablePromiseMap.set(nextValue, continuablePromise!) curr = nextValue nextValue.then(onFulfilled(nextValue), onRejected(nextValue)) - registerAbortHandler(store, nextValue, onAbort, ...buildingBlocks) + registerAbortHandler(buildingBlocks, nextValue, onAbort) } else { resolve(nextValue) } @@ -94,7 +94,7 @@ const createContinuablePromise = ( } } promise.then(onFulfilled(promise), onRejected(promise)) - registerAbortHandler(store, promise, onAbort, ...buildingBlocks) + registerAbortHandler(buildingBlocks, promise, onAbort) }) continuablePromiseMap.set(promise, continuablePromise) } diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index cde51ed8ba..e9379d8192 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -97,64 +97,69 @@ type AtomOnMount = ( ) => OnUnmount | void type EnsureAtomState = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, ) => AtomState -type FlushCallbacks = (buildingBlocks: BuildingBlocks) => void -type RecomputeInvalidatedAtoms = (buildingBlocks: BuildingBlocks) => void +type FlushCallbacks = (buildingBlocks: Readonly) => void +type RecomputeInvalidatedAtoms = ( + buildingBlocks: Readonly, +) => void type ReadAtomState = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, ) => AtomState type InvalidateDependents = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: AnyAtom, ) => void type WriteAtomState = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: WritableAtom, args: Args, ) => Result -type MountDependencies = (buildingBlocks: BuildingBlocks, atom: AnyAtom) => void +type MountDependencies = ( + buildingBlocks: Readonly, + atom: AnyAtom, +) => void type MountAtom = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, ) => Mounted type UnmountAtom = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, ) => Mounted | undefined type SetAtomStateValueOrPromise = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, valueOrPromise: Value, ) => void type StoreGet = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: Atom, ) => Value type StoreSet = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: WritableAtom, args: Args, ) => Result type StoreSub = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, atom: AnyAtom, listener: () => void, ) => () => void type EnhanceBuildingBlocks = ( - buildingBlocks: Readonly, + buildingBlocks: Readonly>, store: Store, -) => Readonly +) => Readonly> type AbortHandlersMap = WeakMapLike, Set<() => void>> type RegisterAbortHandler = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, promise: PromiseLike, abortHandler: () => void, ) => void type AbortPromise = ( - buildingBlocks: BuildingBlocks, + buildingBlocks: Readonly, promise: PromiseLike, ) => void type StoreEpochHolder = [n: EpochNumber] @@ -510,7 +515,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } // Dev only -const storeMutationSet = new WeakSet() +const storeMutationSet = new WeakSet>() const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { const BB_mountedMap = buildingBlocks[1] From a810624460a1755c4254f6c1affbbd6f7ffd8e4a Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 Apr 2026 23:59:04 +0100 Subject: [PATCH 07/24] fix tests --- tests/vanilla/internals.test.tsx | 11 ++++++----- tests/vanilla/storedev.test.tsx | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/vanilla/internals.test.tsx b/tests/vanilla/internals.test.tsx index acc2df4634..881c2bf4a5 100644 --- a/tests/vanilla/internals.test.tsx +++ b/tests/vanilla/internals.test.tsx @@ -199,9 +199,9 @@ describe('internals', () => { } as INTERNAL_InvalidatedAtoms })() - const buildingBlocks: Partial = [] - buildingBlocks[2] = invalidatedAtoms - const store = INTERNAL_buildStore(...buildingBlocks) + const partialBuildingBlocks: Partial = [] + partialBuildingBlocks[2] = invalidatedAtoms + const store = INTERNAL_buildStore(...partialBuildingBlocks) const baseAtom = atom(0) const midAtom1 = atom((get) => get(baseAtom)) @@ -209,8 +209,9 @@ describe('internals', () => { const leafAtom = atom((get) => get(midAtom1) + get(midAtom2)) const unsub = store.sub(leafAtom, () => {}) - const invalidateDependents = INTERNAL_getBuildingBlocks(store)[15] - expect(() => invalidateDependents(store, baseAtom)).not.toThrow() + const buildingBlocks = INTERNAL_getBuildingBlocks(store) + const invalidateDependents = buildingBlocks[15] + expect(() => invalidateDependents(buildingBlocks, baseAtom)).not.toThrow() unsub() }) }) diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index 91c0f61fa6..e0def82f4f 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -32,7 +32,7 @@ const createDevStore = (): INTERNAL_Store & DevStore => { undefined, storeHooks, undefined, - (_store, atom, get, set, ...args) => { + (atom, get, set, ...args) => { if (inRestoreAtom) { return set(atom, ...(args as any)) } From f05efd6cfdfdfb72c5b161e67b9b7accd7ded6a1 Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 3 Apr 2026 00:05:10 +0100 Subject: [PATCH 08/24] refactor --- src/vanilla/internals.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index e9379d8192..4b91018aea 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -1029,15 +1029,12 @@ function getBuildingBlocks(store: Store): Readonly { function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { - const BB_storeGet = buildingBlocks[21] return BB_storeGet(buildingBlocks, atom) }, set(atom, ...args) { - const BB_storeSet = buildingBlocks[22] return BB_storeSet(buildingBlocks, atom, args) }, sub(atom, listener) { - const BB_storeSub = buildingBlocks[23] return BB_storeSub(buildingBlocks, atom, listener) }, } as Store @@ -1071,7 +1068,7 @@ function buildStore(...buildArgs: Partial): Store { BUILDING_BLOCK_storeGet, BUILDING_BLOCK_storeSet, BUILDING_BLOCK_storeSub, - undefined, + undefined, // enhanceBuildingBlocks // abortable promise support new WeakMap(), // abortHandlersMap BUILDING_BLOCK_registerAbortHandler, @@ -1081,6 +1078,9 @@ function buildStore(...buildArgs: Partial): Store { ] satisfies BuildingBlocks ).map((fn, i) => buildArgs[i] || fn) as BuildingBlocks buildingBlockMap.set(store, Object.freeze(buildingBlocks)) + const BB_storeGet = buildingBlocks[21] + const BB_storeSet = buildingBlocks[22] + const BB_storeSub = buildingBlocks[23] return store } From 7c10e57fb747605975a23f83ba6dcd88e3066e7c Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 3 Apr 2026 00:07:16 +0100 Subject: [PATCH 09/24] remove BB_ prefix --- src/vanilla/internals.ts | 399 +++++++++++++++++++-------------------- 1 file changed, 197 insertions(+), 202 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 4b91018aea..b243957bf8 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -384,29 +384,29 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( buildingBlocks, atom, ) => { - const BB_atomStateMap = buildingBlocks[0] - const BB_storeHooks = buildingBlocks[6] - const BB_atomOnInit = buildingBlocks[9] + const atomStateMap = buildingBlocks[0] + const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] if (import.meta.env?.MODE !== 'production' && !atom) { throw new Error('Atom is undefined or null') } - let atomState = BB_atomStateMap.get(atom) + let atomState = atomStateMap.get(atom) if (!atomState) { atomState = { d: new Map(), p: new Set(), n: 0 } - BB_atomStateMap.set(atom, atomState) - BB_storeHooks.i?.(atom) - BB_atomOnInit?.(atom) + atomStateMap.set(atom, atomState) + storeHooks.i?.(atom) + atomOnInit?.(atom) } return atomState as never } const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { - const BB_mountedMap = buildingBlocks[1] - const BB_changedAtoms = buildingBlocks[3] - const BB_mountCallbacks = buildingBlocks[4] - const BB_unmountCallbacks = buildingBlocks[5] - const BB_storeHooks = buildingBlocks[6] - const BB_recomputeInvalidatedAtoms = buildingBlocks[13] + const mountedMap = buildingBlocks[1] + const changedAtoms = buildingBlocks[3] + const mountCallbacks = buildingBlocks[4] + const unmountCallbacks = buildingBlocks[5] + const storeHooks = buildingBlocks[6] + const recomputeInvalidatedAtoms = buildingBlocks[13] const errors: unknown[] = [] const call = (fn: () => void) => { try { @@ -416,26 +416,22 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { } } do { - if (BB_storeHooks.f) { - call(BB_storeHooks.f) + if (storeHooks.f) { + call(storeHooks.f) } const callbacks = new Set<() => void>() const add = callbacks.add.bind(callbacks) - BB_changedAtoms.forEach((atom) => BB_mountedMap.get(atom)?.l.forEach(add)) - BB_changedAtoms.clear() - BB_unmountCallbacks.forEach(add) - BB_unmountCallbacks.clear() - BB_mountCallbacks.forEach(add) - BB_mountCallbacks.clear() + changedAtoms.forEach((atom) => mountedMap.get(atom)?.l.forEach(add)) + changedAtoms.clear() + unmountCallbacks.forEach(add) + unmountCallbacks.clear() + mountCallbacks.forEach(add) + mountCallbacks.clear() callbacks.forEach(call) - if (BB_changedAtoms.size) { - BB_recomputeInvalidatedAtoms(buildingBlocks) + if (changedAtoms.size) { + recomputeInvalidatedAtoms(buildingBlocks) } - } while ( - BB_changedAtoms.size || - BB_unmountCallbacks.size || - BB_mountCallbacks.size - ) + } while (changedAtoms.size || unmountCallbacks.size || mountCallbacks.size) if (errors.length) { throw new AggregateError(errors) } @@ -444,12 +440,12 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( buildingBlocks, ) => { - const BB_mountedMap = buildingBlocks[1] - const BB_invalidatedAtoms = buildingBlocks[2] - const BB_changedAtoms = buildingBlocks[3] - const BB_ensureAtomState = buildingBlocks[11] - const BB_readAtomState = buildingBlocks[14] - const BB_mountDependencies = buildingBlocks[17] + const mountedMap = buildingBlocks[1] + const invalidatedAtoms = buildingBlocks[2] + const changedAtoms = buildingBlocks[3] + const ensureAtomState = buildingBlocks[11] + const readAtomState = buildingBlocks[14] + const mountDependencies = buildingBlocks[17] // Step 1: traverse the dependency graph to build the topologically sorted atom list // We don't bother to check for cycles, which simplifies the algorithm. // This is a topological sort via depth-first search, slightly modified from @@ -460,10 +456,10 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const visited = new WeakSet() // Visit the root atom. This is the only atom in the dependency graph // without incoming edges, which is one reason we can simplify the algorithm - const stack: AnyAtom[] = Array.from(BB_changedAtoms) + const stack: AnyAtom[] = Array.from(changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = BB_ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, a) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -473,11 +469,11 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // The algorithm calls for pushing onto the front of the list. For // performance, we will simply push onto the end, and then will iterate in // reverse order later. - if (BB_invalidatedAtoms.get(a) === aState.n) { + if (invalidatedAtoms.get(a) === aState.n) { topSortedReversed.push([a, aState]) } else if ( import.meta.env?.MODE !== 'production' && - BB_invalidatedAtoms.has(a) + invalidatedAtoms.has(a) ) { throw new Error('[Bug] invalidated atom exists') } @@ -488,7 +484,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } visiting.add(a) // Push unvisited dependents onto the stack - for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { + for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { if (!visiting.has(d)) { stack.push(d) } @@ -500,17 +496,17 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const [a, aState] = topSortedReversed[i]! let hasChangedDeps = false for (const dep of aState.d.keys()) { - if (dep !== a && BB_changedAtoms.has(dep)) { + if (dep !== a && changedAtoms.has(dep)) { hasChangedDeps = true break } } if (hasChangedDeps) { - BB_invalidatedAtoms.set(a, aState.n) - BB_readAtomState(buildingBlocks, a) - BB_mountDependencies(buildingBlocks, a) + invalidatedAtoms.set(a, aState.n) + readAtomState(buildingBlocks, a) + mountDependencies(buildingBlocks, a) } - BB_invalidatedAtoms.delete(a) + invalidatedAtoms.delete(a) } } @@ -518,30 +514,29 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const storeMutationSet = new WeakSet>() const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { - const BB_mountedMap = buildingBlocks[1] - const BB_invalidatedAtoms = buildingBlocks[2] - const BB_changedAtoms = buildingBlocks[3] - const BB_storeHooks = buildingBlocks[6] - const BB_atomRead = buildingBlocks[7] - const BB_ensureAtomState = buildingBlocks[11] - const BB_flushCallbacks = buildingBlocks[12] - const BB_recomputeInvalidatedAtoms = buildingBlocks[13] - const BB_readAtomState = buildingBlocks[14] - const BB_writeAtomState = buildingBlocks[16] - const BB_mountDependencies = buildingBlocks[17] - const BB_setAtomStateValueOrPromise = buildingBlocks[20] - const BB_registerAbortHandler = buildingBlocks[26] - const BB_storeEpochHolder = buildingBlocks[28] - const atomState = BB_ensureAtomState(buildingBlocks, atom) - const storeEpochNumber = BB_storeEpochHolder[0] + const mountedMap = buildingBlocks[1] + const invalidatedAtoms = buildingBlocks[2] + const changedAtoms = buildingBlocks[3] + const storeHooks = buildingBlocks[6] + const atomRead = buildingBlocks[7] + const ensureAtomState = buildingBlocks[11] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const readAtomState = buildingBlocks[14] + const writeAtomState = buildingBlocks[16] + const mountDependencies = buildingBlocks[17] + const setAtomStateValueOrPromise = buildingBlocks[20] + const registerAbortHandler = buildingBlocks[26] + const storeEpochHolder = buildingBlocks[28] + const atomState = ensureAtomState(buildingBlocks, atom) + const storeEpochNumber = storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { if ( // If the atom is mounted, we can use cached atom state, // because it should have been updated by dependencies. // We can't use the cache if the atom is invalidated. - (BB_mountedMap.has(atom) && - BB_invalidatedAtoms.get(atom) !== atomState.n) || + (mountedMap.has(atom) && invalidatedAtoms.get(atom) !== atomState.n) || // If atom is not mounted, we can use cached atom state, // only if store hasn't been mutated. atomState.m === storeEpochNumber @@ -553,7 +548,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (BB_readAtomState(buildingBlocks, a).n !== n) { + if (readAtomState(buildingBlocks, a).n !== n) { hasChangedDeps = true break } @@ -575,22 +570,22 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { } } const mountDependenciesIfAsync = () => { - if (BB_mountedMap.has(atom)) { + if (mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it - const shouldRecompute = !BB_changedAtoms.size - BB_mountDependencies(buildingBlocks, atom) + const shouldRecompute = !changedAtoms.size + mountDependencies(buildingBlocks, atom) if (shouldRecompute) { - BB_recomputeInvalidatedAtoms(buildingBlocks) - BB_flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = BB_ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - BB_setAtomStateValueOrPromise(buildingBlocks, a, a.init) + setAtomStateValueOrPromise(buildingBlocks, a, a.init) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -599,7 +594,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { return returnAtomValue(aState) } // a !== atom - const aState = BB_readAtomState(buildingBlocks, a) + const aState = readAtomState(buildingBlocks, a) try { return returnAtomValue(aState) } finally { @@ -608,8 +603,8 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { if (isPromiseLike(atomState.v)) { addPendingPromiseToDependency(atom, atomState.v, aState) } - if (BB_mountedMap.has(atom)) { - BB_mountedMap.get(a)?.t.add(atom) + if (mountedMap.has(atom)) { + mountedMap.get(a)?.t.add(atom) } if (!isSync) { mountDependenciesIfAsync() @@ -645,10 +640,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { } if (!isSync) { try { - return BB_writeAtomState(buildingBlocks, atom, args) + return writeAtomState(buildingBlocks, atom, args) } finally { - BB_recomputeInvalidatedAtoms(buildingBlocks) - BB_flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) } } } @@ -657,12 +652,12 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { }, } const prevEpochNumber = atomState.n - const prevInvalidated = BB_invalidatedAtoms.get(atom) === prevEpochNumber + const prevInvalidated = invalidatedAtoms.get(atom) === prevEpochNumber try { if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(buildingBlocks) } - const valueOrPromise = BB_atomRead(atom, getter, options as never) + const valueOrPromise = atomRead(atom, getter, options as never) if ( import.meta.env?.MODE !== 'production' && storeMutationSet.has(buildingBlocks) @@ -671,9 +666,9 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { 'Detected store mutation during atom read. This is not supported.', ) } - BB_setAtomStateValueOrPromise(buildingBlocks, atom, valueOrPromise) + setAtomStateValueOrPromise(buildingBlocks, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { - BB_registerAbortHandler(buildingBlocks, valueOrPromise, () => + registerAbortHandler(buildingBlocks, valueOrPromise, () => controller?.abort(), ) const settle = () => { @@ -684,7 +679,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { } else { pruneDependencies() } - BB_storeHooks.r?.(atom) + storeHooks.r?.(atom) atomState.m = storeEpochNumber return atomState } catch (error) { @@ -696,9 +691,9 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { } finally { isSync = false if (atomState.n !== prevEpochNumber && prevInvalidated) { - BB_invalidatedAtoms.set(atom, atomState.n) - BB_changedAtoms.add(atom) - BB_storeHooks.c?.(atom) + invalidatedAtoms.set(atom, atomState.n) + changedAtoms.add(atom) + storeHooks.c?.(atom) } } } @@ -707,17 +702,17 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( buildingBlocks, atom, ) => { - const BB_mountedMap = buildingBlocks[1] - const BB_invalidatedAtoms = buildingBlocks[2] - const BB_ensureAtomState = buildingBlocks[11] + const mountedMap = buildingBlocks[1] + const invalidatedAtoms = buildingBlocks[2] + const ensureAtomState = buildingBlocks[11] const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = BB_ensureAtomState(buildingBlocks, a) - for (const d of getMountedOrPendingDependents(a, aState, BB_mountedMap)) { - const dState = BB_ensureAtomState(buildingBlocks, d) - if (BB_invalidatedAtoms.get(d) !== dState.n) { - BB_invalidatedAtoms.set(d, dState.n) + const aState = ensureAtomState(buildingBlocks, a) + for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { + const dState = ensureAtomState(buildingBlocks, d) + if (invalidatedAtoms.get(d) !== dState.n) { + invalidatedAtoms.set(d, dState.n) stack.push(d) } } @@ -729,26 +724,26 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( atom, args, ) => { - const BB_changedAtoms = buildingBlocks[3] - const BB_storeHooks = buildingBlocks[6] - const BB_atomWrite = buildingBlocks[8] - const BB_ensureAtomState = buildingBlocks[11] - const BB_flushCallbacks = buildingBlocks[12] - const BB_recomputeInvalidatedAtoms = buildingBlocks[13] - const BB_readAtomState = buildingBlocks[14] - const BB_invalidateDependents = buildingBlocks[15] - const BB_writeAtomState = buildingBlocks[16] - const BB_mountDependencies = buildingBlocks[17] - const BB_setAtomStateValueOrPromise = buildingBlocks[20] - const BB_storeEpochHolder = buildingBlocks[28] + const changedAtoms = buildingBlocks[3] + const storeHooks = buildingBlocks[6] + const atomWrite = buildingBlocks[8] + const ensureAtomState = buildingBlocks[11] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const readAtomState = buildingBlocks[14] + const invalidateDependents = buildingBlocks[15] + const writeAtomState = buildingBlocks[16] + const mountDependencies = buildingBlocks[17] + const setAtomStateValueOrPromise = buildingBlocks[20] + const storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(BB_readAtomState(buildingBlocks, a)) + returnAtomValue(readAtomState(buildingBlocks, a)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = BB_ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -760,27 +755,27 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const prevEpochNumber = aState.n const v = args[0] as V - BB_setAtomStateValueOrPromise(buildingBlocks, a, v) - BB_mountDependencies(buildingBlocks, a) + setAtomStateValueOrPromise(buildingBlocks, a, v) + mountDependencies(buildingBlocks, a) if (prevEpochNumber !== aState.n) { - ++BB_storeEpochHolder[0] - BB_changedAtoms.add(a) - BB_invalidateDependents(buildingBlocks, a) - BB_storeHooks.c?.(a) + ++storeEpochHolder[0] + changedAtoms.add(a) + invalidateDependents(buildingBlocks, a) + storeHooks.c?.(a) } return undefined as R } else { - return BB_writeAtomState(buildingBlocks, a, args) + return writeAtomState(buildingBlocks, a, args) } } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(buildingBlocks) - BB_flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) } } } try { - return BB_atomWrite(atom, getter, setter, ...args) + return atomWrite(atom, getter, setter, ...args) } finally { isSync = false } @@ -790,33 +785,33 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( buildingBlocks, atom, ) => { - const BB_mountedMap = buildingBlocks[1] - const BB_changedAtoms = buildingBlocks[3] - const BB_storeHooks = buildingBlocks[6] - const BB_ensureAtomState = buildingBlocks[11] - const BB_invalidateDependents = buildingBlocks[15] - const BB_mountAtom = buildingBlocks[18] - const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(buildingBlocks, atom) - const mounted = BB_mountedMap.get(atom) + const mountedMap = buildingBlocks[1] + const changedAtoms = buildingBlocks[3] + const storeHooks = buildingBlocks[6] + const ensureAtomState = buildingBlocks[11] + const invalidateDependents = buildingBlocks[15] + const mountAtom = buildingBlocks[18] + const unmountAtom = buildingBlocks[19] + const atomState = ensureAtomState(buildingBlocks, atom) + const mounted = mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = BB_ensureAtomState(buildingBlocks, a) - const aMounted = BB_mountAtom(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, a) + const aMounted = mountAtom(buildingBlocks, a) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { - BB_changedAtoms.add(a) - BB_invalidateDependents(buildingBlocks, a) - BB_storeHooks.c?.(a) + changedAtoms.add(a) + invalidateDependents(buildingBlocks, a) + storeHooks.c?.(a) } } } for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = BB_unmountAtom(buildingBlocks, a) + const aMounted = unmountAtom(buildingBlocks, a) aMounted?.t.delete(atom) } } @@ -824,24 +819,24 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( } const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { - const BB_mountedMap = buildingBlocks[1] - const BB_mountCallbacks = buildingBlocks[4] - const BB_storeHooks = buildingBlocks[6] - const BB_atomOnMount = buildingBlocks[10] - const BB_ensureAtomState = buildingBlocks[11] - const BB_flushCallbacks = buildingBlocks[12] - const BB_recomputeInvalidatedAtoms = buildingBlocks[13] - const BB_readAtomState = buildingBlocks[14] - const BB_writeAtomState = buildingBlocks[16] - const BB_mountAtom = buildingBlocks[18] - const atomState = BB_ensureAtomState(buildingBlocks, atom) - let mounted = BB_mountedMap.get(atom) + const mountedMap = buildingBlocks[1] + const mountCallbacks = buildingBlocks[4] + const storeHooks = buildingBlocks[6] + const atomOnMount = buildingBlocks[10] + const ensureAtomState = buildingBlocks[11] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const readAtomState = buildingBlocks[14] + const writeAtomState = buildingBlocks[16] + const mountAtom = buildingBlocks[18] + const atomState = ensureAtomState(buildingBlocks, atom) + let mounted = mountedMap.get(atom) if (!mounted) { // recompute atom state - BB_readAtomState(buildingBlocks, atom) + readAtomState(buildingBlocks, atom) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = BB_mountAtom(buildingBlocks, a) + const aMounted = mountAtom(buildingBlocks, a) aMounted.t.add(atom) } // mount self @@ -850,22 +845,22 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { d: new Set(atomState.d.keys()), t: new Set(), } - BB_mountedMap.set(atom, mounted) + mountedMap.set(atom, mounted) if (isActuallyWritableAtom(atom)) { const processOnMount = () => { let isSync = true const setAtom = (...args: unknown[]) => { try { - return BB_writeAtomState(buildingBlocks, atom, args) + return writeAtomState(buildingBlocks, atom, args) } finally { if (!isSync) { - BB_recomputeInvalidatedAtoms(buildingBlocks) - BB_flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) } } } try { - const onUnmount = BB_atomOnMount(atom, setAtom) + const onUnmount = atomOnMount(atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -880,27 +875,27 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { isSync = false } } - BB_mountCallbacks.add(processOnMount) + mountCallbacks.add(processOnMount) } - BB_storeHooks.m?.(atom) + storeHooks.m?.(atom) } return mounted } const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { - const BB_mountedMap = buildingBlocks[1] - const BB_unmountCallbacks = buildingBlocks[5] - const BB_storeHooks = buildingBlocks[6] - const BB_ensureAtomState = buildingBlocks[11] - const BB_unmountAtom = buildingBlocks[19] - const atomState = BB_ensureAtomState(buildingBlocks, atom) - let mounted = BB_mountedMap.get(atom) + const mountedMap = buildingBlocks[1] + const unmountCallbacks = buildingBlocks[5] + const storeHooks = buildingBlocks[6] + const ensureAtomState = buildingBlocks[11] + const unmountAtom = buildingBlocks[19] + const atomState = ensureAtomState(buildingBlocks, atom) + let mounted = mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted } let isDependent = false for (const a of mounted.t) { - if (BB_mountedMap.get(a)?.d.has(atom)) { + if (mountedMap.get(a)?.d.has(atom)) { isDependent = true break } @@ -908,16 +903,16 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { if (!isDependent) { // unmount self if (mounted.u) { - BB_unmountCallbacks.add(mounted.u) + unmountCallbacks.add(mounted.u) } mounted = undefined - BB_mountedMap.delete(atom) + mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = BB_unmountAtom(buildingBlocks, a) + const aMounted = unmountAtom(buildingBlocks, a) aMounted?.t.delete(atom) } - BB_storeHooks.u?.(atom) + storeHooks.u?.(atom) return undefined } return mounted @@ -928,9 +923,9 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( atom, valueOrPromise, ) => { - const BB_ensureAtomState = buildingBlocks[11] - const BB_abortPromise = buildingBlocks[27] - const atomState = BB_ensureAtomState(buildingBlocks, atom) + const ensureAtomState = buildingBlocks[11] + const abortPromise = buildingBlocks[27] + const atomState = ensureAtomState(buildingBlocks, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -938,7 +933,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - BB_ensureAtomState(buildingBlocks, a), + ensureAtomState(buildingBlocks, a), ) } } @@ -947,44 +942,44 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( if (!hasPrevValue || !Object.is(prevValue, atomState.v)) { ++atomState.n if (isPromiseLike(prevValue)) { - BB_abortPromise(buildingBlocks, prevValue) + abortPromise(buildingBlocks, prevValue) } } } const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, atom) => { - const BB_readAtomState = buildingBlocks[14] - return returnAtomValue(BB_readAtomState(buildingBlocks, atom)) as any + const readAtomState = buildingBlocks[14] + return returnAtomValue(readAtomState(buildingBlocks, atom)) as any } const BUILDING_BLOCK_storeSet: StoreSet = (buildingBlocks, atom, args) => { - const BB_changedAtoms = buildingBlocks[3] - const BB_flushCallbacks = buildingBlocks[12] - const BB_recomputeInvalidatedAtoms = buildingBlocks[13] - const BB_writeAtomState = buildingBlocks[16] - const prevChangedAtomsSize = BB_changedAtoms.size + const changedAtoms = buildingBlocks[3] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const writeAtomState = buildingBlocks[16] + const prevChangedAtomsSize = changedAtoms.size try { - return BB_writeAtomState(buildingBlocks, atom, args) as any + return writeAtomState(buildingBlocks, atom, args) as any } finally { - if (BB_changedAtoms.size !== prevChangedAtomsSize) { - BB_recomputeInvalidatedAtoms(buildingBlocks) - BB_flushCallbacks(buildingBlocks) + if (changedAtoms.size !== prevChangedAtomsSize) { + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) } } } const BUILDING_BLOCK_storeSub: StoreSub = (buildingBlocks, atom, listener) => { - const BB_flushCallbacks = buildingBlocks[12] - const BB_mountAtom = buildingBlocks[18] - const BB_unmountAtom = buildingBlocks[19] - const mounted = BB_mountAtom(buildingBlocks, atom) + const flushCallbacks = buildingBlocks[12] + const mountAtom = buildingBlocks[18] + const unmountAtom = buildingBlocks[19] + const mounted = mountAtom(buildingBlocks, atom) const listeners = mounted.l listeners.add(listener) - BB_flushCallbacks(buildingBlocks) + flushCallbacks(buildingBlocks) return () => { listeners.delete(listener) - BB_unmountAtom(buildingBlocks, atom) - BB_flushCallbacks(buildingBlocks) + unmountAtom(buildingBlocks, atom) + flushCallbacks(buildingBlocks) } } @@ -993,20 +988,20 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( promise, abortHandler, ) => { - const BB_abortHandlersMap = buildingBlocks[25] - let abortHandlers = BB_abortHandlersMap.get(promise) + const abortHandlersMap = buildingBlocks[25] + let abortHandlers = abortHandlersMap.get(promise) if (!abortHandlers) { abortHandlers = new Set() - BB_abortHandlersMap.set(promise, abortHandlers) - const cleanup = () => BB_abortHandlersMap.delete(promise) + abortHandlersMap.set(promise, abortHandlers) + const cleanup = () => abortHandlersMap.delete(promise) promise.then(cleanup, cleanup) } abortHandlers.add(abortHandler) } const BUILDING_BLOCK_abortPromise: AbortPromise = (buildingBlocks, promise) => { - const BB_abortHandlersMap = buildingBlocks[25] - const abortHandlers = BB_abortHandlersMap.get(promise) + const abortHandlersMap = buildingBlocks[25] + const abortHandlers = abortHandlersMap.get(promise) abortHandlers?.forEach((fn) => fn()) } @@ -1019,9 +1014,9 @@ function getBuildingBlocks(store: Store): Readonly { 'Store must be created by buildStore to read its building blocks', ) } - const BB_enhanceBuildingBlocks = buildingBlocks[24] - if (BB_enhanceBuildingBlocks) { - return BB_enhanceBuildingBlocks(buildingBlocks, store) + const enhanceBuildingBlocks = buildingBlocks[24] + if (enhanceBuildingBlocks) { + return enhanceBuildingBlocks(buildingBlocks, store) } return buildingBlocks } @@ -1029,13 +1024,13 @@ function getBuildingBlocks(store: Store): Readonly { function buildStore(...buildArgs: Partial): Store { const store = { get(atom) { - return BB_storeGet(buildingBlocks, atom) + return storeGet(buildingBlocks, atom) }, set(atom, ...args) { - return BB_storeSet(buildingBlocks, atom, args) + return storeSet(buildingBlocks, atom, args) }, sub(atom, listener) { - return BB_storeSub(buildingBlocks, atom, listener) + return storeSub(buildingBlocks, atom, listener) }, } as Store @@ -1078,9 +1073,9 @@ function buildStore(...buildArgs: Partial): Store { ] satisfies BuildingBlocks ).map((fn, i) => buildArgs[i] || fn) as BuildingBlocks buildingBlockMap.set(store, Object.freeze(buildingBlocks)) - const BB_storeGet = buildingBlocks[21] - const BB_storeSet = buildingBlocks[22] - const BB_storeSub = buildingBlocks[23] + const storeGet = buildingBlocks[21] + const storeSet = buildingBlocks[22] + const storeSub = buildingBlocks[23] return store } From d6e7bd31ea23108383472acdfacdf0ffa1ab0525 Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 3 Apr 2026 08:19:25 +0100 Subject: [PATCH 10/24] make BUILDING_BLOCK_atomOnInit pure again --- src/vanilla/internals.ts | 63 +++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index b243957bf8..94545bab57 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -83,15 +83,21 @@ type ChangedAtoms = SetLike type Callbacks = SetLike<() => void> type AtomRead = ( + buildingBlocks: Readonly, atom: Atom, ...params: Parameters['read']> ) => Value type AtomWrite = ( + buildingBlocks: Readonly, atom: WritableAtom, ...params: Parameters['write']> ) => Result -type AtomOnInit = (atom: Atom) => void +type AtomOnInit = ( + buildingBlocks: Readonly, + atom: Atom, +) => void type AtomOnMount = ( + buildingBlocks: Readonly, atom: WritableAtom, setAtom: (...args: Args) => Result, ) => OnUnmount | void @@ -380,6 +386,36 @@ function initializeStoreHooks(storeHooks: StoreHooks): Required { // Main functions // +const BUILDING_BLOCK_atomRead: AtomRead = (_buildingBlocks, atom, ...params) => + atom.read(...params) +const BUILDING_BLOCK_atomWrite: AtomWrite = ( + _buildingBlocks, + atom, + ...params +) => atom.write(...params) +const BUILDING_BLOCK_atomOnInit: AtomOnInit = (buildingBlocks, atom) => { + const storeGet = buildingBlocks[21] + const storeSet = buildingBlocks[22] + const storeSub = buildingBlocks[23] + const store: Store = { + get(atom) { + return storeGet(buildingBlocks, atom) + }, + set(atom, ...args) { + return storeSet(buildingBlocks, atom, args) + }, + sub(atom, listener) { + return storeSub(buildingBlocks, atom, listener) + }, + } + return atom.INTERNAL_onInit?.(store) +} +const BUILDING_BLOCK_atomOnMount: AtomOnMount = ( + _buildingBlocks, + atom, + setAtom, +) => atom.onMount?.(setAtom) + const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( buildingBlocks, atom, @@ -395,7 +431,7 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( atomState = { d: new Map(), p: new Set(), n: 0 } atomStateMap.set(atom, atomState) storeHooks.i?.(atom) - atomOnInit?.(atom) + atomOnInit?.(buildingBlocks, atom) } return atomState as never } @@ -657,7 +693,12 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(buildingBlocks) } - const valueOrPromise = atomRead(atom, getter, options as never) + const valueOrPromise = atomRead( + buildingBlocks, + atom, + getter, + options as never, + ) if ( import.meta.env?.MODE !== 'production' && storeMutationSet.has(buildingBlocks) @@ -775,7 +816,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } } try { - return atomWrite(atom, getter, setter, ...args) + return atomWrite(buildingBlocks, atom, getter, setter, ...args) } finally { isSync = false } @@ -860,7 +901,7 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { } } try { - const onUnmount = atomOnMount(atom, setAtom) + const onUnmount = atomOnMount(buildingBlocks, atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -1022,7 +1063,7 @@ function getBuildingBlocks(store: Store): Readonly { } function buildStore(...buildArgs: Partial): Store { - const store = { + const store: Store = { get(atom) { return storeGet(buildingBlocks, atom) }, @@ -1032,7 +1073,7 @@ function buildStore(...buildArgs: Partial): Store { sub(atom, listener) { return storeSub(buildingBlocks, atom, listener) }, - } as Store + } const buildingBlocks = ( [ @@ -1045,10 +1086,10 @@ function buildStore(...buildArgs: Partial): Store { new Set(), // unmountCallbacks {}, // storeHooks // atom interceptors - (atom, ...params) => atom.read(...params), - (atom, ...params) => atom.write(...params), - (atom) => atom.INTERNAL_onInit?.(store), - (atom, setAtom) => atom.onMount?.(setAtom), + BUILDING_BLOCK_atomRead, + BUILDING_BLOCK_atomWrite, + BUILDING_BLOCK_atomOnInit, + BUILDING_BLOCK_atomOnMount, // building-block functions BUILDING_BLOCK_ensureAtomState, BUILDING_BLOCK_flushCallbacks, From c60ab0e441150204cdff2ab6f2eac7a944bfa589 Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 3 Apr 2026 08:41:14 +0100 Subject: [PATCH 11/24] store as a building block --- src/vanilla/internals.ts | 113 +++++++++++++-------------------------- 1 file changed, 37 insertions(+), 76 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 94545bab57..3ac4aaf304 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -140,20 +140,6 @@ type SetAtomStateValueOrPromise = ( atom: Atom, valueOrPromise: Value, ) => void -type StoreGet = ( - buildingBlocks: Readonly, - atom: Atom, -) => Value -type StoreSet = ( - buildingBlocks: Readonly, - atom: WritableAtom, - args: Args, -) => Result -type StoreSub = ( - buildingBlocks: Readonly, - atom: AnyAtom, - listener: () => void, -) => () => void type EnhanceBuildingBlocks = ( buildingBlocks: Readonly>, store: Store, @@ -205,9 +191,9 @@ type BuildingBlocks = [ unmountAtom: UnmountAtom, // 19 setAtomStateValueOrPromise: SetAtomStateValueOrPromise, // 20 // store api - storeGet: StoreGet, // 21 - storeSet: StoreSet, // 22 - storeSub: StoreSub, // 23 + UNUSED_storeGet: undefined, // 21 + UNUSED_storeSet: undefined, // 22 + UNUSED_storeSub: undefined, // 23 enhanceBuildingBlocks: EnhanceBuildingBlocks | undefined, // 24 // abortable promise support abortHandlersMap: AbortHandlersMap, // 25 @@ -215,6 +201,8 @@ type BuildingBlocks = [ abortPromise: AbortPromise, // 27 // store epoch storeEpochHolder: StoreEpochHolder, // 28 + // store api + store: Store, // 29 ] export type { @@ -394,20 +382,7 @@ const BUILDING_BLOCK_atomWrite: AtomWrite = ( ...params ) => atom.write(...params) const BUILDING_BLOCK_atomOnInit: AtomOnInit = (buildingBlocks, atom) => { - const storeGet = buildingBlocks[21] - const storeSet = buildingBlocks[22] - const storeSub = buildingBlocks[23] - const store: Store = { - get(atom) { - return storeGet(buildingBlocks, atom) - }, - set(atom, ...args) { - return storeSet(buildingBlocks, atom, args) - }, - sub(atom, listener) { - return storeSub(buildingBlocks, atom, listener) - }, - } + const store = buildingBlocks[29] return atom.INTERNAL_onInit?.(store) } const BUILDING_BLOCK_atomOnMount: AtomOnMount = ( @@ -988,42 +963,6 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( } } -const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, atom) => { - const readAtomState = buildingBlocks[14] - return returnAtomValue(readAtomState(buildingBlocks, atom)) as any -} - -const BUILDING_BLOCK_storeSet: StoreSet = (buildingBlocks, atom, args) => { - const changedAtoms = buildingBlocks[3] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const writeAtomState = buildingBlocks[16] - const prevChangedAtomsSize = changedAtoms.size - try { - return writeAtomState(buildingBlocks, atom, args) as any - } finally { - if (changedAtoms.size !== prevChangedAtomsSize) { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) - } - } -} - -const BUILDING_BLOCK_storeSub: StoreSub = (buildingBlocks, atom, listener) => { - const flushCallbacks = buildingBlocks[12] - const mountAtom = buildingBlocks[18] - const unmountAtom = buildingBlocks[19] - const mounted = mountAtom(buildingBlocks, atom) - const listeners = mounted.l - listeners.add(listener) - flushCallbacks(buildingBlocks) - return () => { - listeners.delete(listener) - unmountAtom(buildingBlocks, atom) - flushCallbacks(buildingBlocks) - } -} - const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( buildingBlocks, promise, @@ -1065,13 +1004,29 @@ function getBuildingBlocks(store: Store): Readonly { function buildStore(...buildArgs: Partial): Store { const store: Store = { get(atom) { - return storeGet(buildingBlocks, atom) + return returnAtomValue(readAtomState(buildingBlocks, atom)) as any }, set(atom, ...args) { - return storeSet(buildingBlocks, atom, args) + const prevChangedAtomsSize = changedAtoms.size + try { + return writeAtomState(buildingBlocks, atom, args) as any + } finally { + if (changedAtoms.size !== prevChangedAtomsSize) { + recomputeInvalidatedAtoms(buildingBlocks) + flushCallbacks(buildingBlocks) + } + } }, sub(atom, listener) { - return storeSub(buildingBlocks, atom, listener) + const mounted = mountAtom(buildingBlocks, atom) + const listeners = mounted.l + listeners.add(listener) + flushCallbacks(buildingBlocks) + return () => { + listeners.delete(listener) + unmountAtom(buildingBlocks, atom) + flushCallbacks(buildingBlocks) + } }, } @@ -1101,9 +1056,9 @@ function buildStore(...buildArgs: Partial): Store { BUILDING_BLOCK_mountAtom, BUILDING_BLOCK_unmountAtom, BUILDING_BLOCK_setAtomStateValueOrPromise, - BUILDING_BLOCK_storeGet, - BUILDING_BLOCK_storeSet, - BUILDING_BLOCK_storeSub, + undefined, // UNUSED_storeGet + undefined, // UNUSED_storeSet + undefined, // UNUSED_storeSub undefined, // enhanceBuildingBlocks // abortable promise support new WeakMap(), // abortHandlersMap @@ -1111,12 +1066,18 @@ function buildStore(...buildArgs: Partial): Store { BUILDING_BLOCK_abortPromise, // store epoch [0], + // store api + store, ] satisfies BuildingBlocks ).map((fn, i) => buildArgs[i] || fn) as BuildingBlocks buildingBlockMap.set(store, Object.freeze(buildingBlocks)) - const storeGet = buildingBlocks[21] - const storeSet = buildingBlocks[22] - const storeSub = buildingBlocks[23] + const changedAtoms = buildingBlocks[3] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const readAtomState = buildingBlocks[14] + const writeAtomState = buildingBlocks[16] + const mountAtom = buildingBlocks[18] + const unmountAtom = buildingBlocks[19] return store } From 0c4fc250f5cf7c2f7d97e274529c2beb400a594f Mon Sep 17 00:00:00 2001 From: daishi Date: Fri, 3 Apr 2026 10:43:39 +0100 Subject: [PATCH 12/24] fix tests --- tests/vanilla/internals.test.tsx | 23 +++++++++++++++-------- tests/vanilla/storedev.test.tsx | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/vanilla/internals.test.tsx b/tests/vanilla/internals.test.tsx index 881c2bf4a5..11ee8416ed 100644 --- a/tests/vanilla/internals.test.tsx +++ b/tests/vanilla/internals.test.tsx @@ -12,7 +12,7 @@ import { INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' -const buildingBlockLength = 29 +const buildingBlockLength = 30 describe('internals', () => { it('should not return a sparse building blocks array', () => { @@ -65,18 +65,25 @@ describe('internals', () => { internal: vi.fn(), external: vi.fn(), } - const bb0 = [] as Partial - bb0[21] = function storeGet1() { + const bb0 = [ + ...INTERNAL_getBuildingBlocks(INTERNAL_buildStore()), + ] as INTERNAL_BuildingBlocks + const readAtomState = bb0[14] + bb0[14] = function readAtomState1(...args) { didRun.internal() - } as INTERNAL_BuildingBlocks[21] + return readAtomState(...args) + } as INTERNAL_BuildingBlocks[14] let bbInternal: Readonly | undefined - function storeGet() { + function wrappedReadAtomState( + ...args: Parameters + ) { didRun.external() + return readAtomState(...args) } bb0[24] = (bbi) => { bbInternal = bbi const bb1 = [...bbi] as INTERNAL_BuildingBlocks - bb1[21] = storeGet as INTERNAL_BuildingBlocks[21] + bb1[14] = wrappedReadAtomState as INTERNAL_BuildingBlocks[14] return bb1 } const store1 = INTERNAL_buildStore(...bb0) @@ -87,8 +94,8 @@ describe('internals', () => { const bb2 = INTERNAL_getBuildingBlocks(store2) expect(isBuildingBlocks(bb2)).toBe(true) expect(isBuildingBlocks(bbInternal)).toBe(true) - expect(bb0[21]).not.toBe(bb1[21]) - expect(bb1[21]).toBe(bb2[21]) + expect(bb0[14]).not.toBe(bb1[14]) + expect(bb1[14]).toBe(bb2[14]) store1.get(atom(0)) expect(didRun.internal).toBeCalledTimes(1) expect(didRun.external).toBeCalledTimes(0) diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index e0def82f4f..e65f6aa00f 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -32,7 +32,7 @@ const createDevStore = (): INTERNAL_Store & DevStore => { undefined, storeHooks, undefined, - (atom, get, set, ...args) => { + (_buildingBlocks, atom, get, set, ...args) => { if (inRestoreAtom) { return set(atom, ...(args as any)) } From fc3660c613bf00e763998d5b7fc4cfeacdb61110 Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 7 Apr 2026 11:22:16 +0900 Subject: [PATCH 13/24] refactor back internals --- src/vanilla/internals.ts | 279 +++++++++++++++++++++++---------------- 1 file changed, 164 insertions(+), 115 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 3ac4aaf304..bde0034581 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -83,63 +83,86 @@ type ChangedAtoms = SetLike type Callbacks = SetLike<() => void> type AtomRead = ( - buildingBlocks: Readonly, atom: Atom, ...params: Parameters['read']> ) => Value type AtomWrite = ( - buildingBlocks: Readonly, atom: WritableAtom, ...params: Parameters['write']> ) => Result -type AtomOnInit = ( - buildingBlocks: Readonly, - atom: Atom, -) => void +type AtomOnInit = (atom: Atom, store: Store) => void type AtomOnMount = ( - buildingBlocks: Readonly, atom: WritableAtom, setAtom: (...args: Args) => Result, ) => OnUnmount | void type EnsureAtomState = ( buildingBlocks: Readonly, + store: Store, atom: Atom, ) => AtomState -type FlushCallbacks = (buildingBlocks: Readonly) => void +type FlushCallbacks = ( + buildingBlocks: Readonly, + store: Store, +) => void type RecomputeInvalidatedAtoms = ( buildingBlocks: Readonly, + store: Store, ) => void type ReadAtomState = ( buildingBlocks: Readonly, + store: Store, atom: Atom, ) => AtomState type InvalidateDependents = ( buildingBlocks: Readonly, + store: Store, atom: AnyAtom, ) => void type WriteAtomState = ( buildingBlocks: Readonly, + store: Store, atom: WritableAtom, args: Args, ) => Result type MountDependencies = ( buildingBlocks: Readonly, + store: Store, atom: AnyAtom, ) => void type MountAtom = ( buildingBlocks: Readonly, + store: Store, atom: Atom, ) => Mounted type UnmountAtom = ( buildingBlocks: Readonly, + store: Store, atom: Atom, ) => Mounted | undefined type SetAtomStateValueOrPromise = ( buildingBlocks: Readonly, + store: Store, atom: Atom, valueOrPromise: Value, ) => void +type StoreGet = ( + buildingBlocks: Readonly, + store: Store, + atom: Atom, +) => Value +type StoreSet = ( + buildingBlocks: Readonly, + store: Store, + atom: WritableAtom, + ...args: Args +) => Result +type StoreSub = ( + buildingBlocks: Readonly, + store: Store, + atom: AnyAtom, + listener: () => void, +) => () => void type EnhanceBuildingBlocks = ( buildingBlocks: Readonly>, store: Store, @@ -191,9 +214,9 @@ type BuildingBlocks = [ unmountAtom: UnmountAtom, // 19 setAtomStateValueOrPromise: SetAtomStateValueOrPromise, // 20 // store api - UNUSED_storeGet: undefined, // 21 - UNUSED_storeSet: undefined, // 22 - UNUSED_storeSub: undefined, // 23 + storeGet: StoreGet, // 21 + storeSet: StoreSet, // 22 + storeSub: StoreSub, // 23 enhanceBuildingBlocks: EnhanceBuildingBlocks | undefined, // 24 // abortable promise support abortHandlersMap: AbortHandlersMap, // 25 @@ -201,8 +224,6 @@ type BuildingBlocks = [ abortPromise: AbortPromise, // 27 // store epoch storeEpochHolder: StoreEpochHolder, // 28 - // store api - store: Store, // 29 ] export type { @@ -374,25 +395,18 @@ function initializeStoreHooks(storeHooks: StoreHooks): Required { // Main functions // -const BUILDING_BLOCK_atomRead: AtomRead = (_buildingBlocks, atom, ...params) => +const BUILDING_BLOCK_atomRead: AtomRead = (atom, ...params) => atom.read(...params) -const BUILDING_BLOCK_atomWrite: AtomWrite = ( - _buildingBlocks, - atom, - ...params -) => atom.write(...params) -const BUILDING_BLOCK_atomOnInit: AtomOnInit = (buildingBlocks, atom) => { - const store = buildingBlocks[29] - return atom.INTERNAL_onInit?.(store) -} -const BUILDING_BLOCK_atomOnMount: AtomOnMount = ( - _buildingBlocks, - atom, - setAtom, -) => atom.onMount?.(setAtom) +const BUILDING_BLOCK_atomWrite: AtomWrite = (atom, ...params) => + atom.write(...params) +const BUILDING_BLOCK_atomOnInit: AtomOnInit = (atom, store) => + atom.INTERNAL_onInit?.(store) +const BUILDING_BLOCK_atomOnMount: AtomOnMount = (atom, setAtom) => + atom.onMount?.(setAtom) const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( buildingBlocks, + store, atom, ) => { const atomStateMap = buildingBlocks[0] @@ -406,12 +420,15 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( atomState = { d: new Map(), p: new Set(), n: 0 } atomStateMap.set(atom, atomState) storeHooks.i?.(atom) - atomOnInit?.(buildingBlocks, atom) + atomOnInit?.(atom, store) } return atomState as never } -const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { +const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( + buildingBlocks, + store, +) => { const mountedMap = buildingBlocks[1] const changedAtoms = buildingBlocks[3] const mountCallbacks = buildingBlocks[4] @@ -440,7 +457,7 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { mountCallbacks.clear() callbacks.forEach(call) if (changedAtoms.size) { - recomputeInvalidatedAtoms(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks, store) } } while (changedAtoms.size || unmountCallbacks.size || mountCallbacks.size) if (errors.length) { @@ -450,6 +467,7 @@ const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (buildingBlocks) => { const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( buildingBlocks, + store, ) => { const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] @@ -470,7 +488,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( const stack: AnyAtom[] = Array.from(changedAtoms) while (stack.length) { const a = stack[stack.length - 1]! - const aState = ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, store, a) if (visited.has(a)) { // All dependents have been processed, now process this atom stack.pop() @@ -514,8 +532,8 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } if (hasChangedDeps) { invalidatedAtoms.set(a, aState.n) - readAtomState(buildingBlocks, a) - mountDependencies(buildingBlocks, a) + readAtomState(buildingBlocks, store, a) + mountDependencies(buildingBlocks, store, a) } invalidatedAtoms.delete(a) } @@ -524,7 +542,11 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // Dev only const storeMutationSet = new WeakSet>() -const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { +const BUILDING_BLOCK_readAtomState: ReadAtomState = ( + buildingBlocks, + store, + atom, +) => { const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] const changedAtoms = buildingBlocks[3] @@ -539,7 +561,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { const setAtomStateValueOrPromise = buildingBlocks[20] const registerAbortHandler = buildingBlocks[26] const storeEpochHolder = buildingBlocks[28] - const atomState = ensureAtomState(buildingBlocks, atom) + const atomState = ensureAtomState(buildingBlocks, store, atom) const storeEpochNumber = storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -559,7 +581,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { // If all dependencies haven't changed, we can use the cache. let hasChangedDeps = false for (const [a, n] of atomState.d) { - if (readAtomState(buildingBlocks, a).n !== n) { + if (readAtomState(buildingBlocks, store, a).n !== n) { hasChangedDeps = true break } @@ -584,19 +606,19 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { if (mountedMap.has(atom)) { // If changedAtoms is already populated, an outer recompute cycle will handle it const shouldRecompute = !changedAtoms.size - mountDependencies(buildingBlocks, atom) + mountDependencies(buildingBlocks, store, atom) if (shouldRecompute) { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks, store) + flushCallbacks(buildingBlocks, store) } } } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, store, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { - setAtomStateValueOrPromise(buildingBlocks, a, a.init) + setAtomStateValueOrPromise(buildingBlocks, store, a, a.init) } else { // NOTE invalid derived atoms can reach here throw new Error('no atom init') @@ -605,7 +627,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { return returnAtomValue(aState) } // a !== atom - const aState = readAtomState(buildingBlocks, a) + const aState = readAtomState(buildingBlocks, store, a) try { return returnAtomValue(aState) } finally { @@ -651,10 +673,10 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { } if (!isSync) { try { - return writeAtomState(buildingBlocks, atom, args) + return writeAtomState(buildingBlocks, store, atom, args) } finally { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks, store) + flushCallbacks(buildingBlocks, store) } } } @@ -668,12 +690,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(buildingBlocks) } - const valueOrPromise = atomRead( - buildingBlocks, - atom, - getter, - options as never, - ) + const valueOrPromise = atomRead(atom, getter, options as never) if ( import.meta.env?.MODE !== 'production' && storeMutationSet.has(buildingBlocks) @@ -682,7 +699,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { 'Detected store mutation during atom read. This is not supported.', ) } - setAtomStateValueOrPromise(buildingBlocks, atom, valueOrPromise) + setAtomStateValueOrPromise(buildingBlocks, store, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { registerAbortHandler(buildingBlocks, valueOrPromise, () => controller?.abort(), @@ -716,6 +733,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = (buildingBlocks, atom) => { const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( buildingBlocks, + store, atom, ) => { const mountedMap = buildingBlocks[1] @@ -724,9 +742,9 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, store, a) for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { - const dState = ensureAtomState(buildingBlocks, d) + const dState = ensureAtomState(buildingBlocks, store, d) if (invalidatedAtoms.get(d) !== dState.n) { invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -737,6 +755,7 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( buildingBlocks, + store, atom, args, ) => { @@ -754,12 +773,12 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const storeEpochHolder = buildingBlocks[28] let isSync = true const getter: Getter = (a: Atom) => - returnAtomValue(readAtomState(buildingBlocks, a)) + returnAtomValue(readAtomState(buildingBlocks, store, a)) const setter: Setter = ( a: WritableAtom, ...args: As ) => { - const aState = ensureAtomState(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, store, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -771,27 +790,27 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } const prevEpochNumber = aState.n const v = args[0] as V - setAtomStateValueOrPromise(buildingBlocks, a, v) - mountDependencies(buildingBlocks, a) + setAtomStateValueOrPromise(buildingBlocks, store, a, v) + mountDependencies(buildingBlocks, store, a) if (prevEpochNumber !== aState.n) { ++storeEpochHolder[0] changedAtoms.add(a) - invalidateDependents(buildingBlocks, a) + invalidateDependents(buildingBlocks, store, a) storeHooks.c?.(a) } return undefined as R } else { - return writeAtomState(buildingBlocks, a, args) + return writeAtomState(buildingBlocks, store, a, args) } } finally { if (!isSync) { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks, store) + flushCallbacks(buildingBlocks, store) } } } try { - return atomWrite(buildingBlocks, atom, getter, setter, ...args) + return atomWrite(atom, getter, setter, ...args) } finally { isSync = false } @@ -799,6 +818,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( const BUILDING_BLOCK_mountDependencies: MountDependencies = ( buildingBlocks, + store, atom, ) => { const mountedMap = buildingBlocks[1] @@ -808,18 +828,18 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( const invalidateDependents = buildingBlocks[15] const mountAtom = buildingBlocks[18] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(buildingBlocks, atom) + const atomState = ensureAtomState(buildingBlocks, store, atom) const mounted = mountedMap.get(atom) if (mounted) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = ensureAtomState(buildingBlocks, a) - const aMounted = mountAtom(buildingBlocks, a) + const aState = ensureAtomState(buildingBlocks, store, a) + const aMounted = mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) mounted.d.add(a) if (n !== aState.n) { changedAtoms.add(a) - invalidateDependents(buildingBlocks, a) + invalidateDependents(buildingBlocks, store, a) storeHooks.c?.(a) } } @@ -827,14 +847,14 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( for (const a of mounted.d) { if (!atomState.d.has(a)) { mounted.d.delete(a) - const aMounted = unmountAtom(buildingBlocks, a) + const aMounted = unmountAtom(buildingBlocks, store, a) aMounted?.t.delete(atom) } } } } -const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { +const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { const mountedMap = buildingBlocks[1] const mountCallbacks = buildingBlocks[4] const storeHooks = buildingBlocks[6] @@ -845,14 +865,14 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { const readAtomState = buildingBlocks[14] const writeAtomState = buildingBlocks[16] const mountAtom = buildingBlocks[18] - const atomState = ensureAtomState(buildingBlocks, atom) + const atomState = ensureAtomState(buildingBlocks, store, atom) let mounted = mountedMap.get(atom) if (!mounted) { // recompute atom state - readAtomState(buildingBlocks, atom) + readAtomState(buildingBlocks, store, atom) // mount dependencies first for (const a of atomState.d.keys()) { - const aMounted = mountAtom(buildingBlocks, a) + const aMounted = mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) } // mount self @@ -867,16 +887,16 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { let isSync = true const setAtom = (...args: unknown[]) => { try { - return writeAtomState(buildingBlocks, atom, args) + return writeAtomState(buildingBlocks, store, atom, args) } finally { if (!isSync) { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) + recomputeInvalidatedAtoms(buildingBlocks, store) + flushCallbacks(buildingBlocks, store) } } } try { - const onUnmount = atomOnMount(buildingBlocks, atom, setAtom) + const onUnmount = atomOnMount(atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -898,13 +918,17 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, atom) => { return mounted } -const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { +const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( + buildingBlocks, + store, + atom, +) => { const mountedMap = buildingBlocks[1] const unmountCallbacks = buildingBlocks[5] const storeHooks = buildingBlocks[6] const ensureAtomState = buildingBlocks[11] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(buildingBlocks, atom) + const atomState = ensureAtomState(buildingBlocks, store, atom) let mounted = mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -925,7 +949,7 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { mountedMap.delete(atom) // unmount dependencies for (const a of atomState.d.keys()) { - const aMounted = unmountAtom(buildingBlocks, a) + const aMounted = unmountAtom(buildingBlocks, store, a) aMounted?.t.delete(atom) } storeHooks.u?.(atom) @@ -936,12 +960,13 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = (buildingBlocks, atom) => { const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( buildingBlocks, + store, atom, valueOrPromise, ) => { const ensureAtomState = buildingBlocks[11] const abortPromise = buildingBlocks[27] - const atomState = ensureAtomState(buildingBlocks, atom) + const atomState = ensureAtomState(buildingBlocks, store, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -949,7 +974,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - ensureAtomState(buildingBlocks, a), + ensureAtomState(buildingBlocks, store, a), ) } } @@ -963,6 +988,52 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( } } +const BUILDING_BLOCK_storeGet: StoreGet = (buildingBlocks, store, atom) => { + const readAtomState = buildingBlocks[14] + return returnAtomValue(readAtomState(buildingBlocks, store, atom)) as any +} + +const BUILDING_BLOCK_storeSet: StoreSet = ( + buildingBlocks, + store, + atom, + ...args +) => { + const changedAtoms = buildingBlocks[3] + const flushCallbacks = buildingBlocks[12] + const recomputeInvalidatedAtoms = buildingBlocks[13] + const writeAtomState = buildingBlocks[16] + const prevChangedAtomsSize = changedAtoms.size + try { + return writeAtomState(buildingBlocks, store, atom, args) as any + } finally { + if (changedAtoms.size !== prevChangedAtomsSize) { + recomputeInvalidatedAtoms(buildingBlocks, store) + flushCallbacks(buildingBlocks, store) + } + } +} + +const BUILDING_BLOCK_storeSub: StoreSub = ( + buildingBlocks, + store, + atom, + listener, +) => { + const flushCallbacks = buildingBlocks[12] + const mountAtom = buildingBlocks[18] + const unmountAtom = buildingBlocks[19] + const mounted = mountAtom(buildingBlocks, store, atom) + const listeners = mounted.l + listeners.add(listener) + flushCallbacks(buildingBlocks, store) + return () => { + listeners.delete(listener) + unmountAtom(buildingBlocks, store, atom) + flushCallbacks(buildingBlocks, store) + } +} + const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( buildingBlocks, promise, @@ -1001,32 +1072,16 @@ function getBuildingBlocks(store: Store): Readonly { return buildingBlocks } -function buildStore(...buildArgs: Partial): Store { +function buildStore(...partialBuildingBlocks: Partial): Store { const store: Store = { get(atom) { - return returnAtomValue(readAtomState(buildingBlocks, atom)) as any + return storeGet(buildingBlocks, store, atom) }, set(atom, ...args) { - const prevChangedAtomsSize = changedAtoms.size - try { - return writeAtomState(buildingBlocks, atom, args) as any - } finally { - if (changedAtoms.size !== prevChangedAtomsSize) { - recomputeInvalidatedAtoms(buildingBlocks) - flushCallbacks(buildingBlocks) - } - } + return storeSet(buildingBlocks, store, atom, ...args) }, sub(atom, listener) { - const mounted = mountAtom(buildingBlocks, atom) - const listeners = mounted.l - listeners.add(listener) - flushCallbacks(buildingBlocks) - return () => { - listeners.delete(listener) - unmountAtom(buildingBlocks, atom) - flushCallbacks(buildingBlocks) - } + return storeSub(buildingBlocks, store, atom, listener) }, } @@ -1056,9 +1111,9 @@ function buildStore(...buildArgs: Partial): Store { BUILDING_BLOCK_mountAtom, BUILDING_BLOCK_unmountAtom, BUILDING_BLOCK_setAtomStateValueOrPromise, - undefined, // UNUSED_storeGet - undefined, // UNUSED_storeSet - undefined, // UNUSED_storeSub + BUILDING_BLOCK_storeGet, + BUILDING_BLOCK_storeSet, + BUILDING_BLOCK_storeSub, undefined, // enhanceBuildingBlocks // abortable promise support new WeakMap(), // abortHandlersMap @@ -1066,18 +1121,12 @@ function buildStore(...buildArgs: Partial): Store { BUILDING_BLOCK_abortPromise, // store epoch [0], - // store api - store, ] satisfies BuildingBlocks - ).map((fn, i) => buildArgs[i] || fn) as BuildingBlocks + ).map((fn, i) => partialBuildingBlocks[i] || fn) as BuildingBlocks buildingBlockMap.set(store, Object.freeze(buildingBlocks)) - const changedAtoms = buildingBlocks[3] - const flushCallbacks = buildingBlocks[12] - const recomputeInvalidatedAtoms = buildingBlocks[13] - const readAtomState = buildingBlocks[14] - const writeAtomState = buildingBlocks[16] - const mountAtom = buildingBlocks[18] - const unmountAtom = buildingBlocks[19] + const storeGet = buildingBlocks[21] + const storeSet = buildingBlocks[22] + const storeSub = buildingBlocks[23] return store } From 7403a4fd0976c06f4a13c677f98e5dd9d2d0c23f Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 7 Apr 2026 11:22:34 +0900 Subject: [PATCH 14/24] Revert "fix tests" This reverts commit 0c4fc250f5cf7c2f7d97e274529c2beb400a594f. --- tests/vanilla/internals.test.tsx | 23 ++++++++--------------- tests/vanilla/storedev.test.tsx | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tests/vanilla/internals.test.tsx b/tests/vanilla/internals.test.tsx index 11ee8416ed..881c2bf4a5 100644 --- a/tests/vanilla/internals.test.tsx +++ b/tests/vanilla/internals.test.tsx @@ -12,7 +12,7 @@ import { INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' -const buildingBlockLength = 30 +const buildingBlockLength = 29 describe('internals', () => { it('should not return a sparse building blocks array', () => { @@ -65,25 +65,18 @@ describe('internals', () => { internal: vi.fn(), external: vi.fn(), } - const bb0 = [ - ...INTERNAL_getBuildingBlocks(INTERNAL_buildStore()), - ] as INTERNAL_BuildingBlocks - const readAtomState = bb0[14] - bb0[14] = function readAtomState1(...args) { + const bb0 = [] as Partial + bb0[21] = function storeGet1() { didRun.internal() - return readAtomState(...args) - } as INTERNAL_BuildingBlocks[14] + } as INTERNAL_BuildingBlocks[21] let bbInternal: Readonly | undefined - function wrappedReadAtomState( - ...args: Parameters - ) { + function storeGet() { didRun.external() - return readAtomState(...args) } bb0[24] = (bbi) => { bbInternal = bbi const bb1 = [...bbi] as INTERNAL_BuildingBlocks - bb1[14] = wrappedReadAtomState as INTERNAL_BuildingBlocks[14] + bb1[21] = storeGet as INTERNAL_BuildingBlocks[21] return bb1 } const store1 = INTERNAL_buildStore(...bb0) @@ -94,8 +87,8 @@ describe('internals', () => { const bb2 = INTERNAL_getBuildingBlocks(store2) expect(isBuildingBlocks(bb2)).toBe(true) expect(isBuildingBlocks(bbInternal)).toBe(true) - expect(bb0[14]).not.toBe(bb1[14]) - expect(bb1[14]).toBe(bb2[14]) + expect(bb0[21]).not.toBe(bb1[21]) + expect(bb1[21]).toBe(bb2[21]) store1.get(atom(0)) expect(didRun.internal).toBeCalledTimes(1) expect(didRun.external).toBeCalledTimes(0) diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index e65f6aa00f..e0def82f4f 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -32,7 +32,7 @@ const createDevStore = (): INTERNAL_Store & DevStore => { undefined, storeHooks, undefined, - (_buildingBlocks, atom, get, set, ...args) => { + (atom, get, set, ...args) => { if (inRestoreAtom) { return set(atom, ...(args as any)) } From 52b1a2247434a9ac1ee94a02b246a19328edc304 Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 7 Apr 2026 11:24:42 +0900 Subject: [PATCH 15/24] fix test --- tests/vanilla/internals.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/vanilla/internals.test.tsx b/tests/vanilla/internals.test.tsx index 881c2bf4a5..9a7cede98a 100644 --- a/tests/vanilla/internals.test.tsx +++ b/tests/vanilla/internals.test.tsx @@ -211,7 +211,9 @@ describe('internals', () => { const unsub = store.sub(leafAtom, () => {}) const buildingBlocks = INTERNAL_getBuildingBlocks(store) const invalidateDependents = buildingBlocks[15] - expect(() => invalidateDependents(buildingBlocks, baseAtom)).not.toThrow() + expect(() => + invalidateDependents(buildingBlocks, store, baseAtom), + ).not.toThrow() unsub() }) }) From f2642514105e0fb002477b4bb2fb87db3bef9037 Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 7 Apr 2026 11:32:13 +0900 Subject: [PATCH 16/24] fix type --- src/vanilla/internals.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index bde0034581..3f4e5ace92 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -164,9 +164,9 @@ type StoreSub = ( listener: () => void, ) => () => void type EnhanceBuildingBlocks = ( - buildingBlocks: Readonly>, + buildingBlocks: Readonly, store: Store, -) => Readonly> +) => Readonly type AbortHandlersMap = WeakMapLike, Set<() => void>> type RegisterAbortHandler = ( buildingBlocks: Readonly, From c541c12c2f0ad428f876e702c0409e0ea4dad359 Mon Sep 17 00:00:00 2001 From: daishi Date: Wed, 8 Apr 2026 11:56:01 +0900 Subject: [PATCH 17/24] use store for the key --- src/vanilla/internals.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 5340fe03cc..7b4774bc93 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -594,7 +594,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( } // Dev only -const storeMutationSet = new WeakSet>() +const storeMutationSet = new WeakSet() const BUILDING_BLOCK_readAtomState: ReadAtomState = ( buildingBlocks, @@ -739,12 +739,12 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const prevInvalidated = invalidatedAtoms.get(atom) === prevEpochNumber try { if (import.meta.env?.MODE !== 'production') { - storeMutationSet.delete(buildingBlocks) + storeMutationSet.delete(store) } const valueOrPromise = atomRead(atom, getter, options as never) if ( import.meta.env?.MODE !== 'production' && - storeMutationSet.has(buildingBlocks) + storeMutationSet.has(store) ) { console.warn( 'Detected store mutation during atom read. This is not supported.', @@ -837,7 +837,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( throw new Error('atom not writable') } if (import.meta.env?.MODE !== 'production') { - storeMutationSet.add(buildingBlocks) + storeMutationSet.add(store) } const prevEpochNumber = aState.n const v = args[0] as V From 74684cb78b683b2eb10d1d1581be637ebe755766 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 13 Apr 2026 12:40:59 +0900 Subject: [PATCH 18/24] fix format --- src/vanilla/internals.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 7b4774bc93..95fd71660a 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -742,10 +742,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( storeMutationSet.delete(store) } const valueOrPromise = atomRead(atom, getter, options as never) - if ( - import.meta.env?.MODE !== 'production' && - storeMutationSet.has(store) - ) { + if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) { console.warn( 'Detected store mutation during atom read. This is not supported.', ) From 0d5fb00f847e1dbeda25d524c658ae1b2a8bbe98 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 13 Apr 2026 13:01:34 +0900 Subject: [PATCH 19/24] bump building blocks rev --- src/react/useAtomValue.ts | 2 +- src/vanilla/internals.ts | 6 +++--- src/vanilla/store.ts | 2 +- tests/vanilla/effect.test.ts | 4 ++-- tests/vanilla/internals.test.tsx | 6 +++--- tests/vanilla/store.test.tsx | 6 +++--- tests/vanilla/storedev.test.tsx | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/react/useAtomValue.ts b/src/react/useAtomValue.ts index 5708d51085..d46d86f0d7 100644 --- a/src/react/useAtomValue.ts +++ b/src/react/useAtomValue.ts @@ -1,5 +1,5 @@ import React, { useDebugValue, useEffect, useReducer } from 'react' -import { INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks } from '../vanilla/internals.ts' +import { INTERNAL_getBuildingBlocksRev3 as INTERNAL_getBuildingBlocks } from '../vanilla/internals.ts' import type { Atom, ExtractAtomValue } from '../vanilla.ts' import { useStore } from './Provider.ts' diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 95fd71660a..9b49930e6e 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -1182,9 +1182,9 @@ export { // // Export internal functions // - buildStore as INTERNAL_buildStoreRev2, - getBuildingBlocks as INTERNAL_getBuildingBlocksRev2, - initializeStoreHooks as INTERNAL_initializeStoreHooksRev2, + buildStore as INTERNAL_buildStoreRev3, + getBuildingBlocks as INTERNAL_getBuildingBlocksRev3, + initializeStoreHooks as INTERNAL_initializeStoreHooksRev3, // // Still experimental and some of them will be gone soon diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index efa974e469..0a6e027efb 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -1,4 +1,4 @@ -import { INTERNAL_buildStoreRev2 as INTERNAL_buildStore } from './internals.ts' +import { INTERNAL_buildStoreRev3 as INTERNAL_buildStore } from './internals.ts' import type { INTERNAL_Store } from './internals.ts' export type Store = INTERNAL_Store diff --git a/tests/vanilla/effect.test.ts b/tests/vanilla/effect.test.ts index 503f68cde2..454d5cf7e3 100644 --- a/tests/vanilla/effect.test.ts +++ b/tests/vanilla/effect.test.ts @@ -2,8 +2,8 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest' import type { Atom, Getter, Setter, WritableAtom } from 'jotai/vanilla' import { atom, createStore } from 'jotai/vanilla' import { - INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks, - INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, + INTERNAL_getBuildingBlocksRev3 as INTERNAL_getBuildingBlocks, + INTERNAL_initializeStoreHooksRev3 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' beforeEach(() => { diff --git a/tests/vanilla/internals.test.tsx b/tests/vanilla/internals.test.tsx index 5a72ffae26..0b1005b162 100644 --- a/tests/vanilla/internals.test.tsx +++ b/tests/vanilla/internals.test.tsx @@ -7,9 +7,9 @@ import type { INTERNAL_InvalidatedAtoms, } from 'jotai/vanilla/internals' import { - INTERNAL_buildStoreRev2 as INTERNAL_buildStore, - INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks, - INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, + INTERNAL_buildStoreRev3 as INTERNAL_buildStore, + INTERNAL_getBuildingBlocksRev3 as INTERNAL_getBuildingBlocks, + INTERNAL_initializeStoreHooksRev3 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' const buildingBlockLength = 29 diff --git a/tests/vanilla/store.test.tsx b/tests/vanilla/store.test.tsx index b048605b5d..1a3269f709 100644 --- a/tests/vanilla/store.test.tsx +++ b/tests/vanilla/store.test.tsx @@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { atom, createStore } from 'jotai/vanilla' import type { Atom, Getter, PrimitiveAtom } from 'jotai/vanilla' import { - INTERNAL_buildStoreRev2 as INTERNAL_buildStore, - INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks, - INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, + INTERNAL_buildStoreRev3 as INTERNAL_buildStore, + INTERNAL_getBuildingBlocksRev3 as INTERNAL_getBuildingBlocks, + INTERNAL_initializeStoreHooksRev3 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' import type { INTERNAL_Store } from 'jotai/vanilla/internals' import { sleep } from '../test-utils' diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index 0560359a41..8d092cb979 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -5,8 +5,8 @@ import { describe, expect, it, vi } from 'vitest' import { atom } from 'jotai/vanilla' import type { Atom, WritableAtom } from 'jotai/vanilla' import { - INTERNAL_buildStoreRev2 as INTERNAL_buildStore, - INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks, + INTERNAL_buildStoreRev3 as INTERNAL_buildStore, + INTERNAL_initializeStoreHooksRev3 as INTERNAL_initializeStoreHooks, } from 'jotai/vanilla/internals' import type { INTERNAL_AtomState, From a1818de1cd599bf96e938cafff1c85d1087985cb Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 13 Apr 2026 13:04:21 +0900 Subject: [PATCH 20/24] revert atom intercepters signature --- src/vanilla/internals.ts | 21 ++++++++++++--------- tests/vanilla/storedev.test.tsx | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index 9b49930e6e..bcef131ce3 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -89,15 +89,18 @@ type ChangedAtoms = SetLike type Callbacks = SetLike<() => void> type AtomRead = ( + store: Store, atom: Atom, ...params: Parameters['read']> ) => Value type AtomWrite = ( + store: Store, atom: WritableAtom, ...params: Parameters['write']> ) => Result -type AtomOnInit = (atom: Atom, store: Store) => void +type AtomOnInit = (store: Store, atom: Atom) => void type AtomOnMount = ( + store: Store, atom: WritableAtomWithOnMount, setAtom: (...args: Args) => Result, ) => OnUnmount | void @@ -414,13 +417,13 @@ function initializeStoreHooks(storeHooks: StoreHooks): Required { // Main functions // -const BUILDING_BLOCK_atomRead: AtomRead = (atom, ...params) => +const BUILDING_BLOCK_atomRead: AtomRead = (_store, atom, ...params) => atom.read(...params) -const BUILDING_BLOCK_atomWrite: AtomWrite = (atom, ...params) => +const BUILDING_BLOCK_atomWrite: AtomWrite = (_store, atom, ...params) => atom.write(...params) -const BUILDING_BLOCK_atomOnInit: AtomOnInit = (atom, store) => +const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => atom.INTERNAL_onInit?.(store) -const BUILDING_BLOCK_atomOnMount: AtomOnMount = (atom, setAtom) => +const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => atom.onMount?.(setAtom) const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( @@ -439,7 +442,7 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( atomState = { d: new Map(), p: new Set(), n: 0 } atomStateMap.set(atom, atomState) storeHooks.i?.(atom) - atomOnInit?.(atom, store) + atomOnInit?.(store, atom) } return atomState as never } @@ -741,7 +744,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(store) } - const valueOrPromise = atomRead(atom, getter, options as never) + const valueOrPromise = atomRead(store, atom, getter, options as never) if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) { console.warn( 'Detected store mutation during atom read. This is not supported.', @@ -858,7 +861,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } } try { - return atomWrite(atom, getter, setter, ...args) + return atomWrite(store, atom, getter, setter, ...args) } finally { isSync = false } @@ -944,7 +947,7 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { } } try { - const onUnmount = atomOnMount(atom, setAtom) + const onUnmount = atomOnMount(store, atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index 8d092cb979..eab9b0f28a 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -35,7 +35,7 @@ const createDevStore = (): INTERNAL_Store & DevStore => { undefined, storeHooks, undefined, - (atom, get, set, ...args) => { + (_store, atom, get, set, ...args) => { if (inRestoreAtom) { return set(atom, ...(args as any)) } From 604978471acb778de9ea4899797e2d38324ac216 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 13 Apr 2026 13:08:07 +0900 Subject: [PATCH 21/24] fix workflow file --- .github/workflows/test-multiple-builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-multiple-builds.yml b/.github/workflows/test-multiple-builds.yml index 1ec7646014..49233be8b9 100644 --- a/.github/workflows/test-multiple-builds.yml +++ b/.github/workflows/test-multiple-builds.yml @@ -43,7 +43,7 @@ jobs: sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\1.js')/" vitest.config.mts sed -i~ "s/import { useResetAtom } from 'jotai\/react\/utils'/const { useResetAtom } = require('..\/..\/..\/dist\/react\/utils.js')/" tests/react/utils/useResetAtom.test.tsx sed -i~ "s/import { RESET, atomWithReducer, atomWithReset } from 'jotai\/vanilla\/utils'/const { RESET, atomWithReducer, atomWithReset } = require('..\/..\/..\/dist\/vanilla\/utils.js')/" tests/react/utils/useResetAtom.test.tsx - perl -i~ -0777 -pe "s/import {[^}]+} from 'jotai\/vanilla\/internals'/const { INTERNAL_buildStoreRev2: INTERNAL_buildStore, INTERNAL_initializeStoreHooksRev2: INTERNAL_initializeStoreHooks, INTERNAL_getBuildingBlocksRev2: INTERNAL_getBuildingBlocks } = require('..\/..\/dist\/vanilla\/internals.js')/g" tests/vanilla/store.test.tsx tests/vanilla/internals.test.tsx tests/vanilla/derive.test.tsx tests/vanilla/effect.test.ts + perl -i~ -0777 -pe "s/import {[^}]+} from 'jotai\/vanilla\/internals'/const { INTERNAL_buildStoreRev3: INTERNAL_buildStore, INTERNAL_initializeStoreHooksRev3: INTERNAL_initializeStoreHooks, INTERNAL_getBuildingBlocksRev3: INTERNAL_getBuildingBlocks } = require('..\/..\/dist\/vanilla\/internals.js')/g" tests/vanilla/store.test.tsx tests/vanilla/internals.test.tsx tests/vanilla/derive.test.tsx tests/vanilla/effect.test.ts - name: Patch for ESM if: ${{ matrix.build == 'esm' }} run: | From 47fc00796345f058e37bb16b10291626beb03faa Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 16 Apr 2026 11:52:40 +0900 Subject: [PATCH 22/24] extract ensureAtomState from building blocks --- src/vanilla/internals.ts | 169 +++++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 50 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index bcef131ce3..dba17f9120 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -105,11 +105,6 @@ type AtomOnMount = ( setAtom: (...args: Args) => Result, ) => OnUnmount | void -type EnsureAtomState = ( - buildingBlocks: Readonly, - store: Store, - atom: Atom, -) => AtomState type FlushCallbacks = ( buildingBlocks: Readonly, store: Store, @@ -212,7 +207,7 @@ type BuildingBlocks = [ atomOnInit: AtomOnInit, // 9 atomOnMount: AtomOnMount, // 10 // building-block functions - ensureAtomState: EnsureAtomState, // 11 + obsolete_ensureAtomState: undefined, // 11 flushCallbacks: FlushCallbacks, // 12 recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms, // 13 readAtomState: ReadAtomState, // 14 @@ -247,7 +242,6 @@ export type { AtomWrite as INTERNAL_AtomWrite, AtomOnInit as INTERNAL_AtomOnInit, AtomOnMount as INTERNAL_AtomOnMount, - EnsureAtomState as INTERNAL_EnsureAtomState, FlushCallbacks as INTERNAL_FlushCallbacks, RecomputeInvalidatedAtoms as INTERNAL_RecomputeInvalidatedAtoms, ReadAtomState as INTERNAL_ReadAtomState, @@ -299,6 +293,26 @@ function isPromiseLike(p: unknown): p is PromiseLike { return typeof (p as PromiseLike)?.then === 'function' } +function ensureAtomState( + atom: Atom, + atomStateMap: AtomStateMap, + storeHooks: StoreHooks, + atomOnInit: AtomOnInit | undefined, + store: Store, +): AtomState { + if (import.meta.env?.MODE !== 'production' && !atom) { + throw new Error('Atom is undefined or null') + } + let atomState = atomStateMap.get(atom) + if (!atomState) { + atomState = { d: new Map(), p: new Set(), n: 0 } + atomStateMap.set(atom, atomState) + storeHooks.i?.(atom) + atomOnInit?.(store, atom) + } + return atomState as never +} + function addPendingPromiseToDependency( atom: AnyAtom, promise: PromiseLike, @@ -426,27 +440,6 @@ const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => atom.onMount?.(setAtom) -const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( - buildingBlocks, - store, - atom, -) => { - const atomStateMap = buildingBlocks[0] - const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] - if (import.meta.env?.MODE !== 'production' && !atom) { - throw new Error('Atom is undefined or null') - } - let atomState = atomStateMap.get(atom) - if (!atomState) { - atomState = { d: new Map(), p: new Set(), n: 0 } - atomStateMap.set(atom, atomState) - storeHooks.i?.(atom) - atomOnInit?.(store, atom) - } - return atomState as never -} - const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( buildingBlocks, store, @@ -511,10 +504,12 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( buildingBlocks, store, ) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] const changedAtoms = buildingBlocks[3] - const ensureAtomState = buildingBlocks[11] + const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] const readAtomState = buildingBlocks[14] const mountDependencies = buildingBlocks[17] if (!changedAtoms.size) { @@ -535,7 +530,9 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // without incoming edges, which is one reason we can simplify the algorithm for (const atom of changedAtoms) { stackAtoms.push(atom) - stackStates.push(ensureAtomState(buildingBlocks, store, atom)) + stackStates.push( + ensureAtomState(atom, atomStateMap, storeHooks, atomOnInit, store), + ) } while (stackAtoms.length) { const top = stackAtoms.length - 1 @@ -571,7 +568,9 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { if (!visiting.has(d)) { stackAtoms.push(d) - stackStates.push(ensureAtomState(buildingBlocks, store, d)) + stackStates.push( + ensureAtomState(d, atomStateMap, storeHooks, atomOnInit, store), + ) } } } @@ -604,12 +603,13 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( store, atom, ) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] const atomRead = buildingBlocks[7] - const ensureAtomState = buildingBlocks[11] + const atomOnInit = buildingBlocks[9] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] @@ -618,7 +618,13 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const setAtomStateValueOrPromise = buildingBlocks[20] const registerAbortHandler = buildingBlocks[26] const storeEpochHolder = buildingBlocks[28] - const atomState = ensureAtomState(buildingBlocks, store, atom) + const atomState = ensureAtomState( + atom, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) const storeEpochNumber = storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -669,7 +675,13 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = ensureAtomState(buildingBlocks, store, a) + const aState = ensureAtomState( + a, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { setAtomStateValueOrPromise(buildingBlocks, store, a, a.init) @@ -787,15 +799,29 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( store, atom, ) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] - const ensureAtomState = buildingBlocks[11] + const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = ensureAtomState(buildingBlocks, store, a) + const aState = ensureAtomState( + a, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { - const dState = ensureAtomState(buildingBlocks, store, d) + const dState = ensureAtomState( + d, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) if (invalidatedAtoms.get(d) !== dState.n) { invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -810,10 +836,11 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( atom, args, ) => { + const atomStateMap = buildingBlocks[0] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] const atomWrite = buildingBlocks[8] - const ensureAtomState = buildingBlocks[11] + const atomOnInit = buildingBlocks[9] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] @@ -829,7 +856,13 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( a: WritableAtom, ...args: As ) => { - const aState = ensureAtomState(buildingBlocks, store, a) + const aState = ensureAtomState( + a, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -872,19 +905,32 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( store, atom, ) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] - const ensureAtomState = buildingBlocks[11] + const atomOnInit = buildingBlocks[9] const invalidateDependents = buildingBlocks[15] const mountAtom = buildingBlocks[18] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(buildingBlocks, store, atom) + const atomState = ensureAtomState( + atom, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) const mounted = mountedMap.get(atom) if (mounted && atomState.d.size > 0) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = ensureAtomState(buildingBlocks, store, a) + const aState = ensureAtomState( + a, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) const aMounted = mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) mounted.d.add(a) @@ -906,17 +952,24 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( } const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const mountCallbacks = buildingBlocks[4] const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] const atomOnMount = buildingBlocks[10] - const ensureAtomState = buildingBlocks[11] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] const writeAtomState = buildingBlocks[16] const mountAtom = buildingBlocks[18] - const atomState = ensureAtomState(buildingBlocks, store, atom) + const atomState = ensureAtomState( + atom, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) let mounted = mountedMap.get(atom) if (!mounted) { // recompute atom state @@ -974,12 +1027,19 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( store, atom, ) => { + const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const unmountCallbacks = buildingBlocks[5] const storeHooks = buildingBlocks[6] - const ensureAtomState = buildingBlocks[11] + const atomOnInit = buildingBlocks[9] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState(buildingBlocks, store, atom) + const atomState = ensureAtomState( + atom, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) let mounted = mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -1015,9 +1075,17 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( atom, valueOrPromise, ) => { - const ensureAtomState = buildingBlocks[11] + const atomStateMap = buildingBlocks[0] + const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] const abortPromise = buildingBlocks[27] - const atomState = ensureAtomState(buildingBlocks, store, atom) + const atomState = ensureAtomState( + atom, + atomStateMap, + storeHooks, + atomOnInit, + store, + ) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -1025,7 +1093,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - ensureAtomState(buildingBlocks, store, a), + ensureAtomState(a, atomStateMap, storeHooks, atomOnInit, store), ) } } @@ -1152,7 +1220,7 @@ function buildStore(...partialBuildingBlocks: Partial): Store { BUILDING_BLOCK_atomOnInit, BUILDING_BLOCK_atomOnMount, // building-block functions - BUILDING_BLOCK_ensureAtomState, + undefined, BUILDING_BLOCK_flushCallbacks, BUILDING_BLOCK_recomputeInvalidatedAtoms, BUILDING_BLOCK_readAtomState, @@ -1197,6 +1265,7 @@ export { isAtomStateInitialized as INTERNAL_isAtomStateInitialized, returnAtomValue as INTERNAL_returnAtomValue, isPromiseLike as INTERNAL_isPromiseLike, + ensureAtomState as INTERNAL_ensureAtomState, addPendingPromiseToDependency as INTERNAL_addPendingPromiseToDependency, getMountedOrPendingDependents as INTERNAL_getMountedOrPendingDependents, } From 96f5dbb3c4b236a25a40358ccc8fd745ebb23c22 Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 21 Apr 2026 16:12:39 +0900 Subject: [PATCH 23/24] Revert "extract ensureAtomState from building blocks" This reverts commit 47fc00796345f058e37bb16b10291626beb03faa. --- src/vanilla/internals.ts | 169 ++++++++++++--------------------------- 1 file changed, 50 insertions(+), 119 deletions(-) diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index dba17f9120..bcef131ce3 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -105,6 +105,11 @@ type AtomOnMount = ( setAtom: (...args: Args) => Result, ) => OnUnmount | void +type EnsureAtomState = ( + buildingBlocks: Readonly, + store: Store, + atom: Atom, +) => AtomState type FlushCallbacks = ( buildingBlocks: Readonly, store: Store, @@ -207,7 +212,7 @@ type BuildingBlocks = [ atomOnInit: AtomOnInit, // 9 atomOnMount: AtomOnMount, // 10 // building-block functions - obsolete_ensureAtomState: undefined, // 11 + ensureAtomState: EnsureAtomState, // 11 flushCallbacks: FlushCallbacks, // 12 recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms, // 13 readAtomState: ReadAtomState, // 14 @@ -242,6 +247,7 @@ export type { AtomWrite as INTERNAL_AtomWrite, AtomOnInit as INTERNAL_AtomOnInit, AtomOnMount as INTERNAL_AtomOnMount, + EnsureAtomState as INTERNAL_EnsureAtomState, FlushCallbacks as INTERNAL_FlushCallbacks, RecomputeInvalidatedAtoms as INTERNAL_RecomputeInvalidatedAtoms, ReadAtomState as INTERNAL_ReadAtomState, @@ -293,26 +299,6 @@ function isPromiseLike(p: unknown): p is PromiseLike { return typeof (p as PromiseLike)?.then === 'function' } -function ensureAtomState( - atom: Atom, - atomStateMap: AtomStateMap, - storeHooks: StoreHooks, - atomOnInit: AtomOnInit | undefined, - store: Store, -): AtomState { - if (import.meta.env?.MODE !== 'production' && !atom) { - throw new Error('Atom is undefined or null') - } - let atomState = atomStateMap.get(atom) - if (!atomState) { - atomState = { d: new Map(), p: new Set(), n: 0 } - atomStateMap.set(atom, atomState) - storeHooks.i?.(atom) - atomOnInit?.(store, atom) - } - return atomState as never -} - function addPendingPromiseToDependency( atom: AnyAtom, promise: PromiseLike, @@ -440,6 +426,27 @@ const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => atom.onMount?.(setAtom) +const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( + buildingBlocks, + store, + atom, +) => { + const atomStateMap = buildingBlocks[0] + const storeHooks = buildingBlocks[6] + const atomOnInit = buildingBlocks[9] + if (import.meta.env?.MODE !== 'production' && !atom) { + throw new Error('Atom is undefined or null') + } + let atomState = atomStateMap.get(atom) + if (!atomState) { + atomState = { d: new Map(), p: new Set(), n: 0 } + atomStateMap.set(atom, atomState) + storeHooks.i?.(atom) + atomOnInit?.(store, atom) + } + return atomState as never +} + const BUILDING_BLOCK_flushCallbacks: FlushCallbacks = ( buildingBlocks, store, @@ -504,12 +511,10 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( buildingBlocks, store, ) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] const changedAtoms = buildingBlocks[3] - const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const readAtomState = buildingBlocks[14] const mountDependencies = buildingBlocks[17] if (!changedAtoms.size) { @@ -530,9 +535,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( // without incoming edges, which is one reason we can simplify the algorithm for (const atom of changedAtoms) { stackAtoms.push(atom) - stackStates.push( - ensureAtomState(atom, atomStateMap, storeHooks, atomOnInit, store), - ) + stackStates.push(ensureAtomState(buildingBlocks, store, atom)) } while (stackAtoms.length) { const top = stackAtoms.length - 1 @@ -568,9 +571,7 @@ const BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = ( for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { if (!visiting.has(d)) { stackAtoms.push(d) - stackStates.push( - ensureAtomState(d, atomStateMap, storeHooks, atomOnInit, store), - ) + stackStates.push(ensureAtomState(buildingBlocks, store, d)) } } } @@ -603,13 +604,12 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( store, atom, ) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] const atomRead = buildingBlocks[7] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] @@ -618,13 +618,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( const setAtomStateValueOrPromise = buildingBlocks[20] const registerAbortHandler = buildingBlocks[26] const storeEpochHolder = buildingBlocks[28] - const atomState = ensureAtomState( - atom, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const atomState = ensureAtomState(buildingBlocks, store, atom) const storeEpochNumber = storeEpochHolder[0] // See if we can skip recomputing this atom. if (isAtomStateInitialized(atomState)) { @@ -675,13 +669,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } const getter = (a: Atom) => { if (a === (atom as AnyAtom)) { - const aState = ensureAtomState( - a, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const aState = ensureAtomState(buildingBlocks, store, a) if (!isAtomStateInitialized(aState)) { if (hasInitialValue(a)) { setAtomStateValueOrPromise(buildingBlocks, store, a, a.init) @@ -799,29 +787,15 @@ const BUILDING_BLOCK_invalidateDependents: InvalidateDependents = ( store, atom, ) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const invalidatedAtoms = buildingBlocks[2] - const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const stack: AnyAtom[] = [atom] while (stack.length) { const a = stack.pop()! - const aState = ensureAtomState( - a, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const aState = ensureAtomState(buildingBlocks, store, a) for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) { - const dState = ensureAtomState( - d, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const dState = ensureAtomState(buildingBlocks, store, d) if (invalidatedAtoms.get(d) !== dState.n) { invalidatedAtoms.set(d, dState.n) stack.push(d) @@ -836,11 +810,10 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( atom, args, ) => { - const atomStateMap = buildingBlocks[0] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] const atomWrite = buildingBlocks[8] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] @@ -856,13 +829,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( a: WritableAtom, ...args: As ) => { - const aState = ensureAtomState( - a, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const aState = ensureAtomState(buildingBlocks, store, a) try { if (a === (atom as AnyAtom)) { if (!hasInitialValue(a)) { @@ -905,32 +872,19 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( store, atom, ) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const changedAtoms = buildingBlocks[3] const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const invalidateDependents = buildingBlocks[15] const mountAtom = buildingBlocks[18] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState( - atom, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const atomState = ensureAtomState(buildingBlocks, store, atom) const mounted = mountedMap.get(atom) if (mounted && atomState.d.size > 0) { for (const [a, n] of atomState.d) { if (!mounted.d.has(a)) { - const aState = ensureAtomState( - a, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const aState = ensureAtomState(buildingBlocks, store, a) const aMounted = mountAtom(buildingBlocks, store, a) aMounted.t.add(atom) mounted.d.add(a) @@ -952,24 +906,17 @@ const BUILDING_BLOCK_mountDependencies: MountDependencies = ( } const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const mountCallbacks = buildingBlocks[4] const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] const atomOnMount = buildingBlocks[10] + const ensureAtomState = buildingBlocks[11] const flushCallbacks = buildingBlocks[12] const recomputeInvalidatedAtoms = buildingBlocks[13] const readAtomState = buildingBlocks[14] const writeAtomState = buildingBlocks[16] const mountAtom = buildingBlocks[18] - const atomState = ensureAtomState( - atom, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const atomState = ensureAtomState(buildingBlocks, store, atom) let mounted = mountedMap.get(atom) if (!mounted) { // recompute atom state @@ -1027,19 +974,12 @@ const BUILDING_BLOCK_unmountAtom: UnmountAtom = ( store, atom, ) => { - const atomStateMap = buildingBlocks[0] const mountedMap = buildingBlocks[1] const unmountCallbacks = buildingBlocks[5] const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const unmountAtom = buildingBlocks[19] - const atomState = ensureAtomState( - atom, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const atomState = ensureAtomState(buildingBlocks, store, atom) let mounted = mountedMap.get(atom) if (!mounted || mounted.l.size) { return mounted @@ -1075,17 +1015,9 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( atom, valueOrPromise, ) => { - const atomStateMap = buildingBlocks[0] - const storeHooks = buildingBlocks[6] - const atomOnInit = buildingBlocks[9] + const ensureAtomState = buildingBlocks[11] const abortPromise = buildingBlocks[27] - const atomState = ensureAtomState( - atom, - atomStateMap, - storeHooks, - atomOnInit, - store, - ) + const atomState = ensureAtomState(buildingBlocks, store, atom) const hasPrevValue = 'v' in atomState const prevValue = atomState.v if (isPromiseLike(valueOrPromise)) { @@ -1093,7 +1025,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( addPendingPromiseToDependency( atom, valueOrPromise, - ensureAtomState(a, atomStateMap, storeHooks, atomOnInit, store), + ensureAtomState(buildingBlocks, store, a), ) } } @@ -1220,7 +1152,7 @@ function buildStore(...partialBuildingBlocks: Partial): Store { BUILDING_BLOCK_atomOnInit, BUILDING_BLOCK_atomOnMount, // building-block functions - undefined, + BUILDING_BLOCK_ensureAtomState, BUILDING_BLOCK_flushCallbacks, BUILDING_BLOCK_recomputeInvalidatedAtoms, BUILDING_BLOCK_readAtomState, @@ -1265,7 +1197,6 @@ export { isAtomStateInitialized as INTERNAL_isAtomStateInitialized, returnAtomValue as INTERNAL_returnAtomValue, isPromiseLike as INTERNAL_isPromiseLike, - ensureAtomState as INTERNAL_ensureAtomState, addPendingPromiseToDependency as INTERNAL_addPendingPromiseToDependency, getMountedOrPendingDependents as INTERNAL_getMountedOrPendingDependents, } From ac38969c97790e44972089f70df5b151039c828b Mon Sep 17 00:00:00 2001 From: daishi Date: Tue, 21 Apr 2026 17:51:13 +0900 Subject: [PATCH 24/24] fix: make bb functions to receive the pair of (buildingBlocks, store) consistently --- src/react/useAtomValue.ts | 4 +-- src/vanilla/internals.ts | 62 +++++++++++++++++++++++++-------- tests/vanilla/storedev.test.tsx | 2 +- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/react/useAtomValue.ts b/src/react/useAtomValue.ts index d46d86f0d7..84a5847227 100644 --- a/src/react/useAtomValue.ts +++ b/src/react/useAtomValue.ts @@ -85,7 +85,7 @@ const createContinuablePromise = ( continuablePromiseMap.set(nextValue, continuablePromise!) curr = nextValue nextValue.then(onFulfilled(nextValue), onRejected(nextValue)) - registerAbortHandler(buildingBlocks, nextValue, onAbort) + registerAbortHandler(buildingBlocks, store, nextValue, onAbort) } else { resolve(nextValue) } @@ -94,7 +94,7 @@ const createContinuablePromise = ( } } promise.then(onFulfilled(promise), onRejected(promise)) - registerAbortHandler(buildingBlocks, promise, onAbort) + registerAbortHandler(buildingBlocks, store, promise, onAbort) }) continuablePromiseMap.set(promise, continuablePromise) } diff --git a/src/vanilla/internals.ts b/src/vanilla/internals.ts index bcef131ce3..86540466af 100644 --- a/src/vanilla/internals.ts +++ b/src/vanilla/internals.ts @@ -89,17 +89,24 @@ type ChangedAtoms = SetLike type Callbacks = SetLike<() => void> type AtomRead = ( + buildingBlocks: Readonly, store: Store, atom: Atom, ...params: Parameters['read']> ) => Value type AtomWrite = ( + buildingBlocks: Readonly, store: Store, atom: WritableAtom, ...params: Parameters['write']> ) => Result -type AtomOnInit = (store: Store, atom: Atom) => void +type AtomOnInit = ( + buildingBlocks: Readonly, + store: Store, + atom: Atom, +) => void type AtomOnMount = ( + buildingBlocks: Readonly, store: Store, atom: WritableAtomWithOnMount, setAtom: (...args: Args) => Result, @@ -179,11 +186,13 @@ type EnhanceBuildingBlocks = ( type AbortHandlersMap = WeakMapLike, Set<() => void>> type RegisterAbortHandler = ( buildingBlocks: Readonly, + store: Store, promise: PromiseLike, abortHandler: () => void, ) => void type AbortPromise = ( buildingBlocks: Readonly, + store: Store, promise: PromiseLike, ) => void type StoreEpochHolder = [n: EpochNumber] @@ -417,14 +426,26 @@ function initializeStoreHooks(storeHooks: StoreHooks): Required { // Main functions // -const BUILDING_BLOCK_atomRead: AtomRead = (_store, atom, ...params) => - atom.read(...params) -const BUILDING_BLOCK_atomWrite: AtomWrite = (_store, atom, ...params) => - atom.write(...params) -const BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) => +const BUILDING_BLOCK_atomRead: AtomRead = ( + _buildingBlocks, + _store, + atom, + ...params +) => atom.read(...params) +const BUILDING_BLOCK_atomWrite: AtomWrite = ( + _buildingBlocks, + _store, + atom, + ...params +) => atom.write(...params) +const BUILDING_BLOCK_atomOnInit: AtomOnInit = (_buildingBlocks, store, atom) => atom.INTERNAL_onInit?.(store) -const BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) => - atom.onMount?.(setAtom) +const BUILDING_BLOCK_atomOnMount: AtomOnMount = ( + _buildingBlocks, + _store, + atom, + setAtom, +) => atom.onMount?.(setAtom) const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( buildingBlocks, @@ -442,7 +463,7 @@ const BUILDING_BLOCK_ensureAtomState: EnsureAtomState = ( atomState = { d: new Map(), p: new Set(), n: 0 } atomStateMap.set(atom, atomState) storeHooks.i?.(atom) - atomOnInit?.(store, atom) + atomOnInit?.(buildingBlocks, store, atom) } return atomState as never } @@ -744,7 +765,13 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( if (import.meta.env?.MODE !== 'production') { storeMutationSet.delete(store) } - const valueOrPromise = atomRead(store, atom, getter, options as never) + const valueOrPromise = atomRead( + buildingBlocks, + store, + atom, + getter, + options as never, + ) if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) { console.warn( 'Detected store mutation during atom read. This is not supported.', @@ -752,7 +779,7 @@ const BUILDING_BLOCK_readAtomState: ReadAtomState = ( } setAtomStateValueOrPromise(buildingBlocks, store, atom, valueOrPromise) if (isPromiseLike(valueOrPromise)) { - registerAbortHandler(buildingBlocks, valueOrPromise, () => + registerAbortHandler(buildingBlocks, store, valueOrPromise, () => controller?.abort(), ) const settle = () => { @@ -861,7 +888,7 @@ const BUILDING_BLOCK_writeAtomState: WriteAtomState = ( } } try { - return atomWrite(store, atom, getter, setter, ...args) + return atomWrite(buildingBlocks, store, atom, getter, setter, ...args) } finally { isSync = false } @@ -947,7 +974,7 @@ const BUILDING_BLOCK_mountAtom: MountAtom = (buildingBlocks, store, atom) => { } } try { - const onUnmount = atomOnMount(store, atom, setAtom) + const onUnmount = atomOnMount(buildingBlocks, store, atom, setAtom) if (onUnmount) { mounted!.u = () => { isSync = true @@ -1034,7 +1061,7 @@ const BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = ( if (!hasPrevValue || !Object.is(prevValue, atomState.v)) { ++atomState.n if (isPromiseLike(prevValue)) { - abortPromise(buildingBlocks, prevValue) + abortPromise(buildingBlocks, store, prevValue) } } } @@ -1087,6 +1114,7 @@ const BUILDING_BLOCK_storeSub: StoreSub = ( const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( buildingBlocks, + _store, promise, abortHandler, ) => { @@ -1101,7 +1129,11 @@ const BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = ( abortHandlers.add(abortHandler) } -const BUILDING_BLOCK_abortPromise: AbortPromise = (buildingBlocks, promise) => { +const BUILDING_BLOCK_abortPromise: AbortPromise = ( + buildingBlocks, + _store, + promise, +) => { const abortHandlersMap = buildingBlocks[25] const abortHandlers = abortHandlersMap.get(promise) abortHandlers?.forEach((fn) => fn()) diff --git a/tests/vanilla/storedev.test.tsx b/tests/vanilla/storedev.test.tsx index eab9b0f28a..3926a335ed 100644 --- a/tests/vanilla/storedev.test.tsx +++ b/tests/vanilla/storedev.test.tsx @@ -35,7 +35,7 @@ const createDevStore = (): INTERNAL_Store & DevStore => { undefined, storeHooks, undefined, - (_store, atom, get, set, ...args) => { + (_buildingBlocks, _store, atom, get, set, ...args) => { if (inRestoreAtom) { return set(atom, ...(args as any)) }