import { is } from '@alloc/is' import { globals } from './globals' import { noto } from './noto' import { Change, Observable, ObservedSlot, ObserverTarget } from './observable' import { $O, SIZE } from './symbols' const { get } = Map.prototype function onChange(observable: Observable, key: any, change: Change) { const observers: ObservedSlot = get.call(observable, key) if (observers) { // Increase the nonce even if no observers exist, because there // might be a pending observer (like a "withAuto" component). observers.nonce++ observers.onChange(change) } } function emit(target: ObserverTarget, change: Change) { noto(() => { const observable = target[$O]! if (globals.onChange) { globals.onChange(change) } // The "clear" op never has a key. if (change.op !== 'clear') { onChange(observable, change.key, change) } // When a `Map` object is cleared, notify every key observer. else if (is.map(change.oldValue)) { change.oldValue.forEach((_, key) => onChange(observable, key, change)) } // Size changes always come after a related change, // so avoid notifying `$O` observers more than once. if (change.key !== SIZE) { onChange(observable, $O, change) } }) return true } export const emitAdd = (target: object, key: any, value: any) => emit(target, { op: 'add', target, key, value, }) export const emitRemove = (target: object, key: any, oldValue: any) => emit(target, { op: 'remove', target, key, oldValue, }) export const emitReplace = ( target: object, key: any, value: any, oldValue: any ) => emit(target, { op: 'replace', target, key, value, oldValue, }) export const emitSplice = ( target: any[], key: number, value: readonly any[], oldValue: readonly any[] ) => emit(target, { op: 'splice', target, key, value, oldValue, }) export const emitClear = (target: object, oldValue: any) => emit(target, { op: 'clear', target, oldValue, }) export const emitDefine = ( target: object, key: keyof any, value: PropertyDescriptor, oldValue?: PropertyDescriptor ) => emit(target, { op: 'define', target, key, value, oldValue, })