export const BUILD_PROTOCOL_VERSION = 1 as const; export type Sha256Digest = `sha256:${string}`; export type BuildArchitecture = "amd64" | "arm64"; export type WorkspaceFile = { path: string; type: "file" | "symlink"; digest: Sha256Digest; size: number; mode: 0o644 | 0o755 | 0o777; }; export type WorkspaceManifest = { version: typeof BUILD_PROTOCOL_VERSION; files: WorkspaceFile[]; }; export type BuildSpec = { architecture: BuildArchitecture; image: string; context: string; dockerfile?: string; target?: string; buildArgs: string[]; workspace: Sha256Digest; }; export type BuildRequest = { version: typeof BUILD_PROTOCOL_VERSION; id: string; project: string; service: string; spec: BuildSpec; /** Additional service destinations for the same build artifact. */ destinations?: Array<{ service: string; image: string }>; }; export type BuildState = "queued" | "running" | "succeeded" | "failed"; export type BuildPhase = | "queued" | "creating" | "starting" | "running" | "done"; export type BuildStatus = { version: typeof BUILD_PROTOCOL_VERSION; id: string; state: BuildState; /** Optional lifecycle detail; absent on older persisted builds and servers. */ phase?: BuildPhase; createdAt: string; startedAt?: string; finishedAt?: string; digest?: Sha256Digest; error?: string; }; export type BuildEvent = | { type: "status"; status: BuildStatus } | { type: "log"; id: string; sequence: number; message: string }; export function isSha256Digest(value: unknown): value is Sha256Digest { return typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value); } export function assertSha256Digest( value: unknown, ): asserts value is Sha256Digest { if (!isSha256Digest(value)) throw new Error(`Invalid SHA-256 digest: ${value}`); }