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 = { /** Absent means the legacy BuildKit Dockerfile builder. */ builder?: "buildkit" | "buildpacks"; /** GitHub release CNB package, optionally pinned with #sha256=. */ buildpackUri?: string; 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 validateBuildpackUri(value: unknown): asserts value is string { if (typeof value !== "string" || value.length > 4096 || /[\s\\]/.test(value)) throw new Error("Invalid buildpack URI"); let url: URL; try { url = new URL(value); } catch { throw new Error("Invalid buildpack URI"); } if ( url.protocol !== "https:" || url.hostname !== "github.com" || url.port || url.username || url.password || url.search || !/^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/releases\/download\/[^/]+\/[^/]+\.cnb$/.test( url.pathname, ) || (url.hash && !/^#sha256=[a-f0-9]{64}$/.test(url.hash)) ) throw new Error( "Buildpack URI must be an HTTPS GitHub release .cnb URL (optional #sha256=)", ); } export function assertSha256Digest( value: unknown, ): asserts value is Sha256Digest { if (!isSha256Digest(value)) throw new Error(`Invalid SHA-256 digest: ${value}`); }