import * as O from 'fp-ts/Option' ; import * as Op from 'monocle-ts/Optional' ; import { pipe } from 'fp-ts/function' ; import { ChannelOptions , createChannel , createStream } from 'effection' ; import { MakeSlice , Slice } from './types' ; import { unique } from './unique' ; param initialState param options export function createAtom < S > ( initialState , options = { } ) { let lens = pipe ( Op . id < O . Option < S > > ( ) , Op . some ) ; let state = O . fromNullable ( initialState ) ; let states = createChannel ( options ) ; let valueQueue = [ ] ; let didEnter = false ; function getState ( ) { return state ; } function setState ( value ) { let next = O . fromNullable ( value ) ; if ( next === getState ( ) ) { return ; } state = next ; valueQueue . push ( O . toUndefined ( next ) as S ) ; if ( ! didEnter ) { try { didEnter = true ; let nextValue ; while ( nextValue = valueQueue . shift ( ) ) { states . send ( nextValue ) ; } } finally { didEnter = false ; } } } let sliceMaker = < A > ( parentPath , parentOptional = lens as unknown as Op . Optional < O . Option < S > , A > ) : MakeSlice < any > => < P extends keyof A > ( ... path ) : Slice < A [ P ] > => { let getters = ( path || [ ] ) . map ( p => ( typeof p === 'number' ) ? Op . index ( p ) : Op . prop < A , P > ( p ) ) as [ any , any ] ; let fullPath = [ ... parentPath , ... path ] as string [ ] ; let sliceOptional = pipe ( parentOptional , Op . fromNullable , ... getters ) as Op . Optional < O . Option < S > , A [ P ] > ; let streamName = fullPath . length ? `slice(${ fullPath . map ( ( s ) => `'${ s }'` ) . join ( ', ' ) })` : 'atom' ; let stream = createStream < A [ P ] > ( publish => { publish ( get ( ) ) ; return states . map ( ( s ) => pipe ( s as S , O . fromNullable , sliceOptional . getOption , O . toUndefined ) as A [ P ] ) . filter ( unique ( get ( ) ) ) . forEach ( publish ) ; } , streamName ) ; function getOption ( ) { let current = pipe ( getState ( ) , sliceOptional . getOption ) ; return current ; } function get ( ) { return pipe ( getOption ( ) , O . toUndefined ) as A [ P ] ; } function set ( value ) { if ( value === get ( ) ) { return ; } let next = pipe ( getState ( ) , sliceOptional . set ( value ) , O . toUndefined ) ; setState ( next as S ) ; } function update ( fn ) { let next = pipe ( sliceOptional , Op . modify ( fn ) ) ( getState ( ) ) ; setState ( O . toUndefined ( next ) as S ) ; } function remove ( ) { let next = pipe ( sliceOptional , Op . modify ( ( ) => undefined as any ) ) ( getState ( ) ) ; setState ( O . toUndefined ( next ) as S ) ; } return Object . assign ( { get , set , update , stream , slice : sliceMaker ( fullPath , sliceOptional ) , remove } , stream ) ; } ; return sliceMaker ( [ ] ) ( ) ; }