import { ArrayConcat , getPrototypeOf , getOwnPropertyNames , getOwnPropertySymbols , getOwnPropertyDescriptor , isUndefined , isExtensible , hasOwnProperty , ObjectDefineProperty , preventExtensions , ArrayPush , ObjectCreate , ProxyPropertyKey , } from './shared' ; import { ObservableMembrane } from './observable-membrane' ; export type ShadowTarget = object ; export abstract class BaseProxyHandler { originalTarget : any ; membrane : ObservableMembrane ; constructor ( membrane , value ) { this . originalTarget = value ; this . membrane = membrane ; } abstract wrapValue ( value ) ; abstract wrapGetter ( originalGet ) ; abstract wrapSetter ( originalSet ) ; wrapDescriptor ( descriptor ) { if ( hasOwnProperty . call ( descriptor , 'value' ) ) { descriptor . value = this . wrapValue ( descriptor . value ) ; } else { const { set : originalSet , get : originalGet } = descriptor ; if ( ! isUndefined ( originalGet ) ) { descriptor . get = this . wrapGetter ( originalGet ) ; } if ( ! isUndefined ( originalSet ) ) { descriptor . set = this . wrapSetter ( originalSet ) ; } } return descriptor ; } copyDescriptorIntoShadowTarget ( shadowTarget , key ) { const { originalTarget } = this ; const originalDescriptor = getOwnPropertyDescriptor ( originalTarget , key ) ; if ( ! isUndefined ( originalDescriptor ) ) { const wrappedDesc = this . wrapDescriptor ( originalDescriptor ) ; ObjectDefineProperty ( shadowTarget , key , wrappedDesc ) ; } } lockShadowTarget ( shadowTarget ) { const { originalTarget } = this ; const targetKeys = ArrayConcat . call ( getOwnPropertyNames ( originalTarget ) , getOwnPropertySymbols ( originalTarget ) ) ; targetKeys . forEach ( ( key ) => { this . copyDescriptorIntoShadowTarget ( shadowTarget , key ) ; } ) ; const { membrane : { tagPropertyKey } , } = this ; if ( ! isUndefined ( tagPropertyKey ) && ! hasOwnProperty . call ( shadowTarget , tagPropertyKey ) ) { ObjectDefineProperty ( shadowTarget , tagPropertyKey , ObjectCreate ( null ) ) ; } preventExtensions ( shadowTarget ) ; } abstract set ( shadowTarget , key , value ) ; abstract deleteProperty ( shadowTarget , key ) ; abstract setPrototypeOf ( shadowTarget , prototype ) ; abstract preventExtensions ( shadowTarget ) ; abstract defineProperty ( shadowTarget , key , descriptor ) ; apply ( shadowTarget , thisArg , argArray ) { } construct ( shadowTarget , argArray , newTarget ? ) { } get ( shadowTarget , key ) { const { originalTarget , membrane : { valueObserved } , } = this ; const value = originalTarget [ key ] ; valueObserved ( originalTarget , key ) ; return this . wrapValue ( value ) ; } has ( shadowTarget , key ) { const { originalTarget , membrane : { tagPropertyKey , valueObserved } , } = this ; valueObserved ( originalTarget , key ) ; return key in originalTarget || key === tagPropertyKey ; } ownKeys ( shadowTarget ) { const { originalTarget , membrane : { tagPropertyKey } , } = this ; const keys = isUndefined ( tagPropertyKey ) || hasOwnProperty . call ( originalTarget , tagPropertyKey ) ? [ ] : [ tagPropertyKey ] ; ArrayPush . apply ( keys , getOwnPropertyNames ( originalTarget ) ) ; ArrayPush . apply ( keys , getOwnPropertySymbols ( originalTarget ) ) ; return keys ; } isExtensible ( shadowTarget ) { const { originalTarget } = this ; if ( ! isExtensible ( shadowTarget ) ) { return false ; } if ( ! isExtensible ( originalTarget ) ) { this . lockShadowTarget ( shadowTarget ) ; return false ; } return true ; } getPrototypeOf ( shadowTarget ) { const { originalTarget } = this ; return getPrototypeOf ( originalTarget ) ; } getOwnPropertyDescriptor ( shadowTarget , key ) { const { originalTarget , membrane : { valueObserved , tagPropertyKey } , } = this ; valueObserved ( originalTarget , key ) ; let desc = getOwnPropertyDescriptor ( originalTarget , key ) ; if ( isUndefined ( desc ) ) { if ( key !== tagPropertyKey ) { return undefined ; } desc = { value : undefined , writable : false , configurable : false , enumerable : false } ; ObjectDefineProperty ( shadowTarget , tagPropertyKey , desc ) ; return desc ; } if ( desc . configurable === false ) { this . copyDescriptorIntoShadowTarget ( shadowTarget , key ) ; } return this . wrapDescriptor ( desc ) ; } }