import { z } from "zod"; import { installationId, releaseIdentity } from "./installation-metadata.ts"; const fingerprint = z.string().regex(/^[a-f0-9]{64}$/); const remoteId = z .string() .regex(/^(?:[a-f0-9]{32}|[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12})$/); export const upgradeBaseline = z.strictObject({ fromRelease: releaseIdentity, toRelease: releaseIdentity, deployedOperationId: installationId, configDigest: fingerprint, versionId: remoteId, deploymentId: remoteId, sourceCodeHash: fingerprint.nullable().default(null), fingerprint, containerFingerprint: fingerprint, targetContainerFingerprint: fingerprint, }); export type UpgradeBaseline = z.infer; export const operationSchema = z.strictObject({ operationId: installationId, deadline: z.number().int().positive(), upgrade: upgradeBaseline.nullable().default(null), rolloutId: remoteId.nullable().default(null), // Intent is persisted BEFORE a mutation, and survives a new authorized attempt. // An ambiguous absent resource must not cause a blind replay of that mutation. workerIntent: z .strictObject({ operationId: installationId, release: releaseIdentity, configDigest: fingerprint.nullable().default(null), }) .nullable(), containerIntent: z.boolean(), containerUpdate: z.boolean(), rolloutIntent: z .string() .regex(/^[a-f0-9]{32}$/) .nullable(), }); export type InstallationOperation = z.infer; // Mutation input must not reuse defaulted storage fields: Zod defaults inside // optional fields would clear an existing upgrade/rollout pin on unrelated writes. export const operationChanges = z.strictObject({ workerIntent: operationSchema.shape.workerIntent.optional(), containerIntent: z.boolean().optional(), containerUpdate: z.boolean().optional(), rolloutIntent: installationId.nullable().optional(), rolloutId: remoteId.nullable().optional(), });