import type { ChildEnvPolicy } from "@fjall/util"; import { CloudFormationClient } from "@aws-sdk/client-cloudformation"; import { type Result } from "@fjall/generator"; import type { DeployCallbacks } from "../../types/callbacks.js"; import type { AwsCredentials } from "../../types/credentials.js"; import type { DomainConfig, DomainDeployProvider, DomainTakeoverSynth } from "../domainInterface.js"; import type { DelegatedDomainDeployer } from "./delegatedDomainDeploy.js"; /** * The deployment worker's `DomainDeployProvider` (P6): the cascade's domains * phase finally runs on webapp org deploys. Built on deploy-core's own * primitives — `CdkService` with EXPLICIT job credentials, never the CLI's * cwd-bound `Config.loadConfig` or ambient `AwsContext` — and parameterised * by the job's `workingDirectory` + resolved `boundaryDir` (the CLI's path * helpers are cwd-relative literals and its config walk goes UP, so a * containered boundary like `infra/fjall` is unresolvable from * workingDirectory alone; the job payload carries it instead). * * `delegatedDeployer` gives delegated children the D8 two-phase ceremony * (zone deploy, NS-propagation gate, CAA preflight, full deploy) at parity * with the CLI. Parent-DelegationRole resolution is worker-shaped: a literal * `parentDelegationRoleArn` in the child's infrastructure.ts wins (no * network, nothing injected — the construct reads its own props), then a * same-account parent resolves through its stack's delegation-role export * with the job credentials. A parent registered in a DIFFERENT account * resolves through `resolveForeignParentCfn` when the T2 factory wiring * supplied one (per-home credentials exist, so the export is read in the * parent's own account), and otherwise fails closed with the * `delegation_role_unusable` cure — this provider holds one OIDC-minted * credential set and must not invent a cross-account hop itself. * A parent absent from the registry is the externally-owned Case 3: the * deploy proceeds uninjected with a loud warning, exactly like the CLI. */ export interface WorkerDomainDeployProviderParams { /** Absolute path of the checked-out repository the job deploys from. */ workingDirectory: string; /** * Repo-relative boundary directory (e.g. `fjall`, `infra/fjall`) from the * organisation's tier pointer — resolved webapp-side and carried on the * job payload. */ boundaryDir: string; /** The job's explicit OIDC-minted credentials. */ credentials: AwsCredentials; /** * Serialised org config (`OrgConfigClient.serialise` shape) the worker * already fetched for the deploy — threaded into CDK context exactly as * the CLI's `assembleSynthContext` threads its own fetch. */ orgConfigSerialised?: string; /** * T2 account routing: resolve a CloudFormation reader for a SIBLING * group's account, so a delegated child whose parent zone lives in * another home can read the parent's delegation-role export THERE instead * of failing closed. Supplied by the factory wiring that minted per-home * credentials (it holds every group's client); this provider never mints * a credential hop itself. Absent, or returning `undefined` for the * parent's account: the cross-account rung fails closed exactly as * before. */ resolveForeignParentCfn?: (accountId: string) => Pick | undefined; abortSignal?: AbortSignal; /** * How the CDK children this provider spawns inherit the worker's * environment. REQUIRED — see `DeployParams.childEnvPolicy`. A domain * deploy synthesises the customer's own boundary directory, so it is the * same untrusted-child seam as an application deploy. */ childEnvPolicy: ChildEnvPolicy; } export declare class WorkerDomainDeployProvider implements DomainDeployProvider { private readonly domains; private readonly workingDirectory; private readonly boundaryDir; private readonly credentials; private readonly orgConfigSerialised; private readonly resolveForeignParentCfn; private readonly abortSignal; private readonly childEnvPolicy; private readonly aws; /** Memoised org lookup — one Organizations call per job, not per domain. */ private orgDetailsPromise; private constructor(); /** * Read + validate the domain registry once, fail-closed on malformed * config. A missing config yields a provider with zero domains — the * cascade's domains phase returns early, which is correct topology. */ static create(params: WorkerDomainDeployProviderParams): Promise>; getDomains(): DomainConfig[]; private componentPath; /** * Worker mirror of the CLI's `assembleSynthContext`: same context keys, no * ambient state. The org lookup degrades with a warning (CLI parity — the * generated infrastructure drops the DelegationRole without org context, * it does not fail), but accountId/region always come from the job's * explicit credentials. */ private assembleContext; private cdkCredentials; private componentPreconditionError; synthForTakeover(domainName: string, callbacks: DeployCallbacks): Promise>; deployDomain(domainName: string, callbacks: DeployCallbacks): Promise>; /** * The one CDK deploy path both `deployDomain` and the delegated * `deployPhase` ride: assembled worker context plus the caller's extra * context keys (the delegated phase + resolved DelegationRole), output * lines optionally prefixed so parallel wave members stay attributable. */ private runCdkDeploy; /** * Parse the child component's infrastructure.ts. `undefined` on any miss * (unreadable, unparsable, no Domain call) — the single-shot fallback then * surfaces the real synth error instead of this layer guessing (CLI * parity: `buildDelegatedDomainDeployer` behaves identically). */ private parseDomainComponent; /** * The worker's cure sentence for an unusable parent DelegationRole. The * CLI's trio offers `fjall connect` because the CLI can mint parent-account * credentials interactively; the worker cannot, so its cures are the ones * a webapp-triggered deploy can actually action. */ private delegationCures; /** * Resolve the parent DelegationRole for one delegated phase, worker * rungs: file literal (inject nothing) → registry-managed same-account * stack export (inject) → cross-account fail-closed → unregistered parent * Case-3 advisory (proceed uninjected). */ private resolveDelegationForPhase; /** * D8 delegated-parity seam: the two-phase deployer for one delegated * child. `undefined` when the component is not `external-delegated` (the * single-phase path is then CORRECT, not a downgrade) and when the * component cannot be read or parsed — the single-shot deploy surfaces * the real synth error instead of this layer guessing. */ delegatedDeployer(domainName: string): DelegatedDomainDeployer | undefined; } /** * The provider's public entry point: reads + validates the boundary's domain * registry up front so a malformed config fails the JOB, closed, before the * cascade ever consults `getDomains()`. */ export declare function createWorkerDomainDeployProvider(params: WorkerDomainDeployProviderParams): Promise>;