vscode-extension-messaging-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +139 -0
  2. package/dist/extension/index.d.ts +3 -0
  3. package/dist/extension/index.d.ts.map +1 -0
  4. package/dist/extension/index.js +3 -0
  5. package/dist/extension/index.js.map +1 -0
  6. package/dist/extension/messenger.d.ts +72 -0
  7. package/dist/extension/messenger.d.ts.map +1 -0
  8. package/dist/extension/messenger.js +191 -0
  9. package/dist/extension/messenger.js.map +1 -0
  10. package/dist/extension/pending-requests.d.ts +44 -0
  11. package/dist/extension/pending-requests.d.ts.map +1 -0
  12. package/dist/extension/pending-requests.js +90 -0
  13. package/dist/extension/pending-requests.js.map +1 -0
  14. package/dist/shared/errors.d.ts +52 -0
  15. package/dist/shared/errors.d.ts.map +1 -0
  16. package/dist/shared/errors.js +84 -0
  17. package/dist/shared/errors.js.map +1 -0
  18. package/dist/shared/index.d.ts +7 -0
  19. package/dist/shared/index.d.ts.map +1 -0
  20. package/dist/shared/index.js +5 -0
  21. package/dist/shared/index.js.map +1 -0
  22. package/dist/shared/protocol.d.ts +48 -0
  23. package/dist/shared/protocol.d.ts.map +1 -0
  24. package/dist/shared/protocol.js +35 -0
  25. package/dist/shared/protocol.js.map +1 -0
  26. package/dist/shared/schema.d.ts +118 -0
  27. package/dist/shared/schema.d.ts.map +1 -0
  28. package/dist/shared/schema.js +5 -0
  29. package/dist/shared/schema.js.map +1 -0
  30. package/dist/webview/index.d.ts +3 -0
  31. package/dist/webview/index.d.ts.map +1 -0
  32. package/dist/webview/index.js +3 -0
  33. package/dist/webview/index.js.map +1 -0
  34. package/dist/webview/messenger.d.ts +88 -0
  35. package/dist/webview/messenger.d.ts.map +1 -0
  36. package/dist/webview/messenger.js +227 -0
  37. package/dist/webview/messenger.js.map +1 -0
  38. package/dist/webview/pending-requests.d.ts +46 -0
  39. package/dist/webview/pending-requests.d.ts.map +1 -0
  40. package/dist/webview/pending-requests.js +92 -0
  41. package/dist/webview/pending-requests.js.map +1 -0
  42. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # vscode-extension-messaging-framework
