fix(vscode): prevent early SSE replay loss
This commit is contained in:
@@ -21,7 +21,7 @@ describe('VS Code webview bridge requests', () => {
|
||||
}),
|
||||
});
|
||||
|
||||
const { sendBridgeMessageWithOptions } = await import('./bridge');
|
||||
const { sendBridgeMessageWithOptions, startSseProxy } = await import('./bridge');
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
|
||||
@@ -36,6 +36,19 @@ describe('VS Code webview bridge requests', () => {
|
||||
assert.ok(result instanceof DOMException);
|
||||
assert.equal(result.name, 'AbortError');
|
||||
assert.equal(messages.length, 0);
|
||||
|
||||
const startPromise = startSseProxy({ path: '/global/event', streamId: 'sse_webview_1_1' });
|
||||
const request = messages[0] as { id: string; payload?: { streamId?: string } };
|
||||
assert.equal(request.payload?.streamId, 'sse_webview_1_1');
|
||||
globalThis.window.dispatchEvent(new MessageEvent('message', {
|
||||
data: {
|
||||
id: request.id,
|
||||
type: 'api:sse:start',
|
||||
success: true,
|
||||
data: { status: 200, headers: {}, streamId: 'sse_webview_1_1' },
|
||||
},
|
||||
}));
|
||||
assert.equal((await startPromise).streamId, 'sse_webview_1_1');
|
||||
} finally {
|
||||
Object.defineProperty(globalThis, 'window', { configurable: true, value: originalWindow });
|
||||
Object.defineProperty(globalThis, 'acquireVsCodeApi', { configurable: true, value: originalAcquire });
|
||||
|
||||
@@ -189,6 +189,7 @@ export type ProxiedSseStartResponse = {
|
||||
export async function startSseProxy(options: {
|
||||
path: string;
|
||||
headers?: Record<string, string>;
|
||||
streamId?: string;
|
||||
}): Promise<ProxiedSseStartResponse> {
|
||||
return sendBridgeMessage<ProxiedSseStartResponse>('api:sse:start', options);
|
||||
}
|
||||
|
||||
@@ -1122,6 +1122,7 @@ const handleLocalApiRequest = async (input: RequestInfo | URL, url: URL, init: R
|
||||
};
|
||||
|
||||
const originalFetch = window.fetch.bind(window);
|
||||
let sseStreamCounter = 0;
|
||||
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const targetUrl = typeof input === 'string' || input instanceof URL ? normalizeUrl(input) : normalizeUrl((input as Request).url);
|
||||
const method = (init?.method || (input instanceof Request ? input.method : 'GET')).toUpperCase();
|
||||
@@ -1160,12 +1161,10 @@ window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const headers = { ...headersFromRequest, ...headersFromInit };
|
||||
|
||||
if (isSseApiPath(targetUrl.pathname)) {
|
||||
const start = await vscodeStreamPerfMeasure('vscode.webview.sse_start_ms', () => startSseProxy({ path: suffixPath, headers }));
|
||||
if (!start.streamId) {
|
||||
return new Response(null, { status: start.status || 503, headers: start.headers || {} });
|
||||
}
|
||||
|
||||
const streamId = start.streamId;
|
||||
// Install the listener before the extension opens the upstream stream. A
|
||||
// reconnect can replay an event immediately, before the start response
|
||||
// has crossed the VS Code bridge.
|
||||
const streamId = `sse_webview_${Date.now()}_${++sseStreamCounter}`;
|
||||
const signal = (input instanceof Request ? input.signal : init?.signal) as AbortSignal | undefined;
|
||||
const encoder = new TextEncoder();
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
@@ -1224,6 +1223,18 @@ window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
},
|
||||
});
|
||||
|
||||
let start;
|
||||
try {
|
||||
start = await vscodeStreamPerfMeasure('vscode.webview.sse_start_ms', () => startSseProxy({ path: suffixPath, headers, streamId }));
|
||||
} catch (error) {
|
||||
await stream.cancel();
|
||||
throw error;
|
||||
}
|
||||
if (!start.streamId) {
|
||||
void stream.cancel();
|
||||
return new Response(null, { status: start.status || 503, headers: start.headers || {} });
|
||||
}
|
||||
|
||||
return new Response(stream, { status: start.status || 200, headers: start.headers || { 'content-type': 'text/event-stream' } });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user