import { HttpError } from "../errors";
import { isArtifactsError } from "./artifacts";
import { recordArtifactsCall, recordArtifactsFailure } from "./artifacts-health";
const HTTP_BAD_GATEWAY = 502;
const REFERENCE = /reference\s*=\s*([a-z0-9]+)/i;
const RETRY_BASE_MS = 150;
const MAX_ATTEMPTS = 3;
/** Concurrent binding calls per limiter (one per request's store); bursts are what trip opaque errors. */
const MAX_IN_FLIGHT = 6;
/** A hung binding call must not hold a limiter slot forever. */
const CALL_TIMEOUT_MS = 30_000;
const REMOTE_PREFIX = /^ArtifactsError:\s*/;
const CODE_PATTERNS: Array<[RegExp, ArtifactsErrorCode]> = [
[/being forked|fork is still in progress/i, "FORK_IN_PROGRESS"],
[/being imported|import is still in progress/i, "IMPORT_IN_PROGRESS"],
[/being created/i, "CREATE_IN_PROGRESS"],
[/already exists/i, "ALREADY_EXISTS"],
[/requires? auth/i, "REMOTE_AUTH_REQUIRED"],
[/memory limit/i, "MEMORY_LIMIT"],
[/not found|does not exist/i, "NOT_FOUND"],
];
/**
* Remote bindings in local dev (and some RPC paths) deliver ArtifactsErrors as plain Errors whose message
* starts with "ArtifactsError:", losing `name` and `code`; rebuild a typed error so callers can branch on it.
*/
function revive(error: unknown): ArtifactsError | null {
if (!(error instanceof Error) || !REMOTE_PREFIX.test(error.message)) return null;
const message = error.message.replace(REMOTE_PREFIX, "");
const code = CODE_PATTERNS.find(([pattern]) => pattern.test(message))?.[1] ?? "INTERNAL_ERROR";
return Object.assign(new Error(message), { name: "ArtifactsError" as const, code, numericCode: 0 });
}
/** 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) {