/** * Two-phase delegated-domain deploy orchestration (D8): deploy the domain * stack at `phase: "zone"` (child zone + child-writes NS delegation, NO * certificates), hold at the bounded public-resolver propagation gate, run * the CAA preflight, then deploy `phase: "full"`. Generalises the * dev-substrate design's R2 two-step gate (design :104-121) for every * delegated child domain. * * The construct side of the contract is `composeDelegatedDomain`'s `phase` * prop; the caller (Phase-3 CLI / worker) supplies the deployer that maps a * phase onto its CDK invocation. deploy-core owns the ordering, the gate and * the fail-closed stops — orchestration ships here, not in docs. * * Idempotent resume: when the zone phase already succeeded, a re-run skips * straight to the propagation gate — "zone exists, no cert" is an explicit * retryable intermediate, never an error state. */ import { type Result } from "@fjall/generator"; import type { DeployCallbacks } from "../../types/callbacks.js"; import { type NsPropagationVerdict } from "./nsPropagationGate.js"; import { DomainDelegationError, type DomainDeployPhase, type PublicDnsResolver } from "./types.js"; /** What the caller's zone-phase probe reports for idempotent resume. */ export interface DelegatedZonePhaseState { /** True when the zone phase has already deployed successfully. */ complete: boolean; /** * The child zone's authoritative name servers, when the caller can read * them (domain-stack outputs). Fed to the propagation gate so a STALE * parent delegation (pointing at a previous zone's NS set) is not * mistaken for propagation. */ nameServers?: readonly string[]; } /** * Caller-supplied deploy seam for ONE delegated domain — the delegated-domain * mirror of `DomainDeployProvider.deployDomain`, split by phase. The CLI maps * `deployPhase` onto its CDK deploy with the `phase` context injected; * `zonePhaseState` probes the deployed stack (DescribeStacks + outputs). */ export interface DelegatedDomainDeployer { /** Fully-qualified child zone name, e.g. `dev.example.com`. */ readonly zoneName: string; deployPhase(phase: DomainDeployPhase, callbacks: DeployCallbacks): Promise>; zonePhaseState(): Promise>; } export interface DeployDelegatedDomainParams { deployer: DelegatedDomainDeployer; callbacks: DeployCallbacks; /** * Pre-answer for the CAA preflight (fail-closed ADR shape): skip the check * when the operator has verified the chain out of band. The skip is loud — * a warning names it — never silent. */ skipCaaPreflight?: boolean; propagation?: { maxAttempts?: number; intervalMs?: number; }; /** Injectable resolvers shared by the gate and the CAA preflight. */ resolvers?: readonly PublicDnsResolver[]; /** Injectable clock for the propagation gate. */ sleepFn?: (ms: number) => Promise; abortSignal?: AbortSignal; /** Cure spelling for resume — defaults to `fjall domain deploy `. */ retryCommand?: string; } export interface DelegatedDomainDeployOutcome { /** "resumed" when the zone phase was already deployed and was skipped. */ zonePhase: "deployed" | "resumed"; propagation: NsPropagationVerdict; caaPreflight: "passed" | "skipped"; fullPhase: "deployed"; } /** * Run the full two-phase delegated-domain deploy. Every stop is fail-closed * with a cure; every success path reports which steps ran so surfaces can * render resume vs fresh honestly. */ export declare function deployDelegatedDomain(params: DeployDelegatedDomainParams): Promise>;