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<T>(operation: string, target: string, call: () => Promise<T>): Promise<T> {
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;
}
}