mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-15 03:51:22 +02:00
Show a clear reconnecting state during server restarts and retry safe access checks every five seconds. Preserve open drafts, wait for initial startup readiness, and keep authentication failures separate from temporary outages. Co-Authored-By: Paperclip <noreply@paperclip.ing>
30 lines
1.3 KiB
TypeScript
30 lines
1.3 KiB
TypeScript
/** A proxy or restarting server answered an API request without usable JSON. */
|
|
export class ApiUnavailableError extends Error {
|
|
constructor(public readonly status: number) {
|
|
super("Paperclip is temporarily unavailable. Please try again in a moment.");
|
|
this.name = "ApiUnavailableError";
|
|
}
|
|
}
|
|
|
|
export function isTemporaryApiError(error: unknown): boolean {
|
|
if (error instanceof ApiUnavailableError) return true;
|
|
if (!(error instanceof Error)) return false;
|
|
if ("status" in error && [502, 503, 504].includes(error.status as number)) return true;
|
|
// Fetch uses different network-failure messages across browsers. Do not
|
|
// classify arbitrary TypeErrors (programming bugs) or aborts as outages.
|
|
return error instanceof TypeError && /fetch|network|load failed/i.test(error.message);
|
|
}
|
|
|
|
export async function readApiJson<T = unknown>(response: Response): Promise<T> {
|
|
try {
|
|
return await response.json() as T;
|
|
} catch (error) {
|
|
if (!(error instanceof SyntaxError)) throw error;
|
|
// An HTML fallback can have status 200 during a deployment. Never expose
|
|
// its parser error or treat it as a successful, empty API response.
|
|
if (response.ok || response.status >= 500) throw new ApiUnavailableError(response.status);
|
|
// Preserve HTTP/auth error handling even when a 4xx response has no JSON.
|
|
return null as T;
|
|
}
|
|
}
|