import { isDraftable } from 'immer' ; import { AnyProp , createDescriptor , definePrivate , Dictionary , each , isArray , isFunction , } from '../shared' ; import { $O } from '../shared' ; import { immer } from '../shared/immer' ; import { getObservable , Observable } from './Observable' ; import { OPath } from './OPath' ; /**
 * Create a proxy that mimics the shape of the given object, but its values are
 * converted into nested proxies (for objects) or an observable (for leaf nodes).
 */ export function watch < T extends object > ( root ) ; /**
 * Create a "watch proxy" and pass it into the given function.
 *
 * This is useful when one or more properties have multiple possible types.
 */ export function watch < T extends object , S extends ( proxy : WatchProxy < T > ) => any > ( root , selector ) ; /** Track any property of an object. */ export function watch < T extends Dictionary < any > , P extends string | number > ( parent , prop ) ; export function watch ( parent , arg ? ) { let observable = getObservable ( parent ) ; if ( ! observable ) { throw Error ( 'Parent must be observable' ) ; } if ( arguments . length == 1 ) { if ( ! isDraftable ( parent ) ) { throw Error ( 'Parent cannot be watched' ) ; } observable = new OPath ( observable , [ ] ) ; return immer . useProxies ? new Proxy ( { target : parent , observable } , watchProxyHandler ) : createLegacyProxy ( parent , observable ) ; } if ( isFunction ( arg ) ) { return arg ( watch ( parent ) ) ; } if ( isWatchable ( observable ) ) { return observable . watch ( arg ) ; } throw Error ( `Cannot watch property: "${ arg }"` ) ; } export type WatchProxy < T > = [ T ] extends [ Proxyable < T > ] ? { readonly [ P in keyof T ] - ? : WatchNode < T [ P ] > ; } : never ; /**
 * Internal
 */ type NotProxyable = Set < any > | Map < any , any > | Promise < any > | Date ; type Proxyable < T > = Exclude < Extract < T , object > , NotProxyable > ; type WatchNode < T > = { 1 : Observable < T > ; 2 : WatchProxy < T > ; 3 : WatchProxy < Proxyable < T > > | Observable < Exclude < T , Proxyable < T > > > ; 4 : NullableBranch < Proxyable < T > > | WatchLeaf < T > ; } [ [ Proxyable < T > ] extends [ never ] ? 1 : [ T ] extends [ Proxyable < T > ] ? 2 : [ T ] extends [ NonNullable < T > ] ? 3 : 4 ] ; type NullableBranch < T > = Solve < { readonly [ P in keyof T ] - ? : WatchNode < T [ P ] | undefined > ; } > ; type WatchLeaf < T > = [ NonNullable < T > ] extends [ Proxyable < T > ] ? never : Observable < Exclude < T , Proxyable < T > > > ; type Solve < T > = T ; function isWatchable < T extends any > ( object ) { return object && typeof object . watch == 'function' ; } /**
 * ES2015+ Proxy
 */ type ProxyState = { target : object ; observable : Observable ; } ; const watchProxyHandler = { get ( state , prop ) { if ( prop == $O ) { return state . observable ; } if ( ! isWatchable ( state . observable ) ) { throw Error ( 'Parent cannot be watched' ) ; } let target = state . target [ prop ] ; let observable = state . observable . watch ( prop ) ; return isDraftable ( target ) ? new Proxy ( { target , observable } , watchProxyHandler ) : observable ; } , } ; /**
 * Legacy Proxy
 */ const defineGetter = createDescriptor ( { get : undefined , enumerable : true } ) ; function createLegacyProxy ( target , observable ) { let proxy = isArray ( target ) ? [ ] : Object . create ( Object . getPrototypeOf ( target ) ) ; definePrivate ( proxy , $O , observable ) ; each ( target , prop => { if ( prop in proxy ) return ; defineGetter ( proxy , prop , ( ) => { if ( ! isWatchable ( observable ) ) { throw Error ( 'Parent cannot be watched' ) ; } let value = target [ prop ] ; return isDraftable ( value ) ? createLegacyProxy ( value , observable . watch ( prop ) ) : observable . watch ( prop ) ; } ) ; } ) ; return proxy ; }