let proxy_lookup ; let object_lookup ; let reads ; type Culled = { path : string ; value : any ; } ; function add_read ( obj , prop ) { if ( ! reads . has ( obj ) ) reads . set ( obj , new Set ( ) ) ; reads . get ( obj ) . add ( prop ) ; } const handler = { get : ( obj , prop ) => { if ( typeof prop === 'symbol' ) return obj [ prop ] ; add_read ( obj , prop as string ) ; return to_proxy ( obj [ prop ] , obj ) ; } , set : ( obj , prop , value ) => { obj [ prop ] = value ; return true ; } } ; function to_proxy ( value , parent ) { if ( typeof value === 'function' && parent ) { return value . bind ( parent ) ; } if ( ! value || typeof value !== 'object' ) { return value ; } if ( ! proxy_lookup . has ( value ) ) { const proxy = new Proxy ( value , handler ) ; proxy_lookup . set ( value , proxy ) ; object_lookup . set ( proxy , value ) ; } return proxy_lookup . get ( value ) ; } export function prepare ( input ) { if ( ! proxy_lookup ) { proxy_lookup = new WeakMap ( ) ; object_lookup = new WeakMap ( ) ; reads = new WeakMap ( ) ; } return to_proxy ( input , null ) ; } function get_type ( thing ) { return Object . prototype . toString . call ( thing ) . slice ( 8 , - 1 ) ; } function apply_at_path ( path , object , culled ) { if ( ! proxy_lookup . has ( object ) ) return object ; const type = get_type ( object ) ; if ( type !== 'Array' && type !== 'Object' ) return object ; const kept = ( type === 'Array' ? [ ] : { } ) ; const was_read = reads . get ( object ) ; Object . keys ( object ) . forEach ( key => { const child_path = path ? `${ path }.${ key }` : key ; if ( was_read && was_read . has ( key ) ) { ( kept as Record < string , any > ) [ key ] = apply_at_path ( child_path , object [ key ] , culled ) ; } else { culled . push ( { path : child_path , value : object [ key ] } ) ; } } ) ; if ( type === 'Array' && was_read && was_read . has ( 'length' ) ) { kept . length = object . length ; } return kept ; } export function apply ( object ) { if ( object_lookup && object_lookup . has ( object ) ) { object = object_lookup . get ( object ) ; } if ( ! proxy_lookup . has ( object ) ) { throw new Error ( 'You must call `prepare` before calling `apply`' ) ; } const culled = [ ] ; const kept = apply_at_path ( '' , object , culled ) ; return { kept , culled } ; }