import { toString , isArray , unwrap , isExtensible , preventExtensions , ObjectDefineProperty , hasOwnProperty , isUndefined , ProxyPropertyKey , } from './shared' ; import { BaseProxyHandler , ShadowTarget } from './base-handler' ; const getterMap = new WeakMap < ( ) => any , ( ) => any > ( ) ; const setterMap = new WeakMap < ( v : any ) => void , ( v : any ) => void > ( ) ; const reverseGetterMap = new WeakMap < ( ) => any , ( ) => any > ( ) ; const reverseSetterMap = new WeakMap < ( v : any ) => void , ( v : any ) => void > ( ) ; export class ReactiveProxyHandler extends BaseProxyHandler { wrapValue ( value ) { return this . membrane . getProxy ( value ) ; } wrapGetter ( originalGet ) { const wrappedGetter = getterMap . get ( originalGet ) ; if ( ! isUndefined ( wrappedGetter ) ) { return wrappedGetter ; } const handler = this ; const get = function ( this ) : any { return handler . wrapValue ( originalGet . call ( unwrap ( this ) ) ) ; } ; getterMap . set ( originalGet , get ) ; reverseGetterMap . set ( get , originalGet ) ; return get ; } wrapSetter ( originalSet ) { const wrappedSetter = setterMap . get ( originalSet ) ; if ( ! isUndefined ( wrappedSetter ) ) { return wrappedSetter ; } const set = function ( this , v ) { originalSet . call ( unwrap ( this ) , unwrap ( v ) ) ; } ; setterMap . set ( originalSet , set ) ; reverseSetterMap . set ( set , originalSet ) ; return set ; } unwrapDescriptor ( descriptor ) { if ( hasOwnProperty . call ( descriptor , 'value' ) ) { descriptor . value = unwrap ( descriptor . value ) ; } else { const { set , get } = descriptor ; if ( ! isUndefined ( get ) ) { descriptor . get = this . unwrapGetter ( get ) ; } if ( ! isUndefined ( set ) ) { descriptor . set = this . unwrapSetter ( set ) ; } } return descriptor ; } unwrapGetter ( redGet ) { const reverseGetter = reverseGetterMap . get ( redGet ) ; if ( ! isUndefined ( reverseGetter ) ) { return reverseGetter ; } const handler = this ; const get = function ( this ) : any { return unwrap ( redGet . call ( handler . wrapValue ( this ) ) ) ; } ; getterMap . set ( get , redGet ) ; reverseGetterMap . set ( redGet , get ) ; return get ; } unwrapSetter ( redSet ) { const reverseSetter = reverseSetterMap . get ( redSet ) ; if ( ! isUndefined ( reverseSetter ) ) { return reverseSetter ; } const handler = this ; const set = function ( this , v ) { redSet . call ( handler . wrapValue ( this ) , handler . wrapValue ( v ) ) ; } ; setterMap . set ( set , redSet ) ; reverseSetterMap . set ( redSet , set ) ; return set ; } set ( shadowTarget , key , value ) { const { originalTarget , membrane : { valueMutated } , } = this ; const oldValue = originalTarget [ key ] ; if ( oldValue !== value ) { originalTarget [ key ] = value ; valueMutated ( originalTarget , key ) ; } else if ( key === 'length' && isArray ( originalTarget ) ) { valueMutated ( originalTarget , key ) ; } return true ; } deleteProperty ( shadowTarget , key ) { const { originalTarget , membrane : { valueMutated } , } = this ; delete originalTarget [ key ] ; valueMutated ( originalTarget , key ) ; return true ; } setPrototypeOf ( shadowTarget , prototype ) { if ( process . env . NODE_ENV !== 'production' ) { throw new Error ( `Invalid setPrototypeOf invocation for reactive proxy ${ toString ( this . originalTarget ) }. Prototype of reactive objects cannot be changed.` ) ; } } preventExtensions ( shadowTarget ) { if ( isExtensible ( shadowTarget ) ) { const { originalTarget } = this ; preventExtensions ( originalTarget ) ; if ( isExtensible ( originalTarget ) ) { return false ; } this . lockShadowTarget ( shadowTarget ) ; } return true ; } defineProperty ( shadowTarget , key , descriptor ) { const { originalTarget , membrane : { valueMutated , tagPropertyKey } , } = this ; if ( key === tagPropertyKey && ! hasOwnProperty . call ( originalTarget , key ) ) { return true ; } ObjectDefineProperty ( originalTarget , key , this . unwrapDescriptor ( descriptor ) ) ; if ( descriptor . configurable === false ) { this . copyDescriptorIntoShadowTarget ( shadowTarget , key ) ; } valueMutated ( originalTarget , key ) ; return true ; } }