@@ -9,6 +9,9 @@ import type {
9
9
} from '../types/appsync-events.js' ;
10
10
import { RouteHandlerRegistry } from './RouteHandlerRegistry.js' ;
11
11
12
+ // Simple global approach - store the last instance per router
13
+ const routerInstanceMap = new WeakMap < Router , unknown > ( ) ;
14
+
12
15
/**
13
16
* Class for registering routes for the `onPublish` and `onSubscribe` events in AWS AppSync Events APIs.
14
17
*/
@@ -194,11 +197,22 @@ class Router {
194
197
return ;
195
198
}
196
199
197
- return ( _target , _propertyKey , descriptor : PropertyDescriptor ) => {
200
+ return ( target , _propertyKey , descriptor : PropertyDescriptor ) => {
198
201
const routeOptions = isRecord ( handler ) ? handler : options ;
202
+ const originalMethod = descriptor ?. value ;
203
+ const routerInstance = this ;
204
+
205
+ this . #bindResolveMethodScope( target ) ;
206
+
207
+ // Create a handler that uses the captured instance
208
+ const boundHandler = ( ...args : unknown [ ] ) => {
209
+ const instance = routerInstanceMap . get ( routerInstance ) ;
210
+ return originalMethod ?. apply ( instance , args ) ;
211
+ } ;
212
+
199
213
this . onPublishRegistry . register ( {
200
214
path,
201
- handler : descriptor . value ,
215
+ handler : boundHandler ,
202
216
aggregate : ( routeOptions ?. aggregate ?? false ) as T ,
203
217
} ) ;
204
218
return descriptor ;
@@ -273,14 +287,89 @@ class Router {
273
287
return ;
274
288
}
275
289
276
- return ( _target , _propertyKey , descriptor : PropertyDescriptor ) => {
290
+ return ( target , propertyKey , descriptor : PropertyDescriptor ) => {
291
+ const originalMethod = descriptor ?. value ;
292
+ const routerInstance = this ;
293
+
294
+ // Patch any method that might call resolve() to capture instance
295
+ this . #bindResolveMethodScope( target ) ;
296
+
297
+ // Create a handler that uses the captured instance
298
+ const boundHandler = ( ...args : unknown [ ] ) => {
299
+ const instance = routerInstanceMap . get ( routerInstance ) ;
300
+ return originalMethod ?. apply ( instance , args ) ;
301
+ } ;
302
+
277
303
this . onSubscribeRegistry . register ( {
278
304
path,
279
- handler : descriptor . value ,
305
+ handler : boundHandler ,
280
306
} ) ;
281
307
return descriptor ;
282
308
} ;
283
309
}
310
+
311
+ /**
312
+ * Binds the resolve method scope to the target object.
313
+ *
314
+ * We patch any method that might call `resolve()` to ensure that
315
+ * the class instance is captured correctly when the method is resolved. We need
316
+ * to do this because when a method is decorated, it loses its context and
317
+ * the `this` keyword inside the method no longer refers to the class instance of the decorated method.
318
+ *
319
+ * We need to apply this two-step process because the decorator is applied to the method
320
+ * before the class instance is created, so we cannot capture the instance directly.
321
+ *
322
+ * @param target - The target object whose methods will be patched to capture the instance scope
323
+ */
324
+ #bindResolveMethodScope( target : object ) {
325
+ const routerInstance = this ;
326
+
327
+ // Patch any method that might call resolve() to capture instance
328
+ if ( ! target . constructor . prototype . _powertoolsPatched ) {
329
+ target . constructor . prototype . _powertoolsPatched = true ;
330
+
331
+ // Get all method names from the prototype
332
+ const proto = target . constructor . prototype ;
333
+ const methodNames = Object . getOwnPropertyNames ( proto ) ;
334
+
335
+ for ( const methodName of methodNames ) {
336
+ if ( methodName === 'constructor' ) continue ;
337
+
338
+ const methodDescriptor = Object . getOwnPropertyDescriptor (
339
+ proto ,
340
+ methodName
341
+ ) ;
342
+ if (
343
+ methodDescriptor ?. value &&
344
+ typeof methodDescriptor . value === 'function'
345
+ ) {
346
+ const originalMethodRef = methodDescriptor . value ;
347
+ const methodSource = originalMethodRef . toString ( ) ;
348
+
349
+ // Check if this method calls .resolve() on our router instance
350
+ if (
351
+ methodSource . includes ( '.resolve(' ) ||
352
+ methodSource . includes ( '.resolve ' )
353
+ ) {
354
+ const patchedMethod = function ( this : unknown , ...args : unknown [ ] ) {
355
+ // Capture instance when any method that calls resolve is called
356
+ if ( this && typeof this === 'object' ) {
357
+ routerInstanceMap . set ( routerInstance , this ) ;
358
+ }
359
+ return originalMethodRef . apply ( this , args ) ;
360
+ } ;
361
+
362
+ Object . defineProperty ( proto , methodName , {
363
+ value : patchedMethod ,
364
+ writable : true ,
365
+ configurable : true ,
366
+ enumerable : true ,
367
+ } ) ;
368
+ }
369
+ }
370
+ }
371
+ }
372
+ }
284
373
}
285
374
286
375
export { Router } ;
0 commit comments