2
+
3
+ A TypeScript library that wraps VS Code's `postMessage` API with typed request/response semantics, timeout handling, and error propagation.
4
+
5
+ ## Why
6
+
7
+ VS Code webview communication via `postMessage` is untyped and fire-and-forget. There's no built-in way to send a request and wait for a response, handle timeouts, or propagate errors. You end up writing boilerplate to correlate messages, track pending requests, and serialize errors.
8
+
9
+ This library gives you:
10
+
11
+ - **Request/response** - send a request, `await` the result
12
+ - **Notifications** - fire-and-forget messages
13
+ - **Bidirectional** - both extension and webview can send requests to each other
14
+ - **Type safety** - define a schema once, get compile-time checks on method names, params, and results
15
+ - **Timeout handling** - configurable per-request or per-messenger, with automatic cleanup
16
+ - **Error propagation** - errors thrown in handlers are serialized, sent across the boundary, and re-thrown as `MessagingError` on the caller side
17
+
18
+ ## Install
19
+
20
+ ```
21
+ npm install vscode-extension-messaging-framework
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### 1. Define a schema
27
+
28
+ Create a shared schema that both your extension and webview code can import:
29
+
30
+ ```ts
31
+ import type { DefineRequest, DefineNotification, MessageSchema } from 'vscode-extension-messaging-framework/shared';
32
+
33
+ type GetUser = DefineRequest<'getUser', { id: string }, { name: string; email: string }>;
34
+ type SaveDraft = DefineRequest<'saveDraft', { content: string }, { saved: boolean }>;
35
+ type ThemeChanged = DefineNotification<'themeChanged', { theme: 'light' | 'dark' }>;
36
+
37
+ export interface MySchema extends MessageSchema {
38
+ webviewRequests: GetUser | SaveDraft; // webview -> extension
39
+ extensionRequests: never; // extension -> webview (none in this example)
40
+ webviewNotifications: never;
41
+ extensionNotifications: ThemeChanged;
42
+ }
43
+ ```
44
+
45
+ ### 2. Extension side
46
+
47
+ ```ts
48
+ import { ExtensionMessenger } from 'vscode-extension-messaging-framework/extension';
49
+ import type { MySchema } from './shared/schema';
50
+
51
+ const messenger = new ExtensionMessenger<MySchema>(panel.webview, {
52
+ defaultTimeout: 5000,
53
+ });
54
+
55
+ // Handle requests from the webview
56
+ messenger.onRequest('getUser', async ({ id }) => {
57
+ const user = await db.findUser(id);
58
+ return { name: user.name, email: user.email };
59
+ });
60
+
61
+ // Send a notification to the webview
62
+ messenger.notify('themeChanged', { theme: 'dark' });
63
+ ```
64
+
65
+ ### 3. Webview side
66
+
67
+ ```ts
68
+ import { WebviewMessenger } from 'vscode-extension-messaging-framework/webview';
69
+ import type { MySchema } from '../shared/schema';
70
+
71
+ const messenger = new WebviewMessenger<MySchema>();
72
+ messenger.start();
73
+
74
+ // Send a request to the extension
75
+ const user = await messenger.request('getUser', { id: '123' });
76
+ console.log(user.name);
77
+
78
+ // Listen for notifications
79
+ messenger.onNotification('themeChanged', ({ theme }) => {
80
+ document.body.className = theme;
81
+ });
82
+ ```
83
+
84
+ ### Error handling
85
+
86
+ Errors thrown in handlers are automatically propagated to the caller:
87
+
88
+ ```ts
89
+ import { MessagingError, ErrorCode } from 'vscode-extension-messaging-framework/shared';
90
+
91
+ // Extension side
92
+ messenger.onRequest('saveDraft', ({ content }) => {
93
+ if (!content) {
94
+ throw new MessagingError(ErrorCode.InvalidParams, 'Content cannot be empty');
95
+ }
96
+ return { saved: true };
97
+ });
98
+
99
+ // Webview side
100
+ try {
101
+ await messenger.request('saveDraft', { content: '' });
102
+ } catch (err) {
103
+ // err is a MessagingError with code, message, and optional data
104
+ }
105
+ ```
106
+
107
+ Requests that exceed the timeout throw a `MessagingError` with `ErrorCode.Timeout`. You can override the timeout per request:
108
+
109
+ ```ts
110
+ const result = await messenger.request('saveDraft', { content }, { timeout: 10000 });
111
+ ```
112
+
113
+ ### Cleanup
114
+
115
+ Both messengers implement `dispose()` which removes listeners and cancels pending requests:
116
+
117
+ ```ts
118
+ messenger.dispose();
119
+ ```
120
+
121
+ ## Demo
122
+
123
+ The `demo/` directory contains a working VS Code extension that exercises the main features of the library:
124
+
125
+ - **Request/response** - increment a counter by sending typed requests from the webview to the extension
126
+ - **Bidirectional requests** - the extension requests an input value from the webview
127
+ - **Timeout handling** - a slow operation that succeeds (2s) vs one that exceeds the 5s timeout
128
+ - **Error propagation** - a handler that intentionally throws, demonstrating error serialization across the boundary
129
+ - **Notifications** - real-time counter updates and log messages pushed from the extension to the webview
130
+
131
+ To run the demo:
132
+
133
+ ```
134
+ cd demo
135
+ npm install
136
+ npm run build
137
+ ```
138
+
139
+ Then press F5 in VS Code to launch the Extension Development Host, and run the **Messaging Demo: Show Panel** command.
@@ -0,0 +1,3 @@
1
+ export { ExtensionMessenger } from './messenger.js';
2
+ export { PendingRequestsManager } from './pending-requests.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extension/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ExtensionMessenger } from './messenger.js';
2
+ export { PendingRequestsManager } from './pending-requests.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extension/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,72 @@
1
+ import type { Webview, Disposable } from 'vscode';
2
+ import { type MessageSchema, type EmptySchema, type WebviewRequestMethods, type WebviewRequestParams, type WebviewRequestResult, type ExtensionRequestMethods, type ExtensionRequestParams, type ExtensionRequestResult, type WebviewNotificationMethods, type WebviewNotificationParams, type ExtensionNotificationMethods, type ExtensionNotificationParams, type RequestHandler, type NotificationHandler, type RequestOptions } from '../shared/schema.js';
3
+ /**
4
+ * Messenger for the VS Code extension host side.
5
+ * Handles communication with a webview via postMessage.
6
+ */
7
+ export declare class ExtensionMessenger<S extends MessageSchema = EmptySchema> implements Disposable {
8
+ private readonly webview;
9
+ private readonly pendingRequests;
10
+ private readonly requestHandlers;
11
+ private readonly notificationHandlers;
12
+ private readonly messageListener;
13
+ private disposed;
14
+ constructor(webview: Webview, options?: {
15
+ defaultTimeout?: number;
16
+ });
17
+ /**
18
+ * Sends a request to the webview and waits for a response.
19
+ * @param method - The request method name
20
+ * @param params - The request parameters
21
+ * @param options - Optional request options (timeout)
22
+ * @returns A promise that resolves with the response result
23
+ */
24
+ request<M extends ExtensionRequestMethods<S>>(method: M, params: ExtensionRequestParams<S, M & string>, options?: RequestOptions): Promise<ExtensionRequestResult<S, M & string>>;
25
+ /**
26
+ * Sends a notification to the webview (fire-and-forget).
27
+ * @param method - The notification method name
28
+ * @param params - The notification parameters
29
+ */
30
+ notify<M extends ExtensionNotificationMethods<S>>(method: M, params: ExtensionNotificationParams<S, M & string>): void;
31
+ /**
32
+ * Registers a handler for incoming requests from the webview.
33
+ * Only one handler can be registered per method.
34
+ * @param method - The request method to handle
35
+ * @param handler - The handler function
36
+ * @returns A disposable to unregister the handler
37
+ */
38
+ onRequest<M extends WebviewRequestMethods<S>>(method: M, handler: RequestHandler<WebviewRequestParams<S, M & string>, WebviewRequestResult<S, M & string>>): Disposable;
39
+ /**
40
+ * Registers a handler for incoming notifications from the webview.
41
+ * Multiple handlers can be registered per method.
42
+ * @param method - The notification method to listen for
43
+ * @param handler - The handler function
44
+ * @returns A disposable to unregister the handler
45
+ */
46
+ onNotification<M extends WebviewNotificationMethods<S>>(method: M, handler: NotificationHandler<WebviewNotificationParams<S, M & string>>): Disposable;
47
+ /**
48
+ * Handles an incoming message from the webview.
49
+ */
50
+ private handleMessage;
51
+ /**
52
+ * Handles an incoming request from the webview.
53
+ */
54
+ private handleRequest;
55
+ /**
56
+ * Handles an incoming response to a previous request.
57
+ */
58
+ private handleResponse;
59
+ /**
60
+ * Handles an incoming notification from the webview.
61
+ */
62
+ private handleNotification;
63
+ /**
64
+ * Sends a response back to the webview.
65
+ */
66
+ private sendResponse;
67
+ /**
68
+ * Disposes the messenger, cancelling all pending requests.
69
+ */
70
+ dispose(): void;
71
+ }
72
+ //# sourceMappingURL=messenger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger.d.ts","sourceRoot":"","sources":["../../src/extension/messenger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAelD,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAG7B;;;GAGG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,aAAa,GAAG,WAAW,CAAE,YAAW,UAAU;IAC1F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuD;IACvF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAwD;IAC7F,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAa;IAC7C,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE;IAUnE;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,uBAAuB,CAAC,CAAC,CAAC,EAC1C,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,sBAAsB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,EAC7C,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;IAyBjD;;;;OAIG;IACH,MAAM,CAAC,CAAC,SAAS,4BAA4B,CAAC,CAAC,CAAC,EAC9C,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,2BAA2B,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,GACjD,IAAI;IAcP;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,SAAS,qBAAqB,CAAC,CAAC,CAAC,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,cAAc,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,GAChG,UAAU;IAUb;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,0BAA0B,CAAC,CAAC,CAAC,EACpD,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,mBAAmB,CAAC,yBAAyB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,GACrE,UAAU;IAsBb;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;YACW,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;IACH,OAAO,IAAI,IAAI;CAWhB"}
@@ -0,0 +1,191 @@
1
+ import { isRequestMessage, isResponseMessage, isNotificationMessage, } from '../shared/protocol.js';
2
+ import { ErrorCode, MessagingError, serializeError, deserializeError, } from '../shared/errors.js';
3
+ import { DEFAULT_TIMEOUT, } from '../shared/schema.js';
4
+ import { PendingRequestsManager } from './pending-requests.js';
5
+ /**
6
+ * Messenger for the VS Code extension host side.
7
+ * Handles communication with a webview via postMessage.
8
+ */
9
+ export class ExtensionMessenger {
10
+ webview;
11
+ pendingRequests;
12
+ requestHandlers = new Map();
13
+ notificationHandlers = new Map();
14
+ messageListener;
15
+ disposed = false;
16
+ constructor(webview, options) {
17
+ this.webview = webview;
18
+ this.pendingRequests = new PendingRequestsManager(options?.defaultTimeout ?? DEFAULT_TIMEOUT);
19
+ // Listen for messages from the webview
20
+ this.messageListener = this.webview.onDidReceiveMessage((message) => {
21
+ this.handleMessage(message);
22
+ });
23
+ }
24
+ /**
25
+ * Sends a request to the webview and waits for a response.
26
+ * @param method - The request method name
27
+ * @param params - The request parameters
28
+ * @param options - Optional request options (timeout)
29
+ * @returns A promise that resolves with the response result
30
+ */
31
+ request(method, params, options) {
32
+ if (this.disposed) {
33
+ return Promise.reject(new MessagingError(ErrorCode.Cancelled, 'Messenger has been disposed'));
34
+ }
35
+ const id = crypto.randomUUID();
36
+ const message = {
37
+ type: 'request',
38
+ id,
39
+ method: method,
40
+ params,
41
+ };
42
+ const promise = this.pendingRequests.add(id, method, options?.timeout);
43
+ this.webview.postMessage(message);
44
+ return promise;
45
+ }
46
+ /**
47
+ * Sends a notification to the webview (fire-and-forget).
48
+ * @param method - The notification method name
49
+ * @param params - The notification parameters
50
+ */
51
+ notify(method, params) {
52
+ if (this.disposed) {
53
+ return;
54
+ }
55
+ const message = {
56
+ type: 'notification',
57
+ method: method,
58
+ params,
59
+ };
60
+ this.webview.postMessage(message);
61
+ }
62
+ /**
63
+ * Registers a handler for incoming requests from the webview.
64
+ * Only one handler can be registered per method.
65
+ * @param method - The request method to handle
66
+ * @param handler - The handler function
67
+ * @returns A disposable to unregister the handler
68
+ */
69
+ onRequest(method, handler) {
70
+ this.requestHandlers.set(method, handler);
71
+ return {
72
+ dispose: () => {
73
+ this.requestHandlers.delete(method);
74
+ },
75
+ };
76
+ }
77
+ /**
78
+ * Registers a handler for incoming notifications from the webview.
79
+ * Multiple handlers can be registered per method.
80
+ * @param method - The notification method to listen for
81
+ * @param handler - The handler function
82
+ * @returns A disposable to unregister the handler
83
+ */
84
+ onNotification(method, handler) {
85
+ let handlers = this.notificationHandlers.get(method);
86
+ if (!handlers) {
87
+ handlers = new Set();
88
+ this.notificationHandlers.set(method, handlers);
89
+ }
90
+ handlers.add(handler);
91
+ return {
92
+ dispose: () => {
93
+ const h = this.notificationHandlers.get(method);
94
+ if (h) {
95
+ h.delete(handler);
96
+ if (h.size === 0) {
97
+ this.notificationHandlers.delete(method);
98
+ }
99
+ }
100
+ },
101
+ };
102
+ }
103
+ /**
104
+ * Handles an incoming message from the webview.
105
+ */
106
+ handleMessage(message) {
107
+ if (isRequestMessage(message)) {
108
+ this.handleRequest(message);
109
+ }
110
+ else if (isResponseMessage(message)) {
111
+ this.handleResponse(message);
112
+ }
113
+ else if (isNotificationMessage(message)) {
114
+ this.handleNotification(message);
115
+ }
116
+ // Ignore unknown message types
117
+ }
118
+ /**
119
+ * Handles an incoming request from the webview.
120
+ */
121
+ async handleRequest(message) {
122
+ const handler = this.requestHandlers.get(message.method);
123
+ if (!handler) {
124
+ this.sendResponse(message.id, undefined, {
125
+ code: ErrorCode.MethodNotFound,
126
+ message: `No handler registered for method '${message.method}'`,
127
+ });
128
+ return;
129
+ }
130
+ try {
131
+ const result = await handler(message.params);
132
+ this.sendResponse(message.id, result);
133
+ }
134
+ catch (error) {
135
+ this.sendResponse(message.id, undefined, serializeError(error));
136
+ }
137
+ }
138
+ /**
139
+ * Handles an incoming response to a previous request.
140
+ */
141
+ handleResponse(message) {
142
+ if (message.error) {
143
+ const error = deserializeError(message.error);
144
+ this.pendingRequests.reject(message.id, error);
145
+ }
146
+ else {
147
+ this.pendingRequests.resolve(message.id, message.result);
148
+ }
149
+ }
150
+ /**
151
+ * Handles an incoming notification from the webview.
152
+ */
153
+ handleNotification(message) {
154
+ const handlers = this.notificationHandlers.get(message.method);
155
+ if (handlers) {
156
+ for (const handler of handlers) {
157
+ try {
158
+ handler(message.params);
159
+ }
160
+ catch {
161
+ // Notification handlers should not throw, but we silently ignore if they do
162
+ }
163
+ }
164
+ }
165
+ }
166
+ /**
167
+ * Sends a response back to the webview.
168
+ */
169
+ sendResponse(id, result, error) {
170
+ const response = {
171
+ type: 'response',
172
+ id,
173
+ ...(error ? { error } : { result }),
174
+ };
175
+ this.webview.postMessage(response);
176
+ }
177
+ /**
178
+ * Disposes the messenger, cancelling all pending requests.
179
+ */
180
+ dispose() {
181
+ if (this.disposed) {
182
+ return;
183
+ }
184
+ this.disposed = true;
185
+ this.messageListener.dispose();
186
+ this.pendingRequests.cancelAll('Messenger disposed');
187
+ this.requestHandlers.clear();
188
+ this.notificationHandlers.clear();
189
+ }
190
+ }
191
+ //# sourceMappingURL=messenger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger.js","sourceRoot":"","sources":["../../src/extension/messenger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Manages pending requests with timeout handling.
3
+ */
4
+ export declare class PendingRequestsManager {
5
+ private readonly pending;
6
+ private readonly defaultTimeout;
7
+ constructor(defaultTimeout: number);
8
+ /**
9
+ * Adds a new pending request.
10
+ * @param id - Unique request ID
11
+ * @param method - The method name for error messages
12
+ * @param timeout - Timeout in milliseconds (optional, uses default if not specified)
13
+ * @returns A promise that resolves with the result or rejects on timeout/error
14
+ */
15
+ add<T>(id: string, method: string, timeout?: number): Promise<T>;
16
+ /**
17
+ * Resolves a pending request with a result.
18
+ * @param id - The request ID
19
+ * @param result - The result value
20
+ * @returns true if the request was found and resolved, false otherwise
21
+ */
22
+ resolve(id: string, result: unknown): boolean;
23
+ /**
24
+ * Rejects a pending request with an error.
25
+ * @param id - The request ID
26
+ * @param error - The error to reject with
27
+ * @returns true if the request was found and rejected, false otherwise
28
+ */
29
+ reject(id: string, error: Error): boolean;
30
+ /**
31
+ * Cancels all pending requests with a cancellation error.
32
+ * @param reason - Optional reason for cancellation
33
+ */
34
+ cancelAll(reason?: string): void;
35
+ /**
36
+ * Returns the number of pending requests.
37
+ */
38
+ get size(): number;
39
+ /**
40
+ * Checks if a request ID is pending.
41
+ */
42
+ has(id: string): boolean;
43
+ }
44
+ //# sourceMappingURL=pending-requests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-requests.d.ts","sourceRoot":"","sources":["../../src/extension/pending-requests.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,cAAc,EAAE,MAAM;IAIlC;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBhE;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAY7C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IAYzC;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAchC;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAGzB"}
@@ -0,0 +1,90 @@
1
+ import { ErrorCode, MessagingError } from '../shared/errors.js';
2
+ /**
3
+ * Manages pending requests with timeout handling.
4
+ */
5
+ export class PendingRequestsManager {
6
+ pending = new Map();
7
+ defaultTimeout;
8
+ constructor(defaultTimeout) {
9
+ this.defaultTimeout = defaultTimeout;
10
+ }
11
+ /**
12
+ * Adds a new pending request.
13
+ * @param id - Unique request ID
14
+ * @param method - The method name for error messages
15
+ * @param timeout - Timeout in milliseconds (optional, uses default if not specified)
16
+ * @returns A promise that resolves with the result or rejects on timeout/error
17
+ */
18
+ add(id, method, timeout) {
19
+ return new Promise((resolve, reject) => {
20
+ const timeoutMs = timeout ?? this.defaultTimeout;
21
+ const timeoutId = setTimeout(() => {
22
+ this.pending.delete(id);
23
+ reject(new MessagingError(ErrorCode.Timeout, `Request '${method}' timed out after ${timeoutMs}ms`));
24
+ }, timeoutMs);
25
+ this.pending.set(id, {
26
+ resolve: resolve,
27
+ reject,
28
+ timeoutId,
29
+ method,
30
+ });
31
+ });
32
+ }
33
+ /**
34
+ * Resolves a pending request with a result.
35
+ * @param id - The request ID
36
+ * @param result - The result value
37
+ * @returns true if the request was found and resolved, false otherwise
38
+ */
39
+ resolve(id, result) {
40
+ const pending = this.pending.get(id);
41
+ if (!pending) {
42
+ return false;
43
+ }
44
+ clearTimeout(pending.timeoutId);
45
+ this.pending.delete(id);
46
+ pending.resolve(result);
47
+ return true;
48
+ }
49
+ /**
50
+ * Rejects a pending request with an error.
51
+ * @param id - The request ID
52
+ * @param error - The error to reject with
53
+ * @returns true if the request was found and rejected, false otherwise
54
+ */
55
+ reject(id, error) {
56
+ const pending = this.pending.get(id);
57
+ if (!pending) {
58
+ return false;
59
+ }
60
+ clearTimeout(pending.timeoutId);
61
+ this.pending.delete(id);
62
+ pending.reject(error);
63
+ return true;
64
+ }
65
+ /**
66
+ * Cancels all pending requests with a cancellation error.
67
+ * @param reason - Optional reason for cancellation
68
+ */
69
+ cancelAll(reason) {
70
+ const error = new MessagingError(ErrorCode.Cancelled, reason ?? 'All pending requests cancelled');
71
+ for (const [id, pending] of this.pending) {
72
+ clearTimeout(pending.timeoutId);
73
+ pending.reject(error);
74
+ }
75
+ this.pending.clear();
76
+ }
77
+ /**
78
+ * Returns the number of pending requests.
79
+ */
80
+ get size() {
81
+ return this.pending.size;
82
+ }
83
+ /**
84
+ * Checks if a request ID is pending.
85
+ */
86
+ has(id) {
87
+ return this.pending.has(id);
88
+ }
89
+ }
90
+ //# sourceMappingURL=pending-requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-requests.js","sourceRoot":"","sources":["../../src/extension/pending-requests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAYhE;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAChB,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC5C,cAAc,CAAS;IAExC,YAAY,cAAsB;QAChC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,GAAG,CAAI,EAAU,EAAE,MAAc,EAAE,OAAgB;QACjD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,SAAS,GAAG,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;YAEjD,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,MAAM,CACJ,IAAI,cAAc,CAChB,SAAS,CAAC,OAAO,EACjB,YAAY,MAAM,qBAAqB,SAAS,IAAI,CACrD,CACF,CAAC;YACJ,CAAC,EAAE,SAAS,CAAC,CAAC;YAEd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE;gBACnB,OAAO,EAAE,OAAmC;gBAC5C,MAAM;gBACN,SAAS;gBACT,MAAM;aACP,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,EAAU,EAAE,MAAe;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QAED,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,EAAU,EAAE,KAAY;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QAED,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,MAAe;QACvB,MAAM,KAAK,GAAG,IAAI,cAAc,CAC9B,SAAS,CAAC,SAAS,EACnB,MAAM,IAAI,gCAAgC,CAC3C,CAAC;QAEF,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Standard error codes for messaging errors.
3
+ */
4
+ export declare enum ErrorCode {
5
+ /** The request timed out waiting for a response */
6
+ Timeout = "TIMEOUT",
7
+ /** No handler registered for the requested method */
8
+ MethodNotFound = "METHOD_NOT_FOUND",
9
+ /** The handler threw an error */
10
+ HandlerError = "HANDLER_ERROR",
11
+ /** The request was cancelled */
12
+ Cancelled = "CANCELLED",
13
+ /** Invalid request parameters */
14
+ InvalidParams = "INVALID_PARAMS",
15
+ /** Internal error */
16
+ InternalError = "INTERNAL_ERROR"
17
+ }
18
+ /**
19
+ * Serialized error format for transmission over postMessage.
20
+ */
21
+ export interface SerializedError {
22
+ code: string;
23
+ message: string;
24
+ data?: unknown;
25
+ stack?: string;
26
+ }
27
+ /**
28
+ * Error class for messaging errors with error codes.
29
+ */
30
+ export declare class MessagingError extends Error {
31
+ readonly code: string;
32
+ readonly data?: unknown;
33
+ constructor(code: string, message: string, data?: unknown);
34
+ /**
35
+ * Creates a MessagingError from a SerializedError.
36
+ */
37
+ static fromSerialized(serialized: SerializedError): MessagingError;
38
+ /**
39
+ * Serializes this error for transmission.
40
+ */
41
+ toSerialized(): SerializedError;
42
+ }
43
+ /**
44
+ * Serializes an error for transmission over postMessage.
45
+ * Handles MessagingError specially, converting other errors to HANDLER_ERROR.
46
+ */
47
+ export declare function serializeError(error: unknown): SerializedError;
48
+ /**
49
+ * Deserializes an error from transmission.
50
+ */
51
+ export declare function deserializeError(serialized: SerializedError): MessagingError;
52
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,SAAS;IACnB,mDAAmD;IACnD,OAAO,YAAY;IACnB,qDAAqD;IACrD,cAAc,qBAAqB;IACnC,iCAAiC;IACjC,YAAY,kBAAkB;IAC9B,gCAAgC;IAChC,SAAS,cAAc;IACvB,iCAAiC;IACjC,aAAa,mBAAmB;IAChC,qBAAqB;IACrB,aAAa,mBAAmB;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,IAAI,CAAC,EAAE,OAAO,CAAC;gBAEnB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAYzD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,eAAe,GAAG,cAAc;IAQlE;;OAEG;IACH,YAAY,IAAI,eAAe;CAGhC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAuB9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,eAAe,GAAG,cAAc,CAE5E"}