feat(web): define ChatEvent discriminated union, eliminate all any sites (R9)

Created web/src/lib/types.ts with discriminated unions for SSE event
payloads: ChatEvent (7 variants: session, tool_use, tool_result,
text_delta, text, done, error), ToolCallResult, MessageContent, and
typed data shapes for live events (PlanProposedData, PlanStepEventData,
QuestionRaisedData, QuestionAnsweredData, EntityTouchedData,
HealthChangedData) plus WailsGlobal for the desktop bridge.

Replaced all ~15 `any` sites across 7 files:
- api.ts: Message.content any -> MessageContent | string; removed local
  ChatEvent interface (now imported from types.ts as a discriminated
  union); JSON.parse cast to ChatEvent.
- stores/chat.ts: removed local ToolCallResult interface (imported from
  types.ts, re-exported for backward compat); extractApprovals accesses
  args with typeof guards instead of implicit any access; toChatMessages
  handles string|object Message.content cleanly.
- stores/activity.ts: update_plan_step seq/status extracted via typeof
  guards instead of `as any` casts; toolActivityLabel uses a str() helper
  for safe string extraction from unknown args.
- stores/workspace.ts: applyPlanStepEvent takes PlanStepEventData;
  applyEvent casts data to Record<string, unknown>; switch cases cast to
  typed interfaces (PlanProposedData, QuestionRaisedData, etc.) instead
  of `as any`; applyHealthChanged uses HealthChangedData.
- Config.svelte: (window as any).wails -> typed WailsGlobal cast;
  catch (e: any) -> catch (e: unknown) with instanceof Error check.
- utils.ts: WithoutChild/WithoutChildren `any` -> `unknown`.
- vite.config.ts: authProxy proxy/proxyReq `any` -> ProxyOptions type.

Result: eslint no-explicit-any warnings dropped 12 -> 0. Tests (6/6) and
build pass. VERSION 0.7.10 -> 0.7.11. Plan R9 marked done.
This commit is contained in:
2026-07-17 23:08:35 +02:00
parent 7dc1c1ae39
commit 6806fac5fd
10 changed files with 184 additions and 67 deletions

View File

@@ -7,6 +7,7 @@
import { fetchWithAuth, setConfig, initConfig, getConfig, clearConfig } from '$lib/config'
import { startLogin, logout as oidcLogout, getUser, isOIDCConfigured } from '$lib/oidc'
import ConfigBackground from '$lib/components/ConfigBackground.svelte'
import type { WailsGlobal } from '$lib/types'
let { onConnected, onCancel }: { onConnected: () => void; onCancel?: () => void } = $props()
@@ -54,7 +55,7 @@
}
function saveToDesktop() {
const wails = (window as any).wails
const wails = (window as unknown as { wails?: WailsGlobal }).wails
if (!wails?.Call?.ByName) return
try {
wails.Call.ByName('SaveConfig', apiUrl.trim(), token.trim())
@@ -71,8 +72,8 @@
try {
await startLogin()
} catch (e: any) {
error = e.message || 'OIDC login failed'
} catch (e: unknown) {
error = e instanceof Error ? e.message : 'OIDC login failed'
oidcLoggingIn = false
}
}