/** * Delete-blocking probe registry — the shared source for every pre-mutation * flow that must know whether CloudFormation's delete of a resource would be * refused by a live service-side guard (RDS DeletionProtection, DynamoDB * DeletionProtectionEnabled, ELBv2 `deletion_protection.enabled`), and, for * the flows authorised to remove such a guard, how to switch it off. * * Two consumers, two polarities, one registry: * - the recreate pre-flight (deploy-side pinned-name surgery) only PROBES and * halts on a blocked resource — a deploy never removes a delete guard; * - the destroy pre-flight probes, then DISABLES. A destroy is consented * (CLI confirmation, a CI pipeline's destroy action, a webapp job) and the * guard exists to stop an unintended delete, not the deliberate one, so * the engine switches it off itself — every lane, one implementation — * instead of teaching each caller a hand-rolled `aws rds modify-*`. * * Both fail closed on an unverifiable resource because their callers mutate * expensively after the check (a burned UpdateStack; emptied S3 buckets). * Types without a registry entry proceed — the post-mutation DELETE_FAILED * verification is the honest backstop for them. * * Failure messages are pre-masked at construction (masked SDK error text; * physical IDs are identifiers, not credentials). */ import { type Result } from "@fjall/generator"; import type { AwsProvider } from "../../aws/AwsProvider.js"; export interface DeleteBlockProbeResult { blocked: boolean; setting: string; } export type DeleteBlockProbe = (aws: AwsProvider, physicalId: string, abortSignal?: AbortSignal) => Promise>; /** * Polling shape for a disable that must wait on the resource: RDS accepts a * modify only on an `available` resource (what follows the modify is * measured — see the RDS section), DynamoDB only on an `ACTIVE` table. * `readyTimeoutMs` bounds each wait; a freshly deployed instance can sit in * `backing-up` for many minutes. */ export interface DeleteBlockDisableTiming { pollMs: number; readyTimeoutMs: number; } export declare const DELETE_BLOCK_DISABLE_TIMING: Readonly; /** * What a disable that waited on the service observed, recorded so every * destroy logs the status sequence it depended on instead of asserting it. */ export interface DeleteBlockDisableEvidence { /** Status carried by the modify acknowledgement — the earliest * post-acceptance view of the resource; undefined when the ack carried * none. */ ackStatus: string | undefined; /** Status on the first read after the acknowledgement. */ firstReadStatus: string; /** The status the disable waited for after the modify — the one the * service refuses a delete without. */ readyStatus: string; /** Milliseconds from the acknowledgement to the read that reported * `readyStatus`. */ readyAfterMs: number; } /** * What the disable saw, discriminated so the destroy log can say which: * `observed` — the service applies the change asynchronously, so the disable * waited for the resource's ready status on both sides of the modify (RDS, * DynamoDB) and carries the sequence it recorded; `synchronous` — the * service applied the change in the call and there was nothing to wait on * (ELBv2); `gone` — the resource was absent, either before the modify landed * or on the first read after its acknowledgement. A physically-deleted * target cannot block its own deletion, so both `gone` shapes succeed. */ export type DeleteBlockDisableOutcome = { kind: "observed"; evidence: DeleteBlockDisableEvidence; } | { kind: "synchronous"; } | { kind: "gone"; seen: "before-modify"; } | { kind: "gone"; seen: "after-ack"; ackStatus: string | undefined; }; /** * Switch the guard off on the live resource and return once the resource is * again in a state CloudFormation can delete, with what was observed on the * way when the service had to be waited on. Succeeds on a resource that has * already gone — a physically-deleted target cannot block its own deletion. */ export type DeleteBlockDisable = (aws: AwsProvider, physicalId: string, abortSignal?: AbortSignal, timing?: DeleteBlockDisableTiming) => Promise>; export interface DeleteBlockProbeEntry { probe: DeleteBlockProbe; /** IAM-method name for the "could not verify" failure copy. */ method: string; disable: DeleteBlockDisable; /** IAM-method name for the "could not switch off" failure copy. */ disableMethod: string; /** [G8] The exact manual cure for the halt copy — per resource type, so a * DBCluster halt never teaches the inexecutable `modify-db-instance` * against a cluster identifier. */ disableCommand: (physicalId: string) => string; } /** * Delete-blocking guards the pre-flights can verify and the destroy * pre-flight can switch off, per CFN resource type. RDS DeletionProtection — * fjall's own database constructs synthesise it ON by default * (`deletionProtection ?? true`); DynamoDB's DeletionProtectionEnabled and * the ELBv2 attribute, which the constructs turn on at the resilient and * enterprise backup tiers (`BACKUP_TIER_DEFAULTS.deletionProtection`) and * which drift or out-of-band mutation can switch on anywhere. */ export declare const DELETE_BLOCK_PROBES: Readonly>;