fix(extension): ws-client browser WebSocket API compat (.onopen/.onmessage)

This commit is contained in:
Variet Worker
2026-03-17 07:41:56 +09:00
parent 36b70505d7
commit b9b240de0b

View File

@@ -212,36 +212,68 @@ export class WSBridgeClient {
this.logFn(`[WS] Connecting to ${this.hubUrl}...`); this.logFn(`[WS] Connecting to ${this.hubUrl}...`);
const ws = new WebSocket(this.hubUrl); const ws = new WebSocket(this.hubUrl);
ws.on('open', () => { // Detect API style: Node.js 'ws' module has .on(), browser WebSocket doesn't
this.logFn('[WS] Connection opened, authenticating...'); const isNodeWs = typeof ws.on === 'function';
this.ws = ws;
this.connected = true;
this._authenticate();
});
ws.on('message', (raw: Buffer | string) => { if (isNodeWs) {
try { // ─── Node.js ws module (EventEmitter API) ───
const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString('utf-8')); ws.on('open', () => {
this._handleMessage(data); this.logFn('[WS] Connection opened, authenticating...');
} catch (e: any) { this.ws = ws;
this.logFn(`[WS] Parse error: ${e.message}`); this.connected = true;
} this._authenticate();
}); });
ws.on('close', (code: number, reason: Buffer) => { ws.on('message', (raw: Buffer | string) => {
const reasonStr = reason ? reason.toString('utf-8') : ''; try {
this.logFn(`[WS] Connection closed: code=${code} reason=${reasonStr}`); const data = JSON.parse(typeof raw === 'string' ? raw : raw.toString('utf-8'));
this._onDisconnect(); this._handleMessage(data);
}); } catch (e: any) {
this.logFn(`[WS] Parse error: ${e.message}`);
}
});
ws.on('error', (err: Error) => { ws.on('close', (code: number, reason: Buffer) => {
this.logFn(`[WS] Error: ${err.message}`); const reasonStr = reason ? reason.toString('utf-8') : '';
// close event will follow this.logFn(`[WS] Connection closed: code=${code} reason=${reasonStr}`);
}); this._onDisconnect();
});
ws.on('pong', () => { ws.on('error', (err: Error) => {
// Server responded to our ping — connection is alive this.logFn(`[WS] Error: ${err.message}`);
}); });
ws.on('pong', () => {
// Server responded to our ping — connection is alive
});
} else {
// ─── Browser-style WebSocket API (.onopen / .onmessage) ───
ws.onopen = () => {
this.logFn('[WS] Connection opened (browser API), authenticating...');
this.ws = ws;
this.connected = true;
this._authenticate();
};
ws.onmessage = (event: any) => {
try {
const raw = typeof event.data === 'string' ? event.data : event.data.toString();
const data = JSON.parse(raw);
this._handleMessage(data);
} catch (e: any) {
this.logFn(`[WS] Parse error: ${e.message}`);
}
};
ws.onclose = (event: any) => {
this.logFn(`[WS] Connection closed: code=${event.code} reason=${event.reason || ''}`);
this._onDisconnect();
};
ws.onerror = (event: any) => {
this.logFn(`[WS] Error: ${event.message || 'connection error'}`);
};
}
} catch (e: any) { } catch (e: any) {
this.logFn(`[WS] Connect failed: ${e.message}`); this.logFn(`[WS] Connect failed: ${e.message}`);
@@ -251,16 +283,16 @@ export class WSBridgeClient {
private async _getWebSocketClass(): Promise<any> { private async _getWebSocketClass(): Promise<any> {
try { try {
// Try Node.js built-in WebSocket (v21+) // Prefer require('ws') — Node.js EventEmitter API with .on()
if (typeof globalThis.WebSocket !== 'undefined') { // VS Code runs in Node.js, so this should be available
return globalThis.WebSocket;
}
// Try require('ws') — should be available in VS Code's Node.js
const ws = require('ws'); const ws = require('ws');
return ws; return ws;
} catch { } catch {
// ws module not available // ws module not available — try built-in WebSocket
try { try {
if (typeof globalThis.WebSocket !== 'undefined') {
return globalThis.WebSocket;
}
// Fallback: try the built-in undici WebSocket // Fallback: try the built-in undici WebSocket
const { WebSocket } = require('undici'); const { WebSocket } = require('undici');
return WebSocket; return WebSocket;
@@ -424,7 +456,13 @@ export class WSBridgeClient {
this.heartbeatTimer = setInterval(() => { this.heartbeatTimer = setInterval(() => {
if (this.ws && this.connected) { if (this.ws && this.connected) {
try { try {
this.ws.ping(); // Node.js ws has .ping(), browser WebSocket doesn't
if (typeof this.ws.ping === 'function') {
this.ws.ping();
} else {
// Fallback: send heartbeat as JSON message
this.ws.send(JSON.stringify({ type: 'heartbeat' }));
}
} catch { } catch {
// ping failure will trigger close event // ping failure will trigger close event
} }