import cbSubscribe from 'callbag-subscribe' ; import cbShare from 'callbag-share' ; import cbMerge from 'callbag-merge' ; import cbTake from 'callbag-take' ; import { Atom , ReadableAtom , DerivedAtomReader , Updater , CustomAtom } from '..' ; import { equal , atomToSource , cachedSubject } from '../inner-utils' ; /**
 * The main atom constructor. Will return Atom, ReadableAtom or CustomAtom
 * depending on how it is called.
 */ export function atom < Value , Update > ( value , write ) ; export function atom < Value > ( value ) ; export function atom < Value > ( value ) ; export function atom < Value , Update = unknown > ( read , write ? ) { if ( read instanceof Function ) { if ( write ) { return customAtom ( read , write ) ; } return derivedAtom ( read ) ; } return baseAtom ( read ) ; } /**
 * Creates a basic read- and writable atom around the given value
 */ const baseAtom = < Value > ( value ) : Atom < Value > => { const subject = cachedSubject ( value ) ; const getValue = subject . getValue ; const obs = cbShare ( subject ) ; const subscribe = listener => { const unsub = cbSubscribe ( listener ) ( obs ) ; return ( ) => unsub ( ) ; } ; const update = ( next ) => { const nextValue = next instanceof Function ? next ( getValue ( ) ) : next ; if ( ! equal ( nextValue , getValue ( ) ) ) { subject ( 1 , nextValue ) ; } } ; return { getValue , update , subscribe } ; } ; /**
 * Creates a read-only atom derived from other atoms and/or other outside sources
 */ const derivedAtom = < Value > ( read ) : ReadableAtom < Value > => { const getter = ( onDependency ) => < A > ( atom ) => { const value = atom . getValue ( ) ; onDependency ( { atom , value } ) ; return value ; } ; const dependencies = [ ] ; const dependencyValueCache = [ ] ; const getValueAndObserver = ( ) => { dependencies . length = 0 ; dependencyValueCache . length = 0 ; const onDependency = ( { atom , value , } ) => { dependencies . push ( atom ) ; dependencyValueCache . push ( value ) ; } ; const computedValue = read ( getter ( onDependency ) ) ; const sources = Array . from ( dependencies ) . map ( atomToSource ) ; const dependencyObserver = cbTake ( 1 ) ( cbMerge ( ... sources ) ) ; return { computedValue , dependencyObserver } as const ; } ; const { computedValue : initialValue } = getValueAndObserver ( ) ; const valueSubject = cachedSubject < Value > ( initialValue ) ; const recalculateValue = ( ) => { const { computedValue , dependencyObserver } = getValueAndObserver ( ) ; if ( subs !== 0 ) { dependencySubscription ?. ( ) ; dependencySubscription = cbSubscribe ( recalculateValue ) ( dependencyObserver ) ; } if ( ! equal ( computedValue , getValue ( ) ) ) { valueSubject ( 1 , computedValue ) ; } } ; const value$ = cbShare ( valueSubject ) ; let dependencySubscription = null ; const getValue = ( ) => { const dependenciesChanged = dependencyValueCache . some ( ( cachedValue , index ) => ! equal ( cachedValue , dependencies [ index ] . getValue ( ) ) ) ; if ( dependenciesChanged ) recalculateValue ( ) ; return valueSubject . getValue ( ) ; } ; let subs = 0 ; const subscribe = listener => { const valueSubscription = cbSubscribe ( listener ) ( value$ ) ; if ( subs === 0 ) { const { dependencyObserver } = getValueAndObserver ( ) ; dependencySubscription ?. ( ) ; dependencySubscription = cbSubscribe ( recalculateValue ) ( dependencyObserver ) ; } let hasUnsubbed = false ; subs ++ ; return ( ) => { if ( hasUnsubbed ) return ; hasUnsubbed = true ; subs -- ; if ( subs === 0 ) { dependencySubscription ?. ( ) ; dependencySubscription = null ; } valueSubscription ( ) ; } ; } ; return { subscribe , getValue } ; } ; /**
 * Creates an updatable atom derived from other atoms and/or other outside sources
 */ const customAtom = < Value , Update > ( read , write ) : CustomAtom < Value , Update > => { return { ... derivedAtom ( read ) , update : write , } ; } ; type Subscribe < S > = ( listener : ( value : S ) => void ) => ( ) => void ;