mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2025-12-24 00:34:21 +01:00
111 lines
3.2 KiB
TypeScript
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(),
|
|
};
|
|
}
|