Files
kandev/apps/web/lib/state/workspace-context.ts
2026-09-13 16:59:03 +01:00

60 lines
2.0 KiB
TypeScript

import type { WorkspaceContextReadError } from "./slices/kanban/types";
export type WorkspaceContextState = {
workspaces: { activeId: string | null };
workspaceContextGeneration: number;
};
export function isCurrentWorkspaceContext(
state: WorkspaceContextState,
requestedWorkspaceId: string | null,
requestedGeneration: number,
): boolean {
return (
state.workspaces.activeId === requestedWorkspaceId &&
state.workspaceContextGeneration === requestedGeneration
);
}
export function classifyWorkspaceContextReadError(error: unknown): WorkspaceContextReadError {
if (isAbortError(error)) return "cancelled";
const status = apiErrorStatus(error);
if (status === undefined) return "transient";
if (status === 401 || status === 403) return "access_denied";
if (status === 404) return "not_found";
if (status === 400 || status === 422) return "invalid";
if ([429, 502, 503, 504].includes(status)) return "transient";
return "unknown";
}
export function isRetryableWorkspaceContextError(error: unknown): boolean {
return classifyWorkspaceContextReadError(error) === "transient";
}
export function retryAfterMilliseconds(error: unknown): number | undefined {
if (!error || typeof error !== "object") return undefined;
const retryAfterSeconds = (error as { retryAfterSeconds?: unknown }).retryAfterSeconds;
if (
typeof retryAfterSeconds !== "number" ||
!Number.isFinite(retryAfterSeconds) ||
retryAfterSeconds <= 0
)
return undefined;
return Math.min(retryAfterSeconds * 1000, 2_147_483_647);
}
function apiErrorStatus(error: unknown): number | undefined {
if (!error || typeof error !== "object") return undefined;
const status = (error as { status?: unknown }).status;
return typeof status === "number" ? status : undefined;
}
function isAbortError(error: unknown): boolean {
return (
(typeof DOMException !== "undefined" &&
error instanceof DOMException &&
error.name === "AbortError") ||
(error instanceof Error && error.name === "AbortError")
);
}