Files
claude-code-viewer/src/lib/sse/sseClient.ts

111 lines
3.2 KiB
TypeScript

import type {
ProjectChangedData,
SessionChangedData,
} from "../../server/service/events/types";
export interface SSEEventHandlers {
onProjectChanged?: (data: ProjectChangedData) => void;
onSessionChanged?: (data: SessionChangedData) => void;
onConnected?: () => void;
onHeartbeat?: (timestamp: string) => void;
onError?: (error: Event) => void;
onClose?: () => void;
}
export class SSEClient {
private eventSource: EventSource | null = null;
private handlers: SSEEventHandlers;
private url: string;
constructor(baseUrl: string = "", handlers: SSEEventHandlers = {}) {
this.url = `${baseUrl}/api/events`;
this.handlers = handlers;
}
public connect(): void {
if (this.eventSource) {
this.disconnect();
}
try {
this.eventSource = new EventSource(this.url);
// 接続確認イベント
this.eventSource.addEventListener("connected", (event) => {
console.log("SSE Connected:", event.data);
this.handlers.onConnected?.();
});
// プロジェクト変更イベント
this.eventSource.addEventListener("project_changed", (event) => {
try {
const data: ProjectChangedData = JSON.parse(event.data);
console.log("Project changed:", data);
this.handlers.onProjectChanged?.(data);
} catch (error) {
console.error("Failed to parse project_changed event:", error);
}
});
// セッション変更イベント
this.eventSource.addEventListener("session_changed", (event) => {
try {
const data: SessionChangedData = JSON.parse(event.data);
console.log("Session changed:", data);
this.handlers.onSessionChanged?.(data);
} catch (error) {
console.error("Failed to parse session_changed event:", error);
}
});
// ハートビートイベント
this.eventSource.addEventListener("heartbeat", (event) => {
try {
const data = JSON.parse(event.data);
this.handlers.onHeartbeat?.(data.timestamp);
} catch (error) {
console.error("Failed to parse heartbeat event:", error);
}
});
// エラーハンドリング
this.eventSource.onerror = (error) => {
console.error("SSE Error:", error);
this.handlers.onError?.(error);
};
// 接続終了
this.eventSource.onopen = () => {
console.log("SSE Connection opened");
};
} catch (error) {
console.error("Failed to establish SSE connection:", error);
this.handlers.onError?.(error as Event);
}
}
public disconnect(): void {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
console.log("SSE Connection closed");
this.handlers.onClose?.();
}
}
public isConnected(): boolean {
return this.eventSource?.readyState === EventSource.OPEN;
}
}
// React Hook example
export function useSSE(handlers: SSEEventHandlers) {
const client = new SSEClient(window?.location?.origin, handlers);
return {
connect: () => client.connect(),
disconnect: () => client.disconnect(),
isConnected: () => client.isConnected(),
};
}