/** * Cascade outcome ledger + pure projections. * * Single source of truth for cascade summary counting. The engine accumulates * ONE `CascadeLedger` of atomic per-(account, region) outcomes during a cascade, * then projects it two ways: * * - `projectScalarSummary` → the wire-level `CascadeDeploymentResult` the webapp * reads (scalar counts + platform flags + errors), emitted via `onCascadeComplete`. * - `projectAccountRows` → the per-account rows the CLI summary renders, carried * via `onCascadeLedger`. * * Both deploy and destroy build the same ledger shape — destroy feeds `succeeded` * for a destroyed stack, so the projector is verb-agnostic (the scalar field names * stay `*Deployed`, carrying destroyed counts for a destroy). Adding a new summary * field means extending the ledger + one projector; every consumer inherits it. */ import type { CascadeDeploymentResult } from "../../types/events.js"; export type CascadeOutcomeResult = "succeeded" | "skipped" | "failed"; /** One account×region pair's outcome. */ export interface CascadeMemberOutcome { accountId: string; accountName: string; region: string; result: CascadeOutcomeResult; durationMs: number; /** Already masked by the producer. */ error?: string; } /** The platform account's outcome (single region). `skipped` only ever occurs on deploy. */ export interface CascadePlatformOutcome { accountId: string; result: CascadeOutcomeResult; durationMs: number; /** Already masked by the producer. */ error?: string; } /** * The domains phase's counts: zones actually deployed this run, and * foreign-pinned zones left untouched because their fjall-config.json entry * names another home and no routed provider was minted. A skip is expected * topology (never an `errors` entry), which is exactly why it must be * counted here: a summary that omits it cannot distinguish "nothing to * deploy" from "N zones deliberately left alone". */ export interface CascadeDomainsOutcome { deployed: number; skipped: number; } /** * The single accumulation a cascade produces. `errors` is the FULL masked list * (platform + domains + members) the producer also uses for its failure gate — * it is echoed verbatim, never re-derived from `members`. */ export interface CascadeLedger { members: CascadeMemberOutcome[]; platform?: CascadePlatformOutcome; domainsDeployed: boolean; /** * Domains-phase counts; absent when no domains phase ran (destroy, or a * deploy without a domain provider). `domainsDeployed` above stays the * boolean the wire scalar carries — these counts travel only on the * account-rows projection, off the wire (the F-R9 deferral). */ domains?: CascadeDomainsOutcome; errors: Array<{ accountId: string; error: string; }>; totalDurationMs: number; } /** A single per-account row for the CLI summary. */ export interface CascadeAccountRow { accountName: string; accountId: string; region: string; success: boolean; duration: number; error?: string; skipped?: boolean; } /** The platform row for the CLI summary. */ export interface CascadePlatformRow { success: boolean; duration: number; error?: string; skipped?: boolean; } export interface CascadeAccountProjection { accountResults: CascadeAccountRow[]; platformResult?: CascadePlatformRow; /** Domains-phase counts, echoed from the ledger (see CascadeLedger.domains). */ domains?: CascadeDomainsOutcome; } /** * Project the ledger to the scalar `CascadeDeploymentResult` (the webapp wire shape). * * Distinct-account counting: an account with ANY succeeded region counts as deployed; * with ANY failed region counts as failed; a succeed-in-A/fail-in-B account lands in * BOTH (preserving the engine's independent-Set behaviour). An account counts as * skipped only when every one of its regions skipped (so it is in neither set). */ export declare function projectScalarSummary(ledger: CascadeLedger): CascadeDeploymentResult; /** * Project the ledger to the per-account rows + platform row the CLI summary renders. * Errors are already masked in the ledger, so they pass through unchanged. */ export declare function projectAccountRows(ledger: CascadeLedger): CascadeAccountProjection;