@@ -185,7 +185,12 @@ class ProxyClientBridge {
185185class ProxyStubHandler < T extends object > implements ProxyHandler < T > {
186186 readonly #version: number ;
187187 readonly #stringifiedTarget: string ;
188- readonly #known = new Map < string , unknown > ( ) ;
188+ readonly #knownValues = new Map < string , unknown > ( ) ;
189+ readonly #knownDescriptors = new Map <
190+ string ,
191+ PropertyDescriptor | undefined
192+ > ( ) ;
193+ #knownOwnKeys?: string [ ] ;
189194
190195 revivers : ReducersRevivers = {
191196 ...revivers ,
@@ -327,7 +332,7 @@ class ProxyStubHandler<T extends object> implements ProxyHandler<T> {
327332 if ( typeof key === "symbol" || key === "then" ) return undefined ;
328333
329334 // See optimisation comments below for cases where this will be set
330- const maybeKnown = this . #known . get ( key ) ;
335+ const maybeKnown = this . #knownValues . get ( key ) ;
331336 if ( maybeKnown !== undefined ) return maybeKnown ;
332337
333338 // Always perform a synchronous GET, if this returns a `Promise`, we'll
@@ -361,7 +366,7 @@ class ProxyStubHandler<T extends object> implements ProxyHandler<T> {
361366 // (e.g. accessing `R2ObjectBody#body` multiple times)
362367 result instanceof ReadableStream
363368 ) {
364- this . #known . set ( key , result ) ;
369+ this . #knownValues . set ( key , result ) ;
365370 }
366371 return result ;
367372 }
@@ -371,6 +376,54 @@ class ProxyStubHandler<T extends object> implements ProxyHandler<T> {
371376 return this . get ( target , key , undefined ) !== undefined ;
372377 }
373378
379+ getOwnPropertyDescriptor ( target : T , key : string | symbol ) {
380+ if ( typeof key === "symbol" ) return undefined ;
381+
382+ // Optimisation: assume constant prototypes of proxied objects, descriptors
383+ // should never change after we've fetched them
384+ const maybeKnown = this . #knownDescriptors. get ( key ) ;
385+ if ( maybeKnown !== undefined ) return maybeKnown ;
386+
387+ const syncRes = this . bridge . sync . fetch ( this . bridge . url , {
388+ method : "POST" ,
389+ headers : {
390+ [ CoreHeaders . OP ] : ProxyOps . GET_OWN_DESCRIPTOR ,
391+ [ CoreHeaders . OP_KEY ] : key ,
392+ [ CoreHeaders . OP_TARGET ] : this . #stringifiedTarget,
393+ } ,
394+ } ) ;
395+ const result = this . #parseSyncResponse(
396+ syncRes ,
397+ this . getOwnPropertyDescriptor
398+ ) as PropertyDescriptor | undefined ;
399+
400+ this . #knownDescriptors. set ( key , result ) ;
401+ return result ;
402+ }
403+
404+ ownKeys ( _target : T ) {
405+ // Optimisation: assume constant prototypes of proxied objects, own keys
406+ // should never change after we've fetched them
407+ if ( this . #knownOwnKeys !== undefined ) return this . #knownOwnKeys;
408+
409+ const syncRes = this . bridge . sync . fetch ( this . bridge . url , {
410+ method : "POST" ,
411+ headers : {
412+ [ CoreHeaders . OP ] : ProxyOps . GET_OWN_KEYS ,
413+ [ CoreHeaders . OP_TARGET ] : this . #stringifiedTarget,
414+ } ,
415+ } ) ;
416+ const result = this . #parseSyncResponse( syncRes , this . ownKeys ) as string [ ] ;
417+
418+ this . #knownOwnKeys = result ;
419+ return result ;
420+ }
421+
422+ getPrototypeOf ( _target : T ) {
423+ // Return a `null` prototype, so users know this isn't a plain object
424+ return null ;
425+ }
426+
374427 #createFunction( key : string ) {
375428 // Optimisation: if the function returns a `Promise`, we know it must be
376429 // async (assuming all async functions always return `Promise`s). When
0 commit comments