refactor(phase-10): complete type safety enhancement

- Add types/api-responses.ts with ApiResponse<T>, PaginatedResponse<T>
- Add types/errors.ts with comprehensive error type hierarchy
- Replace all any usage (53 → 0, 100% reduction)
- Add RawAPI response interfaces for type-safe mapping
- Update catch blocks to use unknown with type narrowing
- Add getState mock to chatStore tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-15 19:52:52 +08:00
parent a6b1255dc0
commit 6a66ce159d
9 changed files with 1467 additions and 92 deletions

View File

@@ -54,21 +54,27 @@ export interface GatewayRequest {
type: 'req';
id: string;
method: string;
params?: Record<string, any>;
params?: Record<string, unknown>;
}
export interface GatewayError {
code?: string;
message?: string;
details?: unknown;
}
export interface GatewayResponse {
type: 'res';
id: string;
ok: boolean;
payload?: any;
error?: any;
payload?: unknown;
error?: GatewayError;
}
export interface GatewayEvent {
type: 'event';
event: string;
payload?: any;
payload?: unknown;
seq?: number;
}
@@ -102,9 +108,30 @@ export interface AgentStreamDelta {
workflowResult?: unknown;
}
/** OpenFang WebSocket stream event types */
export interface OpenFangStreamEvent {
type: 'text_delta' | 'phase' | 'response' | 'typing' | 'tool_call' | 'tool_result' | 'hand' | 'workflow' | 'error';
content?: string;
phase?: 'streaming' | 'done';
state?: 'start' | 'stop';
tool?: string;
input?: unknown;
output?: string;
result?: unknown;
hand_name?: string;
hand_status?: string;
hand_result?: unknown;
workflow_id?: string;
workflow_step?: string;
workflow_status?: string;
workflow_result?: unknown;
message?: string;
code?: string;
}
export type ConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'connected' | 'reconnecting';
type EventCallback = (payload: any) => void;
type EventCallback = (payload: unknown) => void;
export function getStoredGatewayUrl(): string {
try {
@@ -329,8 +356,8 @@ export class GatewayClient {
private state: ConnectionState = 'disconnected';
private requestId = 0;
private pendingRequests = new Map<string, {
resolve: (value: any) => void;
reject: (reason: any) => void;
resolve: (value: unknown) => void;
reject: (reason: unknown) => void;
timer: number;
}>();
private eventListeners = new Map<string, Set<EventCallback>>();
@@ -417,9 +444,10 @@ export class GatewayClient {
} else {
throw new Error('Health check failed');
}
} catch (err: any) {
} catch (err: unknown) {
this.setState('disconnected');
throw new Error(`Failed to connect to OpenFang: ${err.message}`);
const errorMessage = err instanceof Error ? err.message : String(err);
throw new Error(`Failed to connect to OpenFang: ${errorMessage}`);
}
}
@@ -471,8 +499,9 @@ export class GatewayClient {
clearTimeout(handshakeTimer);
settleReject(error);
});
} catch (err: any) {
this.log('error', `Parse error: ${err.message}`);
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err);
this.log('error', `Parse error: ${errorMessage}`);
}
};
@@ -519,7 +548,7 @@ export class GatewayClient {
// === Request/Response ===
async request(method: string, params?: Record<string, any>): Promise<any> {
async request(method: string, params?: Record<string, unknown>): Promise<unknown> {
if (this.state !== 'connected') {
throw new Error(`Not connected (state: ${this.state})`);
}
@@ -650,8 +679,9 @@ export class GatewayClient {
try {
const data = JSON.parse(event.data);
this.handleOpenFangStreamEvent(runId, data, sessionId);
} catch (err: any) {
this.log('error', `Failed to parse stream event: ${err.message}`);
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err);
this.log('error', `Failed to parse stream event: ${errorMessage}`);
}
};
@@ -673,18 +703,19 @@ export class GatewayClient {
this.streamCallbacks.delete(runId);
this.openfangWs = null;
};
} catch (err: any) {
this.log('error', `Failed to create WebSocket: ${err.message}`);
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err);
this.log('error', `Failed to create WebSocket: ${errorMessage}`);
const callbacks = this.streamCallbacks.get(runId);
if (callbacks) {
callbacks.onError(err.message);
callbacks.onError(errorMessage);
this.streamCallbacks.delete(runId);
}
}
}
/** Handle OpenFang stream events */
private handleOpenFangStreamEvent(runId: string, data: any, sessionId: string): void {
private handleOpenFangStreamEvent(runId: string, data: OpenFangStreamEvent, sessionId: string): void {
const callbacks = this.streamCallbacks.get(runId);
if (!callbacks) return;
@@ -736,18 +767,18 @@ export class GatewayClient {
case 'tool_result':
if (callbacks.onTool && data.tool) {
callbacks.onTool(data.tool, '', data.result || data.output || '');
callbacks.onTool(data.tool, '', String(data.result || data.output || ''));
}
break;
case 'hand':
if (callbacks.onHand && data.hand_name) {
callbacks.onHand(data.hand_name, data.status || 'triggered', data.result);
callbacks.onHand(data.hand_name, data.hand_status || 'triggered', data.hand_result);
}
break;
case 'error':
callbacks.onError(data.message || data.error || data.content || 'Unknown error');
callbacks.onError(data.message || data.code || data.content || 'Unknown error');
this.streamCallbacks.delete(runId);
if (this.openfangWs) {
this.openfangWs.close(1011, 'Error');
@@ -1487,7 +1518,7 @@ export class GatewayClient {
};
this.send(connectReq);
} catch (err: any) {
} catch (err: unknown) {
const error = err instanceof Error ? err : new Error(String(err));
this.log('error', error.message);
this.cleanup();
@@ -1514,7 +1545,7 @@ export class GatewayClient {
}
}
private emitEvent(event: string, payload: any) {
private emitEvent(event: string, payload: unknown) {
const listeners = this.eventListeners.get(event);
if (listeners) {
for (const cb of listeners) {