import { HttpError } from "../errors"; import { isArtifactsError } from "./artifacts"; const HTTP_BAD_GATEWAY = 502; const REFERENCE = /reference\s*=\s*([a-z0-9]+)/i; /** An Artifacts failure without a typed error code, e.g. the opaque "internal error; reference = …". */ export class ArtifactsUnavailableError extends HttpError { readonly operation: string; readonly reference: string | null; constructor(operation: string, target: string, cause: unknown) { const detail = cause instanceof Error ? cause.message : String(cause); const reference = REFERENCE.exec(detail)?.[1] ?? null; super( HTTP_BAD_GATEWAY, `Cloudflare Artifacts failed to ${operation} for ${target}${reference ? ` (reference ${reference})` : ""}. ` + "This is usually transient; try again shortly.", ); this.name = "ArtifactsUnavailableError"; this.operation = operation; this.reference = reference; } } /** * Runs an Artifacts binding call, keeping typed ArtifactsErrors intact (callers branch on their codes) * but turning untyped failures into a logged, user-readable error that names the operation and repo. */ export async function artifactsCall(operation: string, target: string, call: () => Promise): Promise { try { return await call(); } catch (error) { if (isArtifactsError(error) || error instanceof ArtifactsUnavailableError) throw error; const wrapped = new ArtifactsUnavailableError(operation, target, error); console.error(`Artifacts ${operation} failed`, { target, reference: wrapped.reference, error }); throw wrapped; } }