55 ******************************************************************************/
66
77import type {
8- CancellationToken ,
8+ CancellationToken , Disposable ,
99 JsonAny , Message , MessageParticipant , MessengerAPI ,
1010 NotificationHandler , NotificationMessage , NotificationType ,
1111 RequestHandler , RequestMessage , RequestType , ResponseError , ResponseMessage
@@ -42,16 +42,103 @@ export class Messenger implements MessengerAPI {
4242 this . options = { ...defaultOptions , ...options } ;
4343 }
4444
45+ /**
46+ * Register a request handler.
47+ * @param type The request type.
48+ * @param handler The request handler.
49+ * @returns The Messenger instance for method chaining.
50+ *
51+ * @see {@link onRequestDisposable } - Alternative method that returns a Disposable for automatic cleanup
52+ * @see {@link unregisterHandler } - Manual method to unregister handlers by method name
53+ *
54+ * @example
55+ * ```typescript
56+ * // Define message types
57+ * const myRequest: RequestType<{ userId: string }, { name: string }> = { method: 'getUser' };
58+ * const myNotification: NotificationType<string> = { method: 'statusUpdate' };
59+ *
60+ * // Method chaining approach
61+ * messenger.onRequest(myRequest, handler).onNotification(myNotification, notifHandler);
62+ *
63+ * // Manual unregistration
64+ * messenger.onRequest(myRequest, handler);
65+ * // Later...
66+ * messenger.unregisterHandler(myRequest.method);
67+ *
68+ * // Or use the disposable variant for automatic cleanup
69+ * const disposable = messenger.onRequestDisposable(myRequest, handler);
70+ * disposable.dispose(); // Clean up when done
71+ * ```
72+ */
4573 onRequest < P , R > ( type : RequestType < P , R > , handler : RequestHandler < P , R > ) : Messenger {
4674 this . handlerRegistry . set ( type . method , handler as RequestHandler < unknown , unknown > ) ;
4775 return this ;
4876 }
4977
78+ /**
79+ * Register a request handler and return a Disposable that can be used to unregister it.
80+ * This is an alternative to onRequest() that returns a Disposable instead of the Messenger instance.
81+ */
82+ onRequestDisposable < P , R > ( type : RequestType < P , R > , handler : RequestHandler < P , R > ) : Disposable {
83+ this . handlerRegistry . set ( type . method , handler as RequestHandler < unknown , unknown > ) ;
84+
85+ return {
86+ dispose : ( ) => {
87+ this . unregisterHandler ( type . method ) ;
88+ }
89+ } ;
90+ }
91+
92+ /**
93+ * Register a notification handler.
94+ * @param type The notification type.
95+ * @param handler The notification handler.
96+ * @returns The Messenger instance for method chaining.
97+ *
98+ * @see {@link onNotificationDisposable } - Alternative method that returns a Disposable for automatic cleanup
99+ * @see {@link unregisterHandler } - Manual method to unregister handlers by method name
100+ *
101+ * @example
102+ * ```typescript
103+ * // Define message types
104+ * const myNotification: NotificationType<{ status: string }> = { method: 'statusChanged' };
105+ * const myRequest: RequestType<string, number> = { method: 'getCount' };
106+ *
107+ * // Method chaining approach
108+ * messenger.onNotification(myNotification, handler).onRequest(myRequest, reqHandler);
109+ *
110+ * // Manual unregistration
111+ * messenger.onNotification(myNotification, handler);
112+ * // Later...
113+ * messenger.unregisterHandler(myNotification.method);
114+ *
115+ * // Or use the disposable variant for automatic cleanup
116+ * const disposable = messenger.onNotificationDisposable(myNotification, handler);
117+ * disposable.dispose(); // Clean up when done
118+ * ```
119+ */
50120 onNotification < P > ( type : NotificationType < P > , handler : NotificationHandler < P > ) : Messenger {
51121 this . handlerRegistry . set ( type . method , handler as NotificationHandler < unknown > ) ;
52122 return this ;
53123 }
54124
125+ /**
126+ * Register a notification handler and return a Disposable that can be used to unregister it.
127+ * This is an alternative to onNotification() that returns a Disposable instead of the Messenger instance.
128+ */
129+ onNotificationDisposable < P > ( type : NotificationType < P > , handler : NotificationHandler < P > ) : Disposable {
130+ this . handlerRegistry . set ( type . method , handler as NotificationHandler < unknown > ) ;
131+
132+ return {
133+ dispose : ( ) => {
134+ this . unregisterHandler ( type . method ) ;
135+ }
136+ } ;
137+ }
138+
139+ /**
140+ * Start the message processing.
141+ */
55142 start ( ) : void {
56143 if ( this . started ) {
57144 return ;
@@ -65,6 +152,15 @@ export class Messenger implements MessengerAPI {
65152 this . started = true ;
66153 }
67154
155+ /**
156+ * Unregisters a handler by its method name.
157+ * @param method The method name of the handler to unregister. Use `<Type>.method` for type safety.
158+ * @returns True if the handler was successfully unregistered, false otherwise.
159+ */
160+ unregisterHandler ( method : string ) : boolean {
161+ return this . handlerRegistry . delete ( method ) ;
162+ }
163+
68164 protected async processMessage ( msg : Message ) : Promise < void > {
69165 if ( msg . receiver . type === 'extension' ) {
70166 // Ignore the message if it's not directed to us
@@ -166,6 +262,57 @@ export class Messenger implements MessengerAPI {
166262 }
167263 }
168264
265+ /**
266+ * Send a request message to another participant and wait for a response.
267+ *
268+ * @template P The type of the request parameters
269+ * @template R The type of the response data
270+ * @param type The request type definition containing the method name
271+ * @param receiver The target participant to send the request to (extension or specific webview)
272+ * @param params Optional parameters to send with the request
273+ * @param cancelable Optional cancellation token to cancel the request
274+ * @returns A Promise that resolves with the response data or rejects if the request fails
275+ *
276+ * @throws {Error } If the receiver is a broadcast participant (broadcasts are only allowed for notifications)
277+ *
278+ * @example
279+ * ```typescript
280+ * // Define a request type
281+ * const GetUserRequest: RequestType<{ userId: string }, { name: string, email: string }> = {
282+ * method: 'getUser'
283+ * };
284+ *
285+ * // Send a request to the host extension
286+ * const user = await messenger.sendRequest(
287+ * GetUserRequest,
288+ * HOST_EXTENSION,
289+ * { userId: '123' }
290+ * );
291+ * console.log(`User: ${user.name} (${user.email})`);
292+ *
293+ * // Send a request with cancellation support
294+ * const controller = new AbortController();
295+ * const cancelToken = createCancellationToken(controller.signal);
296+ *
297+ * try {
298+ * const result = await messenger.sendRequest(
299+ * GetUserRequest,
300+ * HOST_EXTENSION,
301+ * { userId: '456' },
302+ * cancelToken
303+ * );
304+ * } catch (error) {
305+ * if (controller.signal.aborted) {
306+ * console.log('Request was cancelled');
307+ * } else {
308+ * console.error('Request failed:', error);
309+ * }
310+ * }
311+ *
312+ * // Cancel the request after 5 seconds
313+ * setTimeout(() => controller.abort('Timeout'), 5000);
314+ * ```
315+ */
169316 sendRequest < P , R > ( type : RequestType < P , R > , receiver : MessageParticipant , params ?: P , cancelable ?: CancellationToken ) : Promise < R > {
170317 if ( receiver . type === 'broadcast' ) {
171318 throw new Error ( 'Only notification messages are allowed for broadcast.' ) ;
@@ -199,6 +346,50 @@ export class Messenger implements MessengerAPI {
199346 return pending . result ;
200347 }
201348
349+ /**
350+ * Send a notification message to another participant without expecting a response.
351+ *
352+ * Notifications are fire-and-forget messages that don't require acknowledgment or return values.
353+ * Unlike requests, notifications can be sent to broadcast receivers to notify all registered handlers.
354+ *
355+ * @template P The type of the notification parameters
356+ * @param type The notification type definition containing the method name
357+ * @param receiver The target participant to send the notification to (extension, webview, or broadcast)
358+ * @param params Optional parameters to send with the notification
359+ *
360+ * @example
361+ * ```typescript
362+ * // Define a notification type
363+ * const UserLoggedInNotification: NotificationType<{ userId: string, timestamp: number }> = {
364+ * method: 'userLoggedIn'
365+ * };
366+ *
367+ * // Send a notification to the host extension
368+ * messenger.sendNotification(
369+ * UserLoggedInNotification,
370+ * HOST_EXTENSION,
371+ * { userId: '123', timestamp: Date.now() }
372+ * );
373+ *
374+ * // Send a notification to a specific webview
375+ * messenger.sendNotification(
376+ * UserLoggedInNotification,
377+ * { type: 'webview', webviewType: 'dashboard' },
378+ * { userId: '123', timestamp: Date.now() }
379+ * );
380+ *
381+ * // Broadcast a notification to all registered handlers
382+ * messenger.sendNotification(
383+ * UserLoggedInNotification,
384+ * BROADCAST,
385+ * { userId: '123', timestamp: Date.now() }
386+ * );
387+ *
388+ * // Send a simple notification without parameters
389+ * const RefreshNotification: NotificationType<void> = { method: 'refresh' };
390+ * messenger.sendNotification(RefreshNotification, HOST_EXTENSION);
391+ * ```
392+ */
202393 sendNotification < P > ( type : NotificationType < P > , receiver : MessageParticipant , params ?: P ) : void {
203394 const message : NotificationMessage = {
204395 method : type . method ,
0 commit comments