Compare commits
2 Commits
36b70505d7
...
6ea3211a58
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ea3211a58 | ||
|
|
b9b240de0b |
@@ -3,3 +3,12 @@
|
|||||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||||
|---|------|------|------|------|
|
|---|------|------|------|------|
|
||||||
| 009 | 00:00~06:38 | Extension 모듈 분리 + Hub 통합 테스트 + VSIX v0.4.0 빌드 | `5f795b9` | ✅ |
|
| 009 | 00:00~06:38 | Extension 모듈 분리 + Hub 통합 테스트 + VSIX v0.4.0 빌드 | `5f795b9` | ✅ |
|
||||||
|
| 010 | 06:50~07:39 | 문서 전면 재작성 + 서버 배포 + WS 호환 수정 | `b9b240d` | 🔧 |
|
||||||
|
|
||||||
|
### #010 상세
|
||||||
|
- **문서**: architecture.md(250줄), tech-stack.md(100줄), conventions.md(100줄) 전면 재작성 + Wiki 동기화
|
||||||
|
- **태스크 정리**: #296 폐기, #396~#400 신규 5건 등록
|
||||||
|
- **서버 배포**: docker-compose.yml 서버 실제 구성 반영, Caddyfile 제거, ag.variet.net 도메인 확인
|
||||||
|
- **WS 호환**: ws-client.ts 브라우저 WebSocket API 호환 (.onopen/.onmessage) 수정
|
||||||
|
- **Known issue**: VS Code 캐시로 Extension 코드 반영 지연 — 완전 재시작 필요
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,11 @@ 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);
|
||||||
|
|
||||||
|
// Detect API style: Node.js 'ws' module has .on(), browser WebSocket doesn't
|
||||||
|
const isNodeWs = typeof ws.on === 'function';
|
||||||
|
|
||||||
|
if (isNodeWs) {
|
||||||
|
// ─── Node.js ws module (EventEmitter API) ───
|
||||||
ws.on('open', () => {
|
ws.on('open', () => {
|
||||||
this.logFn('[WS] Connection opened, authenticating...');
|
this.logFn('[WS] Connection opened, authenticating...');
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
@@ -236,12 +241,39 @@ export class WSBridgeClient {
|
|||||||
|
|
||||||
ws.on('error', (err: Error) => {
|
ws.on('error', (err: Error) => {
|
||||||
this.logFn(`[WS] Error: ${err.message}`);
|
this.logFn(`[WS] Error: ${err.message}`);
|
||||||
// close event will follow
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('pong', () => {
|
ws.on('pong', () => {
|
||||||
// Server responded to our ping — connection is alive
|
// 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 {
|
||||||
|
// Node.js ws has .ping(), browser WebSocket doesn't
|
||||||
|
if (typeof this.ws.ping === 'function') {
|
||||||
this.ws.ping();
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user