import { unwrap , isArray , isUndefined , getPrototypeOf , isFunction , registerProxy , ProxyPropertyKey , ObjectDotPrototype , } from './shared' ; import { ReactiveProxyHandler } from './reactive-handler' ; import { ReadOnlyHandler } from './read-only-handler' ; import { init as initDevFormatter } from './reactive-dev-formatter' ; if ( process . env . NODE_ENV !== 'production' ) { initDevFormatter ( ) ; } type ValueObservedCallback = ( obj : any , key : ProxyPropertyKey ) => void ; type ValueMutatedCallback = ( obj : any , key : ProxyPropertyKey ) => void ; type IsObservableCallback = ( value : any ) => boolean ; export interface ObservableMembraneInit { valueMutated ? : ValueMutatedCallback ; valueObserved ? : ValueObservedCallback ; valueIsObservable ? : IsObservableCallback ; tagPropertyKey ? : ProxyPropertyKey ; } function defaultValueIsObservable ( value ) { if ( value === null ) { return false ; } if ( typeof value !== 'object' ) { return false ; } if ( isArray ( value ) ) { return true ; } const proto = getPrototypeOf ( value ) ; return proto === ObjectDotPrototype || proto === null || getPrototypeOf ( proto ) === null ; } const defaultValueObserved = ( obj , key ) => { } ; const defaultValueMutated = ( obj , key ) => { } ; function createShadowTarget ( value ) { return isArray ( value ) ? [ ] : { } ; } export class ObservableMembrane { valueMutated : ValueMutatedCallback ; valueObserved : ValueObservedCallback ; valueIsObservable : IsObservableCallback ; tagPropertyKey : ProxyPropertyKey | undefined ; private readOnlyObjectGraph : WeakMap < any , any > = new WeakMap ( ) ; private reactiveObjectGraph : WeakMap < any , any > = new WeakMap ( ) ; constructor ( options = { } ) { const { valueMutated , valueObserved , valueIsObservable , tagPropertyKey } = options ; this . valueMutated = isFunction ( valueMutated ) ? valueMutated : defaultValueMutated ; this . valueObserved = isFunction ( valueObserved ) ? valueObserved : defaultValueObserved ; this . valueIsObservable = isFunction ( valueIsObservable ) ? valueIsObservable : defaultValueIsObservable ; this . tagPropertyKey = tagPropertyKey ; } getProxy ( value ) { const unwrappedValue = unwrap ( value ) ; if ( this . valueIsObservable ( unwrappedValue ) ) { if ( this . readOnlyObjectGraph . get ( unwrappedValue ) === value ) { return value ; } return this . getReactiveHandler ( unwrappedValue ) ; } return unwrappedValue ; } getReadOnlyProxy ( value ) { value = unwrap ( value ) ; if ( this . valueIsObservable ( value ) ) { return this . getReadOnlyHandler ( value ) ; } return value ; } unwrapProxy ( p ) { return unwrap ( p ) ; } private getReactiveHandler ( value ) { let proxy = this . reactiveObjectGraph . get ( value ) ; if ( isUndefined ( proxy ) ) { const handler = new ReactiveProxyHandler ( this , value ) ; proxy = new Proxy ( createShadowTarget ( value ) , handler ) ; registerProxy ( proxy , value ) ; this . reactiveObjectGraph . set ( value , proxy ) ; } return proxy ; } private getReadOnlyHandler ( value ) { let proxy = this . readOnlyObjectGraph . get ( value ) ; if ( isUndefined ( proxy ) ) { const handler = new ReadOnlyHandler ( this , value ) ; proxy = new Proxy ( createShadowTarget ( value ) , handler ) ; registerProxy ( proxy , value ) ; this . readOnlyObjectGraph . set ( value , proxy ) ; } return proxy ; } }