@fjall/deploy-core 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/src/aws/utils/cloudformationEvents.d.ts +1 -1
- package/dist/src/aws/utils/cloudformationEvents.js +1 -1
- package/dist/src/events/index.d.ts +1 -1
- package/dist/src/events/index.js +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.d.ts +18 -0
- package/dist/src/orchestration/application/applicationDeployHelpers.js +3 -3
- package/dist/src/orchestration/application/approvalGate.d.ts +8 -0
- package/dist/src/orchestration/application/approvalGate.js +1 -1
- package/dist/src/orchestration/application/assemblyIntegrity.d.ts +40 -0
- package/dist/src/orchestration/application/assemblyIntegrity.js +1 -0
- package/dist/src/orchestration/application/plan/assemblyDigest.d.ts +29 -5
- package/dist/src/orchestration/application/plan/assemblyDigest.js +4 -2
- package/dist/src/orchestration/application/plan/buildDeployPlan.d.ts +25 -0
- package/dist/src/orchestration/application/plan/buildDeployPlan.js +1 -1
- package/dist/src/orchestration/application/plan/index.d.ts +2 -2
- package/dist/src/orchestration/application/plan/index.js +1 -1
- package/dist/src/orchestration/domain/index.d.ts +1 -1
- package/dist/src/orchestration/domain/zoneClassifier.d.ts +59 -0
- package/dist/src/orchestration/domain/zoneClassifier.js +1 -1
- package/dist/src/orchestration/index.d.ts +1 -1
- package/dist/src/orchestration/remediation/removalUpdate.d.ts +9 -6
- package/dist/src/orchestration/remediation/removalUpdate.js +1 -1
- package/dist/src/services/infrastructure/CdkService.d.ts +11 -0
- package/dist/src/services/infrastructure/CdkService.js +8 -2
- package/dist/src/services/infrastructure/transientNetworkRecovery.d.ts +32 -0
- package/dist/src/services/infrastructure/transientNetworkRecovery.js +1 -0
- package/dist/src/types/deploymentEventSchema.d.ts +28 -0
- package/dist/src/types/deploymentEventSchema.js +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.js +1 -1
- package/package.json +4 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
233 files minified at 2026-07-20T11:33:20.948Z
|
|
@@ -46,7 +46,7 @@ export declare class CloudFormationEventMonitor {
|
|
|
46
46
|
}>;
|
|
47
47
|
private pollContext;
|
|
48
48
|
private pollEvents;
|
|
49
|
-
getStackStatus(stackName: string): Promise<{
|
|
49
|
+
getStackStatus(stackName: string, abortSignal?: AbortSignal): Promise<{
|
|
50
50
|
status: string;
|
|
51
51
|
statusReason?: string;
|
|
52
52
|
} | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var v=(S,t)=>p(S,"name",{value:t,configurable:!0});import{logger as c}from"@fjall/util/logger";import{getErrorMessage as m,sleep as d}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as M}from"@fjall/util/aws";import{isResourceEvent as N,STACK_NOT_FOUND_PATTERN as W,CDK_NO_STACKS_MATCH as U}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as C}from"./cloudformationEventTypes.js";import{isTerminalState as w,isSuccessState as E,IpamConcurrencyTracker as F,pollStackEvents as L,handleStackCompletion as T,fetchStackStatus as R,fetchCurrentResources as A,EVENT_CUTOFF_SKEW_MS as I}from"./cloudformationEventHelpers.js";class K{static{v(this,"CloudFormationEventMonitor")}aws;seenEventIds=new Set;isMonitoring=!1;pollInterval=null;activeNestedStacks=new Map;failureReasons=new Map;eventHistory=new Map;eventLogger=null;failureAnalyser;eventLogWriterFactory;onFailureAnalysis;lastAnalysis=null;maxHistorySize=1e3;maxSeenEventIds=1e4;ipamTracker=new F;eventCutoffMs=0;tokenLatchCutoffMs=0;constructor(t,e){this.aws=t,this.failureAnalyser=e?.failureAnalyser??null,this.eventLogWriterFactory=e?.eventLogWriterFactory,this.onFailureAnalysis=e?.onFailureAnalysis}enableLogging(t,e,r,a){this.eventLogWriterFactory&&(this.eventLogger=this.eventLogWriterFactory(t,e,r,a))}async startMonitoring(t,e,r){if(this.isMonitoring){c.debug("CloudFormation","startMonitoring SKIPPED - already monitoring",{stackName:t});return}c.debug("CloudFormation","startMonitoring STARTED",{stackName:t}),this.isMonitoring=!0,this.seenEventIds.clear(),this.eventHistory.clear();const a=Date.now();this.eventCutoffMs=a-I,this.tokenLatchCutoffMs=a;try{await this.pollEvents(t,()=>{})}catch(o){const h=m(o);h.includes(M)||c.debug("CloudFormation","Initial poll failed",{error:h})}let s=5e3;const n=1e4;let g=0,l=0;const u=v(async()=>{if(this.isMonitoring)try{const o=await this.pollEvents(t,e);g++,o==="throttled"?(l++,s=Math.min(3e4,5e3*Math.pow(2,l-1))):(l=0,g>20&&s<n?s=Math.min(n,s+1e3):g<=20&&(s=5e3)),o===!0?await this.handleStackComplete(t,r):this.pollInterval=setTimeout(u,s)}catch(o){c.debug("CloudFormation","Polling iteration error (continuing)",{error:m(o)}),this.pollInterval=setTimeout(u,s)}},"poll");this.pollInterval=setTimeout(u,s)}stopMonitoring(){c.debug("CloudFormation","stopMonitoring called",{wasMonitoring:this.isMonitoring,seenEventCount:this.seenEventIds.size}),this.isMonitoring=!1,this.pollInterval&&(clearTimeout(this.pollInterval),this.pollInterval=null),this.cleanup()}cleanup(){if(this.activeNestedStacks.clear(),this.failureReasons.clear(),this.eventHistory.clear(),this.ipamTracker.clear(),this.seenEventIds.size>this.maxSeenEventIds){const t=Array.from(this.seenEventIds),e=Math.floor(this.maxSeenEventIds/2);this.seenEventIds=new Set(t.slice(-e))}this.eventLogger&&(this.eventLogger=null)}async handleStackComplete(t,e){const r=new Map(this.failureReasons),a=new Map(this.eventHistory),s=this.eventLogger;this.stopMonitoring();const n=await T(this.aws,t,r,s,this.failureAnalyser,a);this.lastAnalysis=n.analysis,n.analysis&&this.onFailureAnalysis&&this.onFailureAnalysis(n.analysis),e&&e(n.success,n.failureMessage)}getResourceHistory(t){return this.eventHistory.get(t)||[]}getEventHistory(){return new Map(this.eventHistory)}getFailureAnalysis(){return this.lastAnalysis}getFirstFailureReason(){return this.failureReasons.size>0?Array.from(this.failureReasons.values())[0]??null:null}getEventLogger(){return this.eventLogger}getEventLogPath(){return this.eventLogger?.getLogPath()||null}getLogSummary(){return this.eventLogger?.getLogSummary()||null}async waitForStackComplete(t,e={}){const{timeout:r=1800*1e3,pollInterval:a=2e3,onResourceUpdate:s,onStackComplete:n}=e,g=Date.now();let l,u=!1,o=!1,h;c.debug("CloudFormation","waitForStackComplete called",{stackName:t,timeout:r,pollInterval:a,hasOnResourceUpdate:!!s}),await this.startMonitoring(t,i=>{s&&s(i),i.resourceType===C&&i.logicalId===t&&(l=i.status,w(i.status)&&(u=!0,o=E(i.status),o||(h=this.getFirstFailureReason()||i.statusReason||"Stack operation failed")))},(i,f)=>{n&&n(i,f)});try{let i=!1,f=!1;for(;!u&&this.isMonitoring&&Date.now()-g<r;)await d(a),!i&&Date.now()-g>3e4&&!f&&(f=!0,await this.getStackStatus(t)||c.debug("CloudFormation","Stack not found after 30s, continuing to wait (CDK may be uploading assets)",{stackName:t})),l&&(i=!0);if(this.stopMonitoring(),!u){if(h)return{success:!1,status:"FAILED",failureReason:h,logPath:this.getLogSummary()||void 0};const y=await this.getStackStatus(t);return{success:!1,status:y?.status||"UNKNOWN",failureReason:`Deployment timed out after ${r/1e3} seconds. Stack status: ${y?.status||"UNKNOWN"}`,logPath:this.getLogSummary()||void 0}}return{success:o,status:l,failureReason:h,logPath:this.getLogSummary()||void 0}}catch(i){return this.stopMonitoring(),{success:!1,failureReason:`Monitoring error: ${m(i)}`,logPath:this.getLogSummary()||void 0}}}pollContext(){return{aws:this.aws,seenEventIds:this.seenEventIds,activeNestedStacks:this.activeNestedStacks,failureReasons:this.failureReasons,eventHistory:this.eventHistory,maxHistorySize:this.maxHistorySize,ipamTracker:this.ipamTracker,eventLogger:this.eventLogger,eventCutoffMs:this.eventCutoffMs,tokenLatchCutoffMs:this.tokenLatchCutoffMs}}async pollEvents(t,e){return L(this.pollContext(),t,e)}async getStackStatus(t){return R(this.aws,t)}async getCurrentResources(t){return A(this.aws,t)}}export{U as CDK_NO_STACKS_MATCH,K as CloudFormationEventMonitor,W as STACK_NOT_FOUND_PATTERN,N as isResourceEvent};
|
|
1
|
+
var p=Object.defineProperty;var v=(S,t)=>p(S,"name",{value:t,configurable:!0});import{logger as c}from"@fjall/util/logger";import{getErrorMessage as m,sleep as d}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as M}from"@fjall/util/aws";import{isResourceEvent as N,STACK_NOT_FOUND_PATTERN as W,CDK_NO_STACKS_MATCH as U}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as C}from"./cloudformationEventTypes.js";import{isTerminalState as w,isSuccessState as E,IpamConcurrencyTracker as F,pollStackEvents as L,handleStackCompletion as T,fetchStackStatus as R,fetchCurrentResources as A,EVENT_CUTOFF_SKEW_MS as I}from"./cloudformationEventHelpers.js";class K{static{v(this,"CloudFormationEventMonitor")}aws;seenEventIds=new Set;isMonitoring=!1;pollInterval=null;activeNestedStacks=new Map;failureReasons=new Map;eventHistory=new Map;eventLogger=null;failureAnalyser;eventLogWriterFactory;onFailureAnalysis;lastAnalysis=null;maxHistorySize=1e3;maxSeenEventIds=1e4;ipamTracker=new F;eventCutoffMs=0;tokenLatchCutoffMs=0;constructor(t,e){this.aws=t,this.failureAnalyser=e?.failureAnalyser??null,this.eventLogWriterFactory=e?.eventLogWriterFactory,this.onFailureAnalysis=e?.onFailureAnalysis}enableLogging(t,e,r,a){this.eventLogWriterFactory&&(this.eventLogger=this.eventLogWriterFactory(t,e,r,a))}async startMonitoring(t,e,r){if(this.isMonitoring){c.debug("CloudFormation","startMonitoring SKIPPED - already monitoring",{stackName:t});return}c.debug("CloudFormation","startMonitoring STARTED",{stackName:t}),this.isMonitoring=!0,this.seenEventIds.clear(),this.eventHistory.clear();const a=Date.now();this.eventCutoffMs=a-I,this.tokenLatchCutoffMs=a;try{await this.pollEvents(t,()=>{})}catch(o){const h=m(o);h.includes(M)||c.debug("CloudFormation","Initial poll failed",{error:h})}let s=5e3;const n=1e4;let g=0,l=0;const u=v(async()=>{if(this.isMonitoring)try{const o=await this.pollEvents(t,e);g++,o==="throttled"?(l++,s=Math.min(3e4,5e3*Math.pow(2,l-1))):(l=0,g>20&&s<n?s=Math.min(n,s+1e3):g<=20&&(s=5e3)),o===!0?await this.handleStackComplete(t,r):this.pollInterval=setTimeout(u,s)}catch(o){c.debug("CloudFormation","Polling iteration error (continuing)",{error:m(o)}),this.pollInterval=setTimeout(u,s)}},"poll");this.pollInterval=setTimeout(u,s)}stopMonitoring(){c.debug("CloudFormation","stopMonitoring called",{wasMonitoring:this.isMonitoring,seenEventCount:this.seenEventIds.size}),this.isMonitoring=!1,this.pollInterval&&(clearTimeout(this.pollInterval),this.pollInterval=null),this.cleanup()}cleanup(){if(this.activeNestedStacks.clear(),this.failureReasons.clear(),this.eventHistory.clear(),this.ipamTracker.clear(),this.seenEventIds.size>this.maxSeenEventIds){const t=Array.from(this.seenEventIds),e=Math.floor(this.maxSeenEventIds/2);this.seenEventIds=new Set(t.slice(-e))}this.eventLogger&&(this.eventLogger=null)}async handleStackComplete(t,e){const r=new Map(this.failureReasons),a=new Map(this.eventHistory),s=this.eventLogger;this.stopMonitoring();const n=await T(this.aws,t,r,s,this.failureAnalyser,a);this.lastAnalysis=n.analysis,n.analysis&&this.onFailureAnalysis&&this.onFailureAnalysis(n.analysis),e&&e(n.success,n.failureMessage)}getResourceHistory(t){return this.eventHistory.get(t)||[]}getEventHistory(){return new Map(this.eventHistory)}getFailureAnalysis(){return this.lastAnalysis}getFirstFailureReason(){return this.failureReasons.size>0?Array.from(this.failureReasons.values())[0]??null:null}getEventLogger(){return this.eventLogger}getEventLogPath(){return this.eventLogger?.getLogPath()||null}getLogSummary(){return this.eventLogger?.getLogSummary()||null}async waitForStackComplete(t,e={}){const{timeout:r=1800*1e3,pollInterval:a=2e3,onResourceUpdate:s,onStackComplete:n}=e,g=Date.now();let l,u=!1,o=!1,h;c.debug("CloudFormation","waitForStackComplete called",{stackName:t,timeout:r,pollInterval:a,hasOnResourceUpdate:!!s}),await this.startMonitoring(t,i=>{s&&s(i),i.resourceType===C&&i.logicalId===t&&(l=i.status,w(i.status)&&(u=!0,o=E(i.status),o||(h=this.getFirstFailureReason()||i.statusReason||"Stack operation failed")))},(i,f)=>{n&&n(i,f)});try{let i=!1,f=!1;for(;!u&&this.isMonitoring&&Date.now()-g<r;)await d(a),!i&&Date.now()-g>3e4&&!f&&(f=!0,await this.getStackStatus(t)||c.debug("CloudFormation","Stack not found after 30s, continuing to wait (CDK may be uploading assets)",{stackName:t})),l&&(i=!0);if(this.stopMonitoring(),!u){if(h)return{success:!1,status:"FAILED",failureReason:h,logPath:this.getLogSummary()||void 0};const y=await this.getStackStatus(t);return{success:!1,status:y?.status||"UNKNOWN",failureReason:`Deployment timed out after ${r/1e3} seconds. Stack status: ${y?.status||"UNKNOWN"}`,logPath:this.getLogSummary()||void 0}}return{success:o,status:l,failureReason:h,logPath:this.getLogSummary()||void 0}}catch(i){return this.stopMonitoring(),{success:!1,failureReason:`Monitoring error: ${m(i)}`,logPath:this.getLogSummary()||void 0}}}pollContext(){return{aws:this.aws,seenEventIds:this.seenEventIds,activeNestedStacks:this.activeNestedStacks,failureReasons:this.failureReasons,eventHistory:this.eventHistory,maxHistorySize:this.maxHistorySize,ipamTracker:this.ipamTracker,eventLogger:this.eventLogger,eventCutoffMs:this.eventCutoffMs,tokenLatchCutoffMs:this.tokenLatchCutoffMs}}async pollEvents(t,e){return L(this.pollContext(),t,e)}async getStackStatus(t,e){return R(this.aws,t,e)}async getCurrentResources(t){return A(this.aws,t)}}export{U as CDK_NO_STACKS_MATCH,K as CloudFormationEventMonitor,W as STACK_NOT_FOUND_PATTERN,N as isResourceEvent};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* The canonical schema and constants live in ../types/deploymentEventSchema.ts.
|
|
9
9
|
* This barrel re-exports only the browser-safe subset.
|
|
10
10
|
*/
|
|
11
|
-
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "../types/deploymentEventSchema.js";
|
|
11
|
+
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, DEPLOYMENT_ERROR_MESSAGE_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES, clampDeploymentEventStrings, truncateUtf16Safe } from "../types/deploymentEventSchema.js";
|
|
12
12
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "../types/deploymentEventSchema.js";
|
|
13
13
|
export { toCascadePhase } from "../types/deploymentEventSchema.js";
|
|
14
14
|
export { TRAIL_MIGRATION_PHASES, TRAIL_MIGRATION_STATUSES } from "../types/events.js";
|
package/dist/src/events/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as T,DEPLOYMENT_EVENT_TYPES as
|
|
1
|
+
import{DeploymentEventSchema as T,DEPLOYMENT_EVENT_TYPES as S,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as A,DEPLOYMENT_EVENT_STATUS_REASON_MAX as N,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as R,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as e,DEPLOYMENT_ERROR_MESSAGE_MAX as M,CASCADE_PHASES as O,CASCADE_ACCOUNT_STATUSES as t,clampDeploymentEventStrings as D,truncateUtf16Safe as L}from"../types/deploymentEventSchema.js";import{toCascadePhase as o}from"../types/deploymentEventSchema.js";import{TRAIL_MIGRATION_PHASES as I,TRAIL_MIGRATION_STATUSES as r}from"../types/events.js";export{t as CASCADE_ACCOUNT_STATUSES,O as CASCADE_PHASES,M as DEPLOYMENT_ERROR_MESSAGE_MAX,R as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,A as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,N as DEPLOYMENT_EVENT_STATUS_REASON_MAX,e as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,S as DEPLOYMENT_EVENT_TYPES,T as DeploymentEventSchema,I as TRAIL_MIGRATION_PHASES,r as TRAIL_MIGRATION_STATUSES,D as clampDeploymentEventStrings,o as toCascadePhase,L as truncateUtf16Safe};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* responsibility. deploy-core receives credentials and a working
|
|
12
12
|
* directory; it never reads credential files or writes to the terminal.
|
|
13
13
|
*/
|
|
14
|
-
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "./types/index.js";
|
|
14
|
+
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, DEPLOYMENT_ERROR_MESSAGE_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES, clampDeploymentEventStrings, truncateUtf16Safe } from "./types/index.js";
|
|
15
15
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./types/index.js";
|
|
16
16
|
export { DeployEventSchema, StepProgressSchema, DeployResourceSchema, PlanChangeSchema, PlanPropertyChangeSchema, ChangeCountsSchema, DEPLOY_EVENT_CONTRACT, DEPLOY_EVENT_CONTRACT_MAJOR, DEPLOY_EVENT_CONTRACT_MINOR, SUPPORTED_CONTRACT_MARKERS, DEPLOY_EVENT_ID_MAX, DEPLOY_EVENT_NAME_MAX, DEPLOY_EVENT_MESSAGE_MAX, DEPLOY_EVENT_STATUS_REASON_MAX, DEPLOY_EVENT_ERROR_MESSAGE_MAX, DEPLOY_EVENT_TRAIL_DETAIL_MAX, DEPLOY_EVENT_OUTPUT_MAX, DEPLOY_EVENT_ARN_MAX, DEPLOY_EVENT_URL_MAX, STEP_COMPLETE_STATUSES, LOG_LEVELS, STACK_CLEANUP_PHASES, CASCADE_ACCOUNT_PHASES, DEPLOY_RESULTS, DETECTION_PHASES, PHASE_STATUSES, BUILDERS, CASCADE_OUTCOME_RESULTS, PLAN_ACTIONS, REPLACEMENT_MODES, APPROVAL_KINDS, APPROVAL_DECISIONS, ALL_PROGRESS_KINDS, createDeployEmitter, parseDeployEvent, parseDeployContractVersion } from "./types/index.js";
|
|
17
17
|
export type { DeployEvent, ProgressKind, StepProgress, DeployResource, PlanChange, PlanPropertyChange, ChangeCounts, PlanAction, ReplacementMode, ApprovalKind, ApprovalDecision, DeployRunResult, DeployEmitter, CreateDeployEmitterOptions, ParsedDeployEvent, StackCleanupPhase, CascadeAccountPhase, LogLevel } from "./types/index.js";
|
|
@@ -83,7 +83,7 @@ export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult,
|
|
|
83
83
|
export { DOMAIN_DELEGATION_ERROR_TYPES, DomainDelegationError, DEFAULT_PUBLIC_RESOLVER_ADDRESSES, PUBLIC_DNS_QUERY_TIMEOUT_MS, createPublicResolvers, NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS, NS_PROPAGATION_DEFAULT_INTERVAL_MS, waitForNsPropagation, ACM_CAA_ISSUERS, runCaaPreflight, deployDelegatedDomain, CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE, templateOwnsDelegationRecord, deleteChildNsFromParent, nsDelegationPhysicalName, runDelegatedDomainDestroy } from "./orchestration/index.js";
|
|
84
84
|
export type { DomainDeployPhase, DomainDelegationErrorType, PublicCaaRecord, PublicDnsResolver, NsPropagationGateParams, NsPropagationVerdict, CaaPreflightParams, CaaPreflightVerdict, DelegatedDomainDeployer, DelegatedZonePhaseState, DeployDelegatedDomainParams, DelegatedDomainDeployOutcome, NsDeleteOutcome, DeleteChildNsParams, DelegationDestroyNsOutcome, DelegatedDomainDestroyOutcome, DelegatedDomainDestroyParams } from "./orchestration/index.js";
|
|
85
85
|
export { classifyZoneRecords, buildSatelliteRecordIndex, createSystemDnsNameProbe, systemDnsNameProbe, normaliseRecordName, isRoute53NameServer, isDelegatedToRoute53 } from "./orchestration/index.js";
|
|
86
|
-
export type { RecordClassification, ClassifiedRecord, ZoneClassificationReport, ZoneClassifierClients, ZoneClassifierPhase, ClassifyZoneOptions, SatelliteRecordIndex, AwsSdkClientLike, DnsProbeVerdict, DnsNameProbe } from "./orchestration/index.js";
|
|
86
|
+
export type { RecordClassification, ClassifiedRecord, ZoneClassificationReport, ZoneClassifierClients, ZoneClassifierPhase, ClassifyZoneOptions, SatelliteRecordIndex, DelegatedChildDomain, ChildAccountClients, ChildAccountClientFactory, AwsSdkClientLike, DnsProbeVerdict, DnsNameProbe } from "./orchestration/index.js";
|
|
87
87
|
export { resolveBuildSecrets, resolveSecretRefValue, resolveBuildSecretSessionProvider, sourcedRefsFromBuildArgs, resolveBuildArgs, buildBuildSecretSessionPolicy, buildDeploySessionPolicy, partitionForRegion, validateBuildGroup } from "./orchestration/index.js";
|
|
88
88
|
export type { ResolveBuildArgsContext, ResolveBuildArgsResult, SourcedBuildArgResolver, BuildSecretRef, BuildSecretSessionContext, DeploySessionContext, ValidateBuildGroupInput, BuildGroupMemberInputs } from "./orchestration/index.js";
|
|
89
89
|
export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";
|
|
@@ -91,6 +91,6 @@ export { FrameworkRegistry, type ResolvedBuilder } from "./orchestration/index.j
|
|
|
91
91
|
export { openNextBuilder, staticSiteBuilder, dockerBuilder } from "./orchestration/index.js";
|
|
92
92
|
export { StepRegistry, getDestroyStepId } from "./steps/index.js";
|
|
93
93
|
export { buildDeployPlan, computeDeployPlan, computeAssemblyDigest, digestHead, classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, renderPlanSummary, renderPlanLines, toWirePlanChanges, signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN, runApprovalGate, approvalRefusalReason } from "./orchestration/index.js";
|
|
94
|
-
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult, RunApprovalGateParams, ApprovalGateOutcome } from "./orchestration/index.js";
|
|
94
|
+
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, DeployScope, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult, RunApprovalGateParams, ApprovalGateOutcome } from "./orchestration/index.js";
|
|
95
95
|
export { runDestructionGate, buildDestructionTicket, evaluateDestructionConsents, computeTicketDigest, legalRemediationVerbs, executableRemediationVerbs, advertisableRemediationVerbs, renderDestructionTicketLines, renderConsentVerdictLines, loadDeployPlan, createMemoisedPlanLoader } from "./orchestration/index.js";
|
|
96
96
|
export type { RunDestructionGateParams, DestructionGateOutcome, EvaluateDestructionConsentsParams, LoadDeployPlanParams, DeployPlanLoader } from "./orchestration/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as n,CASCADE_PHASES as E,CASCADE_ACCOUNT_STATUSES as c}from"./types/index.js";import{DeployEventSchema as l,StepProgressSchema as A,DeployResourceSchema as T,PlanChangeSchema as R,PlanPropertyChangeSchema as _,ChangeCountsSchema as p,DEPLOY_EVENT_CONTRACT as m,DEPLOY_EVENT_CONTRACT_MAJOR as d,DEPLOY_EVENT_CONTRACT_MINOR as u,SUPPORTED_CONTRACT_MARKERS as D,DEPLOY_EVENT_ID_MAX as P,DEPLOY_EVENT_NAME_MAX as O,DEPLOY_EVENT_MESSAGE_MAX as f,DEPLOY_EVENT_STATUS_REASON_MAX as C,DEPLOY_EVENT_ERROR_MESSAGE_MAX as N,DEPLOY_EVENT_TRAIL_DETAIL_MAX as g,DEPLOY_EVENT_OUTPUT_MAX as L,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as y,STEP_COMPLETE_STATUSES as h,LOG_LEVELS as x,STACK_CLEANUP_PHASES as v,CASCADE_ACCOUNT_PHASES as F,DEPLOY_RESULTS as M,DETECTION_PHASES as U,PHASE_STATUSES as k,BUILDERS as b,CASCADE_OUTCOME_RESULTS as V,PLAN_ACTIONS as Y,REPLACEMENT_MODES as B,APPROVAL_KINDS as G,APPROVAL_DECISIONS as K,ALL_PROGRESS_KINDS as X,createDeployEmitter as H,parseDeployEvent as w,parseDeployContractVersion as J}from"./types/index.js";import{SimpleAwsProvider as j}from"./aws/index.js";import{checkTargetReadiness as Z,describeTargetReadinessReason as q,buildTargetUnreadyAdvisory as z,buildTargetSetUnreadyAdvisory as $,probeAccountStackExists as ee}from"./aws/index.js";import{checkRegionEnabled as te,regionDisabledMessage as oe}from"./aws/index.js";import{ensureOrganisationExists as ae,describeOrganisation as se,enablePolicyTypes as ne,enableServiceAccess as Ee,SERVICE_PRINCIPALS as ce,enableRamSharing as Se,activateTrustedAccess as le,enableIpamDelegatedAdmin as Ae,updateBackupGlobalSettings as Te,listAccounts as Re,findAccount as _e,createAccount as pe,ensureOrganisationalUnitsExist as me,placeAccountsInOUs as de,buildAccountToOUMap as ue,findDevelopmentOuId as De,activateCostAllocationTags as Pe,checkIdentityCentreStatus as Oe,snapshotIdentityCentre as fe,composeSdkAbortSignal as Ce,extractErrorName as Ne,isAborted as ge,DEFAULT_MAX_PAGES as Le,IDENTITY_STORE_PAGE_SIZE as Ie,drainPages as ye,isOULeaf as he,registerSecurityDelegates as xe,SECURITY_SERVICE_PRINCIPALS as ve,enableCentralisedRootAccess as Fe,ROOT_ACCESS_FEATURES as Me,RootAccessEnablementError as Ue}from"./aws/index.js";import{STEP_IDS as be,STEP_NAMES as Ve,INFRASTRUCTURE_STEP_NAMES as Ye,INFRA_STEP_NAME as Be}from"./types/index.js";import{RemediationVerbSchema as Ke,REMEDIATION_VERB_GLOSS as Xe,DestructionFindingSchema as He,DestructionTicketResourceSchema as we,DestructionTicketSchema as Je,DestructionConsentSchema as Qe,DestructionOutcomeSchema as je,DESTRUCTION_FINDINGS as We,CONSENT_VERDICTS as Ze,TICKET_VERDICT_SOURCES as qe,TicketVerdictSourceSchema as ze}from"./types/index.js";import{DRIFT_VERDICTS as er,DriftVerdictSchema as rr,DriftSuspectSchema as tr,DriftFindingSchema as or,DriftJournalRecordSchema as ir,verbsForDriftVerdict as ar}from"./types/index.js";import{REMEDIATION_PHASES as nr,RemediationPhaseSchema as Er,RemediationTargetSchema as cr,RemediationFlipProofSchema as Sr,RemediationJournalRecordSchema as lr,RemediationVerbWithSurgerySchema as Ar,remediationPhaseRank as Tr,remediationPlaceholderPhysicalId as Rr,isRemediationPlaceholderPhysicalId as _r}from"./types/index.js";import{ProgressReporter as mr,APPLICATION_STACKS as dr,ORGANISATION_TYPES as ur,APPLICATION_DEPLOY_ORDER as Dr,APPLICATION_DESTROY_ORDER as Pr,OPENNEXT_DEPLOY_ORDER as Or,OPENNEXT_DESTROY_ORDER as fr,PARALLEL_DEPLOY_GROUPS as Cr,PARALLEL_DESTROY_GROUPS as Nr,OPENNEXT_PARALLEL_GROUPS as gr,PARALLEL_OPERATION_TYPES as Lr,isApplicationOperation as Ir,isOrganisationOperation as yr,getParallelDeployGroups as hr,getParallelDestroyGroups as xr,getApplicationDeployOrder as vr,getApplicationDestroyOrder as Fr,getApplicationStackName as Mr,getOrganisationStackName as Ur,isApplicationStack as kr,isQuarantineDetail as br,isRetainedBucketsDetail as Vr,getApplicationStepName as Yr,getApplicationStepId as Br,toPascalCase as Gr,isOpenNextPattern as Kr,OPENNEXT_PATTERNS as Xr,STACK_NOT_FOUND_PATTERN as Hr,STACK_FAILED_STATE_PATTERN as wr,CDK_NO_STACKS_MATCH as Jr,INFRASTRUCTURE_FILENAME as Qr,ApplicationError as jr,wrapApplicationError as Wr,stubCallerIdentity as Zr}from"./types/index.js";import{deriveResourcesFromManifestStacks as zr}from"./types/detection/patternDetection.js";import{FjallStateFileSchema as et,readStateFile as rt,writeStateFile as tt,createEmptyState as ot,deleteStateFile as it,updateTemplateHash as at,getStateFilePath as st,regionSuffix as nt}from"./types/config/FjallState.js";import{readDomainState as ct,writeDomainState as St,recordDomainDeployState as lt,deleteDomainState as At,hashDomainDirectory as Tt,LEGACY_DOMAIN_STATE_FILENAME as Rt}from"./types/config/DomainState.js";import{CloudFormationEventMonitor as pt}from"./aws/index.js";import{CdkService as dt,CdkArgumentBuilder as ut,CdkProcessManager as Dt,CdkEventMonitor as Pt,startStackMonitoring as Ot,DEFAULT_DEPLOY_TIMEOUT_MS as ft,isCdkError as Ct,formatInfrastructureError as Nt,getStructuralHint as gt,getSourceContext as Lt,hasCdkDifferences as It,parseDiffOutput as yt,CloudFormationService as ht,CloudFormationError as xt,EcsService as vt,EcsError as Ft,EcsServiceResolver as Mt,TemplateHashService as Ut,TemplateHashError as kt,CdkContextBuilder as bt,emitProgress as Vt,PROGRESS_MESSAGES as Yt,parseBuildPhase as Bt,buildStepContextBuildConfig as Gt,convertCloudFormationOutputsToRecord as Kt,ApplicationStackService as Xt}from"./services/index.js";import{CdkError as wt}from"./types/errors/index.js";import{BaseServiceError as Qt,ValidationError as jt,AuthError as Wt,AwsError as Zt,DeploymentError as qt,NetworkError as zt,FileSystemError as $t,ConfigError as eo,toServiceError as ro}from"./types/errors/index.js";import{filterDangerousEnvVars as oo,maskSensitiveOutput as io,parseShellArgs as ao,sleep as so}from"@fjall/util";import{artefactOutputKey as Eo,DeployModeSchema as co,ServiceArtefactSchema as So,ServiceArtefactsSchema as lo}from"@fjall/util";import{hasDockerfile as To}from"./util/dockerfileDetection.js";import{sleepAbortable as _o}from"./util/sleepAbortable.js";import{createSequencedCallbacks as mo}from"./util/sequencedCallbacks.js";import{fileExists as Do}from"@fjall/util/fsHelpers";import{success as Oo,failure as fo,isSuccess as Co,isFailure as No}from"@fjall/generator";import{deploy as Lo}from"./orchestration/index.js";import{destroy as yo}from"./orchestration/index.js";import{restart as xo,probeSecretsDrift as vo,computeSecretsDrift as Fo,deriveStaleParameters as Mo,extractConsumedSsmParameters as Uo}from"./orchestration/index.js";import{partitionAccounts as bo}from"./orchestration/index.js";import{buildRegionList as Yo,buildAccountRegionPairs as Bo,cascadeHomeRegion as Go,cascadeOperationKey as Ko}from"./orchestration/index.js";import{projectScalarSummary as Ho,projectAccountRows as wo}from"./orchestration/index.js";import{reconcileProviderAccounts as Qo,mergeReconciledProviderAccounts as jo}from"./orchestration/index.js";import{decideNextTransition as Zo,reconcileTrailMigration as qo,decommissionMemberTrailStorage as zo,ORG_TRAIL_BUCKET_OUTPUT_KEY as $o,TRAIL_BUCKET_OUTPUT_KEY as ei,TRAIL_KEY_ARN_OUTPUT_KEY as ri}from"./orchestration/index.js";import{classifyDriftFailure as oi,fetchLastOperationEvents as ii,DriftProbe as ai,clearDriftSuspects as si,defaultDriftJournalDir as ni,readDriftSuspects as Ei,recordDriftSuspects as ci,detectStackDrift as Si,runDriftPreFlight as li,formatDriftPreFlightBlock as Ai,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Ti,DRIFT_PREFLIGHT_BUDGET_MS as Ri,runRoute53RecordPreflight as _i}from"./orchestration/index.js";import{archiveCompletedRemediationJournal as mi,computeRemediationOpId as di,defaultRemediationJournalDir as ui,findRemediationJournalByOpId as Di,listActiveRemediationOps as Pi,listActiveRemediationOpsForContext as Oi,readRemediationJournal as fi,sweepExpiredRemediationJournals as Ci,writeRemediationJournal as Ni,REMEDIATION_JOURNAL_RETENTION_DAYS as gi,captureRemediationForensics as Li,defaultRemediationForensicsDir as Ii,applyRetainFlip as yi,composeFlipCapabilities as hi,computeTemplateDigest as xi,deriveRetainFlipTemplate as vi,findMarkedResourcesByOpId as Fi,proveFlipMetadataOnly as Mi,readRetainFlipState as Ui,verifyRetainFlipDiff as ki,FORGET_MARKER_KEY as bi,FORGET_FLIP_SPEC as Vi,RECREATE_MARKER_KEY as Yi,assessForgetResumability as Bi,completeForgetAfterConverge as Gi,completeForgetAfterDeploy as Ki,formatRecreateInFlightCures as Xi,partitionPreFlightByRemediation as Hi,runForgetSurgery as wi,synthTemplateDeclaresResource as Ji,runRecreateSurgery as Qi,snapshotPolicyForType as ji,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Wi,runRecreatePreFlight as Zi,deriveRemovalTemplate as qi,findRemovalDeletionFailures as zi,findResourcesPresent as $i,proveRemovalTargetsOnly as ea,advertisableDriftVerbs as ra,buildDriftRepairTicket as ta,runDriftRepairGate as oa,formatPinOutcomeDetail as ia,pinProbeCoverage as aa,renderPinReportLines as sa,runPinRemediation as na,composePostFailureRepairOffer as Ea,formatDriftRepairOffer as ca,DriftRepairAvailableError as Sa,checkDeployPathStackAvailability as la,isDeployPathBlockingStatus as Aa,StackUnavailableError as Ta,assertLeaseBeforeStack as Ra,LeaseDeniedError as _a}from"./orchestration/index.js";import{unlockBucket as ma,unlockQueue as da}from"./orchestration/index.js";import{triageBucketPolicy as Da,isEnforceSslStatement as Pa,toResourcePolicyStatements as Oa,restoreBucketPolicy as fa,synthesiseEnforceSslDocument as Ca,ensureEnforceSsl as Na,restoreAndReconcileQuarantinedBucket as ga}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Ia}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as ha,isRootTaskPolicyArn as xa,ROOT_TASK_POLICY_ARNS as va,MAX_ROOT_SESSION_SECONDS as Fa}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as Ua,flattenAccountsToEnvironments as ka,extractAllAccountNames as ba,accountsConfigToOUTree as Va,isStringArray as Ya,isAccountsConfig as Ba,isOuOnlyAccountBucket as Ga,OU_ONLY_ACCOUNT_BUCKETS as Ka}from"./orchestration/index.js";import{runOpenNextBuild as Ha}from"./orchestration/index.js";import{runOrganisationSetup as Ja,ORG_SETUP_PHASES as Qa}from"./orchestration/index.js";import{DOMAIN_DELEGATION_ERROR_TYPES as Wa,DomainDelegationError as Za,DEFAULT_PUBLIC_RESOLVER_ADDRESSES as qa,PUBLIC_DNS_QUERY_TIMEOUT_MS as za,createPublicResolvers as $a,NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS as es,NS_PROPAGATION_DEFAULT_INTERVAL_MS as rs,waitForNsPropagation as ts,ACM_CAA_ISSUERS as os,runCaaPreflight as is,deployDelegatedDomain as as,CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE as ss,templateOwnsDelegationRecord as ns,deleteChildNsFromParent as Es,nsDelegationPhysicalName as cs,runDelegatedDomainDestroy as Ss}from"./orchestration/index.js";import{classifyZoneRecords as As,buildSatelliteRecordIndex as Ts,createSystemDnsNameProbe as Rs,systemDnsNameProbe as _s,normaliseRecordName as ps,isRoute53NameServer as ms,isDelegatedToRoute53 as ds}from"./orchestration/index.js";import{resolveBuildSecrets as Ds,resolveSecretRefValue as Ps,resolveBuildSecretSessionProvider as Os,sourcedRefsFromBuildArgs as fs,resolveBuildArgs as Cs,buildBuildSecretSessionPolicy as Ns,buildDeploySessionPolicy as gs,partitionForRegion as Ls,validateBuildGroup as Is}from"./orchestration/index.js";import{FrameworkRegistry as hs}from"./orchestration/index.js";import{openNextBuilder as vs,staticSiteBuilder as Fs,dockerBuilder as Ms}from"./orchestration/index.js";import{StepRegistry as ks,getDestroyStepId as bs}from"./steps/index.js";import{buildDeployPlan as Ys,computeDeployPlan as Bs,computeAssemblyDigest as Gs,digestHead as Ks,classifyImpact as Xs,derivePropertyChanges as Hs,isDataLoss as ws,isStatefulResourceType as Js,renderPlanSummary as Qs,renderPlanLines as js,toWirePlanChanges as Ws,signApprovalToken as Zs,verifyApprovalToken as qs,DEFAULT_APPROVAL_TTL_MS as zs,APPROVAL_TOKEN_PATTERN as $s,runApprovalGate as en,approvalRefusalReason as rn}from"./orchestration/index.js";import{runDestructionGate as on,buildDestructionTicket as an,evaluateDestructionConsents as sn,computeTicketDigest as nn,legalRemediationVerbs as En,executableRemediationVerbs as cn,advertisableRemediationVerbs as Sn,renderDestructionTicketLines as ln,renderConsentVerdictLines as An,loadDeployPlan as Tn,createMemoisedPlanLoader as Rn}from"./orchestration/index.js";export{os as ACM_CAA_ISSUERS,X as ALL_PROGRESS_KINDS,Dr as APPLICATION_DEPLOY_ORDER,Pr as APPLICATION_DESTROY_ORDER,dr as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,$s as APPROVAL_TOKEN_PATTERN,jr as ApplicationError,Xt as ApplicationStackService,Wt as AuthError,Zt as AwsError,b as BUILDERS,Qt as BaseServiceError,F as CASCADE_ACCOUNT_PHASES,c as CASCADE_ACCOUNT_STATUSES,V as CASCADE_OUTCOME_RESULTS,E as CASCADE_PHASES,Jr as CDK_NO_STACKS_MATCH,Ze as CONSENT_VERDICTS,ss as CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE,ut as CdkArgumentBuilder,bt as CdkContextBuilder,wt as CdkError,Pt as CdkEventMonitor,Dt as CdkProcessManager,dt as CdkService,p as ChangeCountsSchema,xt as CloudFormationError,pt as CloudFormationEventMonitor,ht as CloudFormationService,eo as ConfigError,zs as DEFAULT_APPROVAL_TTL_MS,ft as DEFAULT_DEPLOY_TIMEOUT_MS,Le as DEFAULT_MAX_PAGES,qa as DEFAULT_PUBLIC_RESOLVER_ADDRESSES,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,n as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,I as DEPLOY_EVENT_ARN_MAX,m as DEPLOY_EVENT_CONTRACT,d as DEPLOY_EVENT_CONTRACT_MAJOR,u as DEPLOY_EVENT_CONTRACT_MINOR,N as DEPLOY_EVENT_ERROR_MESSAGE_MAX,P as DEPLOY_EVENT_ID_MAX,f as DEPLOY_EVENT_MESSAGE_MAX,O as DEPLOY_EVENT_NAME_MAX,L as DEPLOY_EVENT_OUTPUT_MAX,C as DEPLOY_EVENT_STATUS_REASON_MAX,g as DEPLOY_EVENT_TRAIL_DETAIL_MAX,y as DEPLOY_EVENT_URL_MAX,M as DEPLOY_RESULTS,We as DESTRUCTION_FINDINGS,U as DETECTION_PHASES,Wa as DOMAIN_DELEGATION_ERROR_TYPES,Ri as DRIFT_PREFLIGHT_BUDGET_MS,Ti as DRIFT_PREFLIGHT_TRIGGER_STATUSES,er as DRIFT_VERDICTS,l as DeployEventSchema,co as DeployModeSchema,T as DeployResourceSchema,qt as DeploymentError,t as DeploymentEventSchema,Qe as DestructionConsentSchema,He as DestructionFindingSchema,je as DestructionOutcomeSchema,we as DestructionTicketResourceSchema,Je as DestructionTicketSchema,Za as DomainDelegationError,or as DriftFindingSchema,ir as DriftJournalRecordSchema,ai as DriftProbe,Sa as DriftRepairAvailableError,tr as DriftSuspectSchema,rr as DriftVerdictSchema,Ft as EcsError,vt as EcsService,Mt as EcsServiceResolver,Vi as FORGET_FLIP_SPEC,bi as FORGET_MARKER_KEY,$t as FileSystemError,et as FjallStateFileSchema,hs as FrameworkRegistry,Ie as IDENTITY_STORE_PAGE_SIZE,Qr as INFRASTRUCTURE_FILENAME,Ye as INFRASTRUCTURE_STEP_NAMES,Be as INFRA_STEP_NAME,Rt as LEGACY_DOMAIN_STATE_FILENAME,x as LOG_LEVELS,_a as LeaseDeniedError,Fa as MAX_ROOT_SESSION_SECONDS,rs as NS_PROPAGATION_DEFAULT_INTERVAL_MS,es as NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS,zt as NetworkError,Or as OPENNEXT_DEPLOY_ORDER,fr as OPENNEXT_DESTROY_ORDER,gr as OPENNEXT_PARALLEL_GROUPS,Xr as OPENNEXT_PATTERNS,ur as ORGANISATION_TYPES,Qa as ORG_SETUP_PHASES,$o as ORG_TRAIL_BUCKET_OUTPUT_KEY,Ka as OU_ONLY_ACCOUNT_BUCKETS,Cr as PARALLEL_DEPLOY_GROUPS,Nr as PARALLEL_DESTROY_GROUPS,Lr as PARALLEL_OPERATION_TYPES,k as PHASE_STATUSES,Y as PLAN_ACTIONS,Yt as PROGRESS_MESSAGES,za as PUBLIC_DNS_QUERY_TIMEOUT_MS,R as PlanChangeSchema,_ as PlanPropertyChangeSchema,mr as ProgressReporter,Yi as RECREATE_MARKER_KEY,gi as REMEDIATION_JOURNAL_RETENTION_DAYS,nr as REMEDIATION_PHASES,Xe as REMEDIATION_VERB_GLOSS,B as REPLACEMENT_MODES,Me as ROOT_ACCESS_FEATURES,va as ROOT_TASK_POLICY_ARNS,Sr as RemediationFlipProofSchema,lr as RemediationJournalRecordSchema,Er as RemediationPhaseSchema,cr as RemediationTargetSchema,Ke as RemediationVerbSchema,Ar as RemediationVerbWithSurgerySchema,Ue as RootAccessEnablementError,ve as SECURITY_SERVICE_PRINCIPALS,ce as SERVICE_PRINCIPALS,Wi as SNAPSHOT_CAPABLE_RESOURCE_TYPES,v as STACK_CLEANUP_PHASES,wr as STACK_FAILED_STATE_PATTERN,Hr as STACK_NOT_FOUND_PATTERN,h as STEP_COMPLETE_STATUSES,be as STEP_IDS,Ve as STEP_NAMES,D as SUPPORTED_CONTRACT_MARKERS,So as ServiceArtefactSchema,lo as ServiceArtefactsSchema,j as SimpleAwsProvider,Ta as StackUnavailableError,A as StepProgressSchema,ks as StepRegistry,qe as TICKET_VERDICT_SOURCES,ei as TRAIL_BUCKET_OUTPUT_KEY,ri as TRAIL_KEY_ARN_OUTPUT_KEY,kt as TemplateHashError,Ut as TemplateHashService,ze as TicketVerdictSourceSchema,jt as ValidationError,Va as accountsConfigToOUTree,Pe as activateCostAllocationTags,le as activateTrustedAccess,ra as advertisableDriftVerbs,Sn as advertisableRemediationVerbs,yi as applyRetainFlip,rn as approvalRefusalReason,mi as archiveCompletedRemediationJournal,Eo as artefactOutputKey,Ra as assertLeaseBeforeStack,Bi as assessForgetResumability,ha as assumeRootForTask,Bo as buildAccountRegionPairs,ue as buildAccountToOUMap,Ns as buildBuildSecretSessionPolicy,Ys as buildDeployPlan,gs as buildDeploySessionPolicy,an as buildDestructionTicket,ta as buildDriftRepairTicket,Yo as buildRegionList,Ts as buildSatelliteRecordIndex,Gt as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,z as buildTargetUnreadyAdvisory,Li as captureRemediationForensics,Go as cascadeHomeRegion,Ko as cascadeOperationKey,la as checkDeployPathStackAvailability,Oe as checkIdentityCentreStatus,te as checkRegionEnabled,Z as checkTargetReadiness,oi as classifyDriftFailure,Xs as classifyImpact,As as classifyZoneRecords,si as clearDriftSuspects,Gi as completeForgetAfterConverge,Ki as completeForgetAfterDeploy,hi as composeFlipCapabilities,Ea as composePostFailureRepairOffer,Ce as composeSdkAbortSignal,Gs as computeAssemblyDigest,Bs as computeDeployPlan,di as computeRemediationOpId,Fo as computeSecretsDrift,xi as computeTemplateDigest,nn as computeTicketDigest,Kt as convertCloudFormationOutputsToRecord,pe as createAccount,H as createDeployEmitter,ot as createEmptyState,Rn as createMemoisedPlanLoader,$a as createPublicResolvers,mo as createSequencedCallbacks,Rs as createSystemDnsNameProbe,Zo as decideNextTransition,zo as decommissionMemberTrailStorage,ni as defaultDriftJournalDir,Ii as defaultRemediationForensicsDir,ui as defaultRemediationJournalDir,Es as deleteChildNsFromParent,At as deleteDomainState,it as deleteStateFile,Lo as deploy,as as deployDelegatedDomain,Hs as derivePropertyChanges,qi as deriveRemovalTemplate,zr as deriveResourcesFromManifestStacks,vi as deriveRetainFlipTemplate,Mo as deriveStaleParameters,se as describeOrganisation,q as describeTargetReadinessReason,yo as destroy,Si as detectStackDrift,Ks as digestHead,Ms as dockerBuilder,ye as drainPages,Vt as emitProgress,Fe as enableCentralisedRootAccess,Ae as enableIpamDelegatedAdmin,ne as enablePolicyTypes,Se as enableRamSharing,Ee as enableServiceAccess,Na as ensureEnforceSsl,ae as ensureOrganisationExists,me as ensureOrganisationalUnitsExist,sn as evaluateDestructionConsents,cn as executableRemediationVerbs,ba as extractAllAccountNames,Uo as extractConsumedSsmParameters,Ne as extractErrorName,fo as failure,ii as fetchLastOperationEvents,Do as fileExists,oo as filterDangerousEnvVars,_e as findAccount,De as findDevelopmentOuId,Fi as findMarkedResourcesByOpId,Di as findRemediationJournalByOpId,zi as findRemovalDeletionFailures,$i as findResourcesPresent,ka as flattenAccountsToEnvironments,Ai as formatDriftPreFlightBlock,ca as formatDriftRepairOffer,Nt as formatInfrastructureError,ia as formatPinOutcomeDetail,Xi as formatRecreateInFlightCures,vr as getApplicationDeployOrder,Fr as getApplicationDestroyOrder,Mr as getApplicationStackName,Br as getApplicationStepId,Yr as getApplicationStepName,bs as getDestroyStepId,Ur as getOrganisationStackName,hr as getParallelDeployGroups,xr as getParallelDestroyGroups,Lt as getSourceContext,st as getStateFilePath,gt as getStructuralHint,It as hasCdkDifferences,To as hasDockerfile,Tt as hashDomainDirectory,ge as isAborted,Ba as isAccountsConfig,Ir as isApplicationOperation,kr as isApplicationStack,Ct as isCdkError,ws as isDataLoss,ds as isDelegatedToRoute53,Aa as isDeployPathBlockingStatus,Pa as isEnforceSslStatement,No as isFailure,he as isOULeaf,Kr as isOpenNextPattern,yr as isOrganisationOperation,Ga as isOuOnlyAccountBucket,br as isQuarantineDetail,_r as isRemediationPlaceholderPhysicalId,Vr as isRetainedBucketsDetail,xa as isRootTaskPolicyArn,ms as isRoute53NameServer,Js as isStatefulResourceType,Ya as isStringArray,Co as isSuccess,En as legalRemediationVerbs,Re as listAccounts,Pi as listActiveRemediationOps,Oi as listActiveRemediationOpsForContext,Tn as loadDeployPlan,io as maskSensitiveOutput,jo as mergeReconciledProviderAccounts,ps as normaliseRecordName,cs as nsDelegationPhysicalName,vs as openNextBuilder,Ua as parseAccountsConfiguration,Bt as parseBuildPhase,J as parseDeployContractVersion,w as parseDeployEvent,yt as parseDiffOutput,ao as parseShellArgs,bo as partitionAccounts,Ls as partitionForRegion,Hi as partitionPreFlightByRemediation,aa as pinProbeCoverage,de as placeAccountsInOUs,ee as probeAccountStackExists,vo as probeSecretsDrift,wo as projectAccountRows,Ho as projectScalarSummary,Mi as proveFlipMetadataOnly,ea as proveRemovalTargetsOnly,ct as readDomainState,Ei as readDriftSuspects,fi as readRemediationJournal,Ui as readRetainFlipState,rt as readStateFile,Qo as reconcileProviderAccounts,qo as reconcileTrailMigration,lt as recordDomainDeployState,ci as recordDriftSuspects,oe as regionDisabledMessage,nt as regionSuffix,xe as registerSecurityDelegates,Tr as remediationPhaseRank,Rr as remediationPlaceholderPhysicalId,An as renderConsentVerdictLines,ln as renderDestructionTicketLines,sa as renderPinReportLines,js as renderPlanLines,Qs as renderPlanSummary,Cs as resolveBuildArgs,Os as resolveBuildSecretSessionProvider,Ds as resolveBuildSecrets,Ps as resolveSecretRefValue,xo as restart,ga as restoreAndReconcileQuarantinedBucket,fa as restoreBucketPolicy,en as runApprovalGate,is as runCaaPreflight,Ss as runDelegatedDomainDestroy,on as runDestructionGate,li as runDriftPreFlight,oa as runDriftRepairGate,wi as runForgetSurgery,Ha as runOpenNextBuild,Ja as runOrganisationSetup,na as runPinRemediation,Zi as runRecreatePreFlight,Qi as runRecreateSurgery,_i as runRoute53RecordPreflight,Zs as signApprovalToken,so as sleep,_o as sleepAbortable,fe as snapshotIdentityCentre,ji as snapshotPolicyForType,fs as sourcedRefsFromBuildArgs,Ot as startStackMonitoring,Fs as staticSiteBuilder,Zr as stubCallerIdentity,Oo as success,Ci as sweepExpiredRemediationJournals,Ji as synthTemplateDeclaresResource,Ca as synthesiseEnforceSslDocument,_s as systemDnsNameProbe,ns as templateOwnsDelegationRecord,Gr as toPascalCase,Oa as toResourcePolicyStatements,ro as toServiceError,Ws as toWirePlanChanges,Da as triageBucketPolicy,ma as unlockBucket,da as unlockQueue,Te as updateBackupGlobalSettings,at as updateTemplateHash,Is as validateBuildGroup,ar as verbsForDriftVerdict,qs as verifyApprovalToken,Ia as verifyOrgTrailDelivery,ki as verifyRetainFlipDiff,ts as waitForNsPropagation,Wr as wrapApplicationError,St as writeDomainState,Ni as writeRemediationJournal,tt as writeStateFile};
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as n,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as s,DEPLOYMENT_ERROR_MESSAGE_MAX as E,CASCADE_PHASES as c,CASCADE_ACCOUNT_STATUSES as S,clampDeploymentEventStrings as l,truncateUtf16Safe as A}from"./types/index.js";import{DeployEventSchema as T,StepProgressSchema as _,DeployResourceSchema as p,PlanChangeSchema as m,PlanPropertyChangeSchema as d,ChangeCountsSchema as u,DEPLOY_EVENT_CONTRACT as D,DEPLOY_EVENT_CONTRACT_MAJOR as P,DEPLOY_EVENT_CONTRACT_MINOR as O,SUPPORTED_CONTRACT_MARKERS as f,DEPLOY_EVENT_ID_MAX as C,DEPLOY_EVENT_NAME_MAX as N,DEPLOY_EVENT_MESSAGE_MAX as g,DEPLOY_EVENT_STATUS_REASON_MAX as L,DEPLOY_EVENT_ERROR_MESSAGE_MAX as I,DEPLOY_EVENT_TRAIL_DETAIL_MAX as y,DEPLOY_EVENT_OUTPUT_MAX as h,DEPLOY_EVENT_ARN_MAX as M,DEPLOY_EVENT_URL_MAX as v,STEP_COMPLETE_STATUSES as x,LOG_LEVELS as F,STACK_CLEANUP_PHASES as U,CASCADE_ACCOUNT_PHASES as k,DEPLOY_RESULTS as b,DETECTION_PHASES as Y,PHASE_STATUSES as V,BUILDERS as G,CASCADE_OUTCOME_RESULTS as B,PLAN_ACTIONS as K,REPLACEMENT_MODES as X,APPROVAL_KINDS as H,APPROVAL_DECISIONS as w,ALL_PROGRESS_KINDS as J,createDeployEmitter as Q,parseDeployEvent as j,parseDeployContractVersion as W}from"./types/index.js";import{SimpleAwsProvider as q}from"./aws/index.js";import{checkTargetReadiness as $,describeTargetReadinessReason as ee,buildTargetUnreadyAdvisory as re,buildTargetSetUnreadyAdvisory as te,probeAccountStackExists as oe}from"./aws/index.js";import{checkRegionEnabled as ae,regionDisabledMessage as ne}from"./aws/index.js";import{ensureOrganisationExists as Ee,describeOrganisation as ce,enablePolicyTypes as Se,enableServiceAccess as le,SERVICE_PRINCIPALS as Ae,enableRamSharing as Re,activateTrustedAccess as Te,enableIpamDelegatedAdmin as _e,updateBackupGlobalSettings as pe,listAccounts as me,findAccount as de,createAccount as ue,ensureOrganisationalUnitsExist as De,placeAccountsInOUs as Pe,buildAccountToOUMap as Oe,findDevelopmentOuId as fe,activateCostAllocationTags as Ce,checkIdentityCentreStatus as Ne,snapshotIdentityCentre as ge,composeSdkAbortSignal as Le,extractErrorName as Ie,isAborted as ye,DEFAULT_MAX_PAGES as he,IDENTITY_STORE_PAGE_SIZE as Me,drainPages as ve,isOULeaf as xe,registerSecurityDelegates as Fe,SECURITY_SERVICE_PRINCIPALS as Ue,enableCentralisedRootAccess as ke,ROOT_ACCESS_FEATURES as be,RootAccessEnablementError as Ye}from"./aws/index.js";import{STEP_IDS as Ge,STEP_NAMES as Be,INFRASTRUCTURE_STEP_NAMES as Ke,INFRA_STEP_NAME as Xe}from"./types/index.js";import{RemediationVerbSchema as we,REMEDIATION_VERB_GLOSS as Je,DestructionFindingSchema as Qe,DestructionTicketResourceSchema as je,DestructionTicketSchema as We,DestructionConsentSchema as Ze,DestructionOutcomeSchema as qe,DESTRUCTION_FINDINGS as ze,CONSENT_VERDICTS as $e,TICKET_VERDICT_SOURCES as er,TicketVerdictSourceSchema as rr}from"./types/index.js";import{DRIFT_VERDICTS as or,DriftVerdictSchema as ir,DriftSuspectSchema as ar,DriftFindingSchema as nr,DriftJournalRecordSchema as sr,verbsForDriftVerdict as Er}from"./types/index.js";import{REMEDIATION_PHASES as Sr,RemediationPhaseSchema as lr,RemediationTargetSchema as Ar,RemediationFlipProofSchema as Rr,RemediationJournalRecordSchema as Tr,RemediationVerbWithSurgerySchema as _r,remediationPhaseRank as pr,remediationPlaceholderPhysicalId as mr,isRemediationPlaceholderPhysicalId as dr}from"./types/index.js";import{ProgressReporter as Dr,APPLICATION_STACKS as Pr,ORGANISATION_TYPES as Or,APPLICATION_DEPLOY_ORDER as fr,APPLICATION_DESTROY_ORDER as Cr,OPENNEXT_DEPLOY_ORDER as Nr,OPENNEXT_DESTROY_ORDER as gr,PARALLEL_DEPLOY_GROUPS as Lr,PARALLEL_DESTROY_GROUPS as Ir,OPENNEXT_PARALLEL_GROUPS as yr,PARALLEL_OPERATION_TYPES as hr,isApplicationOperation as Mr,isOrganisationOperation as vr,getParallelDeployGroups as xr,getParallelDestroyGroups as Fr,getApplicationDeployOrder as Ur,getApplicationDestroyOrder as kr,getApplicationStackName as br,getOrganisationStackName as Yr,isApplicationStack as Vr,isQuarantineDetail as Gr,isRetainedBucketsDetail as Br,getApplicationStepName as Kr,getApplicationStepId as Xr,toPascalCase as Hr,isOpenNextPattern as wr,OPENNEXT_PATTERNS as Jr,STACK_NOT_FOUND_PATTERN as Qr,STACK_FAILED_STATE_PATTERN as jr,CDK_NO_STACKS_MATCH as Wr,INFRASTRUCTURE_FILENAME as Zr,ApplicationError as qr,wrapApplicationError as zr,stubCallerIdentity as $r}from"./types/index.js";import{deriveResourcesFromManifestStacks as rt}from"./types/detection/patternDetection.js";import{FjallStateFileSchema as ot,readStateFile as it,writeStateFile as at,createEmptyState as nt,deleteStateFile as st,updateTemplateHash as Et,getStateFilePath as ct,regionSuffix as St}from"./types/config/FjallState.js";import{readDomainState as At,writeDomainState as Rt,recordDomainDeployState as Tt,deleteDomainState as _t,hashDomainDirectory as pt,LEGACY_DOMAIN_STATE_FILENAME as mt}from"./types/config/DomainState.js";import{CloudFormationEventMonitor as ut}from"./aws/index.js";import{CdkService as Pt,CdkArgumentBuilder as Ot,CdkProcessManager as ft,CdkEventMonitor as Ct,startStackMonitoring as Nt,DEFAULT_DEPLOY_TIMEOUT_MS as gt,isCdkError as Lt,formatInfrastructureError as It,getStructuralHint as yt,getSourceContext as ht,hasCdkDifferences as Mt,parseDiffOutput as vt,CloudFormationService as xt,CloudFormationError as Ft,EcsService as Ut,EcsError as kt,EcsServiceResolver as bt,TemplateHashService as Yt,TemplateHashError as Vt,CdkContextBuilder as Gt,emitProgress as Bt,PROGRESS_MESSAGES as Kt,parseBuildPhase as Xt,buildStepContextBuildConfig as Ht,convertCloudFormationOutputsToRecord as wt,ApplicationStackService as Jt}from"./services/index.js";import{CdkError as jt}from"./types/errors/index.js";import{BaseServiceError as Zt,ValidationError as qt,AuthError as zt,AwsError as $t,DeploymentError as eo,NetworkError as ro,FileSystemError as to,ConfigError as oo,toServiceError as io}from"./types/errors/index.js";import{filterDangerousEnvVars as no,maskSensitiveOutput as so,parseShellArgs as Eo,sleep as co}from"@fjall/util";import{artefactOutputKey as lo,DeployModeSchema as Ao,ServiceArtefactSchema as Ro,ServiceArtefactsSchema as To}from"@fjall/util";import{hasDockerfile as po}from"./util/dockerfileDetection.js";import{sleepAbortable as uo}from"./util/sleepAbortable.js";import{createSequencedCallbacks as Po}from"./util/sequencedCallbacks.js";import{fileExists as fo}from"@fjall/util/fsHelpers";import{success as No,failure as go,isSuccess as Lo,isFailure as Io}from"@fjall/generator";import{deploy as ho}from"./orchestration/index.js";import{destroy as vo}from"./orchestration/index.js";import{restart as Fo,probeSecretsDrift as Uo,computeSecretsDrift as ko,deriveStaleParameters as bo,extractConsumedSsmParameters as Yo}from"./orchestration/index.js";import{partitionAccounts as Go}from"./orchestration/index.js";import{buildRegionList as Ko,buildAccountRegionPairs as Xo,cascadeHomeRegion as Ho,cascadeOperationKey as wo}from"./orchestration/index.js";import{projectScalarSummary as Qo,projectAccountRows as jo}from"./orchestration/index.js";import{reconcileProviderAccounts as Zo,mergeReconciledProviderAccounts as qo}from"./orchestration/index.js";import{decideNextTransition as $o,reconcileTrailMigration as ei,decommissionMemberTrailStorage as ri,ORG_TRAIL_BUCKET_OUTPUT_KEY as ti,TRAIL_BUCKET_OUTPUT_KEY as oi,TRAIL_KEY_ARN_OUTPUT_KEY as ii}from"./orchestration/index.js";import{classifyDriftFailure as ni,fetchLastOperationEvents as si,DriftProbe as Ei,clearDriftSuspects as ci,defaultDriftJournalDir as Si,readDriftSuspects as li,recordDriftSuspects as Ai,detectStackDrift as Ri,runDriftPreFlight as Ti,formatDriftPreFlightBlock as _i,DRIFT_PREFLIGHT_TRIGGER_STATUSES as pi,DRIFT_PREFLIGHT_BUDGET_MS as mi,runRoute53RecordPreflight as di}from"./orchestration/index.js";import{archiveCompletedRemediationJournal as Di,computeRemediationOpId as Pi,defaultRemediationJournalDir as Oi,findRemediationJournalByOpId as fi,listActiveRemediationOps as Ci,listActiveRemediationOpsForContext as Ni,readRemediationJournal as gi,sweepExpiredRemediationJournals as Li,writeRemediationJournal as Ii,REMEDIATION_JOURNAL_RETENTION_DAYS as yi,captureRemediationForensics as hi,defaultRemediationForensicsDir as Mi,applyRetainFlip as vi,composeFlipCapabilities as xi,computeTemplateDigest as Fi,deriveRetainFlipTemplate as Ui,findMarkedResourcesByOpId as ki,proveFlipMetadataOnly as bi,readRetainFlipState as Yi,verifyRetainFlipDiff as Vi,FORGET_MARKER_KEY as Gi,FORGET_FLIP_SPEC as Bi,RECREATE_MARKER_KEY as Ki,assessForgetResumability as Xi,completeForgetAfterConverge as Hi,completeForgetAfterDeploy as wi,formatRecreateInFlightCures as Ji,partitionPreFlightByRemediation as Qi,runForgetSurgery as ji,synthTemplateDeclaresResource as Wi,runRecreateSurgery as Zi,snapshotPolicyForType as qi,SNAPSHOT_CAPABLE_RESOURCE_TYPES as zi,runRecreatePreFlight as $i,deriveRemovalTemplate as ea,findRemovalDeletionFailures as ra,findResourcesPresent as ta,proveRemovalTargetsOnly as oa,advertisableDriftVerbs as ia,buildDriftRepairTicket as aa,runDriftRepairGate as na,formatPinOutcomeDetail as sa,pinProbeCoverage as Ea,renderPinReportLines as ca,runPinRemediation as Sa,composePostFailureRepairOffer as la,formatDriftRepairOffer as Aa,DriftRepairAvailableError as Ra,checkDeployPathStackAvailability as Ta,isDeployPathBlockingStatus as _a,StackUnavailableError as pa,assertLeaseBeforeStack as ma,LeaseDeniedError as da}from"./orchestration/index.js";import{unlockBucket as Da,unlockQueue as Pa}from"./orchestration/index.js";import{triageBucketPolicy as fa,isEnforceSslStatement as Ca,toResourcePolicyStatements as Na,restoreBucketPolicy as ga,synthesiseEnforceSslDocument as La,ensureEnforceSsl as Ia,restoreAndReconcileQuarantinedBucket as ya}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Ma}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as xa,isRootTaskPolicyArn as Fa,ROOT_TASK_POLICY_ARNS as Ua,MAX_ROOT_SESSION_SECONDS as ka}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as Ya,flattenAccountsToEnvironments as Va,extractAllAccountNames as Ga,accountsConfigToOUTree as Ba,isStringArray as Ka,isAccountsConfig as Xa,isOuOnlyAccountBucket as Ha,OU_ONLY_ACCOUNT_BUCKETS as wa}from"./orchestration/index.js";import{runOpenNextBuild as Qa}from"./orchestration/index.js";import{runOrganisationSetup as Wa,ORG_SETUP_PHASES as Za}from"./orchestration/index.js";import{DOMAIN_DELEGATION_ERROR_TYPES as za,DomainDelegationError as $a,DEFAULT_PUBLIC_RESOLVER_ADDRESSES as en,PUBLIC_DNS_QUERY_TIMEOUT_MS as rn,createPublicResolvers as tn,NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS as on,NS_PROPAGATION_DEFAULT_INTERVAL_MS as an,waitForNsPropagation as nn,ACM_CAA_ISSUERS as sn,runCaaPreflight as En,deployDelegatedDomain as cn,CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE as Sn,templateOwnsDelegationRecord as ln,deleteChildNsFromParent as An,nsDelegationPhysicalName as Rn,runDelegatedDomainDestroy as Tn}from"./orchestration/index.js";import{classifyZoneRecords as pn,buildSatelliteRecordIndex as mn,createSystemDnsNameProbe as dn,systemDnsNameProbe as un,normaliseRecordName as Dn,isRoute53NameServer as Pn,isDelegatedToRoute53 as On}from"./orchestration/index.js";import{resolveBuildSecrets as Cn,resolveSecretRefValue as Nn,resolveBuildSecretSessionProvider as gn,sourcedRefsFromBuildArgs as Ln,resolveBuildArgs as In,buildBuildSecretSessionPolicy as yn,buildDeploySessionPolicy as hn,partitionForRegion as Mn,validateBuildGroup as vn}from"./orchestration/index.js";import{FrameworkRegistry as Fn}from"./orchestration/index.js";import{openNextBuilder as kn,staticSiteBuilder as bn,dockerBuilder as Yn}from"./orchestration/index.js";import{StepRegistry as Gn,getDestroyStepId as Bn}from"./steps/index.js";import{buildDeployPlan as Xn,computeDeployPlan as Hn,computeAssemblyDigest as wn,digestHead as Jn,classifyImpact as Qn,derivePropertyChanges as jn,isDataLoss as Wn,isStatefulResourceType as Zn,renderPlanSummary as qn,renderPlanLines as zn,toWirePlanChanges as $n,signApprovalToken as es,verifyApprovalToken as rs,DEFAULT_APPROVAL_TTL_MS as ts,APPROVAL_TOKEN_PATTERN as os,runApprovalGate as is,approvalRefusalReason as as}from"./orchestration/index.js";import{runDestructionGate as ss,buildDestructionTicket as Es,evaluateDestructionConsents as cs,computeTicketDigest as Ss,legalRemediationVerbs as ls,executableRemediationVerbs as As,advertisableRemediationVerbs as Rs,renderDestructionTicketLines as Ts,renderConsentVerdictLines as _s,loadDeployPlan as ps,createMemoisedPlanLoader as ms}from"./orchestration/index.js";export{sn as ACM_CAA_ISSUERS,J as ALL_PROGRESS_KINDS,fr as APPLICATION_DEPLOY_ORDER,Cr as APPLICATION_DESTROY_ORDER,Pr as APPLICATION_STACKS,w as APPROVAL_DECISIONS,H as APPROVAL_KINDS,os as APPROVAL_TOKEN_PATTERN,qr as ApplicationError,Jt as ApplicationStackService,zt as AuthError,$t as AwsError,G as BUILDERS,Zt as BaseServiceError,k as CASCADE_ACCOUNT_PHASES,S as CASCADE_ACCOUNT_STATUSES,B as CASCADE_OUTCOME_RESULTS,c as CASCADE_PHASES,Wr as CDK_NO_STACKS_MATCH,$e as CONSENT_VERDICTS,Sn as CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE,Ot as CdkArgumentBuilder,Gt as CdkContextBuilder,jt as CdkError,Ct as CdkEventMonitor,ft as CdkProcessManager,Pt as CdkService,u as ChangeCountsSchema,Ft as CloudFormationError,ut as CloudFormationEventMonitor,xt as CloudFormationService,oo as ConfigError,ts as DEFAULT_APPROVAL_TTL_MS,gt as DEFAULT_DEPLOY_TIMEOUT_MS,he as DEFAULT_MAX_PAGES,en as DEFAULT_PUBLIC_RESOLVER_ADDRESSES,E as DEPLOYMENT_ERROR_MESSAGE_MAX,n as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,s as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,M as DEPLOY_EVENT_ARN_MAX,D as DEPLOY_EVENT_CONTRACT,P as DEPLOY_EVENT_CONTRACT_MAJOR,O as DEPLOY_EVENT_CONTRACT_MINOR,I as DEPLOY_EVENT_ERROR_MESSAGE_MAX,C as DEPLOY_EVENT_ID_MAX,g as DEPLOY_EVENT_MESSAGE_MAX,N as DEPLOY_EVENT_NAME_MAX,h as DEPLOY_EVENT_OUTPUT_MAX,L as DEPLOY_EVENT_STATUS_REASON_MAX,y as DEPLOY_EVENT_TRAIL_DETAIL_MAX,v as DEPLOY_EVENT_URL_MAX,b as DEPLOY_RESULTS,ze as DESTRUCTION_FINDINGS,Y as DETECTION_PHASES,za as DOMAIN_DELEGATION_ERROR_TYPES,mi as DRIFT_PREFLIGHT_BUDGET_MS,pi as DRIFT_PREFLIGHT_TRIGGER_STATUSES,or as DRIFT_VERDICTS,T as DeployEventSchema,Ao as DeployModeSchema,p as DeployResourceSchema,eo as DeploymentError,t as DeploymentEventSchema,Ze as DestructionConsentSchema,Qe as DestructionFindingSchema,qe as DestructionOutcomeSchema,je as DestructionTicketResourceSchema,We as DestructionTicketSchema,$a as DomainDelegationError,nr as DriftFindingSchema,sr as DriftJournalRecordSchema,Ei as DriftProbe,Ra as DriftRepairAvailableError,ar as DriftSuspectSchema,ir as DriftVerdictSchema,kt as EcsError,Ut as EcsService,bt as EcsServiceResolver,Bi as FORGET_FLIP_SPEC,Gi as FORGET_MARKER_KEY,to as FileSystemError,ot as FjallStateFileSchema,Fn as FrameworkRegistry,Me as IDENTITY_STORE_PAGE_SIZE,Zr as INFRASTRUCTURE_FILENAME,Ke as INFRASTRUCTURE_STEP_NAMES,Xe as INFRA_STEP_NAME,mt as LEGACY_DOMAIN_STATE_FILENAME,F as LOG_LEVELS,da as LeaseDeniedError,ka as MAX_ROOT_SESSION_SECONDS,an as NS_PROPAGATION_DEFAULT_INTERVAL_MS,on as NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS,ro as NetworkError,Nr as OPENNEXT_DEPLOY_ORDER,gr as OPENNEXT_DESTROY_ORDER,yr as OPENNEXT_PARALLEL_GROUPS,Jr as OPENNEXT_PATTERNS,Or as ORGANISATION_TYPES,Za as ORG_SETUP_PHASES,ti as ORG_TRAIL_BUCKET_OUTPUT_KEY,wa as OU_ONLY_ACCOUNT_BUCKETS,Lr as PARALLEL_DEPLOY_GROUPS,Ir as PARALLEL_DESTROY_GROUPS,hr as PARALLEL_OPERATION_TYPES,V as PHASE_STATUSES,K as PLAN_ACTIONS,Kt as PROGRESS_MESSAGES,rn as PUBLIC_DNS_QUERY_TIMEOUT_MS,m as PlanChangeSchema,d as PlanPropertyChangeSchema,Dr as ProgressReporter,Ki as RECREATE_MARKER_KEY,yi as REMEDIATION_JOURNAL_RETENTION_DAYS,Sr as REMEDIATION_PHASES,Je as REMEDIATION_VERB_GLOSS,X as REPLACEMENT_MODES,be as ROOT_ACCESS_FEATURES,Ua as ROOT_TASK_POLICY_ARNS,Rr as RemediationFlipProofSchema,Tr as RemediationJournalRecordSchema,lr as RemediationPhaseSchema,Ar as RemediationTargetSchema,we as RemediationVerbSchema,_r as RemediationVerbWithSurgerySchema,Ye as RootAccessEnablementError,Ue as SECURITY_SERVICE_PRINCIPALS,Ae as SERVICE_PRINCIPALS,zi as SNAPSHOT_CAPABLE_RESOURCE_TYPES,U as STACK_CLEANUP_PHASES,jr as STACK_FAILED_STATE_PATTERN,Qr as STACK_NOT_FOUND_PATTERN,x as STEP_COMPLETE_STATUSES,Ge as STEP_IDS,Be as STEP_NAMES,f as SUPPORTED_CONTRACT_MARKERS,Ro as ServiceArtefactSchema,To as ServiceArtefactsSchema,q as SimpleAwsProvider,pa as StackUnavailableError,_ as StepProgressSchema,Gn as StepRegistry,er as TICKET_VERDICT_SOURCES,oi as TRAIL_BUCKET_OUTPUT_KEY,ii as TRAIL_KEY_ARN_OUTPUT_KEY,Vt as TemplateHashError,Yt as TemplateHashService,rr as TicketVerdictSourceSchema,qt as ValidationError,Ba as accountsConfigToOUTree,Ce as activateCostAllocationTags,Te as activateTrustedAccess,ia as advertisableDriftVerbs,Rs as advertisableRemediationVerbs,vi as applyRetainFlip,as as approvalRefusalReason,Di as archiveCompletedRemediationJournal,lo as artefactOutputKey,ma as assertLeaseBeforeStack,Xi as assessForgetResumability,xa as assumeRootForTask,Xo as buildAccountRegionPairs,Oe as buildAccountToOUMap,yn as buildBuildSecretSessionPolicy,Xn as buildDeployPlan,hn as buildDeploySessionPolicy,Es as buildDestructionTicket,aa as buildDriftRepairTicket,Ko as buildRegionList,mn as buildSatelliteRecordIndex,Ht as buildStepContextBuildConfig,te as buildTargetSetUnreadyAdvisory,re as buildTargetUnreadyAdvisory,hi as captureRemediationForensics,Ho as cascadeHomeRegion,wo as cascadeOperationKey,Ta as checkDeployPathStackAvailability,Ne as checkIdentityCentreStatus,ae as checkRegionEnabled,$ as checkTargetReadiness,l as clampDeploymentEventStrings,ni as classifyDriftFailure,Qn as classifyImpact,pn as classifyZoneRecords,ci as clearDriftSuspects,Hi as completeForgetAfterConverge,wi as completeForgetAfterDeploy,xi as composeFlipCapabilities,la as composePostFailureRepairOffer,Le as composeSdkAbortSignal,wn as computeAssemblyDigest,Hn as computeDeployPlan,Pi as computeRemediationOpId,ko as computeSecretsDrift,Fi as computeTemplateDigest,Ss as computeTicketDigest,wt as convertCloudFormationOutputsToRecord,ue as createAccount,Q as createDeployEmitter,nt as createEmptyState,ms as createMemoisedPlanLoader,tn as createPublicResolvers,Po as createSequencedCallbacks,dn as createSystemDnsNameProbe,$o as decideNextTransition,ri as decommissionMemberTrailStorage,Si as defaultDriftJournalDir,Mi as defaultRemediationForensicsDir,Oi as defaultRemediationJournalDir,An as deleteChildNsFromParent,_t as deleteDomainState,st as deleteStateFile,ho as deploy,cn as deployDelegatedDomain,jn as derivePropertyChanges,ea as deriveRemovalTemplate,rt as deriveResourcesFromManifestStacks,Ui as deriveRetainFlipTemplate,bo as deriveStaleParameters,ce as describeOrganisation,ee as describeTargetReadinessReason,vo as destroy,Ri as detectStackDrift,Jn as digestHead,Yn as dockerBuilder,ve as drainPages,Bt as emitProgress,ke as enableCentralisedRootAccess,_e as enableIpamDelegatedAdmin,Se as enablePolicyTypes,Re as enableRamSharing,le as enableServiceAccess,Ia as ensureEnforceSsl,Ee as ensureOrganisationExists,De as ensureOrganisationalUnitsExist,cs as evaluateDestructionConsents,As as executableRemediationVerbs,Ga as extractAllAccountNames,Yo as extractConsumedSsmParameters,Ie as extractErrorName,go as failure,si as fetchLastOperationEvents,fo as fileExists,no as filterDangerousEnvVars,de as findAccount,fe as findDevelopmentOuId,ki as findMarkedResourcesByOpId,fi as findRemediationJournalByOpId,ra as findRemovalDeletionFailures,ta as findResourcesPresent,Va as flattenAccountsToEnvironments,_i as formatDriftPreFlightBlock,Aa as formatDriftRepairOffer,It as formatInfrastructureError,sa as formatPinOutcomeDetail,Ji as formatRecreateInFlightCures,Ur as getApplicationDeployOrder,kr as getApplicationDestroyOrder,br as getApplicationStackName,Xr as getApplicationStepId,Kr as getApplicationStepName,Bn as getDestroyStepId,Yr as getOrganisationStackName,xr as getParallelDeployGroups,Fr as getParallelDestroyGroups,ht as getSourceContext,ct as getStateFilePath,yt as getStructuralHint,Mt as hasCdkDifferences,po as hasDockerfile,pt as hashDomainDirectory,ye as isAborted,Xa as isAccountsConfig,Mr as isApplicationOperation,Vr as isApplicationStack,Lt as isCdkError,Wn as isDataLoss,On as isDelegatedToRoute53,_a as isDeployPathBlockingStatus,Ca as isEnforceSslStatement,Io as isFailure,xe as isOULeaf,wr as isOpenNextPattern,vr as isOrganisationOperation,Ha as isOuOnlyAccountBucket,Gr as isQuarantineDetail,dr as isRemediationPlaceholderPhysicalId,Br as isRetainedBucketsDetail,Fa as isRootTaskPolicyArn,Pn as isRoute53NameServer,Zn as isStatefulResourceType,Ka as isStringArray,Lo as isSuccess,ls as legalRemediationVerbs,me as listAccounts,Ci as listActiveRemediationOps,Ni as listActiveRemediationOpsForContext,ps as loadDeployPlan,so as maskSensitiveOutput,qo as mergeReconciledProviderAccounts,Dn as normaliseRecordName,Rn as nsDelegationPhysicalName,kn as openNextBuilder,Ya as parseAccountsConfiguration,Xt as parseBuildPhase,W as parseDeployContractVersion,j as parseDeployEvent,vt as parseDiffOutput,Eo as parseShellArgs,Go as partitionAccounts,Mn as partitionForRegion,Qi as partitionPreFlightByRemediation,Ea as pinProbeCoverage,Pe as placeAccountsInOUs,oe as probeAccountStackExists,Uo as probeSecretsDrift,jo as projectAccountRows,Qo as projectScalarSummary,bi as proveFlipMetadataOnly,oa as proveRemovalTargetsOnly,At as readDomainState,li as readDriftSuspects,gi as readRemediationJournal,Yi as readRetainFlipState,it as readStateFile,Zo as reconcileProviderAccounts,ei as reconcileTrailMigration,Tt as recordDomainDeployState,Ai as recordDriftSuspects,ne as regionDisabledMessage,St as regionSuffix,Fe as registerSecurityDelegates,pr as remediationPhaseRank,mr as remediationPlaceholderPhysicalId,_s as renderConsentVerdictLines,Ts as renderDestructionTicketLines,ca as renderPinReportLines,zn as renderPlanLines,qn as renderPlanSummary,In as resolveBuildArgs,gn as resolveBuildSecretSessionProvider,Cn as resolveBuildSecrets,Nn as resolveSecretRefValue,Fo as restart,ya as restoreAndReconcileQuarantinedBucket,ga as restoreBucketPolicy,is as runApprovalGate,En as runCaaPreflight,Tn as runDelegatedDomainDestroy,ss as runDestructionGate,Ti as runDriftPreFlight,na as runDriftRepairGate,ji as runForgetSurgery,Qa as runOpenNextBuild,Wa as runOrganisationSetup,Sa as runPinRemediation,$i as runRecreatePreFlight,Zi as runRecreateSurgery,di as runRoute53RecordPreflight,es as signApprovalToken,co as sleep,uo as sleepAbortable,ge as snapshotIdentityCentre,qi as snapshotPolicyForType,Ln as sourcedRefsFromBuildArgs,Nt as startStackMonitoring,bn as staticSiteBuilder,$r as stubCallerIdentity,No as success,Li as sweepExpiredRemediationJournals,Wi as synthTemplateDeclaresResource,La as synthesiseEnforceSslDocument,un as systemDnsNameProbe,ln as templateOwnsDelegationRecord,Hr as toPascalCase,Na as toResourcePolicyStatements,io as toServiceError,$n as toWirePlanChanges,fa as triageBucketPolicy,A as truncateUtf16Safe,Da as unlockBucket,Pa as unlockQueue,pe as updateBackupGlobalSettings,Et as updateTemplateHash,vn as validateBuildGroup,Er as verbsForDriftVerdict,rs as verifyApprovalToken,Ma as verifyOrgTrailDelivery,Vi as verifyRetainFlipDiff,nn as waitForNsPropagation,zr as wrapApplicationError,Rt as writeDomainState,Ii as writeRemediationJournal,at as writeStateFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Te=Object.defineProperty;var N=(e,n)=>Te(e,"name",{value:n,configurable:!0});import{join as ce}from"path";import{CloudFormationClient as Ee}from"@aws-sdk/client-cloudformation";import{EC2Client as Ne}from"@aws-sdk/client-ec2";import{success as D,failure as d}from"@fjall/generator";import{logger as B}from"@fjall/util/logger";import{ImageTagSchema as Pe,getErrorMessage as le,imageTagParameterName as De,maskSensitiveOutput as S}from"@fjall/util";import{parseDockerDeclarationsFromManifest as ve}from"@fjall/util/manifest";import{stubCallerIdentity as Re}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Ce,getApplicationStackName as I,getApplicationStepName as de,getApplicationStepId as ue,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as Ie}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Oe,bootstrapOrFail as Ae}from"../contextHelpers.js";import{buildBootstrapTagsIfResolved as Le}from"../bootstrapTags.js";import{hasOrganisationTierAccount as Me}from"../../aws/organisations/accountGlobals.js";import{runDetectionPipeline as Be}from"./detectionPipeline.js";import{STEP_IDS as _,STEP_NAMES as Fe}from"../../types/stepDefinitions.js";import{StepRegistry as $e}from"../../steps/stepRegistry.js";import{checkTargetReadiness as xe}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as je}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as Ge}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as ge,runDockerBuild as Ue}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as _e,getStackNamesToDeploy as He,deployParallelPhase as Ke,deployStackSequential as We,runDockerPreCompute as qe,runLambdaVersionPublish as Ve,createBuildCallbacks as ze}from"./applicationDeployHelpers.js";import{captureDatabaseConnectionOutputs as Je,captureServiceTaskDefinitions as Ye,reconcileDatabaseEndpointChange as Qe}from"./databaseEndpointReconcile.js";import{deployCodeOnly as Xe}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as fe}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as H}from"./leaseGate.js";import{collectFullDeployArtefacts as Ze}from"./artefactCollection.js";import{CfnRegistryService as et}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as tt}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as rt}from"./approvalGate.js";import{buildDestructionTicket as nt,runDestructionGate as at}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as ot,signApprovalToken as it}from"./plan/approvalToken.js";import{computeAssemblyDigest as st}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as ct}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as lt}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as dt}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as pe}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as ut,runPinRemediation as gt}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as ft}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as pt}from"../remediation/recreateResource.js";const F="applicationDeploy";function mt(e){const n=Object.entries(e);if(n.length===0)return;const a={};for(const[t,i]of n){const h=De(t);a[h]=i}return a}N(mt,"buildCdkImageTagParameters");function kt(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return d(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return d(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return d(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return d(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.serviceImageTags!==void 0){if(e.serviceName)return d(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const n=Object.entries(e.serviceImageTags);if(n.length===0)return d(new Error("serviceImageTags must name at least one service"));for(const[a,t]of n){const i=Pe.safeParse(t);if(!i.success)return d(new Error(`serviceImageTags.${a} is not a valid image tag: ${i.error.message}`))}}return D(void 0)}N(kt,"validateRollbackOptions");function me(e,n,a){if(e.onStackTargets===void 0||a.length===0)return;const t=n.awsProvider.getAccountId(),i=n.awsProvider.getRegion();e.onStackTargets(a.map(h=>({stackName:h,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}N(me,"emitStackTargets");async function nr(e,n,a){const{callbacks:t,options:i}=e,h=Date.now(),$=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if($==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),d(r)}const z=kt(i);if(!z.success){const r=S(z.error.message),o=new Error(r);return t.onError?.(o),d(o)}const x=Ie.buildDeploymentContext({deployType:"application",target:a.appName,path:a.path,region:n.awsProvider.getRegion(),callerIdentity:Re(n.awsProvider.getAccountId()),...Oe({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc}),...e.managedDomainBindings!==void 0?{managedDomainBindings:e.managedDomainBindings}:{}},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),b=n.frameworkRegistry.resolve({appPath:a.path});let J;const O=$==="rollback";if(b&&!O){J=b.builder.plan({appPath:a.path},b.detection);const r=ze(t),o=await b.builder.build(a.path,J,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly,...e.abortSignal!==void 0&&{abortSignal:e.abortSignal}});if(!o.success){const s=new Error(S(o.error.message));return t.onError?.(s),d(s)}}if($==="code-only"||O){t.onLog?.(O?i?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(i.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const r=ce(a.path,"cdk.out"),o=ve(r),s=o.declaresBuild,g=b?.detection.hasDockerfile===!0,c=s||g;B.debug(F,"Deploy-only branch entered",{mode:$,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:a.appName,appPath:a.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:b?.builder.name,hasDockerfileFromManifest:s,hasDockerfileFromDisk:g,hasDockerfile:c,manifestDockerServiceCount:o.ecsServices.length,manifestDockerPaths:o.ecsServices.map(f=>f.docker.path),manifestLambdaDockerEntryCount:o.lambdaEntries.length}),!s&&!g&&t.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");const u=await fe([I(a.appName,P.COMPUTE)],n.cfnService,e.abortSignal);if(!u.success)return t.onError?.(u.error),d(u.error);me(t,n,[I(a.appName,P.COMPUTE)]);let y={},l;if(!O&&e.dockerProvider!==void 0&&c){B.debug(F,"Running Docker build before code-only deploy",{source:s?"manifest":"disk"});const f=await Ue(e,n,a,t);if(!f.success)return d(f.error);y=f.data.contentHashTagsByService,l=f.data}else B.debug(F,"Skipping Docker build",{reason:O?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const k=await H(e.assertLeaseForStack,I(a.appName,P.COMPUTE),e.abortSignal);return k!==void 0?(t.onError?.(k),d(k)):Xe(e,n,a,y,l)}t.onLog?.("Analysing infrastructure\u2026","info");const j=await Be(a,n,x,t,e.abortSignal);if(!j.success){const r=new Error(S(j.error.message),{cause:j.error});return t.onError?.(r),d(r)}const m=j.data;try{await t.onDetectionComplete?.({...m,builderName:b?.builder.name??"unknown"})}catch(r){const o=new Error(S(le(r)),{cause:r});return t.onError?.(o),d(o)}const ke={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:m.hasDockerfile,pattern:m.pattern,resources:m.resources,...b&&{builderName:b.builder.name}},Y=$e.getSteps(ke),Q=Y.findIndex(r=>r.id===_.TARGET_READINESS),X=await Ge(t,{stepId:_.TARGET_READINESS,stepName:Fe.TARGET_READINESS,...Q>=0&&{stepIndex:Q,totalSteps:Y.length}},async()=>{if(i?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const r=n.awsProvider.getAccountId(),o=e.orgConfig?.providerAccounts.find(u=>u.id===r)?.name,s=n.awsProvider.getCredentials(),g=je(e.orgConfig),c=await xe({cloudFormation:n.awsProvider.getClient(Ee),ec2:new Ne({region:g,...s!==void 0&&{credentials:s}})},{id:r,...o!==void 0&&{name:o}},n.awsProvider.getRegion(),e.orgConfig,e.abortSignal);return c.success?c.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(S(c.data.advisory))}:{kind:"error",error:new Error(S(c.error.message),{cause:c.error})}},e.abortSignal);if(!X.success)return X;const w=Ce({pattern:m.pattern,resources:m.resources}),v=w.length,Z=m.hasDockerfile&&e.dockerProvider!==void 0&&w.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!m.hasDifferences&&!i?.force&&!Z){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(i?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const r=m.hasDockerfile&&e.dockerProvider!==void 0&&w.includes(P.COMPUTE);for(let s=0;s<w.length;s++){const g=w[s];r&&g===P.COMPUTE&&(t.onStepStart?.(_.DOCKER_OPERATIONS,ge),t.onStepComplete?.(_.DOCKER_OPERATIONS,ge,"skipped"));const c=ue(g,"deploy"),u=de(g,"deploy");t.onStepStart?.(c,u,s,v),t.onStepComplete?.(c,u,"skipped",s,v)}const o=await n.stackService.resolveWebsiteUrl(a.appName);return D({target:a.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-h})}const G=ce(a.path,"cdk.out"),E=He({deployOrder:w,stackChanges:m.stackChanges,appName:a.appName,force:i?.force===!0,computeBuildPossible:Z});me(t,n,[...E]);const K=await fe([...E],n.cfnService,e.abortSignal);if(!K.success)return t.onError?.(K.error),d(K.error);const ee=st(m.currentHashes),W=ct({cfnService:n.cfnService,cdkOutPath:G,changedStacks:[...E],assemblyDigest:ee,registry:new et(n.awsProvider),changeSetProbe:new tt(n.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),q=await lt(n.awsProvider,{stackNames:[...E],cdkOutPath:G,cfnService:n.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(q.kind==="blocked"){const r=new Error(q.message);return t.onError?.(r),d(r)}const te=q.verifiedAbsentByNecessity??[],re=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(re){const r=await rt({approvalGate:e.approvalGate,options:i??{},target:a.appName,cdkOutPath:G,detection:{stackChanges:m.stackChanges,currentHashes:m.currentHashes},cfnService:n.cfnService,loadPlan:W,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!r.success)return r;const o=r.data;if(o.kind==="rejected")return t.onLog?.(`Deploy rejected: ${S(o.reason)}`,"warn"),D({target:a.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-h});if(o.kind==="awaiting"){const s=await W(),g=s.success?nt(s.data,te):void 0;return D({target:a.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,...g!==void 0?{destructionTicket:g}:{},artefacts:[],durationMs:Date.now()-h})}}const T=await at({loadPlan:W,options:i??{},target:a.appName,callbacks:t,verifiedAbsentByNecessity:te,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!T.success)return t.onError?.(T.error),d(T.error);if(T.data.kind==="withheld"){const r=re?it({assemblyDigest:ee,ttlMs:ot}):void 0;return D({target:a.appName,deploymentType:"application",destructionPending:!0,destructionTicket:T.data.ticket,destructionOutcome:T.data.outcome,destructionHaltReasons:T.data.reasons,...r!==void 0?{destructionResumeToken:r.token,destructionResumeExpiresAt:r.expiresAt}:{},artefacts:[],durationMs:Date.now()-h})}if(T.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=T.data,s=r.resources.filter(c=>o.get(c.physicalName)==="pin");if(s.length>0){t.onLog?.(`Pin remediation for ${a.appName}: probing live value(s) for ${s.length} consented pin(s) \u2014 deploy stopped for config write-back; nothing was applied`,"warn");const c=await gt(n.awsProvider,{targets:s.map(u=>({stack:u.stack,logicalId:u.logicalId,resourceType:u.resourceType,physicalName:u.physicalName,offendingProperties:u.offendingProperties})),...e.infrastructureSource!==void 0?{infrastructureContent:e.infrastructureSource.content,infrastructureFilePath:e.infrastructureSource.filePath}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});for(const u of ut(c))t.onLog?.(u,"warn");return s.length<r.resources.length&&t.onLog?.("Remaining consented change(s) were not applied \u2014 the config write-back changes the plan, so they re-consent on the next deploy","warn"),D({target:a.appName,deploymentType:"application",pinPending:!0,pinReport:c,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-h})}const g=r.resources.filter(c=>c.finding==="pinned-name-replacement"&&o.get(c.physicalName)==="recreate");if(g.length>0){const c=new Map;for(const l of g){const k=c.get(l.stack)??[];k.push(l),c.set(l.stack,k)}const u=await ft(n.awsProvider,{stacks:[...c].map(([l,k])=>({stackName:l,targets:k.map(f=>({logicalId:f.logicalId,resourceType:f.resourceType,physicalId:f.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const l=new Error(S(u.error.message),{cause:u.error});return t.onError?.(l),d(l)}const y=[];for(const[l,k]of c){const f=await H(e.assertLeaseForStack,l,e.abortSignal,"destroy");if(f!==void 0)return y.length>0&&t.onLog?.(`Surgery already completed on ${y.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`,"warn"),t.onError?.(f),d(f);const C=k.map(p=>p.physicalName);t.onLog?.(`Recreate remediation for ${a.appName}: orchestrating the pinned-name replacement of ${C.join(", ")} on ${l}`,"warn");const M=await pt(n.awsProvider,{stackName:l,targets:k.map(p=>({logicalId:p.logicalId,resourceType:p.resourceType,physicalId:p.physicalName})),appName:a.appName,consentedPhysicalNames:C,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:k.map(p=>p.logicalId),onProgress:N(p=>t.onLog?.(p,"info"),"onProgress"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!M.success){const p=y.length>0?` Surgery already completed on ${y.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`:"",U=new Error(S(`${M.error.message}${p}`));return t.onError?.(U),d(U)}y.push(`${l} (${C.join(", ")})`)}}}const ye=e.orgConfig!==void 0&&!Me(e.orgConfig.providerAccounts),Se=Le({account:e.orgConfig?.providerAccounts.find(r=>r.id===n.awsProvider.getAccountId()),estate:ye?"solo":"org"}),ne=await Ae(n,x,t,Se);if(!ne.success)return ne;const ae=I(a.appName,P.DATABASE),V=E.has(ae),he=V?await Je(n.cfnService,a.appName,e.abortSignal):void 0,be=V?await Ye(n.ecsResolver,n.ecsService,a.appName,e.abortSignal):void 0,A={},L=new Map;let oe,ie={};const R=N(async()=>{!V||!L.has(ae)||await Qe({appName:a.appName,connectionOutputsBefore:he,serviceTaskDefinitionsBefore:be,services:{cfnService:n.cfnService,ecsService:n.ecsService,ecsResolver:n.ecsResolver},callbacks:t,...e.confirmDatabaseEndpointRestart!==void 0?{confirmRestart:e.confirmDatabaseEndpointRestart}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})},"reconcileIfDatabaseReplaced");for(let r=0;r<w.length;r++){const o=w[r],s=I(a.appName,o),g=ue(o,"deploy"),c=de(o,"deploy");if(!E.has(s)){t.onStepStart?.(g,c,r,v),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(g,c,"skipped",r,v);continue}const u=await H(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await R(),t.onError?.(u),d(u);const y=_e(w,r,a.appName,E);if(y.length>=2){const p=await Ke(y,a,n,x,t,r,v,m,A,L);if(!p.success)return await R(),d(await pe(n.awsProvider,{stackNames:y.map(U=>I(a.appName,U)),appName:a.appName,originalError:p.error,cfnService:n.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=y.length-1;continue}const l=await qe(o,e,n,a,t,m.hasDockerfile);if(l!==null&&!l.success)return await R(),d(l.error);l!==null&&l.success&&(oe=l.data);const k=l!==null&&l.success?l.data.contentHashTagsByService:{},f=mt(k);if(l!==null){const p=await H(e.assertLeaseForStack,s,e.abortSignal);if(p!==void 0)return await R(),t.onError?.(p),d(p)}const C=await We(o,n,x,t,r,v,A,f!==void 0||e.abortSignal!==void 0?{...f!==void 0?{parameters:f}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}:void 0);if(!C.success)return await R(),d(await pe(n.awsProvider,{stackNames:[s],appName:a.appName,originalError:C.error,cfnService:n.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));o===P.COMPUTE&&(ie=await Ve(a,n,t,k,e.abortSignal));const M=m.currentHashes.get(s);M&&L.set(s,M)}if(await R(),L.size>0){const r=await n.hashService.updateStateAfterDeploy(a.path,L);if(!r.success){const o=S(r.error.message);B.debug(F,"Failed to update state file",{error:o}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${o}`,"warn")}}const se=await n.stackService.resolveWebsiteUrl(a.appName);se&&(A.websiteUrl=se);const we=await Ze(n,a.appName,oe,t,G,ie,e.abortSignal);return await dt(n.awsProvider,{stackNames:[...E],appName:a.appName,onLog:N((r,o)=>t.onLog?.(r,o),"onLog"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{B.debug(F,"Forget completion sweep failed",{error:S(le(r))})}),D({target:a.appName,deploymentType:"application",outputs:Object.keys(A).length>0?A:void 0,artefacts:we,durationMs:Date.now()-h})}N(nr,"deployApplication");export{nr as deployApplication,me as emitStackTargets};
|
|
1
|
+
var ve=Object.defineProperty;var v=(e,a)=>ve(e,"name",{value:a,configurable:!0});import{join as ue}from"path";import{CloudFormationClient as Pe}from"@aws-sdk/client-cloudformation";import{EC2Client as Re}from"@aws-sdk/client-ec2";import{success as C,failure as c}from"@fjall/generator";import{logger as $}from"@fjall/util/logger";import{ImageTagSchema as Ce,getErrorMessage as ge,imageTagParameterName as Ie,maskSensitiveOutput as y}from"@fjall/util";import{parseDockerDeclarationsFromManifest as Oe}from"@fjall/util/manifest";import{stubCallerIdentity as Ae}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Le,getApplicationStackName as I,getApplicationStepName as pe,getApplicationStepId as fe,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as Be}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Me,bootstrapOrFail as Fe}from"../contextHelpers.js";import{buildBootstrapTagsIfResolved as $e}from"../bootstrapTags.js";import{hasOrganisationTierAccount as xe}from"../../aws/organisations/accountGlobals.js";import{runDetectionPipeline as je}from"./detectionPipeline.js";import{STEP_IDS as _,STEP_NAMES as Ge}from"../../types/stepDefinitions.js";import{StepRegistry as He}from"../../steps/stepRegistry.js";import{checkTargetReadiness as Ue}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as _e}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as Ke}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as me,runDockerBuild as We}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as qe,getStackNamesToDeploy as Ve,deployParallelPhase as ze,deployStackSequential as Je,runDockerPreCompute as Ye,runLambdaVersionPublish as Qe,createBuildCallbacks as Xe,willRunDockerBuild as Ze}from"./applicationDeployHelpers.js";import{verifyAssemblyIntegrity as ke}from"./assemblyIntegrity.js";import{captureDatabaseConnectionOutputs as et,captureServiceTaskDefinitions as tt,reconcileDatabaseEndpointChange as rt}from"./databaseEndpointReconcile.js";import{deployCodeOnly as at}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as he}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as K}from"./leaseGate.js";import{collectFullDeployArtefacts as nt}from"./artefactCollection.js";import{CfnRegistryService as ot}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as it}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as st}from"./approvalGate.js";import{buildDestructionTicket as ct,runDestructionGate as lt}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as dt,signApprovalToken as ut}from"./plan/approvalToken.js";import{computeAssemblyDigest as gt}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as pt}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as ft}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as mt}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as ye}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as kt,runPinRemediation as ht}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as yt}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as St}from"../remediation/recreateResource.js";const x="applicationDeploy";function bt(e){const a=Object.entries(e);if(a.length===0)return;const n={};for(const[t,i]of a){const S=Ie(t);n[S]=i}return n}v(bt,"buildCdkImageTagParameters");function wt(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return c(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return c(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return c(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return c(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.serviceImageTags!==void 0){if(e.serviceName)return c(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const a=Object.entries(e.serviceImageTags);if(a.length===0)return c(new Error("serviceImageTags must name at least one service"));for(const[n,t]of a){const i=Ce.safeParse(t);if(!i.success)return c(new Error(`serviceImageTags.${n} is not a valid image tag: ${i.error.message}`))}}return C(void 0)}v(wt,"validateRollbackOptions");function Se(e,a,n){if(e.onStackTargets===void 0||n.length===0)return;const t=a.awsProvider.getAccountId(),i=a.awsProvider.getRegion();e.onStackTargets(n.map(S=>({stackName:S,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}v(Se,"emitStackTargets");async function lr(e,a,n){const{callbacks:t,options:i}=e,S=Date.now(),j=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if(j==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),c(r)}const Y=wt(i);if(!Y.success){const r=y(Y.error.message),o=new Error(r);return t.onError?.(o),c(o)}const G=Be.buildDeploymentContext({deployType:"application",target:n.appName,path:n.path,region:a.awsProvider.getRegion(),callerIdentity:Ae(a.awsProvider.getAccountId()),...Me({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc}),...e.managedDomainBindings!==void 0?{managedDomainBindings:e.managedDomainBindings}:{}},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),T=a.frameworkRegistry.resolve({appPath:n.path});let Q;const L=j==="rollback";if(T&&!L){Q=T.builder.plan({appPath:n.path},T.detection);const r=Xe(t),o=await T.builder.build(n.path,Q,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly,...e.abortSignal!==void 0&&{abortSignal:e.abortSignal}});if(!o.success){const s=new Error(y(o.error.message));return t.onError?.(s),c(s)}}if(j==="code-only"||L){t.onLog?.(L?i?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(i.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const r=ue(n.path,"cdk.out"),o=Oe(r),s=o.declaresBuild,p=T?.detection.hasDockerfile===!0,l=s||p;$.debug(x,"Deploy-only branch entered",{mode:j,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:n.appName,appPath:n.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:T?.builder.name,hasDockerfileFromManifest:s,hasDockerfileFromDisk:p,hasDockerfile:l,manifestDockerServiceCount:o.ecsServices.length,manifestDockerPaths:o.ecsServices.map(f=>f.docker.path),manifestLambdaDockerEntryCount:o.lambdaEntries.length}),!s&&!p&&t.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");const u=await he([I(n.appName,P.COMPUTE)],a.cfnService,e.abortSignal);if(!u.success)return t.onError?.(u.error),c(u.error);Se(t,a,[I(n.appName,P.COMPUTE)]);let k={},d;if(!L&&e.dockerProvider!==void 0&&l){$.debug(x,"Running Docker build before code-only deploy",{source:s?"manifest":"disk"});const f=await We(e,a,n,t);if(!f.success)return c(f.error);k=f.data.contentHashTagsByService,d=f.data}else $.debug(x,"Skipping Docker build",{reason:L?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const m=await K(e.assertLeaseForStack,I(n.appName,P.COMPUTE),e.abortSignal);return m!==void 0?(t.onError?.(m),c(m)):at(e,a,n,k,d)}t.onLog?.("Analysing infrastructure\u2026","info");const H=await je(n,a,G,t,e.abortSignal);if(!H.success){const r=new Error(y(H.error.message),{cause:H.error});return t.onError?.(r),c(r)}const g=H.data;try{await t.onDetectionComplete?.({...g,builderName:T?.builder.name??"unknown"})}catch(r){const o=new Error(y(ge(r)),{cause:r});return t.onError?.(o),c(o)}const be={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:g.hasDockerfile,pattern:g.pattern,resources:g.resources,...T&&{builderName:T.builder.name}},X=He.getSteps(be),Z=X.findIndex(r=>r.id===_.TARGET_READINESS),ee=await Ke(t,{stepId:_.TARGET_READINESS,stepName:Ge.TARGET_READINESS,...Z>=0&&{stepIndex:Z,totalSteps:X.length}},async()=>{if(i?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const r=a.awsProvider.getAccountId(),o=e.orgConfig?.providerAccounts.find(u=>u.id===r)?.name,s=a.awsProvider.getCredentials(),p=_e(e.orgConfig),l=await Ue({cloudFormation:a.awsProvider.getClient(Pe),ec2:new Re({region:p,...s!==void 0&&{credentials:s}})},{id:r,...o!==void 0&&{name:o}},a.awsProvider.getRegion(),e.orgConfig,e.abortSignal);return l.success?l.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(y(l.data.advisory))}:{kind:"error",error:new Error(y(l.error.message),{cause:l.error})}},e.abortSignal);if(!ee.success)return ee;const N=Le({pattern:g.pattern,resources:g.resources}),O=N.length,te=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!g.hasDifferences&&!i?.force&&!te){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(i?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const r=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE);for(let s=0;s<N.length;s++){const p=N[s];r&&p===P.COMPUTE&&(t.onStepStart?.(_.DOCKER_OPERATIONS,me),t.onStepComplete?.(_.DOCKER_OPERATIONS,me,"skipped"));const l=fe(p,"deploy"),u=pe(p,"deploy");t.onStepStart?.(l,u,s,O),t.onStepComplete?.(l,u,"skipped",s,O)}const o=await a.stackService.resolveWebsiteUrl(n.appName);return C({target:n.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-S})}const A=ue(n.path,"cdk.out"),b=Ve({deployOrder:N,stackChanges:g.stackChanges,appName:n.appName,force:i?.force===!0,computeBuildPossible:te});Se(t,a,[...b]);const W=await he([...b],a.cfnService,e.abortSignal);if(!W.success)return t.onError?.(W.error),c(W.error);const re={stacksToDeploy:[...b],imageBuild:Ze({stackNamesToDeploy:b,appName:n.appName,hasDockerProvider:e.dockerProvider!==void 0,hasDockerfile:g.hasDockerfile})},ae=gt(g.currentHashes,re),q=pt({cfnService:a.cfnService,cdkOutPath:A,changedStacks:[...b],assemblyDigest:ae,registry:new ot(a.awsProvider),changeSetProbe:new it(a.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),V=await ft(a.awsProvider,{stackNames:[...b],cdkOutPath:A,cfnService:a.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(V.kind==="blocked"){const r=new Error(V.message);return t.onError?.(r),c(r)}const ne=V.verifiedAbsentByNecessity??[],U=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(U){const r=await st({approvalGate:e.approvalGate,options:i??{},target:n.appName,cdkOutPath:A,detection:{stackChanges:g.stackChanges,currentHashes:g.currentHashes},scope:re,cfnService:a.cfnService,loadPlan:q,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!r.success)return r;const o=r.data;if(o.kind==="rejected")return t.onLog?.(`Deploy rejected: ${y(o.reason)}`,"warn"),C({target:n.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-S});if(o.kind==="awaiting"){const s=await q(),p=s.success?ct(s.data,ne):void 0;return C({target:n.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,...p!==void 0?{destructionTicket:p}:{},artefacts:[],durationMs:Date.now()-S})}}const E=await lt({loadPlan:q,options:i??{},target:n.appName,callbacks:t,verifiedAbsentByNecessity:ne,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!E.success)return t.onError?.(E.error),c(E.error);if(E.data.kind==="withheld"){const r=U?ut({assemblyDigest:ae,ttlMs:dt}):void 0;return C({target:n.appName,deploymentType:"application",destructionPending:!0,destructionTicket:E.data.ticket,destructionOutcome:E.data.outcome,destructionHaltReasons:E.data.reasons,...r!==void 0?{destructionResumeToken:r.token,destructionResumeExpiresAt:r.expiresAt}:{},artefacts:[],durationMs:Date.now()-S})}if(E.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=E.data,s=r.resources.filter(l=>o.get(l.physicalName)==="pin");if(s.length>0){t.onLog?.(`Pin remediation for ${n.appName}: probing live value(s) for ${s.length} consented pin(s) \u2014 deploy stopped for config write-back; nothing was applied`,"warn");const l=await ht(a.awsProvider,{targets:s.map(u=>({stack:u.stack,logicalId:u.logicalId,resourceType:u.resourceType,physicalName:u.physicalName,offendingProperties:u.offendingProperties})),...e.infrastructureSource!==void 0?{infrastructureContent:e.infrastructureSource.content,infrastructureFilePath:e.infrastructureSource.filePath}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});for(const u of kt(l))t.onLog?.(u,"warn");return s.length<r.resources.length&&t.onLog?.("Remaining consented change(s) were not applied \u2014 the config write-back changes the plan, so they re-consent on the next deploy","warn"),C({target:n.appName,deploymentType:"application",pinPending:!0,pinReport:l,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-S})}const p=r.resources.filter(l=>l.finding==="pinned-name-replacement"&&o.get(l.physicalName)==="recreate");if(p.length>0){const l=new Map;for(const d of p){const m=l.get(d.stack)??[];m.push(d),l.set(d.stack,m)}const u=await yt(a.awsProvider,{stacks:[...l].map(([d,m])=>({stackName:d,targets:m.map(f=>({logicalId:f.logicalId,resourceType:f.resourceType,physicalId:f.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const d=new Error(y(u.error.message),{cause:u.error});return t.onError?.(d),c(d)}const k=[];for(const[d,m]of l){const f=await K(e.assertLeaseForStack,d,e.abortSignal,"destroy");if(f!==void 0)return k.length>0&&t.onLog?.(`Surgery already completed on ${k.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`,"warn"),t.onError?.(f),c(f);const R=m.map(h=>h.physicalName);t.onLog?.(`Recreate remediation for ${n.appName}: orchestrating the pinned-name replacement of ${R.join(", ")} on ${d}`,"warn");const F=await St(a.awsProvider,{stackName:d,targets:m.map(h=>({logicalId:h.logicalId,resourceType:h.resourceType,physicalId:h.physicalName})),appName:n.appName,consentedPhysicalNames:R,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:m.map(h=>h.logicalId),onProgress:v(h=>t.onLog?.(h,"info"),"onProgress"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!F.success){const h=k.length>0?` Surgery already completed on ${k.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`:"",w=new Error(y(`${F.error.message}${h}`));return t.onError?.(w),c(w)}k.push(`${d} (${R.join(", ")})`)}}}const we=e.orgConfig!==void 0&&!xe(e.orgConfig.providerAccounts),Te=$e({account:e.orgConfig?.providerAccounts.find(r=>r.id===a.awsProvider.getAccountId()),estate:we?"solo":"org"}),oe=await Fe(a,G,t,Te);if(!oe.success)return oe;const ie=I(n.appName,P.DATABASE),z=b.has(ie),Ne=z?await et(a.cfnService,n.appName,e.abortSignal):void 0,Ee=z?await tt(a.ecsResolver,a.ecsService,n.appName,e.abortSignal):void 0,B={},M=new Map;let se,ce={};const D=v(async()=>{!z||!M.has(ie)||await rt({appName:n.appName,connectionOutputsBefore:Ne,serviceTaskDefinitionsBefore:Ee,services:{cfnService:a.cfnService,ecsService:a.ecsService,ecsResolver:a.ecsResolver},callbacks:t,...e.confirmDatabaseEndpointRestart!==void 0?{confirmRestart:e.confirmDatabaseEndpointRestart}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})},"reconcileIfDatabaseReplaced");for(let r=0;r<N.length;r++){const o=N[r],s=I(n.appName,o),p=fe(o,"deploy"),l=pe(o,"deploy");if(!b.has(s)){t.onStepStart?.(p,l,r,O),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(p,l,"skipped",r,O);continue}const u=await K(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await D(),t.onError?.(u),c(u);const k=qe(N,r,n.appName,b);if(k.length>=2){const w=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:k.map(J=>I(n.appName,J)),detectionHashes:g.currentHashes,approvalGated:U});if(!w.success)return await D(),t.onError?.(w.error),c(w.error);const de=await ze(k,n,a,G,t,r,O,g,B,M);if(!de.success)return await D(),c(await ye(a.awsProvider,{stackNames:k.map(J=>I(n.appName,J)),appName:n.appName,originalError:de.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=k.length-1;continue}const d=await Ye(o,e,a,n,t,g.hasDockerfile);if(d!==null&&!d.success)return await D(),c(d.error);d!==null&&d.success&&(se=d.data);const m=d!==null&&d.success?d.data.contentHashTagsByService:{},f=bt(m);if(d!==null){const w=await K(e.assertLeaseForStack,s,e.abortSignal);if(w!==void 0)return await D(),t.onError?.(w),c(w)}const R=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:[s],detectionHashes:g.currentHashes,approvalGated:U});if(!R.success)return await D(),t.onError?.(R.error),c(R.error);const F=await Je(o,a,G,t,r,O,B,f!==void 0||e.abortSignal!==void 0?{...f!==void 0?{parameters:f}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}:void 0);if(!F.success)return await D(),c(await ye(a.awsProvider,{stackNames:[s],appName:n.appName,originalError:F.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));o===P.COMPUTE&&(ce=await Qe(n,a,t,m,e.abortSignal));const h=g.currentHashes.get(s);h&&M.set(s,h)}if(await D(),M.size>0){const r=await a.hashService.updateStateAfterDeploy(n.path,M);if(!r.success){const o=y(r.error.message);$.debug(x,"Failed to update state file",{error:o}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${o}`,"warn")}}const le=await a.stackService.resolveWebsiteUrl(n.appName);le&&(B.websiteUrl=le);const De=await nt(a,n.appName,se,t,A,ce,e.abortSignal);return await mt(a.awsProvider,{stackNames:[...b],appName:n.appName,onLog:v((r,o)=>t.onLog?.(r,o),"onLog"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{$.debug(x,"Forget completion sweep failed",{error:y(ge(r))})}),C({target:n.appName,deploymentType:"application",outputs:Object.keys(B).length>0?B:void 0,artefacts:De,durationMs:Date.now()-S})}v(lr,"deployApplication");export{lr as deployApplication,Se as emitStackTargets};
|
|
@@ -25,6 +25,24 @@ export declare function getStackNamesToDeploy(params: {
|
|
|
25
25
|
force: boolean;
|
|
26
26
|
computeBuildPossible: boolean;
|
|
27
27
|
}): ReadonlySet<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Single source of the deploy loop's "will an image build/push run" outcome,
|
|
30
|
+
* bound into the approval digest (DeployScope.imageBuild) so a token approves
|
|
31
|
+
* the exact build behaviour, not the flag that requested it. Mirrors the
|
|
32
|
+
* loop's ACTUAL gates in order: the skip predicate (Compute must be in the
|
|
33
|
+
* to-deploy set), `runDockerPreCompute`'s provider guard, and its
|
|
34
|
+
* hasDockerfile branch. Deliberately NOT `computeBuildPossible`: that flag
|
|
35
|
+
* decides whether Compute must deploy FOR a build (false under
|
|
36
|
+
* `--infra-only`), but a hash-changed Compute stack still builds regardless —
|
|
37
|
+
* the loop consults only the three gates above, so `--infra-only` reaches
|
|
38
|
+
* this predicate solely through the to-deploy set.
|
|
39
|
+
*/
|
|
40
|
+
export declare function willRunDockerBuild(params: {
|
|
41
|
+
stackNamesToDeploy: ReadonlySet<string>;
|
|
42
|
+
appName: string;
|
|
43
|
+
hasDockerProvider: boolean;
|
|
44
|
+
hasDockerfile: boolean;
|
|
45
|
+
}): boolean;
|
|
28
46
|
/**
|
|
29
47
|
* Identify Phase 2 stacks (Storage, Messaging, Database) that are consecutive
|
|
30
48
|
* in the deploy order and included in the to-deploy set. Returns them for
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var B=Object.defineProperty;var
|
|
2
|
-
`);return
|
|
1
|
+
var B=Object.defineProperty;var a=(e,r)=>B(e,"name",{value:r,configurable:!0});import{join as T}from"node:path";import{success as N,failure as S}from"@fjall/generator";import{maskSensitiveOutput as w}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as D}from"@fjall/util/manifest";import{isAborted as L}from"../../aws/organisations/types.js";import{APPLICATION_STACKS as k,getApplicationStackName as P,getApplicationStepName as E,getApplicationStepId as b,PARALLEL_DEPLOY_GROUPS as R}from"../../types/operations.js";import{emitProgress as $}from"../../services/supporting/helpers.js";import{forwardOutput as x,forwardResourceProgress as j}from"../contextHelpers.js";import{emptyBuildProducts as v,runDockerBuild as I}from"./dockerBuildHelper.js";import{runWelcomeImageSetup as _}from"./welcomeImageHelper.js";import{withStepLifecycle as U}from"../stepLifecycle.js";function X(e){const r=new Set;for(const s of e.deployOrder){const i=P(e.appName,s),o=e.stackChanges.get(i)??!0,u=s===k.COMPUTE&&e.computeBuildPossible;(o||e.force||u)&&r.add(i)}return r}a(X,"getStackNamesToDeploy");function Z(e){return e.stackNamesToDeploy.has(P(e.appName,k.COMPUTE))&&e.hasDockerProvider&&e.hasDockerfile}a(Z,"willRunDockerBuild");function ee(e,r,s,i){const o=new Set(R.PHASE_2.stacks),u=[];for(let n=r;n<e.length&&o.has(e[n]);n++)u.push(e[n]);if(u.length<2)return[];const l=u.filter(n=>i.has(P(s,n)));return l.length>=2?l:[]}a(ee,"getParallelPhase2Stacks");async function te(e,r,s,i,o,u,l,n,m,p){for(let t=0;t<e.length;t++){const c=e[t],f=b(c,"deploy"),g=E(c,"deploy");o.onStepStart?.(f,g,u+t,l)}$(o,"Deploying infrastructure in parallel\u2026"),o.onParallelPhaseStart?.(e,"Storage and database resources (parallel)");const d=await s.stackService.deployStacksInParallel(e,i,{onOutput:x(o),onResourceProgress:a((t,c)=>{o.onResourceProgress?.(t),c&&o.onParallelStackResourceProgress?.(c,t)},"onResourceProgress"),onStackComplete:a((t,c,f,g)=>{const y=b(t,"deploy"),O=E(t,"deploy"),A=u+e.indexOf(t),C=L(s.abortSignal);o.onStepComplete?.(y,O,c?"completed":C?"cancelled":"error",A,l),!c&&g&&!C&&o.onError?.(new Error(w(g.message)))},"onStackComplete")});if(!d.success)return o.onParallelPhaseComplete?.([]),S(d.error);const h=d.data.filter(t=>!t.success);if(o.onParallelPhaseComplete?.(d.data.map(t=>({stack:t.stack,success:t.success,error:t.error}))),h.length>0){const t=h.map(f=>f.stack).join(", "),c=h.map(f=>`${f.stack}: ${w(f.error?.message??"Unknown error")}`).join(`
|
|
2
|
+
`);return S(new Error(`Failed to deploy stacks: ${t}
|
|
3
3
|
|
|
4
|
-
${c}`))}for(const t of
|
|
4
|
+
${c}`))}for(const t of d.data){if(t.success&&t.outputs)for(const[g,y]of Object.entries(t.outputs))m[g]=String(y);const c=P(r.appName,t.stack),f=n.currentHashes.get(c);f&&p.set(c,f)}return N(void 0)}a(te,"deployParallelPhase");async function re(e,r,s,i,o,u,l,n){const m=b(e,"deploy"),p=E(e,"deploy");return U(i,{stepId:m,stepName:p,stepIndex:o,totalSteps:u},async()=>{const d=await r.stackService.deployStack(e,s,{onOutput:x(i),onResourceProgress:j(i)},n?.parameters!==void 0||n?.abortSignal!==void 0?{...n?.parameters!==void 0?{parameters:n.parameters}:{},...n?.abortSignal!==void 0?{abortSignal:n.abortSignal}:{}}:void 0);if(!d.success)return{kind:"error",error:d.error};if(d.data.outputs)for(const[h,t]of Object.entries(d.data.outputs))l[h]=String(t);return{kind:"completed",data:void 0}},r.abortSignal)}a(re,"deployStackSequential");async function oe(e,r,s,i,o,u){if(e!==k.COMPUTE||!r.dockerProvider)return null;if(u){const n=await I(r,s,i,o);return n.success?N(n.data):S(n.error)}const l=await _(r,s,i,o);return l.success?N(v()):S(l.error)}a(oe,"runDockerPreCompute");async function ne(e,r,s,i,o){if(Object.keys(i).length===0)return{};const u=T(e.path,"cdk.out"),n=D(u).filter(p=>i[p.imageKey]!==void 0);if(n.length===0)return{};const m={};for(const p of n){if(L(o)){s.onLog?.(`Skipping the Lambda version publish for '${p.name}' \u2014 the deployment was cancelled. The function's $LATEST code is up to date; only the published-version record is affected.`,"warn");continue}const d=await r.lambdaService.publishVersion(p.name,o);if(!d.success){s.onLog?.(`Published image for Lambda '${p.name}', but publishing a new version failed: ${w(d.error.message)}. The function's $LATEST code is up to date; only the published-version record is affected.`,"warn");continue}m[p.name]=d.data}return m}a(ne,"runLambdaVersionPublish");function se(e){return{onBuildStart:a(r=>{e.onOpenNextBuildStart?.(),e.onLog?.(`${r} build started`,"info")},"onBuildStart"),onBuildProgress:a((r,s)=>{e.onOpenNextProgress?.(s)},"onBuildProgress"),onBuildComplete:a(r=>{e.onOpenNextBuildComplete?.(),e.onLog?.(`${r} build complete`,"info")},"onBuildComplete"),onBuildError:a((r,s)=>{e.onOpenNextBuildError?.(s)},"onBuildError")}}a(se,"createBuildCallbacks");export{se as createBuildCallbacks,te as deployParallelPhase,re as deployStackSequential,ee as getParallelPhase2Stacks,X as getStackNamesToDeploy,oe as runDockerPreCompute,ne as runLambdaVersionPublish,Z as willRunDockerBuild};
|
|
@@ -17,6 +17,7 @@ import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
|
17
17
|
import type { DeployOptions } from "../../types/params.js";
|
|
18
18
|
import type { ApprovalGate, ApprovalGatePlanDetail } from "../../types/approval.js";
|
|
19
19
|
import type { CfnRegistrySummaryReader } from "../../services/infrastructure/CfnRegistryService.js";
|
|
20
|
+
import { type DeployScope } from "./plan/assemblyDigest.js";
|
|
20
21
|
import type { CfnTemplateReader } from "./plan/computeDeployPlan.js";
|
|
21
22
|
import { type DeployPlanLoader } from "./plan/loadDeployPlan.js";
|
|
22
23
|
import { type VerifyApprovalTokenResult } from "./plan/approvalToken.js";
|
|
@@ -30,6 +31,13 @@ export interface RunApprovalGateParams {
|
|
|
30
31
|
stackChanges: ReadonlyMap<string, boolean>;
|
|
31
32
|
currentHashes: ReadonlyMap<string, string>;
|
|
32
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* The RESOLVED deploy scope (exact stack subset + image-build decision),
|
|
36
|
+
* folded into the digest so mint and verify bind identically. Must derive
|
|
37
|
+
* from the same values the deploy loop consumes — a token minted under
|
|
38
|
+
* `--infra-only` then refuses as `superseded` on a full apply.
|
|
39
|
+
*/
|
|
40
|
+
scope: DeployScope;
|
|
33
41
|
cfnService: CfnTemplateReader;
|
|
34
42
|
/**
|
|
35
43
|
* Registry oracle for replacement-verdict recomputation. Optional — without
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var k=Object.defineProperty;var p=(e,n)=>k(e,"name",{value:n,configurable:!0});import{success as o,failure as v}from"@fjall/generator";import{computeAssemblyDigest as h}from"./plan/assemblyDigest.js";import{loadDeployPlan as y}from"./plan/loadDeployPlan.js";import{renderPlanLines as A}from"./plan/renderDeployPlan.js";import{signApprovalToken as w,verifyApprovalToken as m}from"./plan/approvalToken.js";function x(e){switch(e){case"superseded":return"the plan changed since it was approved (re-plan and approve again)";case"expired":return"the approval window expired";case"tampered":return"the approval signature did not verify";case"malformed":return"the approval token was malformed"}}p(x,"approvalRefusalReason");function P(e,n,r){const i=n.changeCount===0&&r.length>0?`no resource changes against the live stacks; template hashes differ from the last recorded deploy for ${r.length} stack(s) (${r.join(", ")}) \u2014 applying will redeploy them`:n.summary;e.onLog?.(`Deploy plan: ${i}`,"info");for(const u of A(n))e.onLog?.(u,"info")}p(P,"emitPlanSummary");async function j(e){const n=h(e.detection.currentHashes),r=e.options.approvalToken;if(r!==void 0&&r!==""){const t=m({token:r,assemblyDigest:n});if(t.ok)return e.callbacks.onLog?.("Approval token verified against the current plan \u2014 proceeding","info"),o({kind:"proceed"});const f=x(t.reason);return e.callbacks.onLog?.(`Approval refused: ${f}`,"warn"),o({kind:"rejected",reason:f})}const i=[...e.detection.stackChanges.entries()].filter(([,t])=>t).map(([t])=>t),l=await(e.loadPlan??(()=>y({cfnService:e.cfnService,cdkOutPath:e.cdkOutPath,changedStacks:i,assemblyDigest:n,...e.registry!==void 0?{registry:e.registry}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})))();if(!l.success)return v(l.error);const s=l.data;P(e.callbacks,s,i);const c={hashDriftStacks:i,resourceChangeCount:s.changeCount,resourceChangedStacks:[...new Set(s.changes.map(t=>t.stack))]},a=w({assemblyDigest:n});if(e.options.autoApprove===!0)return e.callbacks.onLog?.("Plan auto-approved (--auto-approve)","info"),o({kind:"proceed"});if(e.options.planOnly===!0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c});if(e.approvalGate===void 0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c});const g={target:e.target,plan:s,approvalToken:a.token,expiresAt:a.expiresAt,hasDestructiveChanges:s.hasDestructiveChanges},d=await e.approvalGate.resolve(g);switch(d.kind){case"approved":return o({kind:"proceed"});case"rejected":return o({kind:"rejected",reason:d.reason,planDetail:c});case"suspended":return o({kind:"awaiting",approvalToken:d.approvalToken,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c})}}p(j,"runApprovalGate");export{x as approvalRefusalReason,j as runApprovalGate};
|
|
1
|
+
var k=Object.defineProperty;var p=(e,n)=>k(e,"name",{value:n,configurable:!0});import{success as o,failure as v}from"@fjall/generator";import{computeAssemblyDigest as h}from"./plan/assemblyDigest.js";import{loadDeployPlan as y}from"./plan/loadDeployPlan.js";import{renderPlanLines as A}from"./plan/renderDeployPlan.js";import{signApprovalToken as w,verifyApprovalToken as m}from"./plan/approvalToken.js";function x(e){switch(e){case"superseded":return"the plan changed since it was approved (re-plan and approve again)";case"expired":return"the approval window expired";case"tampered":return"the approval signature did not verify";case"malformed":return"the approval token was malformed"}}p(x,"approvalRefusalReason");function P(e,n,r){const i=n.changeCount===0&&r.length>0?`no resource changes against the live stacks; template hashes differ from the last recorded deploy for ${r.length} stack(s) (${r.join(", ")}) \u2014 applying will redeploy them`:n.summary;e.onLog?.(`Deploy plan: ${i}`,"info");for(const u of A(n))e.onLog?.(u,"info")}p(P,"emitPlanSummary");async function j(e){const n=h(e.detection.currentHashes,e.scope),r=e.options.approvalToken;if(r!==void 0&&r!==""){const t=m({token:r,assemblyDigest:n});if(t.ok)return e.callbacks.onLog?.("Approval token verified against the current plan \u2014 proceeding","info"),o({kind:"proceed"});const f=x(t.reason);return e.callbacks.onLog?.(`Approval refused: ${f}`,"warn"),o({kind:"rejected",reason:f})}const i=[...e.detection.stackChanges.entries()].filter(([,t])=>t).map(([t])=>t),l=await(e.loadPlan??(()=>y({cfnService:e.cfnService,cdkOutPath:e.cdkOutPath,changedStacks:i,assemblyDigest:n,...e.registry!==void 0?{registry:e.registry}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})))();if(!l.success)return v(l.error);const s=l.data;P(e.callbacks,s,i);const c={hashDriftStacks:i,resourceChangeCount:s.changeCount,resourceChangedStacks:[...new Set(s.changes.map(t=>t.stack))]},a=w({assemblyDigest:n});if(e.options.autoApprove===!0)return e.callbacks.onLog?.("Plan auto-approved (--auto-approve)","info"),o({kind:"proceed"});if(e.options.planOnly===!0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c});if(e.approvalGate===void 0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c});const g={target:e.target,plan:s,approvalToken:a.token,expiresAt:a.expiresAt,hasDestructiveChanges:s.hasDestructiveChanges},d=await e.approvalGate.resolve(g);switch(d.kind){case"approved":return o({kind:"proceed"});case"rejected":return o({kind:"rejected",reason:d.reason,planDetail:c});case"suspended":return o({kind:"awaiting",approvalToken:d.approvalToken,assemblyDigest:n,expiresAt:a.expiresAt,planDetail:c})}}p(j,"runApprovalGate");export{x as approvalRefusalReason,j as runApprovalGate};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-spawn assembly integrity re-verification (the 2026-07-20 incident fix).
|
|
3
|
+
*
|
|
4
|
+
* A deploy hashes the synthesised assembly ONCE, at detection, and the gates
|
|
5
|
+
* verify against that snapshot — but bootstrap plus a docker build/push can
|
|
6
|
+
* hold the read→mutate window open for many minutes, during which a
|
|
7
|
+
* concurrent process (another deploy, lint-staged stashing the working tree
|
|
8
|
+
* under a re-synth) can rewrite the SHARED `<app>/cdk.out`. The deploy loop
|
|
9
|
+
* ships the assembly as-is (`cdk deploy --app cdk.out`), so a swapped
|
|
10
|
+
* template reaches CloudFormation carrying content nobody detected — or, on
|
|
11
|
+
* a token-gated run, content the approver never saw.
|
|
12
|
+
*
|
|
13
|
+
* This check re-hashes each stack's template immediately before its cdk
|
|
14
|
+
* spawn, using the SAME hasher detection used (TemplateHashService normalises
|
|
15
|
+
* JSON before hashing, so the values are directly comparable), and fails
|
|
16
|
+
* closed on any divergence. It runs UNCONDITIONALLY — gated and ungated
|
|
17
|
+
* deploys alike — because the reference is the detection-time hash either
|
|
18
|
+
* way; only the operator-facing wording differs.
|
|
19
|
+
*/
|
|
20
|
+
import { type Result } from "@fjall/generator";
|
|
21
|
+
import type { TemplateHashService } from "../../services/supporting/TemplateHashService.js";
|
|
22
|
+
export interface VerifyAssemblyIntegrityParams {
|
|
23
|
+
hashService: Pick<TemplateHashService, "computeTemplateHash">;
|
|
24
|
+
cdkOutPath: string;
|
|
25
|
+
/** The stack(s) about to spawn — one for sequential, the set for a parallel phase. */
|
|
26
|
+
stackNames: readonly string[];
|
|
27
|
+
/** Detection-time template hashes (the gates verified against these). */
|
|
28
|
+
detectionHashes: ReadonlyMap<string, string>;
|
|
29
|
+
/** True when this run was authorised through the approval gate. */
|
|
30
|
+
approvalGated: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Re-hash each named stack's synthesised template and compare against the
|
|
34
|
+
* detection-time hash. Fails closed on a mismatch OR an unreadable template
|
|
35
|
+
* (a clobbered assembly can manifest as either). Stacks detection never
|
|
36
|
+
* hashed are skipped: there is no reference to compare against, the pre-fix
|
|
37
|
+
* loop deployed them, and a genuinely missing template still fails inside
|
|
38
|
+
* cdk itself.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verifyAssemblyIntegrity(params: VerifyAssemblyIntegrityParams): Promise<Result<void, Error>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var s=(e,t)=>c(e,"name",{value:t,configurable:!0});import{join as d}from"node:path";import{success as l,failure as a}from"@fjall/generator";function i(e,t,n,o){return`Assembly integrity check failed for ${e}: ${o} after change detection.${n?" The approved plan no longer matches what would deploy.":""} Another process likely re-synthesised ${t} mid-deploy. Nothing was deployed for this stack \u2014 re-run 'fjall deploy --plan' against the current working tree and approve the new plan.`}s(i,"integrityFailureMessage");async function f(e){for(const t of e.stackNames){const n=e.detectionHashes.get(t);if(n===void 0)continue;const o=d(e.cdkOutPath,`${t}.template.json`),r=await e.hashService.computeTemplateHash(o);if(!r.success)return a(new Error(i(t,e.cdkOutPath,e.approvalGated,"its synthesised template could no longer be read")));if(r.data!==n)return a(new Error(i(t,e.cdkOutPath,e.approvalGated,"its synthesised template changed")))}return l(void 0)}s(f,"verifyAssemblyIntegrity");export{f as verifyAssemblyIntegrity};
|
|
@@ -5,14 +5,38 @@
|
|
|
5
5
|
* that the plan the approver saw is the plan that executes (no TOCTOU): if any
|
|
6
6
|
* stack's synthesised template changed between plan and apply, the digest
|
|
7
7
|
* diverges and the gate refuses the stale token.
|
|
8
|
+
*
|
|
9
|
+
* The optional {@link DeployScope} folds the RESOLVED deploy behaviour into the
|
|
10
|
+
* digest as well: the exact stack subset the deploy loop will deploy and
|
|
11
|
+
* whether an image build/push will run. A token minted under `--infra-only`
|
|
12
|
+
* then refuses as `superseded` on a full apply (and vice versa) — scope flags
|
|
13
|
+
* are part of what the approver sanctioned, not just the templates. Scopeless
|
|
14
|
+
* calls (domain deploys, which always `cdk deploy --all` and never build)
|
|
15
|
+
* produce the legacy digest byte-for-byte.
|
|
8
16
|
*/
|
|
9
17
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `{a, bc}`.
|
|
18
|
+
* The resolved deploy scope bound into the digest. Derived from the SAME
|
|
19
|
+
* values the deploy loop consumes (`getStackNamesToDeploy`,
|
|
20
|
+
* `willRunDockerBuild`) — never re-derived from raw option flags.
|
|
14
21
|
*/
|
|
15
|
-
export
|
|
22
|
+
export interface DeployScope {
|
|
23
|
+
/** The exact stack names the deploy loop will deploy (any order). */
|
|
24
|
+
readonly stacksToDeploy: readonly string[];
|
|
25
|
+
/** True when the pipeline will build and push application image(s). */
|
|
26
|
+
readonly imageBuild: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fold the stack-name → template-hash map (and, when supplied, the resolved
|
|
30
|
+
* deploy scope) into a single sha256 hex digest. Entries are sorted by stack
|
|
31
|
+
* name so the digest is independent of map order, and each pair is framed
|
|
32
|
+
* with a NUL separator so `{ab, c}` cannot collide with `{a, bc}`. The NUL
|
|
33
|
+
* pair separator is the SHIPPED wire derivation — changing it invalidates
|
|
34
|
+
* every outstanding token and stored digest, so the scopeless legacy pin in
|
|
35
|
+
* assemblyDigest.test.ts locks it byte-for-byte. Scope lines carry a
|
|
36
|
+
* `scope:` prefix (CloudFormation stack names cannot contain `:`), appended
|
|
37
|
+
* after the hash lines, with the stack subset sorted for order-independence.
|
|
38
|
+
*/
|
|
39
|
+
export declare function computeAssemblyDigest(stackHashes: ReadonlyMap<string, string>, scope?: DeployScope): string;
|
|
16
40
|
/**
|
|
17
41
|
* The short head of the digest embedded in the approval token wire format
|
|
18
42
|
* (`signPlanToken` reuses the planToken shape). The full digest stays in the
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);
|
|
1
|
+
var r=Object.defineProperty;var u=(t,a)=>r(t,"name",{value:a,configurable:!0});import{createHash as o}from"node:crypto";function n(t,a){return t<a?-1:t>a?1:0}u(n,"compareNames");function f(t,a){const p=[...t.entries()].sort(([s],[d])=>n(s,d)),e=o("sha256");for(const[s,d]of p)e.update(s),e.update("\0"),e.update(d),e.update(`
|
|
2
|
+
`);if(a!==void 0){for(const s of[...a.stacksToDeploy].sort(n))e.update("scope:stack"),e.update("\0"),e.update(s),e.update(`
|
|
3
|
+
`);e.update("scope:imageBuild"),e.update("\0"),e.update(a.imageBuild?"true":"false"),e.update(`
|
|
4
|
+
`)}return e.digest("hex")}u(f,"computeAssemblyDigest");function m(t){return o("sha256").update(t).digest("hex").slice(0,32)}u(m,"digestHead");export{f as computeAssemblyDigest,m as digestHead};
|
|
@@ -11,4 +11,29 @@ export interface StackTemplatePair {
|
|
|
11
11
|
/** The freshly synthesised cdk.out template. */
|
|
12
12
|
newTemplate: unknown;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Reproduce CloudFormation GetTemplate's ASCII folding on a parsed template.
|
|
16
|
+
*
|
|
17
|
+
* WHY: GetTemplate returns stored template bodies with every non-ASCII
|
|
18
|
+
* character folded to `?` (verified 2026-07-20: em-dashes in alarm
|
|
19
|
+
* descriptions came back as `?` while the same strings via describe-alarms
|
|
20
|
+
* were clean UTF-8 — a retrieval artefact). Diffing that folded live body
|
|
21
|
+
* against the clean local synth made every plan report phantom
|
|
22
|
+
* encoding-only "changes" that could never converge, padding
|
|
23
|
+
* resourceChangeCount and resourceChangedStacks with noise. Folding BOTH
|
|
24
|
+
* operands immediately before `diffTemplate` neutralises the artefact at
|
|
25
|
+
* that single choke point; template-hash drift detection against recorded
|
|
26
|
+
* state stays byte-exact.
|
|
27
|
+
*
|
|
28
|
+
* Accepted trade-off: a real change that alters ONLY non-ASCII characters
|
|
29
|
+
* (e.g. swapping one dash variant for another in a description) becomes
|
|
30
|
+
* invisible to this differ — accepted because such changes are
|
|
31
|
+
* cosmetic-description class.
|
|
32
|
+
*
|
|
33
|
+
* Pure and non-mutating: returns a new structure with string values AND
|
|
34
|
+
* object keys folded, one `?` per non-ASCII code point (the `[^\x00-\x7F]`
|
|
35
|
+
* class, applied per character); non-string primitives pass through
|
|
36
|
+
* untouched.
|
|
37
|
+
*/
|
|
38
|
+
export declare function foldTemplateToAscii(value: unknown): unknown;
|
|
14
39
|
export declare function buildDeployPlan(stacks: StackTemplatePair[], assemblyDigest: string): DeployPlan;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g=Object.defineProperty;var c=(e,r)=>g(e,"name",{value:r,configurable:!0});import{diffTemplate as T}from"@aws-cdk/cloudformation-diff";import{classifyImpact as C,derivePropertyChanges as A,isDataLoss as k}from"./classify.js";import{renderPlanSummary as D}from"./renderDeployPlan.js";function P(){return{create:0,update:0,replace:0,delete:0,read:0,"no-op":0}}c(P,"emptyCounts");function m(e){let r="";for(const t of e)r+=(t.codePointAt(0)??0)>127?"?":t;return r}c(m,"foldStringToAscii");function a(e){if(typeof e=="string")return m(e);if(Array.isArray(e))return e.map(a);if(typeof e=="object"&&e!==null){const r={};for(const[t,n]of Object.entries(e))r[m(t)]=a(n);return r}return e}c(a,"foldTemplateToAscii");function R(e,r){const t=[],n=P();let p=!1,d=!1;for(const s of e){const f=T(a(s.oldTemplate??{}),a(s.newTemplate??{}));f.iamChanges.permissionsBroadened&&(p=!0),f.securityGroupChanges.hasChanges&&(d=!0),f.resources.forEachDifference((h,i)=>{const o=C(i.changeImpact);if(n[o.action]+=1,o.action==="no-op")return;const u=i.newResourceType??i.oldResourceType??"AWS::Unknown::Resource";t.push({stack:s.stackName,address:h,resourceType:u,action:o.action,replacementMode:o.replacementMode,destructive:o.destructive,retained:o.retained,dataLoss:k({action:o.action,retained:o.retained,resourceType:u}),propertyChanges:A(i.propertyUpdates)})})}const l=t.length,y=t.some(s=>s.destructive);return{changes:t,changeCount:l,counts:n,hasDestructiveChanges:y,iamBroadening:p,securityGroupBroadening:d,assemblyDigest:r,summary:D({counts:n,changes:t})}}c(R,"buildDeployPlan");export{R as buildDeployPlan,a as foldTemplateToAscii};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Deploy-plan computation + rendering — the approval gate's read-side core. */
|
|
2
2
|
export { REPLACEMENT_VERDICT_SOURCES, type DeployPlan, type DeployPlanResourceChange, type ReplacementVerdictSource } from "./types.js";
|
|
3
|
-
export { buildDeployPlan, type StackTemplatePair } from "./buildDeployPlan.js";
|
|
3
|
+
export { buildDeployPlan, foldTemplateToAscii, type StackTemplatePair } from "./buildDeployPlan.js";
|
|
4
4
|
export { applyRegistryOverlay, overlayResourceChange, registryVerdictForProperty, type ApplyRegistryOverlayParams, type OverlayResourceChangeResult, type RegistryVerdictDisagreement } from "./registryOverlay.js";
|
|
5
5
|
export { applyChangeSetEscalation, needsChangeSetEscalation, type ApplyChangeSetEscalationParams } from "./changeSetEscalation.js";
|
|
6
6
|
export { findPinnedPhysicalName, resourcePropertiesFromTemplate } from "./pinnedNames.js";
|
|
7
7
|
export { computeDeployPlan, type CfnTemplateReader, type ComputeDeployPlanParams, type ComputeDeployPlanStack } from "./computeDeployPlan.js";
|
|
8
|
-
export { computeAssemblyDigest, digestHead } from "./assemblyDigest.js";
|
|
8
|
+
export { computeAssemblyDigest, digestHead, type DeployScope } from "./assemblyDigest.js";
|
|
9
9
|
export { classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, type ImpactClassification } from "./classify.js";
|
|
10
10
|
export { renderPlanSummary, renderPlanLines, toWirePlanChanges } from "./renderDeployPlan.js";
|
|
11
11
|
export { signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN, type SignApprovalTokenOptions, type SignedApprovalToken, type VerifyApprovalTokenOptions, type VerifyApprovalTokenResult } from "./approvalToken.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{REPLACEMENT_VERDICT_SOURCES as o}from"./types.js";import{buildDeployPlan as t}from"./buildDeployPlan.js";import{applyRegistryOverlay as
|
|
1
|
+
import{REPLACEMENT_VERDICT_SOURCES as o}from"./types.js";import{buildDeployPlan as t,foldTemplateToAscii as p}from"./buildDeployPlan.js";import{applyRegistryOverlay as l,overlayResourceChange as i,registryVerdictForProperty as n}from"./registryOverlay.js";import{applyChangeSetEscalation as y,needsChangeSetEscalation as P}from"./changeSetEscalation.js";import{findPinnedPhysicalName as T,resourcePropertiesFromTemplate as c}from"./pinnedNames.js";import{computeDeployPlan as A}from"./computeDeployPlan.js";import{computeAssemblyDigest as x,digestHead as E}from"./assemblyDigest.js";import{classifyImpact as u,derivePropertyChanges as C,isDataLoss as L,isStatefulResourceType as S}from"./classify.js";import{renderPlanSummary as h,renderPlanLines as v,toWirePlanChanges as D}from"./renderDeployPlan.js";import{signApprovalToken as N,verifyApprovalToken as V,DEFAULT_APPROVAL_TTL_MS as F,APPROVAL_TOKEN_PATTERN as b}from"./approvalToken.js";export{b as APPROVAL_TOKEN_PATTERN,F as DEFAULT_APPROVAL_TTL_MS,o as REPLACEMENT_VERDICT_SOURCES,y as applyChangeSetEscalation,l as applyRegistryOverlay,t as buildDeployPlan,u as classifyImpact,x as computeAssemblyDigest,A as computeDeployPlan,C as derivePropertyChanges,E as digestHead,T as findPinnedPhysicalName,p as foldTemplateToAscii,L as isDataLoss,S as isStatefulResourceType,P as needsChangeSetEscalation,i as overlayResourceChange,n as registryVerdictForProperty,v as renderPlanLines,h as renderPlanSummary,c as resourcePropertiesFromTemplate,N as signApprovalToken,D as toWirePlanChanges,V as verifyApprovalToken};
|
|
@@ -8,5 +8,5 @@ export { NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS, NS_PROPAGATION_DEFAULT_INTERVAL_MS
|
|
|
8
8
|
export { ACM_CAA_ISSUERS, caaAncestorChain, formatCaaRecord, runCaaPreflight, type CaaPreflightParams, type CaaPreflightVerdict } from "./caaPreflight.js";
|
|
9
9
|
export { deployDelegatedDomain, type DelegatedDomainDeployer, type DelegatedZonePhaseState, type DeployDelegatedDomainParams, type DelegatedDomainDeployOutcome } from "./delegatedDomainDeploy.js";
|
|
10
10
|
export { CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE, templateOwnsDelegationRecord, deleteChildNsFromParent, nsDelegationPhysicalName, runDelegatedDomainDestroy, type NsDeleteOutcome, type DeleteChildNsParams, type DelegationDestroyNsOutcome, type DelegatedDomainDestroyOutcome, type DelegatedDomainDestroyParams } from "./delegationDestroyMirror.js";
|
|
11
|
-
export { classifyZoneRecords, buildSatelliteRecordIndex, createSystemDnsNameProbe, systemDnsNameProbe, normaliseRecordName, type RecordClassification, type ClassifiedRecord, type ZoneClassificationReport, type ZoneClassifierClients, type ZoneClassifierPhase, type ClassifyZoneOptions, type SatelliteRecordIndex, type AwsSdkClientLike, type DnsProbeVerdict, type DnsNameProbe } from "./zoneClassifier.js";
|
|
11
|
+
export { classifyZoneRecords, buildSatelliteRecordIndex, createSystemDnsNameProbe, systemDnsNameProbe, normaliseRecordName, type RecordClassification, type ClassifiedRecord, type ZoneClassificationReport, type ZoneClassifierClients, type ZoneClassifierPhase, type ClassifyZoneOptions, type SatelliteRecordIndex, type DelegatedChildDomain, type ChildAccountClients, type ChildAccountClientFactory, type AwsSdkClientLike, type DnsProbeVerdict, type DnsNameProbe } from "./zoneClassifier.js";
|
|
12
12
|
export { isRoute53NameServer, isDelegatedToRoute53 } from "./route53Delegation.js";
|
|
@@ -13,6 +13,17 @@ import { type Result } from "@fjall/generator";
|
|
|
13
13
|
* affected records resolve to the conservative bucket — `unknown`, never
|
|
14
14
|
* `residue` — and the report carries a warning naming the gap and its cure.
|
|
15
15
|
*
|
|
16
|
+
* Evidence is account-local with ONE deliberate exception: callers may name
|
|
17
|
+
* delegated child domains (ClassifyZoneOptions.delegatedChildren) plus a
|
|
18
|
+
* child-account client factory, and the classifier then proves a child's
|
|
19
|
+
* parent-side NS row `satellite` against the CHILD account. Proof needs all
|
|
20
|
+
* three steps: a live child domain stack, a Processed template whose
|
|
21
|
+
* delegation resource literally claims the row, and stack outputs whose
|
|
22
|
+
* nameservers SET-match the live values. Any gap in that three-step proof
|
|
23
|
+
* warns and leaves the row `unknown`. The cross-account surface is
|
|
24
|
+
* deliberately ONLY the delegated-child NS row; arbitrary child-account
|
|
25
|
+
* stacks earn no satellite coverage here.
|
|
26
|
+
*
|
|
16
27
|
* Residue evidence comes in exactly two shapes, both verified against live
|
|
17
28
|
* state: ACM validation CNAMEs no live certificate references, and dangling
|
|
18
29
|
* A/AAAA aliases into AWS-managed namespaces (CloudFront/ELB) whose target a
|
|
@@ -124,6 +135,22 @@ export interface ClassifyZoneOptions {
|
|
|
124
135
|
* Injected by tests; production callers rely on the system-resolver default.
|
|
125
136
|
*/
|
|
126
137
|
readonly dnsProbe?: DnsNameProbe;
|
|
138
|
+
/**
|
|
139
|
+
* Delegated child domains that may hold a cross-account claim on their
|
|
140
|
+
* parent-side NS rows (see module doc). Children whose NS row is absent,
|
|
141
|
+
* or already claimed by the domain stack or the satellite index (the
|
|
142
|
+
* same-account delegation path), are skipped silently and cost nothing.
|
|
143
|
+
* Omitted, the classifier behaves exactly as before: account-local
|
|
144
|
+
* evidence only.
|
|
145
|
+
*/
|
|
146
|
+
readonly delegatedChildren?: readonly DelegatedChildDomain[];
|
|
147
|
+
/**
|
|
148
|
+
* Factory minting read-only clients into a delegated child's account.
|
|
149
|
+
* Required for any cross-account proof: when children need evidence and
|
|
150
|
+
* this is absent, ONE warning names the cure and every child's NS row
|
|
151
|
+
* stays 'unknown', fail-closed.
|
|
152
|
+
*/
|
|
153
|
+
readonly childAccountClientFactory?: ChildAccountClientFactory;
|
|
127
154
|
readonly abortSignal?: AbortSignal;
|
|
128
155
|
readonly onPhase?: (phase: ZoneClassifierPhase) => void;
|
|
129
156
|
}
|
|
@@ -141,6 +168,38 @@ export interface SatelliteRecordIndex {
|
|
|
141
168
|
readonly stackNames: readonly string[];
|
|
142
169
|
readonly warnings: readonly string[];
|
|
143
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* A delegated child domain whose parent-side NS row may be written by a
|
|
173
|
+
* domain stack in ANOTHER AWS account. Enumerated by the caller (the CLI
|
|
174
|
+
* reads the delegated domains out of fjall-config.json); the classifier
|
|
175
|
+
* only proves or declines each claim, fail-closed.
|
|
176
|
+
*/
|
|
177
|
+
export interface DelegatedChildDomain {
|
|
178
|
+
/** Child zone FQDN, e.g. "development.fjall.io". */
|
|
179
|
+
readonly zoneName: string;
|
|
180
|
+
/**
|
|
181
|
+
* 12-digit AWS account id the child's domain stack deploys into. Absent,
|
|
182
|
+
* the child is skipped fail-closed with a warning naming the config cure.
|
|
183
|
+
*/
|
|
184
|
+
readonly accountId?: string;
|
|
185
|
+
/**
|
|
186
|
+
* Child domain stack name override. Defaults to the deploy-state
|
|
187
|
+
* convention, getDomainStackName(zoneName).
|
|
188
|
+
*/
|
|
189
|
+
readonly stackName?: string;
|
|
190
|
+
}
|
|
191
|
+
/** Clients minted into a delegated child's account, read-only use only. */
|
|
192
|
+
export interface ChildAccountClients {
|
|
193
|
+
readonly cloudFormation: AwsSdkClientLike;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Mint clients for a delegated child's account. Any error, including an
|
|
197
|
+
* interactivity requirement (an expired SSO session must never hang a
|
|
198
|
+
* non-interactive run), resolves the Result to failure; the classifier then
|
|
199
|
+
* warns and leaves that child's NS row 'unknown' without touching its
|
|
200
|
+
* siblings.
|
|
201
|
+
*/
|
|
202
|
+
export type ChildAccountClientFactory = (accountId: string) => Promise<Result<ChildAccountClients, Error>>;
|
|
144
203
|
/**
|
|
145
204
|
* Route53 returns names with a trailing dot and octal-escaped bytes
|
|
146
205
|
* (`\052` = `*`). Normalise both directions (live names AND template names)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var z=Object.defineProperty;var l=(t,e)=>z(t,"name",{value:e,configurable:!0});import{ListResourceRecordSetsCommand as K}from"@aws-sdk/client-route-53";import{DescribeStacksCommand as U,DescribeStackResourcesCommand as B,GetTemplateCommand as V}from"@aws-sdk/client-cloudformation";import{ListCertificatesCommand as F,DescribeCertificateCommand as G}from"@aws-sdk/client-acm";import{Resolver as j}from"node:dns/promises";import{success as R,failure as S}from"@fjall/generator";import{maskSensitiveOutput as h,getErrorMessage as A}from"@fjall/util";import{composeSdkAbortSignal as N,isAborted as C}from"../../aws/organisations/types.js";import{CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE as x}from"./delegationDestroyMirror.js";const W=5e3,Z=2;async function J(t,e,a){const n=new j({timeout:W,tries:Z}),o=l(()=>{n.cancel()},"cancel");a?.addEventListener("abort",o,{once:!0});try{return e==="A"?await n.resolve4(t):await n.resolve6(t)}finally{a?.removeEventListener("abort",o)}}l(J,"resolveWithSystemResolver");function X(t=J){const e=l(async(a,n,o)=>{try{return(await t(a,n,o)).length>0?"answers":"indeterminate"}catch(s){const i=s.code;return i==="ENOTFOUND"?"nxdomain":i==="ENODATA"?"nodata":"indeterminate"}},"queryFamily");return async(a,n)=>{const o=await e(a,"A",n);if(o==="answers")return"resolves";if(o==="nxdomain")return"nxdomain";if(o==="indeterminate")return"indeterminate";const s=await e(a,"AAAA",n);return s==="answers"?"resolves":s==="nxdomain"?"nxdomain":"indeterminate"}}l(X,"createSystemDnsNameProbe");const q=X(),Y=new Set(["CREATE_COMPLETE","UPDATE_COMPLETE","UPDATE_ROLLBACK_COMPLETE","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE","UPDATE_IN_PROGRESS","UPDATE_COMPLETE_CLEANUP_IN_PROGRESS","UPDATE_ROLLBACK_IN_PROGRESS","UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS"]),H="fjall:",Q="Fjall",ee=/^_[0-9a-f]{8,64}$/,te=".acm-validations.aws",ne=[/\.cloudfront\.net$/,/\.elb\.amazonaws\.com$/,/\.elb\.[a-z0-9-]+\.amazonaws\.com$/];function g(t){const e=t.replace(/\\(\d{3})/g,(n,o)=>String.fromCharCode(parseInt(o,8)));return(e.endsWith(".")?e.slice(0,-1):e).toLowerCase()}l(g,"normaliseRecordName");function E(t,e,a){return`${g(t)}|${e.toUpperCase()}|${a??""}`}l(E,"recordKey");async function ae(t,e,a){const n=[];let o,s,i;do{if(C(a))throw new Error("record listing aborted by shutdown signal");const c=await t.send(new K({HostedZoneId:e,StartRecordName:o,StartRecordType:s,StartRecordIdentifier:i}),{abortSignal:N(a)});for(const r of c.ResourceRecordSets??[]){if(!r.Name||!r.Type)continue;const f=r.AliasTarget?.DNSName!==void 0?g(r.AliasTarget.DNSName):void 0,u=r.AliasTarget?.DNSName!==void 0?[r.AliasTarget.DNSName]:(r.ResourceRecords??[]).flatMap(p=>p.Value!==void 0?[p.Value]:[]);n.push({name:g(r.Name),type:r.Type,...r.SetIdentifier!==void 0?{setIdentifier:r.SetIdentifier}:{},values:u,...f!==void 0?{aliasTargetDnsName:f}:{}})}c.IsTruncated?(o=c.NextRecordName,s=c.NextRecordType,i=c.NextRecordIdentifier):(o=void 0,s=void 0,i=void 0)}while(o);return n}l(ae,"listLiveRecords");function re(t){const e=new Set;let a;try{a=JSON.parse(t)}catch{return S(new Error("stack template is not JSON (likely a hand-written YAML template), so its Route53 record declarations cannot be indexed. Cure: convert the template to JSON or accept conservative classification."))}if(typeof a!="object"||a===null)return R(e);const n=a.Resources;if(typeof n!="object"||n===null)return R(e);const o=l(s=>{const i=s.Name,c=s.Type,r=s.SetIdentifier;typeof i!="string"||typeof c!="string"||r!==void 0&&typeof r!="string"||e.add(E(i,c,r))},"addKey");for(const[s,i]of Object.entries(n)){if(typeof i!="object"||i===null)continue;const{Type:c,Properties:r}=i,f=typeof r=="object"&&r!==null?r:void 0;if(c===x){const u=f?.DelegatedZoneName;if(typeof u!="string")return S(new Error(`${x} resource '${s}' has no literal DelegatedZoneName, so the parent-zone NS row it writes cannot be indexed. Cure: synthesise the delegation with a literal delegated zone name or accept conservative classification.`));e.add(E(u,"NS"));continue}if(f!==void 0){if(c==="AWS::Route53::RecordSet")o(f);else if(c==="AWS::Route53::RecordSetGroup"){const u=f.RecordSets;if(!Array.isArray(u))continue;for(const p of u)typeof p=="object"&&p!==null&&o(p)}}}return R(e)}l(re,"extractTemplateRecordKeys");async function M(t,e,a){try{const n=await t.send(new V({StackName:e,TemplateStage:"Processed"}),{abortSignal:N(a)});if(n.TemplateBody===void 0)return S(new Error(`zone classification: CloudFormation returned no template body for stack '${e}'`));const o=re(n.TemplateBody);return o.success?o:S(new Error(`stack '${e}': ${o.error.message}`))}catch(n){return S(n instanceof Error?n:new Error(String(n)))}}l(M,"readStackRecordKeys");function oe(t){return t.Tags?.some(e=>e.Key?.startsWith(H))===!0?!0:t.StackName?.startsWith(Q)===!0}l(oe,"isFjallStack");async function ie(t,e={}){const a=new Map,n=[],o=new Set(e.excludeStackNames??[]),s=[];try{let i;do{if(C(e.abortSignal))throw new Error("stack enumeration aborted by shutdown signal");const c=await t.send(new U({...i!==void 0?{NextToken:i}:{}}),{abortSignal:N(e.abortSignal)});for(const r of c.Stacks??[])r.StackName===void 0||o.has(r.StackName)||r.StackStatus===void 0||!Y.has(r.StackStatus)||!oe(r)||s.push(r.StackName);i=c.NextToken}while(i!==void 0)}catch(i){return n.push(h(`zone classification: could not enumerate CloudFormation stacks (${A(i)}) \u2014 satellite ownership cannot be proven, so unmatched records classify 'unknown'. Cure: grant cloudformation:DescribeStacks and retry.`)),{byRecordKey:a,stackNames:[],warnings:n}}for(const i of s){const c=await M(t,i,e.abortSignal);if(!c.success){n.push(h(`zone classification: could not read template for stack '${i}' (${A(c.error)}) \u2014 its records classify 'unknown'. Cure: grant cloudformation:GetTemplate and retry.`));continue}for(const r of c.data)a.has(r)||a.set(r,i)}return{byRecordKey:a,stackNames:s,warnings:n}}l(ie,"buildSatelliteRecordIndex");async function se(t,e){const a=new Map;for(const[n,o]of t)try{const s=[];let i;do{if(C(e))throw new Error("certificate listing aborted by shutdown signal");const c=await o.send(new F({...i!==void 0?{NextToken:i}:{}}),{abortSignal:N(e)});for(const r of c.CertificateSummaryList??[])r.CertificateArn!==void 0&&s.push(r.CertificateArn);i=c.NextToken}while(i!==void 0);for(const c of s){if(C(e))throw new Error("certificate description aborted by shutdown signal");const r=await o.send(new G({CertificateArn:c}),{abortSignal:N(e)});for(const f of r.Certificate?.DomainValidationOptions??[]){const u=f.ResourceRecord;if(u?.Type==="CNAME"&&u.Name!==void 0){const p=g(u.Name),w=a.get(p)??new Set;w.add(c),a.set(p,w)}}}}catch(s){return S(new Error(`zone classification: could not read ACM certificates in ${n} (${A(s)}). Cure: grant acm:ListCertificates and acm:DescribeCertificate in ${n}, then retry.`))}return R(a)}l(se,"collectAcmValidationReferences");async function ce(t,e,a,n){const o=new Map,s=[];for(const i of e){if(o.size===a.size)break;try{if(C(n))throw new Error("stack resource listing aborted by shutdown signal");const c=await t.send(new B({StackName:i}),{abortSignal:N(n)});for(const r of c.StackResources??[])r.ResourceType!=="AWS::CertificateManager::Certificate"||r.PhysicalResourceId===void 0||!a.has(r.PhysicalResourceId)||o.has(r.PhysicalResourceId)||o.set(r.PhysicalResourceId,i)}catch(c){s.push(h(`zone classification: could not list resources for stack '${i}' (${A(c)}), so certificate ownership cannot be proven there and its validation CNAMEs stay 'unknown'. Cure: grant cloudformation:DescribeStackResources and retry.`))}}return{ownerByArn:o,warnings:s}}l(ce,"resolveCertificateOwnership");function b(t){if(t.type!=="CNAME")return!1;const e=t.name.split(".")[0]??"";return ee.test(e)?t.values.some(a=>g(a).endsWith(te)):!1}l(b,"isAcmValidationShaped");function P(t){if(t.type!=="A"&&t.type!=="AAAA")return!1;const e=t.aliasTargetDnsName;return e===void 0?!1:ne.some(a=>a.test(e))}l(P,"isAwsManagedAliasCandidate");async function Ne(t){const{zone:e,domainStackName:a,clients:n,abortSignal:o,onPhase:s}=t,i=[];s?.("read-records");let c;try{c=await ae(n.route53,e.hostedZoneId,o)}catch(d){return S(new Error(`zone classification: could not list record sets for zone ${e.hostedZoneId} (${e.zoneName}): ${A(d)}. Cure: grant route53:ListResourceRecordSets on the zone and retry.`))}let r=new Set,f=!0;if(a!==void 0){const d=await M(n.cloudFormation,a,o);d.success?r=d.data:(f=!1,i.push(h(`zone classification: could not read the domain stack template for '${a}' (${A(d.error)}) \u2014 only zone-owned SOA/NS classify 'declared'. Cure: deploy the domain stack with 'fjall domain deploy ${e.zoneName}' or grant cloudformation:GetTemplate.`)))}s?.("index-stacks");const u=t.satelliteIndex??await ie(n.cloudFormation,{...a!==void 0?{excludeStackNames:[a]}:{},...o!==void 0?{abortSignal:o}:{}});i.push(...u.warnings),s?.("check-acm");const p=c.some(b);let w;if(p){const d=await se(n.acmByRegion,o);d.success?w=d.data:i.push(h(d.error.message))}let I;if(w!==void 0){const d=new Set;for(const m of c)if(b(m))for(const y of w.get(m.name)??[])d.add(y);if(d.size>0){const m=await ce(n.cloudFormation,[...a!==void 0?[a]:[],...u.stackNames],d,o);I=m.ownerByArn,i.push(...m.warnings)}}const _=f&&u.warnings.length===0,T=new Set;if(_)for(const d of c){if(!P(d))continue;const m=E(d.name,d.type,d.setIdentifier);r.has(m)||u.byRecordKey.has(m)||T.add(d.aliasTargetDnsName)}let O;if(T.size>0){const d=t.dnsProbe??q,m=new Map;for(const y of T){let k;try{k=await d(y,o)}catch($){k="indeterminate",i.push(h(`zone classification: DNS probe for alias target '${y}' failed (${A($)}) \u2014 dependent alias records classify 'unknown', never 'residue'. Cure: retry with a working resolver.`)),m.set(y,k);continue}k==="indeterminate"&&i.push(h(`zone classification: DNS probe for alias target '${y}' was inconclusive \u2014 dependent alias records classify 'unknown', never 'residue'. Cure: retry once the resolver answers definitively.`)),m.set(y,k)}O=m}s?.("classify");const v=g(e.zoneName),D=c.map(d=>le(d,{zoneApex:v,domainStackName:a,declaredKeys:r,satelliteIndex:u,acmValidationReferences:w,certificateOwnerByArn:I,templateEvidenceComplete:_,aliasProbeVerdicts:O})).sort((d,m)=>d.name.localeCompare(m.name)||d.type.localeCompare(m.type)||(d.setIdentifier??"").localeCompare(m.setIdentifier??"")),L={declared:0,satellite:0,residue:0,unknown:0};for(const d of D)L[d.classification]+=1;return R({hostedZoneId:e.hostedZoneId,zoneName:v,records:D,counts:L,warnings:i})}l(Ne,"classifyZoneRecords");function de(t,e){const a=new Set;for(const n of t){const o=e?.get(n);if(o===void 0)return;a.add(o)}return a}l(de,"provenCertificateOwners");function le(t,e){const a=E(t.name,t.type,t.setIdentifier),n={name:t.name,type:t.type,...t.setIdentifier!==void 0?{setIdentifier:t.setIdentifier}:{},values:t.values};if(t.name===e.zoneApex&&(t.type==="SOA"||t.type==="NS"))return{...n,classification:"declared",...e.domainStackName!==void 0?{ownerStack:e.domainStackName}:{},detail:"zone-owned record, created with the hosted zone"};if(e.declaredKeys.has(a))return{...n,classification:"declared",...e.domainStackName!==void 0?{ownerStack:e.domainStackName}:{}};const o=e.satelliteIndex.byRecordKey.get(a);if(o!==void 0)return{...n,classification:"satellite",ownerStack:o};if(b(t)){if(e.acmValidationReferences===void 0)return{...n,classification:"unknown",detail:"ACM validation CNAME shape, but live-certificate evidence was unavailable \u2014 fail-closed as unknown rather than residue"};const s=e.acmValidationReferences.get(t.name);if(s!==void 0&&s.size>0){const i=de(s,e.certificateOwnerByArn);if(i!==void 0){const c=[...i].sort();if(e.domainStackName!==void 0&&c.every(f=>f===e.domainStackName))return{...n,classification:"declared",ownerStack:e.domainStackName,detail:"ACM validation CNAME for the domain stack's live certificates; required for issuance and auto-renewal"};const r=c.find(f=>f!==e.domainStackName);if(r!==void 0)return{...n,classification:"satellite",ownerStack:r,detail:`ACM validation CNAME for live certificates owned by ${c.map(f=>`'${f}'`).join(", ")}; required for issuance and auto-renewal`}}return{...n,classification:"unknown",detail:"ACM validation CNAME still referenced by a live certificate \u2014 keep it; it is not residue"}}return{...n,classification:"residue",detail:"ACM validation CNAME not referenced by any live certificate in the scanned regions"}}if(P(t)){if(!e.templateEvidenceComplete)return{...n,classification:"unknown",detail:"alias to an AWS-managed target, but template evidence was incomplete \u2014 fail-closed as unknown rather than residue"};const s=e.aliasProbeVerdicts?.get(t.aliasTargetDnsName);return s==="nxdomain"?{...n,classification:"residue",detail:`dangling alias: target ${t.aliasTargetDnsName} no longer exists (NXDOMAIN) and no live template references this record`}:s==="resolves"?{...n,classification:"unknown",detail:"alias target still exists in DNS, so the record cannot be dangling; not residue"}:{...n,classification:"unknown",detail:"alias target probe was inconclusive \u2014 fail-closed as unknown rather than residue"}}return{...n,classification:"unknown"}}l(le,"classifyRecord");export{ie as buildSatelliteRecordIndex,Ne as classifyZoneRecords,X as createSystemDnsNameProbe,re as extractTemplateRecordKeys,g as normaliseRecordName,q as systemDnsNameProbe};
|
|
1
|
+
var B=Object.defineProperty;var u=(t,e)=>B(t,"name",{value:e,configurable:!0});import{ListResourceRecordSetsCommand as U}from"@aws-sdk/client-route-53";import{DescribeStacksCommand as P,DescribeStackResourcesCommand as G,GetTemplateCommand as W}from"@aws-sdk/client-cloudformation";import{ListCertificatesCommand as Z,DescribeCertificateCommand as q}from"@aws-sdk/client-acm";import{Resolver as J}from"node:dns/promises";import{success as T,failure as A}from"@fjall/generator";import{maskSensitiveOutput as N,getErrorMessage as y,getDomainExportNames as X,getDomainStackName as Y}from"@fjall/util";import{composeSdkAbortSignal as E,isAborted as I}from"../../aws/organisations/types.js";import{CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE as V}from"./delegationDestroyMirror.js";const H=5e3,Q=2;async function ee(t,e,n){const a=new J({timeout:H,tries:Q}),r=u(()=>{a.cancel()},"cancel");n?.addEventListener("abort",r,{once:!0});try{return e==="A"?await a.resolve4(t):await a.resolve6(t)}finally{n?.removeEventListener("abort",r)}}u(ee,"resolveWithSystemResolver");function te(t=ee){const e=u(async(n,a,r)=>{try{return(await t(n,a,r)).length>0?"answers":"indeterminate"}catch(c){const o=c.code;return o==="ENOTFOUND"?"nxdomain":o==="ENODATA"?"nodata":"indeterminate"}},"queryFamily");return async(n,a)=>{const r=await e(n,"A",a);if(r==="answers")return"resolves";if(r==="nxdomain")return"nxdomain";if(r==="indeterminate")return"indeterminate";const c=await e(n,"AAAA",a);return c==="answers"?"resolves":c==="nxdomain"?"nxdomain":"indeterminate"}}u(te,"createSystemDnsNameProbe");const ne=te(),j=new Set(["CREATE_COMPLETE","UPDATE_COMPLETE","UPDATE_ROLLBACK_COMPLETE","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE","UPDATE_IN_PROGRESS","UPDATE_COMPLETE_CLEANUP_IN_PROGRESS","UPDATE_ROLLBACK_IN_PROGRESS","UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS"]),ae="fjall:",oe="Fjall",re=/^_[0-9a-f]{8,64}$/,se=".acm-validations.aws",ie=[/\.cloudfront\.net$/,/\.elb\.amazonaws\.com$/,/\.elb\.[a-z0-9-]+\.amazonaws\.com$/];function b(t){const e=t.replace(/\\(\d{3})/g,(a,r)=>String.fromCharCode(parseInt(r,8)));return(e.endsWith(".")?e.slice(0,-1):e).toLowerCase()}u(b,"normaliseRecordName");function C(t,e,n){return`${b(t)}|${e.toUpperCase()}|${n??""}`}u(C,"recordKey");async function ce(t,e,n){const a=[];let r,c,o;do{if(I(n))throw new Error("record listing aborted by shutdown signal");const i=await t.send(new U({HostedZoneId:e,StartRecordName:r,StartRecordType:c,StartRecordIdentifier:o}),{abortSignal:E(n)});for(const s of i.ResourceRecordSets??[]){if(!s.Name||!s.Type)continue;const f=s.AliasTarget?.DNSName!==void 0?b(s.AliasTarget.DNSName):void 0,l=s.AliasTarget?.DNSName!==void 0?[s.AliasTarget.DNSName]:(s.ResourceRecords??[]).flatMap(h=>h.Value!==void 0?[h.Value]:[]);a.push({name:b(s.Name),type:s.Type,...s.SetIdentifier!==void 0?{setIdentifier:s.SetIdentifier}:{},values:l,...f!==void 0?{aliasTargetDnsName:f}:{}})}i.IsTruncated?(r=i.NextRecordName,c=i.NextRecordType,o=i.NextRecordIdentifier):(r=void 0,c=void 0,o=void 0)}while(r);return a}u(ce,"listLiveRecords");function de(t){const e=new Set;let n;try{n=JSON.parse(t)}catch{return A(new Error("stack template is not JSON (likely a hand-written YAML template), so its Route53 record declarations cannot be indexed. Cure: convert the template to JSON or accept conservative classification."))}if(typeof n!="object"||n===null)return T(e);const a=n.Resources;if(typeof a!="object"||a===null)return T(e);const r=u(c=>{const o=c.Name,i=c.Type,s=c.SetIdentifier;typeof o!="string"||typeof i!="string"||s!==void 0&&typeof s!="string"||e.add(C(o,i,s))},"addKey");for(const[c,o]of Object.entries(a)){if(typeof o!="object"||o===null)continue;const{Type:i,Properties:s}=o,f=typeof s=="object"&&s!==null?s:void 0;if(i===V){const l=f?.DelegatedZoneName;if(typeof l!="string")return A(new Error(`${V} resource '${c}' has no literal DelegatedZoneName, so the parent-zone NS row it writes cannot be indexed. Cure: synthesise the delegation with a literal delegated zone name or accept conservative classification.`));e.add(C(l,"NS"));continue}if(f!==void 0){if(i==="AWS::Route53::RecordSet")r(f);else if(i==="AWS::Route53::RecordSetGroup"){const l=f.RecordSets;if(!Array.isArray(l))continue;for(const h of l)typeof h=="object"&&h!==null&&r(h)}}}return T(e)}u(de,"extractTemplateRecordKeys");async function _(t,e,n){try{const a=await t.send(new W({StackName:e,TemplateStage:"Processed"}),{abortSignal:E(n)});if(a.TemplateBody===void 0)return A(new Error(`zone classification: CloudFormation returned no template body for stack '${e}'`));const r=de(a.TemplateBody);return r.success?r:A(new Error(`stack '${e}': ${r.error.message}`))}catch(a){return A(a instanceof Error?a:new Error(String(a)))}}u(_,"readStackRecordKeys");function le(t){return t.Tags?.some(e=>e.Key?.startsWith(ae))===!0?!0:t.StackName?.startsWith(oe)===!0}u(le,"isFjallStack");async function ue(t,e={}){const n=new Map,a=[],r=new Set(e.excludeStackNames??[]),c=[];try{let o;do{if(I(e.abortSignal))throw new Error("stack enumeration aborted by shutdown signal");const i=await t.send(new P({...o!==void 0?{NextToken:o}:{}}),{abortSignal:E(e.abortSignal)});for(const s of i.Stacks??[])s.StackName===void 0||r.has(s.StackName)||s.StackStatus===void 0||!j.has(s.StackStatus)||!le(s)||c.push(s.StackName);o=i.NextToken}while(o!==void 0)}catch(o){return a.push(N(`zone classification: could not enumerate CloudFormation stacks (${y(o)}) \u2014 satellite ownership cannot be proven, so unmatched records classify 'unknown'. Cure: grant cloudformation:DescribeStacks and retry.`)),{byRecordKey:n,stackNames:[],warnings:a}}for(const o of c){const i=await _(t,o,e.abortSignal);if(!i.success){a.push(N(`zone classification: could not read template for stack '${o}' (${y(i.error)}) \u2014 its records classify 'unknown'. Cure: grant cloudformation:GetTemplate and retry.`));continue}for(const s of i.data)n.has(s)||n.set(s,o)}return{byRecordKey:n,stackNames:c,warnings:a}}u(ue,"buildSatelliteRecordIndex");function F(t){return new Set(t.map(e=>b(e.trim())).filter(e=>e!==""))}u(F,"normaliseNsValueSet");function fe(t,e){if(t.size!==e.size)return!1;for(const n of t)if(!e.has(n))return!1;return!0}u(fe,"nsValueSetsEqual");async function me(t){const{factory:e,accountId:n,zoneName:a,stackName:r,abortSignal:c}=t,o=u(p=>A(new Error(`zone classification: cross-account delegation for '${a}': ${p}`)),"fail");let i;try{const p=await e(n);if(!p.success)return o(`account ${n} is unreachable (${y(p.error)}), so the NS row classifies 'unknown'. Cure: restore access to account ${n} and retry.`);i=p.data}catch(p){return o(`account ${n} is unreachable (${y(p)}), so the NS row classifies 'unknown'. Cure: restore access to account ${n} and retry.`)}let s;try{s=await i.cloudFormation.send(new P({StackName:r}),{abortSignal:E(c)})}catch(p){return o(`could not describe stack '${r}' in account ${n} (${y(p)}), so the NS row classifies 'unknown'. Cure: deploy the child with 'fjall domain deploy ${a}' or grant cloudformation:DescribeStacks in that account.`)}const f=s.Stacks?.[0];if(f===void 0)return o(`stack '${r}' was not found in account ${n}, so the NS row classifies 'unknown'. Cure: deploy the child with 'fjall domain deploy ${a}'.`);if(f.StackStatus===void 0||!j.has(f.StackStatus))return o(`stack '${r}' in account ${n} has status ${f.StackStatus??"unknown"}, which does not describe live resources, so the NS row classifies 'unknown'. Cure: repair the child stack, then redeploy with 'fjall domain deploy ${a}'.`);const l=await _(i.cloudFormation,r,c);if(!l.success)return o(`template evidence failed in account ${n} (${y(l.error)}), so the NS row classifies 'unknown'.`);if(!l.data.has(C(a,"NS")))return o(`stack '${r}' in account ${n} does not claim the NS row for '${a}', so the row classifies 'unknown'. Cure: redeploy the child with 'fjall domain deploy ${a}' so its delegation resource is current.`);const h=X(a).nameservers,k=(f.Outputs??[]).find(p=>p.ExportName===h)?.OutputValue;if(k===void 0||k.trim()==="")return o(`stack '${r}' in account ${n} publishes no '${h}' output, so the delegated nameserver values cannot be verified and the NS row classifies 'unknown'. Cure: redeploy the child with 'fjall domain deploy ${a}' so the export contract is current.`);const R=F(k.split(",")),$=F(t.liveNsValues);if(!fe(R,$)){const p=u(v=>`[${[...v].sort().join(", ")}]`,"quote");return o(`stack '${r}' in account ${n} publishes nameservers ${p(R)} but the live NS row holds ${p($)}, so the row classifies 'unknown'. Cure: re-run 'fjall domain deploy ${a}' so parent and child agree.`)}return T({ownerStack:r,accountId:n})}u(me,"proveCrossAccountDelegation");async function we(t,e){const n=new Map;for(const[a,r]of t)try{const c=[];let o;do{if(I(e))throw new Error("certificate listing aborted by shutdown signal");const i=await r.send(new Z({...o!==void 0?{NextToken:o}:{}}),{abortSignal:E(e)});for(const s of i.CertificateSummaryList??[])s.CertificateArn!==void 0&&c.push(s.CertificateArn);o=i.NextToken}while(o!==void 0);for(const i of c){if(I(e))throw new Error("certificate description aborted by shutdown signal");const s=await r.send(new q({CertificateArn:i}),{abortSignal:E(e)});for(const f of s.Certificate?.DomainValidationOptions??[]){const l=f.ResourceRecord;if(l?.Type==="CNAME"&&l.Name!==void 0){const h=b(l.Name),k=n.get(h)??new Set;k.add(i),n.set(h,k)}}}}catch(c){return A(new Error(`zone classification: could not read ACM certificates in ${a} (${y(c)}). Cure: grant acm:ListCertificates and acm:DescribeCertificate in ${a}, then retry.`))}return T(n)}u(we,"collectAcmValidationReferences");async function pe(t,e,n,a){const r=new Map,c=[];for(const o of e){if(r.size===n.size)break;try{if(I(a))throw new Error("stack resource listing aborted by shutdown signal");const i=await t.send(new G({StackName:o}),{abortSignal:E(a)});for(const s of i.StackResources??[])s.ResourceType!=="AWS::CertificateManager::Certificate"||s.PhysicalResourceId===void 0||!n.has(s.PhysicalResourceId)||r.has(s.PhysicalResourceId)||r.set(s.PhysicalResourceId,o)}catch(i){c.push(N(`zone classification: could not list resources for stack '${o}' (${y(i)}), so certificate ownership cannot be proven there and its validation CNAMEs stay 'unknown'. Cure: grant cloudformation:DescribeStackResources and retry.`))}}return{ownerByArn:r,warnings:c}}u(pe,"resolveCertificateOwnership");function D(t){if(t.type!=="CNAME")return!1;const e=t.name.split(".")[0]??"";return re.test(e)?t.values.some(n=>b(n).endsWith(se)):!1}u(D,"isAcmValidationShaped");function K(t){if(t.type!=="A"&&t.type!=="AAAA")return!1;const e=t.aliasTargetDnsName;return e===void 0?!1:ie.some(n=>n.test(e))}u(K,"isAwsManagedAliasCandidate");async function Te(t){const{zone:e,domainStackName:n,clients:a,abortSignal:r,onPhase:c}=t,o=[];c?.("read-records");let i;try{i=await ce(a.route53,e.hostedZoneId,r)}catch(d){return A(new Error(`zone classification: could not list record sets for zone ${e.hostedZoneId} (${e.zoneName}): ${y(d)}. Cure: grant route53:ListResourceRecordSets on the zone and retry.`))}let s=new Set,f=!0;if(n!==void 0){const d=await _(a.cloudFormation,n,r);d.success?s=d.data:(f=!1,o.push(N(`zone classification: could not read the domain stack template for '${n}' (${y(d.error)}) \u2014 only zone-owned SOA/NS classify 'declared'. Cure: deploy the domain stack with 'fjall domain deploy ${e.zoneName}' or grant cloudformation:GetTemplate.`)))}c?.("index-stacks");const l=t.satelliteIndex??await ue(a.cloudFormation,{...n!==void 0?{excludeStackNames:[n]}:{},...r!==void 0?{abortSignal:r}:{}});o.push(...l.warnings);const h=new Map;if(t.delegatedChildren!==void 0){const d=new Map;for(const m of i)m.type==="NS"&&d.set(C(m.name,m.type,m.setIdentifier),m.values);const w=t.delegatedChildren.filter(m=>{const g=C(m.zoneName,"NS");return d.has(g)&&!s.has(g)&&!l.byRecordKey.has(g)}),S=t.childAccountClientFactory;if(w.length>0&&S===void 0)o.push(N("zone classification: delegated child domains are configured but no child-account client factory was wired, so cross-account delegations cannot be verified and their NS rows classify 'unknown'. Cure: supply childAccountClientFactory in ClassifyZoneOptions."));else if(S!==void 0)for(const m of w){if(m.accountId===void 0){o.push(N(`zone classification: delegated child '${m.zoneName}' has no AWS account id, so its cross-account delegation cannot be verified and its NS row classifies 'unknown'. Cure: set "account" on the delegated domain entry in fjall-config.json.`));continue}const g=C(m.zoneName,"NS"),O=await me({factory:S,accountId:m.accountId,zoneName:m.zoneName,stackName:m.stackName??Y(m.zoneName),liveNsValues:d.get(g)??[],...r!==void 0?{abortSignal:r}:{}});if(!O.success){o.push(N(y(O.error)));continue}h.set(g,O.data)}}c?.("check-acm");const k=i.some(D);let R;if(k){const d=await we(a.acmByRegion,r);d.success?R=d.data:o.push(N(d.error.message))}let $;if(R!==void 0){const d=new Set;for(const w of i)if(D(w))for(const S of R.get(w.name)??[])d.add(S);if(d.size>0){const w=await pe(a.cloudFormation,[...n!==void 0?[n]:[],...l.stackNames],d,r);$=w.ownerByArn,o.push(...w.warnings)}}const p=f&&l.warnings.length===0,v=new Set;if(p)for(const d of i){if(!K(d))continue;const w=C(d.name,d.type,d.setIdentifier);s.has(w)||l.byRecordKey.has(w)||v.add(d.aliasTargetDnsName)}let x;if(v.size>0){const d=t.dnsProbe??ne,w=new Map;for(const S of v){let m;try{m=await d(S,r)}catch(g){m="indeterminate",o.push(N(`zone classification: DNS probe for alias target '${S}' failed (${y(g)}) \u2014 dependent alias records classify 'unknown', never 'residue'. Cure: retry with a working resolver.`)),w.set(S,m);continue}m==="indeterminate"&&o.push(N(`zone classification: DNS probe for alias target '${S}' was inconclusive \u2014 dependent alias records classify 'unknown', never 'residue'. Cure: retry once the resolver answers definitively.`)),w.set(S,m)}x=w}c?.("classify");const z=b(e.zoneName),M=i.map(d=>ye(d,{zoneApex:z,domainStackName:n,declaredKeys:s,satelliteIndex:l,crossAccountDelegations:h,acmValidationReferences:R,certificateOwnerByArn:$,templateEvidenceComplete:p,aliasProbeVerdicts:x})).sort((d,w)=>d.name.localeCompare(w.name)||d.type.localeCompare(w.type)||(d.setIdentifier??"").localeCompare(w.setIdentifier??"")),L={declared:0,satellite:0,residue:0,unknown:0};for(const d of M)L[d.classification]+=1;return T({hostedZoneId:e.hostedZoneId,zoneName:z,records:M,counts:L,warnings:o})}u(Te,"classifyZoneRecords");function he(t,e){const n=new Set;for(const a of t){const r=e?.get(a);if(r===void 0)return;n.add(r)}return n}u(he,"provenCertificateOwners");function ye(t,e){const n=C(t.name,t.type,t.setIdentifier),a={name:t.name,type:t.type,...t.setIdentifier!==void 0?{setIdentifier:t.setIdentifier}:{},values:t.values};if(t.name===e.zoneApex&&(t.type==="SOA"||t.type==="NS"))return{...a,classification:"declared",...e.domainStackName!==void 0?{ownerStack:e.domainStackName}:{},detail:"zone-owned record, created with the hosted zone"};if(e.declaredKeys.has(n))return{...a,classification:"declared",...e.domainStackName!==void 0?{ownerStack:e.domainStackName}:{}};const r=e.satelliteIndex.byRecordKey.get(n);if(r!==void 0)return{...a,classification:"satellite",ownerStack:r};const c=e.crossAccountDelegations.get(n);if(c!==void 0)return{...a,classification:"satellite",ownerStack:c.ownerStack,detail:`cross-account delegation: stack ${c.ownerStack} in account ${c.accountId}`};if(D(t)){if(e.acmValidationReferences===void 0)return{...a,classification:"unknown",detail:"ACM validation CNAME shape, but live-certificate evidence was unavailable \u2014 fail-closed as unknown rather than residue"};const o=e.acmValidationReferences.get(t.name);if(o!==void 0&&o.size>0){const i=he(o,e.certificateOwnerByArn);if(i!==void 0){const s=[...i].sort();if(e.domainStackName!==void 0&&s.every(l=>l===e.domainStackName))return{...a,classification:"declared",ownerStack:e.domainStackName,detail:"ACM validation CNAME for the domain stack's live certificates; required for issuance and auto-renewal"};const f=s.find(l=>l!==e.domainStackName);if(f!==void 0)return{...a,classification:"satellite",ownerStack:f,detail:`ACM validation CNAME for live certificates owned by ${s.map(l=>`'${l}'`).join(", ")}; required for issuance and auto-renewal`}}return{...a,classification:"unknown",detail:"ACM validation CNAME still referenced by a live certificate \u2014 keep it; it is not residue"}}return{...a,classification:"residue",detail:"ACM validation CNAME not referenced by any live certificate in the scanned regions"}}if(K(t)){if(!e.templateEvidenceComplete)return{...a,classification:"unknown",detail:"alias to an AWS-managed target, but template evidence was incomplete \u2014 fail-closed as unknown rather than residue"};const o=e.aliasProbeVerdicts?.get(t.aliasTargetDnsName);return o==="nxdomain"?{...a,classification:"residue",detail:`dangling alias: target ${t.aliasTargetDnsName} no longer exists (NXDOMAIN) and no live template references this record`}:o==="resolves"?{...a,classification:"unknown",detail:"alias target still exists in DNS, so the record cannot be dangling; not residue"}:{...a,classification:"unknown",detail:"alias target probe was inconclusive \u2014 fail-closed as unknown rather than residue"}}return{...a,classification:"unknown"}}u(ye,"classifyRecord");export{ue as buildSatelliteRecordIndex,Te as classifyZoneRecords,te as createSystemDnsNameProbe,de as extractTemplateRecordKeys,b as normaliseRecordName,ne as systemDnsNameProbe};
|
|
@@ -48,7 +48,7 @@ export { runOrganisationSetup, ORG_SETUP_PHASES } from "./organisation/organisat
|
|
|
48
48
|
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult } from "./organisation/organisationSetup.js";
|
|
49
49
|
export * from "./builders/index.js";
|
|
50
50
|
export { buildDeployPlan, computeDeployPlan, computeAssemblyDigest, digestHead, classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, renderPlanSummary, renderPlanLines, toWirePlanChanges, signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN } from "./application/plan/index.js";
|
|
51
|
-
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult } from "./application/plan/index.js";
|
|
51
|
+
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, DeployScope, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult } from "./application/plan/index.js";
|
|
52
52
|
export { runApprovalGate, approvalRefusalReason, type RunApprovalGateParams, type ApprovalGateOutcome } from "./application/approvalGate.js";
|
|
53
53
|
export { runDestructionGate, buildDestructionTicket, evaluateDestructionConsents, computeTicketDigest, legalRemediationVerbs, executableRemediationVerbs, advertisableRemediationVerbs, renderDestructionTicketLines, renderConsentVerdictLines, type RunDestructionGateParams, type DestructionGateOutcome, type EvaluateDestructionConsentsParams } from "./application/destructionGate.js";
|
|
54
54
|
export { loadDeployPlan, createMemoisedPlanLoader, type LoadDeployPlanParams, type DeployPlanLoader } from "./application/plan/loadDeployPlan.js";
|
|
@@ -101,12 +101,15 @@ export interface StrippedExport {
|
|
|
101
101
|
exportName?: string;
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
|
-
* The
|
|
105
|
-
*
|
|
106
|
-
* no side effects beyond detachment (no Custom:: Delete
|
|
107
|
-
* children), and the converge re-creates it verbatim from
|
|
108
|
-
*
|
|
109
|
-
*
|
|
104
|
+
* The resource types the co-removal closure may pull into the removal set
|
|
105
|
+
* unconditionally. Membership requires ALL of: the type holds no data,
|
|
106
|
+
* deleting it runs no side effects beyond detachment (no Custom:: Delete
|
|
107
|
+
* handlers, no nested children), and the converge re-creates it verbatim from
|
|
108
|
+
* synth. One further type is admitted by predicate, not membership:
|
|
109
|
+
* `AWS::IAM::Policy` via `iamPolicyCoRemovalBlocker` (safe only when it
|
|
110
|
+
* attaches exclusively to removal-set roles). Everything else fails the
|
|
111
|
+
* derivation closed — the ceremony consented to the TARGETS only, and
|
|
112
|
+
* `isStatefulResourceType` is a data-loss label, not a
|
|
110
113
|
* deletion-authorisation gate (it does not list nested-stack containers,
|
|
111
114
|
* EC2 instances, Custom:: providers, secrets or user pools, all of which can
|
|
112
115
|
* destroy data or run delete-time code).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var N=Object.defineProperty;var g=(o,s)=>N(o,"name",{value:s,configurable:!0});import{randomUUID as j}from"node:crypto";import{CloudFormationClient as M,CreateChangeSetCommand as F,DeleteChangeSetCommand as x,DescribeChangeSetCommand as _}from"@aws-sdk/client-cloudformation";import{success as $,failure as h}from"@fjall/generator";import{getErrorMessage as P,maskSensitiveOutput as b}from"@fjall/util";import{logger as k}from"@fjall/util/logger";import{composeSdkAbortSignal as C,isAborted as I}from"../../aws/organisations/types.js";import{isStatefulResourceType as U}from"../application/plan/classify.js";import{PREFLIGHT_CHANGE_SET_PREFIX as G}from"../../services/infrastructure/changeSetProbe.js";import{sleepAbortable as B}from"../../util/sleepAbortable.js";import{fetchLastOperationEvents as K}from"../drift/classifyDriftFailure.js";import{composeFlipCapabilities as W,computeTemplateDigest as J,echoPreviousParameters as V,resolveTemplateLocation as H,deleteStagedTemplate as Y}from"./retainFlip.js";const z="RemovalUpdate",X=/didn't contain changes|No updates are to be performed/i,q=new Set(["CREATE_COMPLETE","FAILED"]),Q=3e3,Z=9e4;function O(o,s){let e;try{e=JSON.parse(o)}catch{return h(new Error(`The live template for ${s} is not JSON (YAML-authored stacks are not supported by the removal update) \u2014 aborting to manual repair.`))}return typeof e!="object"||e===null||Array.isArray(e)?h(new Error(`The live template for ${s} did not parse to an object \u2014 aborting to manual repair.`)):$(e)}g(O,"parseTemplate");function A(o,s,e){if(typeof o=="string")return[];if(Array.isArray(o))return o.flatMap((l,f)=>A(l,s,`${e}[${f}]`));if(typeof o!="object"||o===null)return[];const r=o,t=[],a=r.Ref;typeof a=="string"&&s.has(a)&&t.push(`${e}/Ref(${a})`);const n=r["Fn::GetAtt"];typeof n=="string"&&s.has(n.split(".")[0]??"")&&t.push(`${e}/Fn::GetAtt(${n.split(".")[0]??""})`),Array.isArray(n)&&typeof n[0]=="string"&&s.has(n[0])&&t.push(`${e}/Fn::GetAtt(${n[0]})`);const i=r["Fn::Sub"],d=typeof i=="string"?i:Array.isArray(i)&&typeof i[0]=="string"?i[0]:void 0;if(d!==void 0)for(const l of d.matchAll(/\$\{([^!}][^}]*)\}/g)){const f=(l[1]??"").split(".")[0]??"";s.has(f)&&t.push(`${e}/Fn::Sub(${f})`)}for(const[l,f]of Object.entries(r))t.push(...A(f,s,`${e}/${l}`));return t}g(A,"collectTargetReferences");function ye(o,s,e){const r=O(o,e);if(!r.success)return r;const t=new Set(s),a=[];for(const[n,i]of Object.entries(r.data.Resources??{})){const d=t.has(n)?new Set([...t].filter(l=>l!==n)):t;d.size!==0&&A(i,d,n).length>0&&a.push(n)}return $(a)}g(ye,"findTargetReferrers");function Se(o,s,e,r){const t=O(o,r);if(!t.success)return t;const a=t.data.Resources??{},n=[];for(const i of s){const d=a[i];if(d===void 0)continue;const l=e.filter(f=>f!==i&&A(d,new Set([f]),i).length>0);l.length>0&&n.push({pendingTarget:i,deadReferences:l})}return $(n)}g(Se,"findDeadReferencesFromTargets");function ee(o,s){const e=o.DependsOn;if(typeof e=="string"){s.has(e)&&delete o.DependsOn;return}if(Array.isArray(e)){const r=e.filter(t=>typeof t!="string"||!s.has(t));r.length===0?delete o.DependsOn:r.length!==e.length&&(o.DependsOn=r)}}g(ee,"pruneDependsOn");const L=["AWS::SecretsManager::SecretTargetAttachment","AWS::SecretsManager::RotationSchedule"],te=new Set(L);function re(o,s){const e=o.Properties;if(typeof e!="object"||e===null)return"it declares no Properties, so its attachments cannot be proven";const r=e;for(const a of["Users","Groups"]){const n=r[a];if(Array.isArray(n)?n.length>0:n!==void 0)return`it attaches to ${a} the removal does not delete`}const t=r.Roles;if(!Array.isArray(t)||t.length===0)return"its Roles attachment list cannot be read";for(const a of t){const n=typeof a=="object"&&a!==null?a.Ref:void 0;if(typeof n!="string"||!s.has(n))return`it attaches to a role outside the removal set (${typeof n=="string"?n:JSON.stringify(a)})`}}g(re,"iamPolicyCoRemovalBlocker");const ne=new Set(["Retain","RetainExceptOnCreate"]);function w(o){return`${o.slice(0,10).join(", ")}${o.length>10?` (+${o.length-10} more)`:""}`}g(w,"formatReferencePaths");function Ee(o,s,e){const r=O(o,e);if(!r.success)return r;const t=r.data,a=t.Resources??{},n=s.filter(c=>a[c]===void 0),i=new Set(s),d=[];let l=!0;for(;l;){l=!1;for(const[c,u]of Object.entries(a)){if(i.has(c))continue;const p=A(u,i,c);if(p.length===0)continue;const m=u.Type;if(typeof m!="string")return h(new Error(`Cannot orchestrate the recreate for ${e}: ${c} references the removal set at ${w(p)} but declares no resource Type, so fjall cannot prove it is safe to co-remove. Restructure the dependency or perform the replacement manually.`));if(U(m))return h(new Error(`Cannot orchestrate the recreate for ${e}: ${c} (${m}) references the removal set at ${w(p)} \u2014 it is a stateful resource holding data, and removing it was not consented. Restructure the dependency or perform the replacement manually.`));if(!te.has(m)){if(m!=="AWS::IAM::Policy")return h(new Error(`Cannot orchestrate the recreate for ${e}: ${c} (${m}) references the removal set at ${w(p)} \u2014 fjall only co-removes provably-safe glue resources (${L.join(", ")}, and AWS::IAM::Policy attached exclusively to removal-set roles), and removing anything else was not consented. Restructure the dependency or perform the replacement manually.`));const E=re(u,i);if(E!==void 0)return h(new Error(`Cannot orchestrate the recreate for ${e}: ${c} (AWS::IAM::Policy) references the removal set at ${w(p)} \u2014 ${E}, so fjall cannot prove the co-removal is side-effect free. Restructure the dependency or perform the replacement manually.`))}const S=u.DeletionPolicy;if(typeof S=="string"&&ne.has(S))return h(new Error(`Cannot orchestrate the recreate for ${e}: ${c} (${m}) references the removal set at ${w(p)} but carries DeletionPolicy: ${S} \u2014 CloudFormation would skip its physical deletion (DELETE_SKIPPED) and the converge's re-create would collide with the retained resource. Restructure the dependency or perform the replacement manually.`));i.add(c),d.push({logicalId:c,resourceType:m,referencePaths:p}),l=!0}}for(const c of i)delete a[c];const f=[];for(const[c,u]of Object.entries(a))ee(u,i),f.push(...A(u,i,c));if(f.length>0)return h(new Error(`Cannot orchestrate the recreate for ${e}: other resources still reference ${[...i].join(", ")} at ${w(f)} \u2014 removing the target would break them. Restructure the dependency or perform the replacement manually.`));const y=[],R=t.Outputs;if(typeof R=="object"&&R!==null){for(const[c,u]of Object.entries(R))if(A(u,i,`Outputs/${c}`).length>0){const p=typeof u=="object"&&u!==null?u.Export:void 0;if(typeof p=="object"&&p!==null){const m=p.Name;y.push({outputName:c,...typeof m=="string"?{exportName:m}:{}})}delete R[c]}Object.keys(R).length===0&&delete t.Outputs}return $({removalTemplateBody:JSON.stringify(t),liveTemplateDigest:J(o),alreadyAbsent:n,strippedExports:y,coRemoved:d})}g(Ee,"deriveRemovalTemplate");function $e(o,s,e){const r=O(o,e);if(!r.success)return r;const t=r.data.Resources??{};return $(s.filter(a=>t[a]!==void 0))}g($e,"findResourcesPresent");const oe=new Set(["DELETE_FAILED","DELETE_SKIPPED"]);async function Re(o,s,e,r){const t=await K(o,s,r);if(!t.success)return t;const a=new Set(e);return $(t.data.filter(n=>a.has(n.logicalId)&&oe.has(n.status)).map(n=>({logicalId:n.logicalId,status:n.status,...n.statusReason!==void 0?{statusReason:b(n.statusReason)}:{}})))}g(Re,"findRemovalDeletionFailures");async function Ae(o,s,e){const{stackName:r,abortSignal:t}=s,a=e?.pollIntervalMs??Q,n=e?.changeSetTimeoutMs??Z,i=e?.now??(()=>Date.now()),d=o.getClient(M),l=`${G}removal-${j()}`;let f;try{const y=await H(o,l,s.removalTemplateBody,t);if(!y.success)return y;f=y.data.key,await d.send(new F({StackName:r,ChangeSetName:l,ChangeSetType:"UPDATE",IncludeNestedStacks:!0,Capabilities:W([]),Description:"fjall recreate-removal targets-only proof \u2014 review-only, never executed",...V(s.parameterKeys),...y.data.location}),{abortSignal:C(t)});const R=i();let c,u;for(;!I(t);){const E=await d.send(new _({StackName:r,ChangeSetName:l}),{abortSignal:C(t)});if(c=E.Status,u=E.StatusReason,c!==void 0&&q.has(c))break;if(i()-R>=n)return h(new Error(`Recreate-removal proof timed out after ${n}ms for ${r} (last status: ${c??"unknown"})`));await B(a,t)}if(I(t))return h(new Error(`Recreate-removal proof aborted while polling ${r}`));if(c==="FAILED")return u!==void 0&&X.test(u)?$({noChanges:!0}):h(new Error(`Recreate-removal proof change set failed for ${r}: ${b(u??"no status reason reported")}`));const p=[],m=new Set(s.targets);let S;do{if(I(t))return h(new Error(`Recreate-removal proof aborted while reading ${r}`));const E=await d.send(new _({StackName:r,ChangeSetName:l,...S!==void 0?{NextToken:S}:{}}),{abortSignal:C(t)});for(const D of E.Changes??[]){if(D.Type!=="Resource")continue;const T=D.ResourceChange,v=T?.LogicalResourceId;if(!(T===void 0||v===void 0)){if(!m.has(v)){p.push(`${v} (not a removal target)`);continue}T.Action!=="Remove"&&p.push(`${v} (action ${T.Action??"unknown"})`)}}S=E.NextToken}while(S!==void 0);return p.length>0?h(new Error(`Recreate removal is NOT targets-only for ${r} \u2014 the review change set reported changes at ${[...new Set(p)].join(", ")}. Aborting before the removal ran; the snapshot flip has already been applied, so re-run the deploy to resume once the change is understood.`)):$({noChanges:!1})}catch(y){return h(new Error(`Recreate-removal proof failed for ${r}: ${b(P(y))}`))}finally{await d.send(new x({StackName:r,ChangeSetName:l}),{abortSignal:C()}).catch(y=>{k.warn(z,"Failed to delete recreate-removal proof change set",{stackName:r,changeSetName:l,error:b(P(y))})}),f!==void 0&&await Y(o,f)}}g(Ae,"proveRemovalTargetsOnly");export{L as CO_REMOVABLE_RESOURCE_TYPES,Ee as deriveRemovalTemplate,Se as findDeadReferencesFromTargets,Re as findRemovalDeletionFailures,$e as findResourcesPresent,ye as findTargetReferrers,Ae as proveRemovalTargetsOnly};
|
|
@@ -23,5 +23,16 @@ export declare class CdkService {
|
|
|
23
23
|
runCdkBootstrap(context: DeploymentContext, onOutput?: (chunk: string) => void, credentials?: CdkOptions["credentials"], permissionsBoundary?: string, bootstrapTags?: CdkOptions["bootstrapTags"]): Promise<Result<StepOutput, string>>;
|
|
24
24
|
runCdkDiff(context: DeploymentContext, onOutput?: (chunk: string) => void): Promise<Result<StepOutput, string>>;
|
|
25
25
|
runCdkDeploy(context: DeploymentContext, stackPattern?: string, onOutput?: (chunk: string) => void, onResourceProgress?: (event: ResourceEvent) => void, aws?: AwsProvider, credentials?: CdkOptions["credentials"], parameters?: Record<string, string>, exclusively?: boolean): Promise<Result<StepOutput, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* The CDK child died to a network blip but CloudFormation reports the
|
|
28
|
+
* operation still running server-side. Reset the monitor (startMonitoring
|
|
29
|
+
* skips when already live, so waitForStackComplete's stackComplete closure
|
|
30
|
+
* would never register against a running monitor) and watch the operation
|
|
31
|
+
* to its terminal state, reporting success/failure from that status
|
|
32
|
+
* instead of the child's false failure.
|
|
33
|
+
*/
|
|
34
|
+
private reattachToInFlightOperation;
|
|
35
|
+
/** Shared by the Q12 merge path and the re-attach failure path. */
|
|
36
|
+
private journalDriftSuspects;
|
|
26
37
|
runCdkDestroy(context: DeploymentContext, stackPattern?: string, onOutput?: (chunk: string) => void, onResourceProgress?: (event: ResourceEvent) => void, aws?: AwsProvider, useCdkOut?: boolean, credentials?: CdkOptions["credentials"]): Promise<Result<StepOutput, string>>;
|
|
27
38
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
var
|
|
1
|
+
var j=Object.defineProperty;var F=(N,e)=>j(N,"name",{value:e,configurable:!0});import{existsSync as U}from"fs";import{join as $}from"path";import{logger as O}from"@fjall/util/logger";import{success as v,failure as a}from"@fjall/generator";import{DEFAULT_REGION as S}from"../../aws/utils/regions.js";import{getErrorMessage as k,maskSensitiveOutput as h}from"@fjall/util";import{CdkEventMonitor as B,DEFAULT_DEPLOY_TIMEOUT_MS as G,settleMonitoringAnalysis as Y,startStackMonitoring as q}from"./CdkEventMonitoring.js";import{analyseDeployOutput as X,analyseDestroyResult as z,createEnhancedOutputCallback as H,formatAnalysedDeployFailure as _}from"./CdkOutputAnalyser.js";import{recordDriftSuspects as J}from"../../orchestration/drift/driftSuspectJournal.js";import{CdkCommandRunner as Q}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as V}from"./CdkArgumentBuilder.js";import{CdkProcessManager as Z}from"./CdkProcessManager.js";import{cancelInFlightStack as P}from"./cancelInFlightStack.js";import{isAborted as w}from"../../aws/organisations/types.js";import{wrapWithConstructMapEnrichment as x}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as ee,resolveStackName as K,getFallbackStackName as te,buildDeploymentCdkContext as A}from"./cdkServiceHelpers.js";import{ensureSynthDependencies as re}from"./synthDependencies.js";import{isTransientNetworkFailure as ne,isReattachableInProgressStatus as oe,MAX_TRANSIENT_NETWORK_RETRIES as T,TRANSIENT_RETRY_BACKOFF_MS as ae}from"./transientNetworkRecovery.js";import{sleepAbortable as ie}from"../../util/sleepAbortable.js";class Ie{static{F(this,"CdkService")}commandRunner;eventMonitor;abortSignal;constructor(e){const t=e?.processManager??new Z(new V,e?.abortSignal);this.commandRunner=new Q(t),this.eventMonitor=new B({eventLogWriterFactory:e?.eventLogWriterFactory,...e?.onFailureAnalysis!==void 0?{onFailureAnalysis:e.onFailureAnalysis}:{}}),this.abortSignal=e?.abortSignal}dispose(){this.commandRunner.dispose()}async checkDifferences(e,t,r){return this.commandRunner.checkDifferences(e,t,r)}async deploy(e,t,r){return this.commandRunner.deploy(e,t,r)}async destroy(e,t,r){return this.commandRunner.destroy(e,t,r)}async runImport(e,t,r){return this.commandRunner.runImport(e,t,r)}async synth(e,t){return this.commandRunner.synth(e,t)}async bootstrap(e,t,r){return this.commandRunner.bootstrap(e,t,r)}async runCdkSynth(e,t,r){const o=e.callerIdentity?.Account;try{const n=await re(e.path,{...t!==void 0?{onOutput:t}:{},...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});if(!n.success)return a(n.error);const i=await this.synth(e.path,{outputCallback:t,context:A(e,o,e.region||S),...e.assemblyDir!==void 0?{outputDir:e.assemblyDir}:{},...r!==void 0?{credentials:r}:{}});return i.success?v({message:"CloudFormation template synthesised",details:i.data.output?{synthesisTime:i.data.output}:void 0}):a(i.error||"Failed to synthesise CloudFormation template")}catch(n){return a(`CDK synth failed: ${h(k(n))}`)}}async runCdkBootstrap(e,t,r,o,n){const i=e.callerIdentity?.Account,u=e.region||S;try{if(!i)return a("No AWS account ID available");const l=$(e.path,"node_modules");if(!U(l))return a(`Dependencies not installed. Please run 'npm install' in ${e.path} before deploying.`);const d=await this.bootstrap(i,u,{outputCallback:t,credentials:r,...o!==void 0&&o!==""?{permissionsBoundary:o}:{},...n!==void 0&&Object.keys(n).length>0?{bootstrapTags:n}:{}});return d.success?v({message:"AWS environment bootstrapped"}):a(d.error||"Failed to bootstrap AWS environment")}catch(l){return a(`CDK bootstrap failed: ${h(k(l))}`)}}async runCdkDiff(e,t){const r=e.callerIdentity?.Account;try{const o=await this.checkDifferences(e.path,void 0,{verbose:e.options?.verbose,outputCallback:t,context:A(e,r,e.region||S)});return o.success?v({message:"Diff check complete",details:{hasDifferences:o.data.hasDifferences,details:o.data.details}}):a(`CDK diff failed: ${o.error.message}`)}catch(o){return a(`CDK diff failed: ${h(k(o))}`)}}async runCdkDeploy(e,t,r,o,n,i,u,l){const d=e.callerIdentity?.Account,s=e.region||S;if(!d)return a("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!n)return a("AwsProvider is required for deployment monitoring.");const g=e.assemblyDir??$(e.path,"cdk.out"),y=x(g,o);let p=null,b;try{const f=K(t,e)??te(e),m=await this.eventMonitor.createEventMonitor("deploy",f,s,e,n);p=m,r&&(r(h(`Starting CloudFormation deployment of ${f}...
|
|
2
2
|
`)),r(`Monitoring CloudFormation events (CDK process running in background)...
|
|
3
|
-
`));const
|
|
3
|
+
`));const c={cdkOutput:"",actualStackName:f,stackDetected:!1,monitoringPromise:null},L=H(c,r,p,y);b=setTimeout(()=>{!c.stackDetected&&!c.monitoringPromise&&(O.debug("CdkService","Fallback monitoring STARTING",{targetStackName:f,stackDetected:c.stackDetected,hasOnResourceProgress:!!o}),c.monitoringPromise=q(m,f,y))},ee);for(let D=0;;D++){const C=await this.deploy(e.path,f,{verbose:e.options?.verbose,outputCallback:L,...e.assemblyDir!==void 0?{appDir:e.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:m.getEventLogger()??void 0,context:A(e,d,s),credentials:i,...u!==void 0&&Object.keys(u).length>0&&{parameters:u},...l===!0&&{exclusively:!0}});if(w(this.abortSignal))return await P(f,s,i,r),a("Deployment cancelled");const R=X(c.cdkOutput,C,c.actualStackName);if(R.success)return R;const W=[c.cdkOutput,C.success?C.data.output??"":C.error,R.error].join(`
|
|
4
|
+
`);if(ne(W)){r?.(`Transient network failure while running CDK for ${c.actualStackName} \u2014 probing CloudFormation for an in-flight operation...
|
|
5
|
+
`);const M=await m.getStackStatus(c.actualStackName,this.abortSignal);if(M!==null&&oe(M.status))return clearTimeout(b),await this.reattachToInFlightOperation({stackName:c.actualStackName,currentStatus:M.status,region:s,accountId:d,monitor:m,state:c,credentials:i,onOutput:r,onResourceProgress:y});if(D<T){const E=ae[D]??15e3;if(r?.(`No CloudFormation operation in progress \u2014 nothing was mutated. Retrying the CDK deploy in ${E/1e3}s (retry ${D+1} of ${T})...
|
|
6
|
+
`),await ie(E,this.abortSignal),w(this.abortSignal))return a("Deployment cancelled");c.cdkOutput="";continue}r?.(`No CloudFormation operation in progress and the retry budget (${T}) is exhausted \u2014 giving up.
|
|
7
|
+
`)}const I=await Y(m,c.monitoringPromise);return I===null?R:(await this.journalDriftSuspects(d,s,c.actualStackName,I),a(_(I)))}}catch(f){const m=`CDK deploy failed: ${h(k(f))}`;return O.error("CdkService","CDK deployment exception",{error:m}),a(m)}finally{clearTimeout(b),p&&p.stopMonitoring()}}async reattachToInFlightOperation(e){const{stackName:t,monitor:r,state:o,onOutput:n}=e;n?.(`CloudFormation reports ${e.currentStatus} for ${t} \u2014 the operation survived the network failure. Re-attaching and monitoring it to completion...
|
|
8
|
+
`),r.stopMonitoring(),o.monitoringPromise=null;const i=F(()=>{r.stopMonitoring()},"onAbort");this.abortSignal?.addEventListener("abort",i,{once:!0});const u=await r.waitForStackComplete(t,{timeout:G,...e.onResourceProgress!==void 0?{onResourceUpdate:e.onResourceProgress}:{}}).finally(()=>{this.abortSignal?.removeEventListener("abort",i)});if(w(this.abortSignal))return await P(t,e.region,e.credentials,n),a("Deployment cancelled");if(u.success){const s=u.status??"COMPLETE";return n?.(`${t} reached ${s} after re-attach.
|
|
9
|
+
`),v({message:`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${t} reached ${s}`,details:{reattached:!0,status:s}})}const l=r.getFailureAnalysis();if(l!==null)return await this.journalDriftSuspects(e.accountId,e.region,t,l),a(_(l));const d=u.failureReason!==void 0&&u.failureReason!==""?h(u.failureReason):"no failure reason reported";return a(`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${t} reached ${u.status??"UNKNOWN"}: ${d}`)}async journalDriftSuspects(e,t,r,o){o.driftSuspects===void 0||o.driftSuspects.length===0||await J({accountId:e,region:t,stackName:r,recordedAt:new Date().toISOString(),suspects:o.driftSuspects.map(n=>({stackName:r,logicalId:n.logicalId,resourceType:n.resourceType,...n.physicalId!==void 0?{physicalId:n.physicalId}:{},failedStatus:n.failedStatus,statusReason:n.statusReason}))})}async runCdkDestroy(e,t,r,o,n,i,u){const l=e.callerIdentity?.Account,d=e.region||S;let s=null;try{const g=K(t,e);l&&g&&n&&(s=await this.eventMonitor.createEventMonitor("destroy",g,d,e,n),s.startMonitoring(g,p=>{o?.(p)},(p,b)=>{}));const y=await this.destroy(e.path,t,{verbose:e.options?.verbose,outputCallback:r,useCdkOut:i,cdkOutputLogger:s?.getEventLogger()??void 0,context:A(e,l,d),credentials:u});return w(this.abortSignal)?a("Destroy cancelled"):z(y)}catch(g){return a(`CDK destroy failed: ${h(k(g))}`)}finally{s&&s.stopMonitoring()}}}export{Ie as CdkService};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient-network failure classification for CDK deploy children
|
|
3
|
+
* (the 2026-07-20 incident fix).
|
|
4
|
+
*
|
|
5
|
+
* A CDK child killed by a network blip (connection reset, DNS failure, TLS
|
|
6
|
+
* handshake death) does NOT stop the CloudFormation operation it already
|
|
7
|
+
* started — CFN keeps executing server-side while the CLI reports a false
|
|
8
|
+
* failure and wedges the deploy slot. The classifier below decides whether a
|
|
9
|
+
* deploy-child failure is worth probing CloudFormation for an in-flight
|
|
10
|
+
* operation (re-attach) or retrying the spawn (bounded backoff).
|
|
11
|
+
*
|
|
12
|
+
* NOTE: cli/src/util/agent/errorCodes.ts carries a sibling NETWORK_ERROR
|
|
13
|
+
* regex serving a DIFFERENT contract (agent error categorisation for exit
|
|
14
|
+
* codes). Deliberately not shared: that regex matches bare /network/i-class
|
|
15
|
+
* wording, which here would false-positive on stack names like
|
|
16
|
+
* "MyAppNetwork". Do not couple the two.
|
|
17
|
+
*/
|
|
18
|
+
/** True when the failure text carries a transient network signature. */
|
|
19
|
+
export declare function isTransientNetworkFailure(text: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* True when a stack status names a running operation worth re-attaching to.
|
|
22
|
+
* REVIEW_IN_PROGRESS is excluded: it is a pre-execution changeset
|
|
23
|
+
* placeholder, not a running operation that will reach a terminal state.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isReattachableInProgressStatus(status: string): boolean;
|
|
26
|
+
/** Maximum re-spawns after a transient failure with no in-flight operation. */
|
|
27
|
+
export declare const MAX_TRANSIENT_NETWORK_RETRIES = 2;
|
|
28
|
+
/**
|
|
29
|
+
* Backoff before retry N+1 (index N). Length MUST equal
|
|
30
|
+
* MAX_TRANSIENT_NETWORK_RETRIES — pinned by transientNetworkRecovery.test.ts.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TRANSIENT_RETRY_BACKOFF_MS: readonly number[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var E=Object.defineProperty;var n=(e,t)=>E(e,"name",{value:t,configurable:!0});const o=[/\bECONNRESET\b/,/\bETIMEDOUT\b/,/\bEAI_AGAIN\b/,/\bENOTFOUND\b/,/connection reset/i,/socket hang ?up/i,/getaddrinfo/i,/inaccessible host/i,/NetworkingError/,/TLS handshake/i];function N(e){return o.some(t=>t.test(e))}n(N,"isTransientNetworkFailure");function T(e){return e.endsWith("_IN_PROGRESS")&&e!=="REVIEW_IN_PROGRESS"}n(T,"isReattachableInProgressStatus");const i=2,R=[5e3,15e3];export{i as MAX_TRANSIENT_NETWORK_RETRIES,R as TRANSIENT_RETRY_BACKOFF_MS,T as isReattachableInProgressStatus,N as isTransientNetworkFailure};
|
|
@@ -24,6 +24,16 @@ export declare const CASCADE_ACCOUNT_STATUSES: readonly ["started", "deploying",
|
|
|
24
24
|
export declare const DEPLOYMENT_EVENT_STATUS_REASON_MAX = 2048;
|
|
25
25
|
export declare const DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX = 4096;
|
|
26
26
|
export declare const DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX = 2048;
|
|
27
|
+
/**
|
|
28
|
+
* Terminal-status `errorMessage` cap for the deployment status PUT — NOT an
|
|
29
|
+
* event field. Cross-repo coupled value: the webapp's
|
|
30
|
+
* `DEPLOYMENT_ERROR_MESSAGE_MAX` (app/.server/models/deployment/deployment.ts)
|
|
31
|
+
* mirrors the `deployment_error_message_length` DB CHECK
|
|
32
|
+
* (length("errorMessage") <= 10000). Producers mask FIRST, then truncate to
|
|
33
|
+
* this cap, so an oversized CFN dump degrades to truncation instead of a
|
|
34
|
+
* rejected terminal PUT that ghosts the row `running`.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEPLOYMENT_ERROR_MESSAGE_MAX = 10000;
|
|
27
37
|
export declare const DEPLOYMENT_EVENT_TYPES: readonly ["step", "resource", "docker", "ecs", "error", "complete", "cascade_phase", "cascade_account", "cascade_missing_accounts", "parallel_phase", "detection", "trail_migration", "log"];
|
|
28
38
|
export type DeploymentEventType = (typeof DEPLOYMENT_EVENT_TYPES)[number];
|
|
29
39
|
export type DeploymentEventCascadePhase = (typeof CASCADE_PHASES)[number];
|
|
@@ -185,5 +195,23 @@ export declare const DeploymentEventSchema: z.ZodObject<{
|
|
|
185
195
|
}, z.core.$strict>;
|
|
186
196
|
/** Inferred type — safe for client-side import via `import type`. */
|
|
187
197
|
export type DeploymentEvent = z.infer<typeof DeploymentEventSchema>;
|
|
198
|
+
/**
|
|
199
|
+
* Truncate to `max` UTF-16 code units without leaving a trailing lone high
|
|
200
|
+
* surrogate — a bare `.slice(0, max)` can cut a surrogate pair in half, and
|
|
201
|
+
* PostgreSQL rejects the unpaired escape when the string lands in a JSONB
|
|
202
|
+
* column server-side. Webapp counterpart: `app/utils/truncate-utf16-safe.ts`
|
|
203
|
+
* (cross-repo copy, release-decoupled).
|
|
204
|
+
*/
|
|
205
|
+
export declare function truncateUtf16Safe(value: string, max: number): string;
|
|
206
|
+
/**
|
|
207
|
+
* Clamp every string field of an outbound event to this schema's `.max()`
|
|
208
|
+
* bounds, driven by the ZodError `too_big` issue metadata — no parallel field
|
|
209
|
+
* list to drift when a cap or field changes. Non-length violations are left
|
|
210
|
+
* untouched (the server's per-event salvage owns those), so an event that
|
|
211
|
+
* fails validation for other reasons passes through unchanged. Producers mask
|
|
212
|
+
* BEFORE calling this (mask-before-truncate: slicing first can cut a
|
|
213
|
+
* credential mid-token and break the mask regex anchors).
|
|
214
|
+
*/
|
|
215
|
+
export declare function clampDeploymentEventStrings<T>(event: T): T;
|
|
188
216
|
/** Map a deploy type to its cascade phase value for event emission. */
|
|
189
217
|
export declare function toCascadePhase(deployType: string): DeploymentEventCascadePhase | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var m=Object.defineProperty;var n=(e,s)=>m(e,"name",{value:s,configurable:!0});import{z as t}from"zod";import{TRAIL_MIGRATION_PHASES as u,TRAIL_MIGRATION_STATUSES as g}from"./events.js";const d=["security","network","compute","database","storage","monitoring","dns","identity","bootstrap","events","registry","backup"],l=["platform","domains","accounts"],x=["started","deploying","completed","failed"],E=2048,f=4096,_=2048,I=1e4,b=["step","resource","docker","ecs","error","complete","cascade_phase","cascade_account","cascade_missing_accounts","parallel_phase","detection","trail_migration","log"],h={step:"step",resource:"resource",docker:"docker",ecs:"ecs",error:"error",complete:null,cascade_phase:"cascadePhase",cascade_account:"cascadeAccount",cascade_missing_accounts:"cascadeMissingAccounts",parallel_phase:"parallelPhase",detection:"detection",trail_migration:"trailMigration",log:"log"},A=t.object({type:t.enum(b),timestamp:t.string().max(64),sequence:t.number().int().nonnegative().optional(),step:t.object({id:t.string().max(256),name:t.string().max(256),index:t.number(),total:t.number(),status:t.string().max(64)}).strict().optional(),resource:t.object({logicalId:t.string().max(256),resourceType:t.string().max(256),category:t.enum(d),group:t.string().max(128).optional(),constructPath:t.string().max(512).optional(),displayName:t.string().max(256),status:t.string().max(64),statusReason:t.string().max(E).optional(),physicalId:t.string().max(2048).optional(),expectedDurationSeconds:t.number().optional(),stack:t.string().max(256).optional(),clientRequestToken:t.string().max(256).optional()}).strict().optional(),docker:t.object({message:t.string().max(2048),percentage:t.number().optional()}).strict().optional(),ecs:t.object({status:t.string().max(64),message:t.string().max(2048).optional(),percentage:t.number().optional()}).strict().optional(),error:t.object({message:t.string().max(f),category:t.string().max(128).optional(),remediation:t.array(t.string().max(1024)).max(10).optional()}).strict().optional(),message:t.string().max(2048).optional(),cascadePhase:t.object({phase:t.enum(l),status:t.enum(["started","completed"])}).strict().optional(),cascadeAccount:t.object({accountId:t.string().max(32),region:t.string().max(32),operationKey:t.string().max(256),status:t.enum(x),error:t.string().max(2048).optional(),phase:t.enum(["bootstrap","synth","deploy","destroy"]).optional(),cascadePhase:t.enum(l).optional()}).strict().optional(),cascadeMissingAccounts:t.object({accountNames:t.array(t.string().max(256)).max(256)}).strict().optional(),parallelPhase:t.object({stacks:t.array(t.string().max(256)).max(20),status:t.enum(["started","completed"]),results:t.array(t.object({stack:t.string().max(256),success:t.boolean(),error:t.string().max(2048).optional()}).strict()).max(20).optional()}).strict().optional(),detection:t.object({pattern:t.string().max(128).nullable(),hasDockerfile:t.boolean(),hasDifferences:t.boolean(),resources:t.object({hasNetwork:t.boolean(),hasCompute:t.boolean(),hasDatabase:t.boolean(),hasStorage:t.boolean(),hasMessaging:t.boolean(),hasCdn:t.boolean(),hasUsEast1Certificates:t.boolean().optional()}).strict(),requiredSecrets:t.array(t.string().max(512)).max(100).optional()}).strict().optional(),trailMigration:t.object({accountId:t.string().max(32),accountName:t.string().max(256),phase:t.enum(u),status:t.enum(g),detail:t.string().max(_).optional()}).strict().optional(),log:t.object({message:t.string().max(2048),level:t.enum(["info","debug","warn"])}).strict().optional()}).strict().superRefine((e,s)=>{const o=h[e.type],a=o?e[o]:void 0;o&&a==null&&s.addIssue({code:t.ZodIssueCode.custom,message:`"${o}" is required when type is "${e.type}"`,path:[o]}),e.type==="complete"&&!e.message&&s.addIssue({code:t.ZodIssueCode.custom,message:'"message" is required when type is "complete"',path:["message"]})});function y(e,s){if(e.length<=s)return e;const o=e.slice(0,s),a=o.charCodeAt(o.length-1);return a>=55296&&a<=56319?o.slice(0,-1):o}n(y,"truncateUtf16Safe");function M(e){const s=A.safeParse(e);if(s.success)return e;let o;for(const a of s.error.issues)a.code!=="too_big"||typeof a.maximum=="bigint"||(o===void 0&&(o=structuredClone(e)),S(o,a.path,a.maximum));return o??e}n(M,"clampDeploymentEventStrings");function S(e,s,o){let a=e;for(const p of s.slice(0,-1)){if(typeof a!="object"||a===null)return;a=a[p]}const r=s[s.length-1];if(r===void 0||typeof a!="object"||a===null)return;const c=a,i=c[r];typeof i!="string"||i.length<=o||(c[r]=y(i,o))}n(S,"truncateStringAtPath");function P(e){if(e==="platform")return"platform";if(e==="account")return"accounts"}n(P,"toCascadePhase");export{x as CASCADE_ACCOUNT_STATUSES,l as CASCADE_PHASES,I as DEPLOYMENT_ERROR_MESSAGE_MAX,f as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,d as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,E as DEPLOYMENT_EVENT_STATUS_REASON_MAX,_ as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,b as DEPLOYMENT_EVENT_TYPES,A as DeploymentEventSchema,M as clampDeploymentEventStrings,P as toCascadePhase,y as truncateUtf16Safe};
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* MUST be a namespace import never accessed at eval (`import type` is always
|
|
35
35
|
* fine — it is erased).
|
|
36
36
|
*/
|
|
37
|
-
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES, toCascadePhase } from "./deploymentEventSchema.js";
|
|
37
|
+
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, DEPLOYMENT_ERROR_MESSAGE_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES, toCascadePhase, clampDeploymentEventStrings, truncateUtf16Safe } from "./deploymentEventSchema.js";
|
|
38
38
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./deploymentEventSchema.js";
|
|
39
39
|
export type { AwsCredentials, DeployIdentity, ScopedBuildSecretSessionMinter } from "./credentials.js";
|
|
40
40
|
export type { DeployCallbacks, StepCompleteStatus, StackCleanupPhase, CascadeAccountPhase, LogLevel, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, StackTarget } from "./callbacks.js";
|
package/dist/src/types/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as S,DEPLOYMENT_EVENT_TYPES as _,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as T,DEPLOYMENT_EVENT_STATUS_REASON_MAX as A,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as t,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as r,
|
|
1
|
+
import{DeploymentEventSchema as S,DEPLOYMENT_EVENT_TYPES as _,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as T,DEPLOYMENT_EVENT_STATUS_REASON_MAX as A,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as t,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as r,DEPLOYMENT_ERROR_MESSAGE_MAX as o,CASCADE_PHASES as a,CASCADE_ACCOUNT_STATUSES as R,toCascadePhase as O,clampDeploymentEventStrings as P,truncateUtf16Safe as N}from"./deploymentEventSchema.js";import{isQuarantineDetail as D,isRetainedBucketsDetail as c}from"./callbacks.js";import{DeployEventSchema as n,StepProgressSchema as p,DeployResourceSchema as m,PlanChangeSchema as C,PlanPropertyChangeSchema as I,ChangeCountsSchema as s,DEPLOY_EVENT_CONTRACT as l,DEPLOY_EVENT_CONTRACT_MAJOR as M,DEPLOY_EVENT_CONTRACT_MINOR as h,SUPPORTED_CONTRACT_MARKERS as V,DEPLOY_EVENT_ID_MAX as U,DEPLOY_EVENT_NAME_MAX as Y,DEPLOY_EVENT_MESSAGE_MAX as d,DEPLOY_EVENT_STATUS_REASON_MAX as f,DEPLOY_EVENT_ERROR_MESSAGE_MAX as g,DEPLOY_EVENT_TRAIL_DETAIL_MAX as u,DEPLOY_EVENT_OUTPUT_MAX as x,DEPLOY_EVENT_ARN_MAX as G,DEPLOY_EVENT_URL_MAX as X,STEP_COMPLETE_STATUSES as y,LOG_LEVELS as F,STACK_CLEANUP_PHASES as K,CASCADE_ACCOUNT_PHASES as k,DEPLOY_RESULTS as H,DETECTION_PHASES as v,PHASE_STATUSES as b,BUILDERS as B,CASCADE_OUTCOME_RESULTS as J,PLAN_ACTIONS as w,REPLACEMENT_MODES as Q,APPROVAL_KINDS as W,APPROVAL_DECISIONS as j,ALL_PROGRESS_KINDS as q}from"./deployEvent.js";import{createDeployEmitter as Z}from"./deployEmitter.js";import{parseDeployEvent as EE,parseDeployContractVersion as eE}from"./deployEventParse.js";import{TRAIL_MIGRATION_PHASES as _E,TRAIL_MIGRATION_STATUSES as TE}from"./events.js";import{RemediationVerbSchema as tE,REMEDIATION_VERB_GLOSS as rE,DestructionFindingSchema as oE,DestructionTicketResourceSchema as aE,DestructionTicketSchema as RE,DestructionConsentSchema as OE,DestructionOutcomeSchema as PE,DESTRUCTION_FINDINGS as NE,CONSENT_VERDICTS as iE,TICKET_VERDICT_SOURCES as DE,TicketVerdictSourceSchema as cE}from"./destruction.js";import{DRIFT_VERDICTS as nE,DriftVerdictSchema as pE,DriftSuspectSchema as mE,DriftFindingSchema as CE,DriftJournalRecordSchema as IE,verbsForDriftVerdict as sE}from"./drift.js";import{REMEDIATION_PHASES as ME,RemediationPhaseSchema as hE,RemediationTargetSchema as VE,RemediationFlipProofSchema as UE,RemediationJournalRecordSchema as YE,RemediationVerbWithSurgerySchema as dE,remediationPhaseRank as fE,remediationPlaceholderPhysicalId as gE,isRemediationPlaceholderPhysicalId as uE}from"./remediation.js";import{stubCallerIdentity as GE}from"./deployment/index.js";import{ProgressReporter as yE}from"./ProgressEvent.js";import{APPLICATION_STACKS as KE,ORGANISATION_TYPES as kE,APPLICATION_DEPLOY_ORDER as HE,APPLICATION_DESTROY_ORDER as vE,OPENNEXT_DEPLOY_ORDER as bE,OPENNEXT_DESTROY_ORDER as BE,PARALLEL_DEPLOY_GROUPS as JE,PARALLEL_DESTROY_GROUPS as wE,OPENNEXT_PARALLEL_GROUPS as QE,isApplicationOperation as WE,isOrganisationOperation as jE,getParallelDeployGroups as qE,getParallelDestroyGroups as zE,getApplicationDeployOrder as ZE,getApplicationDestroyOrder as $E,getApplicationStackName as Ee,getOrganisationStackName as ee,isApplicationStack as Se,getApplicationStepName as _e,getApplicationStepId as Te,toPascalCase as Ae}from"./operations.js";import{PARALLEL_OPERATION_TYPES as re}from"./deployment/index.js";import{isOpenNextPattern as ae,OPENNEXT_PATTERNS as Re}from"./detection/patternTypes.js";import{STACK_NOT_FOUND_PATTERN as Pe,STACK_FAILED_STATE_PATTERN as Ne,CDK_NO_STACKS_MATCH as ie,INFRASTRUCTURE_FILENAME as De}from"./constants.js";import{ApplicationError as Le,wrapApplicationError as ne}from"./application/index.js";import{STEP_IDS as me,STEP_NAMES as Ce,INFRASTRUCTURE_STEP_NAMES as Ie,INFRA_STEP_NAME as se}from"./stepDefinitions.js";export{q as ALL_PROGRESS_KINDS,HE as APPLICATION_DEPLOY_ORDER,vE as APPLICATION_DESTROY_ORDER,KE as APPLICATION_STACKS,j as APPROVAL_DECISIONS,W as APPROVAL_KINDS,Le as ApplicationError,B as BUILDERS,k as CASCADE_ACCOUNT_PHASES,R as CASCADE_ACCOUNT_STATUSES,J as CASCADE_OUTCOME_RESULTS,a as CASCADE_PHASES,ie as CDK_NO_STACKS_MATCH,iE as CONSENT_VERDICTS,s as ChangeCountsSchema,o as DEPLOYMENT_ERROR_MESSAGE_MAX,t as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,T as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,A as DEPLOYMENT_EVENT_STATUS_REASON_MAX,r as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,_ as DEPLOYMENT_EVENT_TYPES,G as DEPLOY_EVENT_ARN_MAX,l as DEPLOY_EVENT_CONTRACT,M as DEPLOY_EVENT_CONTRACT_MAJOR,h as DEPLOY_EVENT_CONTRACT_MINOR,g as DEPLOY_EVENT_ERROR_MESSAGE_MAX,U as DEPLOY_EVENT_ID_MAX,d as DEPLOY_EVENT_MESSAGE_MAX,Y as DEPLOY_EVENT_NAME_MAX,x as DEPLOY_EVENT_OUTPUT_MAX,f as DEPLOY_EVENT_STATUS_REASON_MAX,u as DEPLOY_EVENT_TRAIL_DETAIL_MAX,X as DEPLOY_EVENT_URL_MAX,H as DEPLOY_RESULTS,NE as DESTRUCTION_FINDINGS,v as DETECTION_PHASES,nE as DRIFT_VERDICTS,n as DeployEventSchema,m as DeployResourceSchema,S as DeploymentEventSchema,OE as DestructionConsentSchema,oE as DestructionFindingSchema,PE as DestructionOutcomeSchema,aE as DestructionTicketResourceSchema,RE as DestructionTicketSchema,CE as DriftFindingSchema,IE as DriftJournalRecordSchema,mE as DriftSuspectSchema,pE as DriftVerdictSchema,De as INFRASTRUCTURE_FILENAME,Ie as INFRASTRUCTURE_STEP_NAMES,se as INFRA_STEP_NAME,F as LOG_LEVELS,bE as OPENNEXT_DEPLOY_ORDER,BE as OPENNEXT_DESTROY_ORDER,QE as OPENNEXT_PARALLEL_GROUPS,Re as OPENNEXT_PATTERNS,kE as ORGANISATION_TYPES,JE as PARALLEL_DEPLOY_GROUPS,wE as PARALLEL_DESTROY_GROUPS,re as PARALLEL_OPERATION_TYPES,b as PHASE_STATUSES,w as PLAN_ACTIONS,C as PlanChangeSchema,I as PlanPropertyChangeSchema,yE as ProgressReporter,ME as REMEDIATION_PHASES,rE as REMEDIATION_VERB_GLOSS,Q as REPLACEMENT_MODES,UE as RemediationFlipProofSchema,YE as RemediationJournalRecordSchema,hE as RemediationPhaseSchema,VE as RemediationTargetSchema,tE as RemediationVerbSchema,dE as RemediationVerbWithSurgerySchema,K as STACK_CLEANUP_PHASES,Ne as STACK_FAILED_STATE_PATTERN,Pe as STACK_NOT_FOUND_PATTERN,y as STEP_COMPLETE_STATUSES,me as STEP_IDS,Ce as STEP_NAMES,V as SUPPORTED_CONTRACT_MARKERS,p as StepProgressSchema,DE as TICKET_VERDICT_SOURCES,_E as TRAIL_MIGRATION_PHASES,TE as TRAIL_MIGRATION_STATUSES,cE as TicketVerdictSourceSchema,P as clampDeploymentEventStrings,Z as createDeployEmitter,ZE as getApplicationDeployOrder,$E as getApplicationDestroyOrder,Ee as getApplicationStackName,Te as getApplicationStepId,_e as getApplicationStepName,ee as getOrganisationStackName,qE as getParallelDeployGroups,zE as getParallelDestroyGroups,WE as isApplicationOperation,Se as isApplicationStack,ae as isOpenNextPattern,jE as isOrganisationOperation,D as isQuarantineDetail,uE as isRemediationPlaceholderPhysicalId,c as isRetainedBucketsDetail,eE as parseDeployContractVersion,EE as parseDeployEvent,fE as remediationPhaseRank,gE as remediationPlaceholderPhysicalId,GE as stubCallerIdentity,O as toCascadePhase,Ae as toPascalCase,N as truncateUtf16Safe,sE as verbsForDriftVerdict,ne as wrapApplicationError};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "Shared deployment engine for Fjall — used by CLI and webapp worker",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"@aws-sdk/client-ssm": "^3.1038.0",
|
|
89
89
|
"@aws-sdk/client-sso-admin": "^3.1038.0",
|
|
90
90
|
"@aws-sdk/client-sts": "^3.1038.0",
|
|
91
|
-
"@fjall/generator": "^3.
|
|
92
|
-
"@fjall/util": "^3.
|
|
91
|
+
"@fjall/generator": "^3.8.1",
|
|
92
|
+
"@fjall/util": "^3.8.1",
|
|
93
93
|
"@smithy/node-http-handler": "^4.6.1",
|
|
94
94
|
"aws-cdk": "^2.1128.1",
|
|
95
95
|
"tsx": "^4.21.0",
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"typescript": "^6.0.3",
|
|
101
101
|
"vitest": "^4.1.5"
|
|
102
102
|
},
|
|
103
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "c15e42f3d2047acbbe086518450c77ae68839e1d"
|
|
104
104
|
}
|