import { type Result } from "@fjall/generator"; import { type BackupVaultVerdict, type CapacityIdentityConfig } from "@fjall/util"; import type { TrailLifecycleState } from "@fjall/util/config"; import type { ResourceEvent } from "@fjall/util/aws"; import type { OrgConfig } from "../types/config/orgConfig.js"; import type { DeployIdentity } from "../types/credentials.js"; import type { AwsProvider, AwsProviderCredentials } from "../aws/AwsProvider.js"; import type { DeploymentContext, StepOutput } from "../types/deployment/DeploymentTypes.js"; import type { DeployCallbacks } from "../types/callbacks.js"; import type { ProgressEvent } from "../types/events.js"; import type { DeployServices } from "./serviceFactory.js"; /** * Whether a deploy targets a region other than the resolved primary. Shared * predicate for the synth-time globals skip (buildParamsContext) AND the * pre-deploy globals existence probe (deploySingleComponent) — the two MUST * agree or a skip can pass unprobed. No primary configured (config never * synced) ⇒ false: the deploy region anchors a first-ever deploy. */ export declare function targetsNonPrimaryRegion(region: string | undefined, primaryRegion: string | undefined): boolean; /** * Build the orgConfig/identity context fields shared by all deployment paths. * CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string. * * Sole derivation site for `fjallAccountGlobalsConfigured`: a deploy skips the * fixed-name account globals iff it targets a region other than the resolved * primary (targetsNonPrimaryRegion above). */ export declare function buildParamsContext(params: { orgConfig?: OrgConfig; identity: DeployIdentity; skipOidc?: boolean; /** Region this synth targets. Omitted ⇒ no globals flag. */ region?: string; /** Resolved primary (home) region; callers pass their resolved anchor, never re-derive. */ primaryRegion?: string; trailLifecycle?: TrailLifecycleState; }): { orgConfig?: string; fjallOrgId: string; fjallOidcConfigured?: boolean; fjallAccountGlobalsConfigured?: boolean; fjallAccountTrailState?: string; }; /** * Project the surface-supplied capacity-identity pins to this deploy's * target and serialise them for the `-c fjall:capacityIdentity=` * channel. Empty when either half is absent — an unpinned synth of a * pre-7.0 stack renames its capacity resources, which the deploy-time * rename gate blocks rather than this helper guessing a target. */ export declare function buildCapacityIdentityContext(params: { capacityIdentity?: CapacityIdentityConfig; deployTargetName?: string; }): { capacityIdentity?: string; }; /** * Effective `skipOidc` for the single-component (platform/account) deploy path. * * A standalone single account's OIDC provider + FjallDeploy role are created by * the connect-time Quick-Create stack (FjallOIDCConnector), so the Account * stack must NOT recreate the account-global `https://fjall.io` provider — a * second create throws EntityAlreadyExistsException and rolls the stack back. * An org MEMBER is the opposite case: its provider + role are created AND * managed by its own Account-stack OidcConnector, so skipping there drops the * construct from the template and lets CloudFormation DELETE the provider and * deploy role the account depends on. An explicit caller value always wins. * * Discriminator — two independent reasons to skip: * 1. Solo estate — no organisation-tier account anywhere → Quick-Create owns * the provider → skip. Absent org config reads as solo (favours the * connected-account case that breaks). * 2. Standalone management account — the estate HAS an organisation-tier * account AND this deploy targets it (`targetIsOrganisationTier`). The only * way an `account`-type deploy targets the organisation-tier (management) * account is a management account connected standalone via Quick-Create; a * real organisation deploys its root through `deployType: "organisation"`, * which returns undefined here. Its provider is Quick-Create-owned → skip. * Any other `account` target is an org member → do NOT skip. `targetIsOrganisationTier` * is the resolved tier of the deploy target, supplied by the orchestrator — the * helper never re-derives it from the estate. * * Maintainer note: Quick-Create is the canonical OWNER of the provider + * FjallDeploy role for a standalone account — a deploy can only authenticate by * assuming that role, so the provider must already exist before any deploy runs. * Do NOT "fix" a removed-provider report by re-enabling Account-stack creation * for a standalone account: a genuinely-deleted provider is restored by RECONNECT * (re-running Quick-Create), not by redeploy. Re-enabling creation reintroduces * the collision. Equally, do NOT widen the skip to org members — that deletes a * provider the Account stack legitimately owns. */ export declare function resolveAccountBootstrapSkipOidc(deployType: "organisation" | "platform" | "account", orgConfig: OrgConfig | undefined, explicitSkipOidc: boolean | undefined, targetIsOrganisationTier?: boolean): boolean | undefined; /** Synth-time context flags the OIDC gate mirror reads — see {@link synthCarriedOidcConnector}. */ export interface SynthOidcContextFlags { fjallOrgId?: string; fjallOidcConfigured?: boolean; fjallAccountGlobalsConfigured?: boolean; } /** * Whether the synthesised Account/Platform stack carried the OidcConnector — * the deploy-core mirror of the construct gate in * `components/infrastructure/lib/patterns/aws/account.ts` (`receivesDeployRole() * && fjallOrgId && !oidcAlreadyConfigured && !accountGlobalsConfigured`). * `receivesDeployRole` is structural here: only platform/account deploys reach * the post-deploy OIDC reconcile, and both patterns receive the deploy role * (the Organisation pattern, which does not, only synthesises under * `deployType: "organisation"`). An empty-string `fjallOrgId` reads as absent, * matching both coupled sites: the construct gate tests truthiness and * CdkArgumentBuilder drops the `-c fjallOrgId` argument entirely for `""`. * * Gates {@link reconcileOidcProviderIfCarried}: the reconcile enforces * provider presence only where the current template claims ownership. A stack * that deliberately skipped the connector — solo/standalone-management * (Quick-Create owns its provider; restore is RECONNECT, never redeploy), * non-home region, or no org identity — must never have a provider created * for it on the deploy path. */ export declare function synthCarriedOidcConnector(context: SynthOidcContextFlags): boolean; /** * Post-CFN OIDC provider reconcile, gated on {@link synthCarriedOidcConnector}. * A pre-2.20.0 → current template migration deletes the account-global OIDC * provider during CloudFormation's cleanup phase (after the replacement * adopt-or-create resource has already run), so callers MUST invoke this only * after their deploy — or unchanged-skip probe — returns. Composing the gate * with the reconcile here keeps any future deploy path from invoking the * reconcile ungated. */ export declare function reconcileOidcProviderIfCarried(context: SynthOidcContextFlags, provider: Pick, callbacks: DeployCallbacks, abortSignal?: AbortSignal): Promise; /** Forward onOutput callback — reduces lambda repetition across orchestration files. */ export declare function forwardOutput(callbacks: DeployCallbacks): (chunk: string) => void; /** Forward onResourceProgress callback — reduces lambda repetition across orchestration files. */ export declare function forwardResourceProgress(callbacks: DeployCallbacks): (event: ResourceEvent) => void; /** * Emit one warning-typed progress event with the external-boundary fallback: * a wired onProgress receives it as today, otherwise it rides the * diagnostics spine (whose bound sink owns visibility) instead of vanishing. * The spine arm never dedupes — repeat emissions (gate warnings per synth) * repeat by design. Consent-flow onWarning lanes must not route through * here: this is fire-and-forget. */ export declare function emitWarningEvent(callbacks: DeployCallbacks | undefined, message: string, opts?: { metadata?: ProgressEvent["metadata"]; }): void; /** * Re-emit a successful step's degradation warnings (`StepOutput.warnings`, * pre-masked at construction) as typed progress events. The raw * onOutput/onCdkOutput chunk channel lands in debug logging on the TUI, * agent and piped-human surfaces, so every step-running caller must route * the payload's warnings through here or the degrade is invisible * (read-failure posture ADR clause 2). Callers without a wired onProgress * fall back to the diagnostics spine via {@link emitWarningEvent}. */ export declare function emitStepWarnings(callbacks: DeployCallbacks | undefined, step: StepOutput): void; /** Convert CloudFormation getStackOutputs result to Record. */ export declare function collectStackOutputs(result: Result, unknown>): Record | undefined; /** Build the IAM role ARN used for cross-account cascade operations. */ export declare function buildCascadeRoleArn(accountId: string): string; export interface CascadeAssumedRole { provider: AwsProvider; credentials: AwsProviderCredentials; } /** * Assume a cross-account cascade role and create a scoped provider. * Callers handle failure (callbacks, logging, return type) — this helper * only owns the assume + provider construction. */ export declare function assumeCascadeRole(awsProvider: AwsProvider, accountId: string, region: string, sessionName: string, signal?: AbortSignal): Promise>; /** * Engine ↔ assembly compatibility chokepoint for organisation-tier synths. * Mirrors the application path's gate (detectionPipeline §2b-gate): runs on * the just-synthesised assembly BEFORE any CFN mutation, so an incompatible * engine/constructs pairing refuses loudly instead of degrading silent-wrong. * WARN verdicts (engine-ahead, audit-logged overrides) route to * `callbacks.onLog` at warn level. Returns the masked refusal so each caller * wires its own site-specific reporting (onError, cascade complete, fail()). * * `backupVault` is the ownership verdict the synth carried, gated against the * constructs floor that reads it (`assertBackupVaultVerdictSupportedByAssembly`). * Required rather than optional so no synth seam can leave the decision out: * a seam with no vault in play passes `undefined`. */ export declare function assertAssemblyEngineCompat(cdkOutPath: string, callbacks: DeployCallbacks, backupVault: BackupVaultVerdict | undefined): Result; /** * Run CDK synth and return failure with masked error if it fails. * Calls `onError` on the callbacks so callers only need to handle * step-specific reporting (e.g. onStepComplete) before returning. * * A successful synth is engine-gated (assertAssemblyEngineCompat) against the * assembly it just wrote — `context.assemblyDir` when set, else the shared * `/cdk.out` — so every synthOrFail caller (organisationDestroy, * singleComponentDeploy, orgCascadeDeploy) inherits the chokepoint. */ export declare function synthOrFail(services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks, errorPrefix: string): Promise>; /** * The synth chokepoint without the `onError` emission: synth, re-emit the * step warnings, engine-gate the assembly. For callers that report the * failure themselves (a converge pass inside a step that already owns its * error reporting). Every failure message it returns is already masked or * identifier-only: synth output is masked at source by CdkProcessManager and * the gates mask their own refusals. */ export declare function synthAndGate(services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks, errorPrefix: string): Promise>; export interface BootstrapBoundaryScope { /** The account whose CDK execution role this bootstrap creates or updates. */ accountId: string; /** Names the `FjallDeployBoundary` policy; from `DeployParams.identity`. */ fjallOrgId: string; abortSignal?: AbortSignal; } export declare function resolveBootstrapBoundary(provider: Pick, scope: BootstrapBoundaryScope, callbacks: DeployCallbacks): Promise; /** * Run CDK bootstrap and return failure with masked error if it fails. * Emits onCDKBootstrap status callbacks and calls onError on failure. * * `bootstrapTags` (from `buildBootstrapTags`) stamps the CDKToolkit stack; * callers that cannot derive a stable, complete set for the target account * pass undefined so the existing stack tags are preserved — see * `orchestration/bootstrapTags.ts` for the replace-vs-preserve contract. * * `identity` names the `FjallDeployBoundary` policy the bootstrap caps * the cfn-exec role with (seam 2). It is the run's `DeployParams.identity`, * taken explicitly rather than read back out of the CDK context bag, whose * `fjallOrgId` mirror is optional by shape. */ export declare function bootstrapOrFail(services: DeployServices, context: DeploymentContext, identity: DeployIdentity, callbacks: DeployCallbacks, bootstrapTags?: Record, abortSignal?: AbortSignal): Promise>;