feat(errors): Enhance error handling with improved display, logging, and user experience utilities

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-02 21:44:54 -07:00
parent c0c7d0d594
commit 5ab4347b39

View file

@ -0,0 +1,63 @@
import type { MutableRefObject } from 'react';
const SESSION_STORAGE_KEY = 'companion_session_id';
/** Module-level lock to prevent concurrent recovery attempts. */
let recoveryPromise: Promise<string> | null = null;
export async function createSession(apiBaseUrl: string): Promise<string> {
try {
const res = await fetch(`${apiBaseUrl}/session`, { method: 'POST' });
if (!res.ok) throw new Error(`POST /session failed: ${res.status}`);
const { session_id } = (await res.json()) as { session_id: string };
sessionStorage.setItem(SESSION_STORAGE_KEY, session_id);
return session_id;
} catch (err) {
const message = err instanceof Error ? err.message : 'Unknown session creation error';
throw new Error(`Session creation failed: ${message}`);
}
}
/**
* POST /chat with automatic session recovery on 404.
*
* If the server returns 404 (session expired/missing), creates a new session
* and retries the request once. Updates `sessionIdRef.current` and sessionStorage
* so subsequent calls use the recovered session.
*/
export async function chatWithRecovery(
apiBaseUrl: string,
sessionIdRef: MutableRefObject<string>,
message: string,
signal: AbortSignal,
): Promise<Response> {
const doFetch = (sid: string) =>
fetch(`${apiBaseUrl}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: sid, message }),
signal,
});
try {
const response = await doFetch(sessionIdRef.current);
if (response.status !== 404) return response;
// Session expired — recover with dedup lock
if (!recoveryPromise) {
recoveryPromise = createSession(apiBaseUrl).finally(() => {
recoveryPromise = null;
});
}
const newSessionId = await recoveryPromise;
sessionIdRef.current = newSessionId;
return doFetch(newSessionId);
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') throw err;
const message_ = err instanceof Error ? err.message : 'Unknown chat error';
throw new Error(`Chat request failed: ${message_}`);
}
}