import { Observable } from 'rxjs' import { Emitter } from './emitter' import { mapValues } from './utils' export type Undux = { [K in keyof State]: { key: K previousValue: State[K] value: State[K] } } /** * Public Store interface. When you want to reference the Store type, * this is the type to use. */ export interface Store { get(key: K): State[K] set(key: K): (value: State[K]) => void setFrom_EXPERIMENTAL(f: (store: Store) => void): void on(key: K): Observable onAll(): Observable[keyof State]> getState(): Readonly } /** * Immutable snapshot of the current store state. One StoreSnapshot per * StoreDefinition is usually alive at a time. */ export class StoreSnapshot implements Store { constructor( private state: State, private storeDefinition: StoreDefinition ) { } get(key: K) { return this.state[key] } set(key: K) { return this.storeDefinition.set(key) } setFrom_EXPERIMENTAL(f: (store: Store) => void): void { return this.storeDefinition.setFrom_EXPERIMENTAL(f) } on(key: K) { return this.storeDefinition.on(key) } onAll() { return this.storeDefinition.onAll() } getState() { return Object.freeze(this.state) } } export type Options = { isDevMode: boolean } let DEFAULT_OPTIONS: Readonly = { isDevMode: false } /** * We create a single instance of this per . */ export class StoreDefinition implements Store { private storeSnapshot: StoreSnapshot private alls: Emitter> private emitter: Emitter private setters: { readonly [K in keyof State]: (value: State[K]) => void } constructor(state: State, options: Options) { // Initialize emitters this.alls = new Emitter(options.isDevMode) this.emitter = new Emitter(options.isDevMode) // Set initial state this.storeSnapshot = new StoreSnapshot(state, this) // Cache setters this.setters = mapValues(state, (v, key) => (value: typeof v) => { let previousValue = this.storeSnapshot.get(key) this.storeSnapshot = new StoreSnapshot( Object.assign({}, this.storeSnapshot.getState(), { [key]: value }), this ) this.emitter.emit(key, value) this.alls.emit(key, { key, previousValue, value }) } ) } on(key: K): Observable { return this.emitter.on(key) } onAll(): Observable[keyof State]> { return this.alls.all() } get(key: K) { return this.storeSnapshot.get(key) } set(key: K): (value: State[K]) => void { return this.setters[key] } setFrom_EXPERIMENTAL(f: (store: Store) => void): void { return f(this.storeSnapshot) } getCurrentSnapshot() { return this.storeSnapshot } toStore(): Store { return this.storeSnapshot } getState() { return this.storeSnapshot.getState() } } /** * @deprecated Use `createConnectedStore` instead. */ export function createStore( initialState: State, options: Options = DEFAULT_OPTIONS ): StoreDefinition { return new StoreDefinition(initialState, options) } export type Effects = (store: StoreDefinition) => StoreDefinition export type EffectsAs = (stores: {[K in keyof States]: StoreDefinition}) => {[K in keyof States]: StoreDefinition} /** * @deprecated Use `Effects` instead. */ export type Plugin = (store: StoreDefinition) => StoreDefinition export * from './plugins/withLogger' export * from './plugins/withReduxDevtools' export * from './react'