@fjall/deploy-core 2.12.0 → 2.14.0

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.
Files changed (126) hide show
  1. package/dist/.minified +1 -1
  2. package/dist/src/aws/cloudtrail/orgTrailDelivery.d.ts +44 -0
  3. package/dist/src/aws/cloudtrail/orgTrailDelivery.js +1 -0
  4. package/dist/src/aws/index.d.ts +6 -2
  5. package/dist/src/aws/index.js +1 -1
  6. package/dist/src/aws/organisations/accountGlobals.d.ts +40 -0
  7. package/dist/src/aws/organisations/accountGlobals.js +1 -0
  8. package/dist/src/aws/organisations/accounts.d.ts +3 -1
  9. package/dist/src/aws/organisations/accounts.js +1 -1
  10. package/dist/src/aws/organisations/backup.d.ts +2 -1
  11. package/dist/src/aws/organisations/backup.js +2 -2
  12. package/dist/src/aws/organisations/importedAccounts.d.ts +16 -0
  13. package/dist/src/aws/organisations/importedAccounts.js +1 -0
  14. package/dist/src/aws/organisations/index.d.ts +3 -1
  15. package/dist/src/aws/organisations/index.js +1 -1
  16. package/dist/src/aws/organisations/organisationalUnits.d.ts +1 -1
  17. package/dist/src/aws/organisations/policies.js +1 -1
  18. package/dist/src/aws/organisations/rootAccess.d.ts +27 -0
  19. package/dist/src/aws/organisations/rootAccess.js +3 -0
  20. package/dist/src/aws/organisations/serviceAccess.d.ts +6 -0
  21. package/dist/src/aws/organisations/serviceAccess.js +1 -1
  22. package/dist/src/aws/organisations/types.d.ts +18 -0
  23. package/dist/src/aws/organisations/types.js +1 -1
  24. package/dist/src/aws/sts/assumeRoot.d.ts +46 -0
  25. package/dist/src/aws/sts/assumeRoot.js +1 -0
  26. package/dist/src/aws/targetReadiness.d.ts +70 -0
  27. package/dist/src/aws/targetReadiness.js +1 -0
  28. package/dist/src/aws/targetSetAdvisory.d.ts +24 -0
  29. package/dist/src/aws/targetSetAdvisory.js +1 -0
  30. package/dist/src/events/index.d.ts +2 -0
  31. package/dist/src/events/index.js +1 -1
  32. package/dist/src/index.d.ts +18 -14
  33. package/dist/src/index.js +1 -1
  34. package/dist/src/orchestration/accountsConfig.d.ts +11 -0
  35. package/dist/src/orchestration/accountsConfig.js +1 -1
  36. package/dist/src/orchestration/applicationDeploy.js +1 -1
  37. package/dist/src/orchestration/applicationDestroy.js +1 -1
  38. package/dist/src/orchestration/cascadeDestroyHelpers.d.ts +12 -1
  39. package/dist/src/orchestration/cascadeDestroyHelpers.js +1 -1
  40. package/dist/src/orchestration/cascadeHelpers.d.ts +36 -5
  41. package/dist/src/orchestration/cascadeHelpers.js +1 -1
  42. package/dist/src/orchestration/contextHelpers.d.ts +20 -2
  43. package/dist/src/orchestration/contextHelpers.js +1 -1
  44. package/dist/src/orchestration/index.d.ts +13 -4
  45. package/dist/src/orchestration/index.js +1 -1
  46. package/dist/src/orchestration/organisationDeploy/cascadeExecution.d.ts +28 -0
  47. package/dist/src/orchestration/organisationDeploy/cascadeExecution.js +1 -0
  48. package/dist/src/orchestration/organisationDeploy/infraSteps.d.ts +40 -0
  49. package/dist/src/orchestration/organisationDeploy/infraSteps.js +1 -0
  50. package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.d.ts +8 -0
  51. package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +5 -0
  52. package/dist/src/orchestration/organisationDeploy/orgContext.d.ts +12 -0
  53. package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -0
  54. package/dist/src/orchestration/organisationDeploy/resolveCascadeAccounts.d.ts +15 -0
  55. package/dist/src/orchestration/organisationDeploy/resolveCascadeAccounts.js +1 -0
  56. package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.d.ts +11 -0
  57. package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -0
  58. package/dist/src/orchestration/organisationDeploy/trailReconciliation.d.ts +21 -0
  59. package/dist/src/orchestration/organisationDeploy/trailReconciliation.js +1 -0
  60. package/dist/src/orchestration/organisationDeploy.d.ts +1 -5
  61. package/dist/src/orchestration/organisationDeploy.js +1 -5
  62. package/dist/src/orchestration/organisationDestroy.d.ts +1 -1
  63. package/dist/src/orchestration/organisationDestroy.js +2 -2
  64. package/dist/src/orchestration/organisationSetup.d.ts +23 -3
  65. package/dist/src/orchestration/organisationSetup.js +1 -1
  66. package/dist/src/orchestration/reconcileProviderAccounts.js +1 -1
  67. package/dist/src/orchestration/stackCleanup/bucketOps.d.ts +54 -0
  68. package/dist/src/orchestration/stackCleanup/bucketOps.js +1 -0
  69. package/dist/src/orchestration/stackCleanup/failedStack.d.ts +34 -0
  70. package/dist/src/orchestration/stackCleanup/failedStack.js +1 -0
  71. package/dist/src/orchestration/stackCleanup/logging.d.ts +9 -0
  72. package/dist/src/orchestration/stackCleanup/logging.js +1 -0
  73. package/dist/src/orchestration/stackCleanup/messages.d.ts +16 -0
  74. package/dist/src/orchestration/stackCleanup/messages.js +1 -0
  75. package/dist/src/orchestration/stackCleanup/orphanSweep.d.ts +25 -0
  76. package/dist/src/orchestration/stackCleanup/orphanSweep.js +1 -0
  77. package/dist/src/orchestration/stackCleanup/preEmpty.d.ts +35 -0
  78. package/dist/src/orchestration/stackCleanup/preEmpty.js +1 -0
  79. package/dist/src/orchestration/stackCleanup/stackResources.d.ts +9 -0
  80. package/dist/src/orchestration/stackCleanup/stackResources.js +1 -0
  81. package/dist/src/orchestration/stackCleanup.d.ts +13 -33
  82. package/dist/src/orchestration/stackCleanup.js +1 -1
  83. package/dist/src/orchestration/trailMigration/memberTrailCleanup.d.ts +43 -0
  84. package/dist/src/orchestration/trailMigration/memberTrailCleanup.js +1 -0
  85. package/dist/src/orchestration/trailMigration/trailMigration.d.ts +64 -0
  86. package/dist/src/orchestration/trailMigration/trailMigration.js +1 -0
  87. package/dist/src/orchestration/unlock/scpRemediation.d.ts +15 -0
  88. package/dist/src/orchestration/unlock/scpRemediation.js +1 -0
  89. package/dist/src/orchestration/unlock/unlockBucket.d.ts +37 -0
  90. package/dist/src/orchestration/unlock/unlockBucket.js +1 -0
  91. package/dist/src/orchestration/unlock/unlockQueue.d.ts +43 -0
  92. package/dist/src/orchestration/unlock/unlockQueue.js +1 -0
  93. package/dist/src/services/application/ApplicationStackService.d.ts +9 -10
  94. package/dist/src/services/application/ApplicationStackService.js +1 -1
  95. package/dist/src/services/application/applicationStackHelpers.d.ts +13 -8
  96. package/dist/src/services/application/applicationStackHelpers.js +3 -3
  97. package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
  98. package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +1 -0
  99. package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
  100. package/dist/src/services/supporting/CdkContextBuilder.d.ts +1 -0
  101. package/dist/src/services/supporting/CdkContextBuilder.js +1 -1
  102. package/dist/src/steps/stepRegistry.js +1 -1
  103. package/dist/src/types/FjallState.d.ts +7 -0
  104. package/dist/src/types/FjallState.js +1 -1
  105. package/dist/src/types/callbackKeys.d.ts +1 -1
  106. package/dist/src/types/callbackKeys.js +1 -1
  107. package/dist/src/types/callbacks.d.ts +58 -3
  108. package/dist/src/types/callbacks.js +1 -0
  109. package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
  110. package/dist/src/types/deploymentEventSchema.d.ts +28 -3
  111. package/dist/src/types/deploymentEventSchema.js +1 -1
  112. package/dist/src/types/events.d.ts +12 -0
  113. package/dist/src/types/events.js +1 -0
  114. package/dist/src/types/index.d.ts +7 -11
  115. package/dist/src/types/index.js +1 -1
  116. package/dist/src/types/orgConfig.d.ts +8 -2
  117. package/dist/src/types/params.d.ts +18 -0
  118. package/dist/src/types/patternDetection.d.ts +0 -25
  119. package/dist/src/types/patternDetection.js +1 -1
  120. package/dist/src/types/stepDefinitions.d.ts +2 -0
  121. package/dist/src/types/stepDefinitions.js +1 -1
  122. package/dist/src/util/index.d.ts +1 -0
  123. package/dist/src/util/index.js +1 -1
  124. package/dist/src/util/sleepAbortable.d.ts +8 -0
  125. package/dist/src/util/sleepAbortable.js +1 -0
  126. package/package.json +8 -4
@@ -11,23 +11,19 @@
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, 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, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "./types/index.js";
15
15
  export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./types/index.js";
16
- export type { AwsCredentials, DeployIdentity, DeployCallbacks, StepCompleteStatus, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, OrgConfig, ProviderAccount, SSOSession, Entitlements } from "./types/index.js";
16
+ export type { AwsCredentials, DeployIdentity, DeployCallbacks, StepCompleteStatus, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
17
17
  export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./aws/index.js";
18
18
  export { SimpleAwsProvider } from "./aws/index.js";
19
- export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS } from "./aws/index.js";
20
- export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult } from "./aws/index.js";
19
+ export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory } from "./aws/index.js";
20
+ export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict, TargetSetUnreadyAdvisoryParams } from "./aws/index.js";
21
+ export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
22
+ export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./aws/index.js";
21
23
  export { STEP_IDS, STEP_NAMES, INFRASTRUCTURE_STEP_NAMES, INFRA_STEP_NAME } from "./types/index.js";
22
24
  export type { StepId, InfrastructureStepName, StepDefinition, StepDeploymentType, Operation, StepContext } from "./types/index.js";
23
25
  export type { DeploymentContext, StepOutput, ApplicationDeploymentContext, CallerIdentity, StackOutput, StackOutputsRecord, ValidationResult, DetectionResult, ProgressCallbacks, ApplicationStack, OrganisationType, DeploymentOperation, ApplicationOperation, OrganisationOperation, DeployOrderOptions, ParallelDeployGroupKey, ParallelDestroyGroupKey, OpenNextPattern, AppResourceFlags, ParallelDeploymentResult, ParallelDeployGroup, ParallelOperation, ParallelOperationType, ParallelOperationState, ApplicationErrorType, StackDeploymentData, StackDeploymentOptions, ApplicationDeploymentData, ApplicationDestructionData, FjallStateFile, TemplateHashEntry } from "./types/index.js";
24
- export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS, deriveResourcesFromManifestStacks, STACK_NOT_FOUND_PATTERN, STACK_FAILED_STATE_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME, ApplicationError, wrapApplicationError, FjallStateFileSchema, readStateFile, writeStateFile, createEmptyState, deleteStateFile, updateTemplateHash, getStateFilePath, stubCallerIdentity } from "./types/index.js";
25
- /** @deprecated Use FrameworkRegistry.resolve() instead */
26
- export { detectPattern } from "./types/index.js";
27
- /** @deprecated Use FrameworkRegistry.resolve() instead */
28
- export { detectPayloadPattern } from "./types/index.js";
29
- /** @deprecated Use FrameworkRegistry.resolve() instead */
30
- export { detectDatabase } from "./types/index.js";
26
+ export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, isQuarantineDetail, isRetainedBucketsDetail, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS, deriveResourcesFromManifestStacks, STACK_NOT_FOUND_PATTERN, STACK_FAILED_STATE_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME, ApplicationError, wrapApplicationError, FjallStateFileSchema, readStateFile, writeStateFile, createEmptyState, deleteStateFile, updateTemplateHash, getStateFilePath, stubCallerIdentity } from "./types/index.js";
31
27
  export { CloudFormationEventMonitor } from "./aws/index.js";
32
28
  export type { AwsClientLike, EventLogWriter, EventFailureAnalyser, EventLogWriterFactory, EventMonitorDeps } from "./aws/index.js";
33
29
  export { CdkService, CdkArgumentBuilder, CdkProcessManager, CdkEventMonitor, startStackMonitoring, DEFAULT_DEPLOY_TIMEOUT_MS, isCdkError, formatInfrastructureError, getStructuralHint, getSourceContext, hasCdkDifferences, parseDiffOutput, CloudFormationService, CloudFormationError, EcsService, EcsError, EcsServiceResolver, type CfnExportsClient, TemplateHashService, TemplateHashError, type TemplateComparisonResult, CdkContextBuilder, emitProgress, PROGRESS_MESSAGES, parseBuildPhase, buildStepContextBuildConfig, convertCloudFormationOutputsToRecord, type CloudFormationOutput, ApplicationStackService } from "./services/index.js";
@@ -43,16 +39,24 @@ export { type Result, success, failure, isSuccess, isFailure } from "@fjall/gene
43
39
  export { deploy } from "./orchestration/index.js";
44
40
  export { destroy } from "./orchestration/index.js";
45
41
  export { partitionAccounts } from "./orchestration/index.js";
46
- export { buildRegionList, buildAccountRegionPairs } from "./orchestration/index.js";
42
+ export { buildRegionList, buildAccountRegionPairs, cascadeHomeRegion, cascadeOperationKey } from "./orchestration/index.js";
47
43
  export type { AccountRegionPair } from "./orchestration/index.js";
48
44
  export { projectScalarSummary, projectAccountRows } from "./orchestration/index.js";
49
45
  export type { CascadeOutcomeResult, CascadeMemberOutcome, CascadePlatformOutcome, CascadeLedger, CascadeAccountRow, CascadePlatformRow, CascadeAccountProjection } from "./orchestration/index.js";
50
46
  export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./orchestration/index.js";
51
47
  export type { ReconcileResult } from "./orchestration/index.js";
52
- export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./orchestration/index.js";
48
+ export { decideNextTransition, reconcileTrailMigration, decommissionMemberTrailStorage, ORG_TRAIL_BUCKET_OUTPUT_KEY, TRAIL_BUCKET_OUTPUT_KEY, TRAIL_KEY_ARN_OUTPUT_KEY } from "./orchestration/index.js";
49
+ export type { MemberTrailFacts, TrailMigrationTransition, TrailMigrationOutcome, DecommissionClients, DecommissionInput, DecommissionOutcome } from "./orchestration/index.js";
50
+ export { unlockBucket, unlockQueue } from "./orchestration/index.js";
51
+ export type { UnlockBucketInput, UnlockBucketReport, UnlockQueueInput, UnlockQueueReport } from "./orchestration/index.js";
52
+ export { verifyOrgTrailDelivery } from "./aws/cloudtrail/orgTrailDelivery.js";
53
+ export type { OrgTrailDeliveryReport, OrgTrailAccountDelivery } from "./aws/cloudtrail/orgTrailDelivery.js";
54
+ export { assumeRootForTask, isRootTaskPolicyArn, ROOT_TASK_POLICY_ARNS, MAX_ROOT_SESSION_SECONDS } from "./aws/sts/assumeRoot.js";
55
+ export type { RootTaskPolicyArn, AssumeRootOptions, RootTaskSession } from "./aws/sts/assumeRoot.js";
56
+ export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig, isOuOnlyAccountBucket, OU_ONLY_ACCOUNT_BUCKETS } from "./orchestration/index.js";
53
57
  export type { AccountsConfig } from "./orchestration/index.js";
54
58
  export { runOpenNextBuild } from "./orchestration/index.js";
55
- export { runOrganisationSetup } from "./orchestration/index.js";
59
+ export { runOrganisationSetup, ORG_SETUP_PHASES } from "./orchestration/index.js";
56
60
  export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
57
61
  export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";
58
62
  export { FrameworkRegistry, type ResolvedBuilder } 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 a,CASCADE_PHASES as i,CASCADE_ACCOUNT_STATUSES as s}from"./types/index.js";import{SimpleAwsProvider as c}from"./aws/index.js";import{ensureOrganisationExists as p,describeOrganisation as S,enablePolicyTypes as l,enableServiceAccess as A,enableRamSharing as u,activateTrustedAccess as m,enableIpamDelegatedAdmin as T,updateBackupGlobalSettings as P,listAccounts as O,findAccount as d,createAccount as R,ensureOrganisationalUnitsExist as _,placeAccountsInOUs as f,buildAccountToOUMap as C,activateCostAllocationTags as g,checkIdentityCentreStatus as N,extractErrorName as x,isOULeaf as D,registerSecurityDelegates as I,SECURITY_SERVICE_PRINCIPALS as L}from"./aws/index.js";import{STEP_IDS as k,STEP_NAMES as y,INFRASTRUCTURE_STEP_NAMES as F,INFRA_STEP_NAME as U}from"./types/index.js";import{ProgressReporter as b,APPLICATION_STACKS as h,ORGANISATION_TYPES as Y,APPLICATION_DEPLOY_ORDER as G,APPLICATION_DESTROY_ORDER as B,OPENNEXT_DEPLOY_ORDER as w,OPENNEXT_DESTROY_ORDER as H,PARALLEL_DEPLOY_GROUPS as K,PARALLEL_DESTROY_GROUPS as V,OPENNEXT_PARALLEL_GROUPS as X,PARALLEL_OPERATION_TYPES as j,isApplicationOperation as q,isOrganisationOperation as z,getParallelDeployGroups as J,getParallelDestroyGroups as Q,getApplicationDeployOrder as W,getApplicationDestroyOrder as Z,getApplicationStackName as $,getOrganisationStackName as ee,isApplicationStack as re,getApplicationStepName as te,getApplicationStepId as oe,toPascalCase as ae,isOpenNextPattern as ie,OPENNEXT_PATTERNS as se,deriveResourcesFromManifestStacks as ne,STACK_NOT_FOUND_PATTERN as ce,STACK_FAILED_STATE_PATTERN as Ee,CDK_NO_STACKS_MATCH as pe,INFRASTRUCTURE_FILENAME as Se,ApplicationError as le,wrapApplicationError as Ae,FjallStateFileSchema as ue,readStateFile as me,writeStateFile as Te,createEmptyState as Pe,deleteStateFile as Oe,updateTemplateHash as de,getStateFilePath as Re,stubCallerIdentity as _e}from"./types/index.js";import{detectPattern as Ce}from"./types/index.js";import{detectPayloadPattern as Ne}from"./types/index.js";import{detectDatabase as De}from"./types/index.js";import{CloudFormationEventMonitor as Le}from"./aws/index.js";import{CdkService as ke,CdkArgumentBuilder as ye,CdkProcessManager as Fe,CdkEventMonitor as Ue,startStackMonitoring as Me,DEFAULT_DEPLOY_TIMEOUT_MS as be,isCdkError as he,formatInfrastructureError as Ye,getStructuralHint as Ge,getSourceContext as Be,hasCdkDifferences as we,parseDiffOutput as He,CloudFormationService as Ke,CloudFormationError as Ve,EcsService as Xe,EcsError as je,EcsServiceResolver as qe,TemplateHashService as ze,TemplateHashError as Je,CdkContextBuilder as Qe,emitProgress as We,PROGRESS_MESSAGES as Ze,parseBuildPhase as $e,buildStepContextBuildConfig as er,convertCloudFormationOutputsToRecord as rr,ApplicationStackService as tr}from"./services/index.js";import{CdkError as ar}from"./types/errors/index.js";import{BaseServiceError as sr,ValidationError as nr,AuthError as cr,AwsError as Er,DeploymentError as pr,NetworkError as Sr,FileSystemError as lr,ConfigError as Ar,toServiceError as ur}from"./types/errors/index.js";import{filterDangerousEnvVars as Tr,maskSensitiveOutput as Pr,parseShellArgs as Or,sleep as dr}from"@fjall/util";import{hasDockerfile as _r}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as Cr}from"./util/sequencedCallbacks.js";import{fileExists as Nr}from"@fjall/util/fsHelpers";import{success as Dr,failure as Ir,isSuccess as Lr,isFailure as vr}from"@fjall/generator";import{deploy as yr}from"./orchestration/index.js";import{destroy as Ur}from"./orchestration/index.js";import{partitionAccounts as br}from"./orchestration/index.js";import{buildRegionList as Yr,buildAccountRegionPairs as Gr}from"./orchestration/index.js";import{projectScalarSummary as wr,projectAccountRows as Hr}from"./orchestration/index.js";import{reconcileProviderAccounts as Vr,mergeReconciledProviderAccounts as Xr}from"./orchestration/index.js";import{parseAccountsConfiguration as qr,flattenAccountsToEnvironments as zr,extractAllAccountNames as Jr,accountsConfigToOUTree as Qr,isStringArray as Wr,isAccountsConfig as Zr}from"./orchestration/index.js";import{runOpenNextBuild as et}from"./orchestration/index.js";import{runOrganisationSetup as tt}from"./orchestration/index.js";import{FrameworkRegistry as at}from"./orchestration/index.js";import{openNextBuilder as st,dockerBuilder as nt}from"./orchestration/index.js";import{StepRegistry as Et,getDestroyStepId as pt}from"./steps/index.js";export{G as APPLICATION_DEPLOY_ORDER,B as APPLICATION_DESTROY_ORDER,h as APPLICATION_STACKS,le as ApplicationError,tr as ApplicationStackService,cr as AuthError,Er as AwsError,sr as BaseServiceError,s as CASCADE_ACCOUNT_STATUSES,i as CASCADE_PHASES,pe as CDK_NO_STACKS_MATCH,ye as CdkArgumentBuilder,Qe as CdkContextBuilder,ar as CdkError,Ue as CdkEventMonitor,Fe as CdkProcessManager,ke as CdkService,Ve as CloudFormationError,Le as CloudFormationEventMonitor,Ke as CloudFormationService,Ar as ConfigError,be as DEFAULT_DEPLOY_TIMEOUT_MS,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,o as DEPLOYMENT_EVENT_TYPES,pr as DeploymentError,t as DeploymentEventSchema,je as EcsError,Xe as EcsService,qe as EcsServiceResolver,lr as FileSystemError,ue as FjallStateFileSchema,at as FrameworkRegistry,Se as INFRASTRUCTURE_FILENAME,F as INFRASTRUCTURE_STEP_NAMES,U as INFRA_STEP_NAME,Sr as NetworkError,w as OPENNEXT_DEPLOY_ORDER,H as OPENNEXT_DESTROY_ORDER,X as OPENNEXT_PARALLEL_GROUPS,se as OPENNEXT_PATTERNS,Y as ORGANISATION_TYPES,K as PARALLEL_DEPLOY_GROUPS,V as PARALLEL_DESTROY_GROUPS,j as PARALLEL_OPERATION_TYPES,Ze as PROGRESS_MESSAGES,b as ProgressReporter,L as SECURITY_SERVICE_PRINCIPALS,Ee as STACK_FAILED_STATE_PATTERN,ce as STACK_NOT_FOUND_PATTERN,k as STEP_IDS,y as STEP_NAMES,c as SimpleAwsProvider,Et as StepRegistry,Je as TemplateHashError,ze as TemplateHashService,nr as ValidationError,Qr as accountsConfigToOUTree,g as activateCostAllocationTags,m as activateTrustedAccess,Gr as buildAccountRegionPairs,C as buildAccountToOUMap,Yr as buildRegionList,er as buildStepContextBuildConfig,N as checkIdentityCentreStatus,rr as convertCloudFormationOutputsToRecord,R as createAccount,Pe as createEmptyState,Cr as createSequencedCallbacks,Oe as deleteStateFile,yr as deploy,ne as deriveResourcesFromManifestStacks,S as describeOrganisation,Ur as destroy,De as detectDatabase,Ce as detectPattern,Ne as detectPayloadPattern,nt as dockerBuilder,We as emitProgress,T as enableIpamDelegatedAdmin,l as enablePolicyTypes,u as enableRamSharing,A as enableServiceAccess,p as ensureOrganisationExists,_ as ensureOrganisationalUnitsExist,Jr as extractAllAccountNames,x as extractErrorName,Ir as failure,Nr as fileExists,Tr as filterDangerousEnvVars,d as findAccount,zr as flattenAccountsToEnvironments,Ye as formatInfrastructureError,W as getApplicationDeployOrder,Z as getApplicationDestroyOrder,$ as getApplicationStackName,oe as getApplicationStepId,te as getApplicationStepName,pt as getDestroyStepId,ee as getOrganisationStackName,J as getParallelDeployGroups,Q as getParallelDestroyGroups,Be as getSourceContext,Re as getStateFilePath,Ge as getStructuralHint,we as hasCdkDifferences,_r as hasDockerfile,Zr as isAccountsConfig,q as isApplicationOperation,re as isApplicationStack,he as isCdkError,vr as isFailure,D as isOULeaf,ie as isOpenNextPattern,z as isOrganisationOperation,Wr as isStringArray,Lr as isSuccess,O as listAccounts,Pr as maskSensitiveOutput,Xr as mergeReconciledProviderAccounts,st as openNextBuilder,qr as parseAccountsConfiguration,$e as parseBuildPhase,He as parseDiffOutput,Or as parseShellArgs,br as partitionAccounts,f as placeAccountsInOUs,Hr as projectAccountRows,wr as projectScalarSummary,me as readStateFile,Vr as reconcileProviderAccounts,I as registerSecurityDelegates,et as runOpenNextBuild,tt as runOrganisationSetup,dr as sleep,Me as startStackMonitoring,_e as stubCallerIdentity,Dr as success,ae as toPascalCase,ur as toServiceError,P as updateBackupGlobalSettings,de as updateTemplateHash,Ae as wrapApplicationError,Te 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 s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as E,CASCADE_PHASES as n,CASCADE_ACCOUNT_STATUSES as c}from"./types/index.js";import{SimpleAwsProvider as A}from"./aws/index.js";import{checkTargetReadiness as T,describeTargetReadinessReason as p,buildTargetUnreadyAdvisory as _,buildTargetSetUnreadyAdvisory as O}from"./aws/index.js";import{ensureOrganisationExists as R,describeOrganisation as m,enablePolicyTypes as d,enableServiceAccess as P,SERVICE_PRINCIPALS as C,enableRamSharing as N,activateTrustedAccess as f,enableIpamDelegatedAdmin as g,updateBackupGlobalSettings as x,listAccounts as D,findAccount as I,createAccount as L,ensureOrganisationalUnitsExist as U,placeAccountsInOUs as k,buildAccountToOUMap as y,activateCostAllocationTags as v,checkIdentityCentreStatus as M,extractErrorName as F,isOULeaf as Y,registerSecurityDelegates as b,SECURITY_SERVICE_PRINCIPALS as B,enableCentralisedRootAccess as h,ROOT_ACCESS_FEATURES as K,RootAccessEnablementError as G}from"./aws/index.js";import{STEP_IDS as H,STEP_NAMES as X,INFRASTRUCTURE_STEP_NAMES as w,INFRA_STEP_NAME as j}from"./types/index.js";import{ProgressReporter as q,APPLICATION_STACKS as z,ORGANISATION_TYPES as J,APPLICATION_DEPLOY_ORDER as W,APPLICATION_DESTROY_ORDER as Z,OPENNEXT_DEPLOY_ORDER as $,OPENNEXT_DESTROY_ORDER as ee,PARALLEL_DEPLOY_GROUPS as re,PARALLEL_DESTROY_GROUPS as te,OPENNEXT_PARALLEL_GROUPS as oe,PARALLEL_OPERATION_TYPES as ie,isApplicationOperation as ae,isOrganisationOperation as se,getParallelDeployGroups as Ee,getParallelDestroyGroups as ne,getApplicationDeployOrder as ce,getApplicationDestroyOrder as Se,getApplicationStackName as Ae,getOrganisationStackName as le,isApplicationStack as Te,isQuarantineDetail as pe,isRetainedBucketsDetail as _e,getApplicationStepName as Oe,getApplicationStepId as ue,toPascalCase as Re,isOpenNextPattern as me,OPENNEXT_PATTERNS as de,deriveResourcesFromManifestStacks as Pe,STACK_NOT_FOUND_PATTERN as Ce,STACK_FAILED_STATE_PATTERN as Ne,CDK_NO_STACKS_MATCH as fe,INFRASTRUCTURE_FILENAME as ge,ApplicationError as xe,wrapApplicationError as De,FjallStateFileSchema as Ie,readStateFile as Le,writeStateFile as Ue,createEmptyState as ke,deleteStateFile as ye,updateTemplateHash as ve,getStateFilePath as Me,stubCallerIdentity as Fe}from"./types/index.js";import{CloudFormationEventMonitor as be}from"./aws/index.js";import{CdkService as he,CdkArgumentBuilder as Ke,CdkProcessManager as Ge,CdkEventMonitor as Ve,startStackMonitoring as He,DEFAULT_DEPLOY_TIMEOUT_MS as Xe,isCdkError as we,formatInfrastructureError as je,getStructuralHint as Qe,getSourceContext as qe,hasCdkDifferences as ze,parseDiffOutput as Je,CloudFormationService as We,CloudFormationError as Ze,EcsService as $e,EcsError as er,EcsServiceResolver as rr,TemplateHashService as tr,TemplateHashError as or,CdkContextBuilder as ir,emitProgress as ar,PROGRESS_MESSAGES as sr,parseBuildPhase as Er,buildStepContextBuildConfig as nr,convertCloudFormationOutputsToRecord as cr,ApplicationStackService as Sr}from"./services/index.js";import{CdkError as lr}from"./types/errors/index.js";import{BaseServiceError as pr,ValidationError as _r,AuthError as Or,AwsError as ur,DeploymentError as Rr,NetworkError as mr,FileSystemError as dr,ConfigError as Pr,toServiceError as Cr}from"./types/errors/index.js";import{filterDangerousEnvVars as fr,maskSensitiveOutput as gr,parseShellArgs as xr,sleep as Dr}from"@fjall/util";import{hasDockerfile as Lr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as kr}from"./util/sequencedCallbacks.js";import{fileExists as vr}from"@fjall/util/fsHelpers";import{success as Fr,failure as Yr,isSuccess as br,isFailure as Br}from"@fjall/generator";import{deploy as Kr}from"./orchestration/index.js";import{destroy as Vr}from"./orchestration/index.js";import{partitionAccounts as Xr}from"./orchestration/index.js";import{buildRegionList as jr,buildAccountRegionPairs as Qr,cascadeHomeRegion as qr,cascadeOperationKey as zr}from"./orchestration/index.js";import{projectScalarSummary as Wr,projectAccountRows as Zr}from"./orchestration/index.js";import{reconcileProviderAccounts as et,mergeReconciledProviderAccounts as rt}from"./orchestration/index.js";import{decideNextTransition as ot,reconcileTrailMigration as it,decommissionMemberTrailStorage as at,ORG_TRAIL_BUCKET_OUTPUT_KEY as st,TRAIL_BUCKET_OUTPUT_KEY as Et,TRAIL_KEY_ARN_OUTPUT_KEY as nt}from"./orchestration/index.js";import{unlockBucket as St,unlockQueue as At}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Tt}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as _t,isRootTaskPolicyArn as Ot,ROOT_TASK_POLICY_ARNS as ut,MAX_ROOT_SESSION_SECONDS as Rt}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as dt,flattenAccountsToEnvironments as Pt,extractAllAccountNames as Ct,accountsConfigToOUTree as Nt,isStringArray as ft,isAccountsConfig as gt,isOuOnlyAccountBucket as xt,OU_ONLY_ACCOUNT_BUCKETS as Dt}from"./orchestration/index.js";import{runOpenNextBuild as Lt}from"./orchestration/index.js";import{runOrganisationSetup as kt,ORG_SETUP_PHASES as yt}from"./orchestration/index.js";import{FrameworkRegistry as Mt}from"./orchestration/index.js";import{openNextBuilder as Yt,dockerBuilder as bt}from"./orchestration/index.js";import{StepRegistry as ht,getDestroyStepId as Kt}from"./steps/index.js";export{W as APPLICATION_DEPLOY_ORDER,Z as APPLICATION_DESTROY_ORDER,z as APPLICATION_STACKS,xe as ApplicationError,Sr as ApplicationStackService,Or as AuthError,ur as AwsError,pr as BaseServiceError,c as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,fe as CDK_NO_STACKS_MATCH,Ke as CdkArgumentBuilder,ir as CdkContextBuilder,lr as CdkError,Ve as CdkEventMonitor,Ge as CdkProcessManager,he as CdkService,Ze as CloudFormationError,be as CloudFormationEventMonitor,We as CloudFormationService,Pr as ConfigError,Xe as DEFAULT_DEPLOY_TIMEOUT_MS,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,E as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,Rr as DeploymentError,t as DeploymentEventSchema,er as EcsError,$e as EcsService,rr as EcsServiceResolver,dr as FileSystemError,Ie as FjallStateFileSchema,Mt as FrameworkRegistry,ge as INFRASTRUCTURE_FILENAME,w as INFRASTRUCTURE_STEP_NAMES,j as INFRA_STEP_NAME,Rt as MAX_ROOT_SESSION_SECONDS,mr as NetworkError,$ as OPENNEXT_DEPLOY_ORDER,ee as OPENNEXT_DESTROY_ORDER,oe as OPENNEXT_PARALLEL_GROUPS,de as OPENNEXT_PATTERNS,J as ORGANISATION_TYPES,yt as ORG_SETUP_PHASES,st as ORG_TRAIL_BUCKET_OUTPUT_KEY,Dt as OU_ONLY_ACCOUNT_BUCKETS,re as PARALLEL_DEPLOY_GROUPS,te as PARALLEL_DESTROY_GROUPS,ie as PARALLEL_OPERATION_TYPES,sr as PROGRESS_MESSAGES,q as ProgressReporter,K as ROOT_ACCESS_FEATURES,ut as ROOT_TASK_POLICY_ARNS,G as RootAccessEnablementError,B as SECURITY_SERVICE_PRINCIPALS,C as SERVICE_PRINCIPALS,Ne as STACK_FAILED_STATE_PATTERN,Ce as STACK_NOT_FOUND_PATTERN,H as STEP_IDS,X as STEP_NAMES,A as SimpleAwsProvider,ht as StepRegistry,Et as TRAIL_BUCKET_OUTPUT_KEY,nt as TRAIL_KEY_ARN_OUTPUT_KEY,or as TemplateHashError,tr as TemplateHashService,_r as ValidationError,Nt as accountsConfigToOUTree,v as activateCostAllocationTags,f as activateTrustedAccess,_t as assumeRootForTask,Qr as buildAccountRegionPairs,y as buildAccountToOUMap,jr as buildRegionList,nr as buildStepContextBuildConfig,O as buildTargetSetUnreadyAdvisory,_ as buildTargetUnreadyAdvisory,qr as cascadeHomeRegion,zr as cascadeOperationKey,M as checkIdentityCentreStatus,T as checkTargetReadiness,cr as convertCloudFormationOutputsToRecord,L as createAccount,ke as createEmptyState,kr as createSequencedCallbacks,ot as decideNextTransition,at as decommissionMemberTrailStorage,ye as deleteStateFile,Kr as deploy,Pe as deriveResourcesFromManifestStacks,m as describeOrganisation,p as describeTargetReadinessReason,Vr as destroy,bt as dockerBuilder,ar as emitProgress,h as enableCentralisedRootAccess,g as enableIpamDelegatedAdmin,d as enablePolicyTypes,N as enableRamSharing,P as enableServiceAccess,R as ensureOrganisationExists,U as ensureOrganisationalUnitsExist,Ct as extractAllAccountNames,F as extractErrorName,Yr as failure,vr as fileExists,fr as filterDangerousEnvVars,I as findAccount,Pt as flattenAccountsToEnvironments,je as formatInfrastructureError,ce as getApplicationDeployOrder,Se as getApplicationDestroyOrder,Ae as getApplicationStackName,ue as getApplicationStepId,Oe as getApplicationStepName,Kt as getDestroyStepId,le as getOrganisationStackName,Ee as getParallelDeployGroups,ne as getParallelDestroyGroups,qe as getSourceContext,Me as getStateFilePath,Qe as getStructuralHint,ze as hasCdkDifferences,Lr as hasDockerfile,gt as isAccountsConfig,ae as isApplicationOperation,Te as isApplicationStack,we as isCdkError,Br as isFailure,Y as isOULeaf,me as isOpenNextPattern,se as isOrganisationOperation,xt as isOuOnlyAccountBucket,pe as isQuarantineDetail,_e as isRetainedBucketsDetail,Ot as isRootTaskPolicyArn,ft as isStringArray,br as isSuccess,D as listAccounts,gr as maskSensitiveOutput,rt as mergeReconciledProviderAccounts,Yt as openNextBuilder,dt as parseAccountsConfiguration,Er as parseBuildPhase,Je as parseDiffOutput,xr as parseShellArgs,Xr as partitionAccounts,k as placeAccountsInOUs,Zr as projectAccountRows,Wr as projectScalarSummary,Le as readStateFile,et as reconcileProviderAccounts,it as reconcileTrailMigration,b as registerSecurityDelegates,Lt as runOpenNextBuild,kt as runOrganisationSetup,Dr as sleep,He as startStackMonitoring,Fe as stubCallerIdentity,Fr as success,Re as toPascalCase,Cr as toServiceError,St as unlockBucket,At as unlockQueue,x as updateBackupGlobalSettings,ve as updateTemplateHash,Tt as verifyOrgTrailDelivery,De as wrapApplicationError,Ue as writeStateFile};
@@ -4,6 +4,17 @@ import type { OUTree } from "../aws/organisations/types.js";
4
4
  export type AccountsConfig = {
5
5
  readonly [key: string]: string | readonly string[] | AccountsConfig;
6
6
  };
7
+ /**
8
+ * Scaffold buckets that are OU structure ONLY — accounts placed here get an
9
+ * organisational unit but no workload stage (ADR
10
+ * 2026-04-07-nested-ou-accounts-structure: environments resolve solely from
11
+ * the five stage leaf keys). Producers mapping leaf keys to wire
12
+ * `environment` values must decode these buckets to a null stage rather than
13
+ * forwarding the key verbatim — the org-config ingress enum rejects them.
14
+ */
15
+ export declare const OU_ONLY_ACCOUNT_BUCKETS: readonly ["security", "suspended"];
16
+ /** Type guard: checks whether an ACCOUNTS leaf key is an OU-only bucket. */
17
+ export declare function isOuOnlyAccountBucket(key: string): boolean;
7
18
  export declare function isStringArray(value: unknown): value is string[];
8
19
  export declare function isAccountsConfig(value: unknown): value is AccountsConfig;
9
20
  /**
@@ -1 +1 @@
1
- import{success as u,failure as a}from"@fjall/generator";import{maskSensitiveOutput as A,getErrorMessage as C}from"@fjall/util";import{ConfigError as f}from"../types/errors/ServiceError.js";const c="infrastructure.ts";function g(n){return Array.isArray(n)&&n.every(t=>typeof t=="string")}function p(n){return typeof n!="object"||n===null?!1:Object.entries(n).every(([t,r])=>typeof t=="string"&&(typeof r=="string"||g(r)||p(r)))}function O(n){const t=[];for(const[r,e]of Object.entries(n))if(typeof e=="string")t.push({accountName:e,environment:r});else if(Array.isArray(e))for(const o of e)t.push({accountName:o,environment:r});else typeof e=="object"&&e!==null&&t.push(...O(e));return t}function d(n){const t=[];for(const r of Object.values(n))typeof r=="string"?t.push(r):Array.isArray(r)?t.push(...r):typeof r=="object"&&r!==null&&t.push(...d(r));return t}function T(n){const t={};for(const[r,e]of Object.entries(n))typeof e=="string"?t[r]=[e]:Array.isArray(e)?t[r]=[...e]:typeof e=="object"&&e!==null&&(t[r]=T(e));return t}async function S(n){try{const t=await import("path"),r=await import("fs"),{pathToFileURL:e}=await import("url"),o=t.join(n,"fjall","organisation",c);if(!r.existsSync(o))return u(null);const{tsImport:l}=await import("tsx/esm/api"),y=e(o).href,s=await l(o,y),m=s.default,i=s.ACCOUNTS||m?.ACCOUNTS;return i?p(i)?u(i):a(new f("ACCOUNTS configuration has invalid structure","ACCOUNTS",o,{providedValue:i})):a(new f(`ACCOUNTS constant not found in organisation/${c}`,"ACCOUNTS",o,{moduleKeys:Object.keys(s),hasDefault:s.default!==void 0}))}catch(t){const r=A(C(t));return a(new f(`Failed to import ACCOUNTS from organisation/${c}: ${r}`,"ACCOUNTS",void 0,{originalError:t}))}}export{T as accountsConfigToOUTree,d as extractAllAccountNames,O as flattenAccountsToEnvironments,p as isAccountsConfig,g as isStringArray,S as parseAccountsConfiguration};
1
+ import{success as f,failure as c}from"@fjall/generator";import{maskSensitiveOutput as C,getErrorMessage as m}from"@fjall/util";import{ConfigError as u}from"../types/errors/ServiceError.js";const a="infrastructure.ts",O=["security","suspended"],g=new Set(O);function x(n){return g.has(n)}function T(n){return Array.isArray(n)&&n.every(t=>typeof t=="string")}function p(n){return typeof n!="object"||n===null?!1:Object.entries(n).every(([t,e])=>typeof t=="string"&&(typeof e=="string"||T(e)||p(e)))}function U(n){const t=[];for(const[e,r]of Object.entries(n))if(typeof r=="string")t.push({accountName:r,environment:e});else if(Array.isArray(r))for(const o of r)t.push({accountName:o,environment:e});else typeof r=="object"&&r!==null&&t.push(...U(r));return t}function N(n){const t=[];for(const e of Object.values(n))typeof e=="string"?t.push(e):Array.isArray(e)?t.push(...e):typeof e=="object"&&e!==null&&t.push(...N(e));return t}function d(n){const t={};for(const[e,r]of Object.entries(n))typeof r=="string"?t[e]=[r]:Array.isArray(r)?t[e]=[...r]:typeof r=="object"&&r!==null&&(t[e]=d(r));return t}async function E(n){try{const t=await import("path"),e=await import("fs"),{pathToFileURL:r}=await import("url"),o=t.join(n,"fjall","organisation",a);if(!e.existsSync(o))return f(null);const{tsImport:l}=await import("tsx/esm/api"),y=r(o).href,s=await l(o,y),A=s.default,i=s.ACCOUNTS||A?.ACCOUNTS;return i?p(i)?f(i):c(new u("ACCOUNTS configuration has invalid structure","ACCOUNTS",o,{providedValue:i})):c(new u(`ACCOUNTS constant not found in organisation/${a}`,"ACCOUNTS",o,{moduleKeys:Object.keys(s),hasDefault:s.default!==void 0}))}catch(t){const e=C(m(t));return c(new u(`Failed to import ACCOUNTS from organisation/${a}: ${e}`,"ACCOUNTS",void 0,{originalError:t}))}}export{O as OU_ONLY_ACCOUNT_BUCKETS,d as accountsConfigToOUTree,N as extractAllAccountNames,U as flattenAccountsToEnvironments,p as isAccountsConfig,x as isOuOnlyAccountBucket,T as isStringArray,E as parseAccountsConfiguration};
@@ -1 +1 @@
1
- import{join as H}from"path";import{success as M,failure as g}from"@fjall/generator";import{logger as O}from"@fjall/util/logger";import{maskSensitiveOutput as w,toPascalCase as K}from"@fjall/util";import{parseDockerServicesFromManifest as W}from"@fjall/util/manifest";import{stubCallerIdentity as $}from"../types/deployment/index.js";import{getApplicationDeployOrder as G,getApplicationStackName as q,getApplicationStepName as B,getApplicationStepId as U,APPLICATION_STACKS as _}from"../types/operations.js";import{CdkContextBuilder as z}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Y,bootstrapOrFail as J}from"./contextHelpers.js";import{runDetectionPipeline as Q}from"./detectionPipeline.js";import{STEP_IDS as x}from"../types/stepDefinitions.js";import{DOCKER_BUILD_STEP_NAME as j,runDockerBuild as V}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as X,deployParallelPhase as Z,deployStackSequential as ee,runDockerPreCompute as te,createBuildCallbacks as oe}from"./applicationDeployHelpers.js";import{deployCodeOnly as re}from"./codeOnlyDeploy.js";const N="applicationDeploy";function ne(s){const a=Object.entries(s);if(a.length===0)return;const o={};for(const[t,i]of a){const D=`${K(t)}ImageTag`;o[D]=i}return o}async function be(s,a,o){const{callbacks:t,options:i}=s,D=Date.now(),S=z.buildDeploymentContext({deployType:"application",target:o.appName,path:o.path,region:a.awsProvider.getRegion(),callerIdentity:$(a.awsProvider.getAccountId()),...Y({orgConfig:s.orgConfig,identity:s.identity,skipOidc:s.options?.skipOidc})},{verbose:i?.verbose,infraOnly:i?.infraOnly},s.orgConfig),u=a.frameworkRegistry.resolve({appPath:o.path});let C;const m=!!i?.imageTag;if(u&&!m){C=u.builder.plan({appPath:o.path},u.detection);const e=oe(t),r=await u.builder.build(o.path,C,e,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly});if(!r.success){const n=new Error(w(r.error.message));return t.onError?.(n),g(n)}}if(i?.deployOnly||m){t.onLog?.(m?`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=H(o.path,"cdk.out"),r=W(e),n=r.length>0,l=u?.detection.hasDockerfile===!0,p=n||l;O.debug(N,"Deploy-only branch entered",{isRollback:m,imageTag:i?.imageTag,appName:o.appName,appPath:o.path,cdkOutPath:e,dockerProviderAvailable:s.dockerProvider!==void 0,builderName:u?.builder.name,hasDockerfileFromManifest:n,hasDockerfileFromDisk:l,hasDockerfile:p,manifestDockerServiceCount:r.length,manifestDockerPaths:r.map(d=>d.docker.path)}),!n&&!l&&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");let h={};if(!m&&s.dockerProvider!==void 0&&p){O.debug(N,"Running Docker build before code-only deploy",{source:n?"manifest":"disk"});const d=await V(s,a,o,t);if(!d.success)return g(d.error);h=d.data}else O.debug(N,"Skipping Docker build",{reason:m?"rollback":s.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return re(s,a,o,h)}t.onLog?.("Analysing infrastructure\u2026","info");const E=await Q(o,a,S,t);if(!E.success){const e=new Error(w(E.error.message));return t.onError?.(e),g(e)}const c=E.data;try{await t.onDetectionComplete?.({...c,builderName:u?.builder.name??"unknown"})}catch(e){const r=e instanceof Error?e.message:String(e),n=new Error(w(r));return t.onError?.(n),g(n)}const f=C?C.deployOrder:G({pattern:c.pattern,resources:c.resources}),k=f.length;if(!c.hasDifferences&&!i?.force){t.onLog?.("No infrastructure changes detected","info");const e=c.hasDockerfile&&s.dockerProvider!==void 0&&f.includes(_.COMPUTE);for(let n=0;n<f.length;n++){const l=f[n];e&&l===_.COMPUTE&&(t.onStepStart?.(x.DOCKER_OPERATIONS,j),t.onStepComplete?.(x.DOCKER_OPERATIONS,j,"skipped"));const p=U(l,"deploy"),h=B(l,"deploy");t.onStepStart?.(p,h,n,k),t.onStepComplete?.(p,h,"skipped",n,k)}const r=await a.stackService.resolveWebsiteUrl(o.appName);return M({target:o.appName,deploymentType:"application",outputs:r?{websiteUrl:r}:void 0,noChanges:!0,durationMs:Date.now()-D})}const T=await J(a,S,t);if(!T.success)return T;const b={},P=new Map;for(let e=0;e<f.length;e++){const r=f[e],n=q(o.appName,r),l=U(r,"deploy"),p=B(r,"deploy");if(!(c.stackChanges.get(n)??!0)&&!i?.force){t.onStepStart?.(l,p,e,k),t.onLog?.(`Skipping ${r} \u2014 no changes detected`,"info"),t.onStepComplete?.(l,p,"skipped",e,k);continue}const d=X(f,e,c.stackChanges,o.appName,i?.force);if(d.length>=2){const L=await Z(d,o,a,S,t,e,k,c,b,P);if(!L.success)return g(L.error);e+=d.length-1;continue}const y=await te(r,s,a,o,t,c.hasDockerfile);if(y!==null&&!y.success)return g(y.error);const F=y!==null&&y.success?y.data.contentHashTagsByService:{},A=ne(F),I=await ee(r,a,S,t,e,k,b,A!==void 0?{parameters:A}:void 0);if(!I.success)return g(I.error);const v=c.currentHashes.get(n);v&&P.set(n,v)}if(P.size>0){const e=await a.hashService.updateStateAfterDeploy(o.path,P);if(!e.success){const r=w(e.error.message);O.debug(N,"Failed to update state file",{error:r}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${r}`,"warn")}}const R=await a.stackService.resolveWebsiteUrl(o.appName);return R&&(b.websiteUrl=R),M({target:o.appName,deploymentType:"application",outputs:Object.keys(b).length>0?b:void 0,durationMs:Date.now()-D})}export{be as deployApplication};
1
+ import{join as $}from"path";import{CloudFormationClient as q}from"@aws-sdk/client-cloudformation";import{EC2Client as z}from"@aws-sdk/client-ec2";import{success as U,failure as m}from"@fjall/generator";import{logger as E}from"@fjall/util/logger";import{maskSensitiveOutput as S,toPascalCase as Y}from"@fjall/util";import{parseDockerServicesFromManifest as J}from"@fjall/util/manifest";import{stubCallerIdentity as Q}from"../types/deployment/index.js";import{getApplicationDeployOrder as V,getApplicationStackName as X,getApplicationStepName as F,getApplicationStepId as H,APPLICATION_STACKS as j}from"../types/operations.js";import{CdkContextBuilder as Z}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as ee,bootstrapOrFail as te}from"./contextHelpers.js";import{runDetectionPipeline as oe}from"./detectionPipeline.js";import{STEP_IDS as O,STEP_NAMES as re}from"../types/stepDefinitions.js";import{StepRegistry as ne}from"../steps/stepRegistry.js";import{checkTargetReadiness as ae}from"../aws/targetReadiness.js";import{cascadeHomeRegion as ie}from"./cascadeHelpers.js";import{withStepLifecycle as se}from"./stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as G,runDockerBuild as ce}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as de,deployParallelPhase as le,deployStackSequential as pe,runDockerPreCompute as ue,createBuildCallbacks as fe}from"./applicationDeployHelpers.js";import{deployCodeOnly as ge}from"./codeOnlyDeploy.js";const R="applicationDeploy";function me(i){const a=Object.entries(i);if(a.length===0)return;const r={};for(const[t,s]of a){const b=`${Y(t)}ImageTag`;r[b]=s}return r}async function Me(i,a,r){const{callbacks:t,options:s}=i,b=Date.now(),D=Z.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:a.awsProvider.getRegion(),callerIdentity:Q(a.awsProvider.getAccountId()),...ee({orgConfig:i.orgConfig,identity:i.identity,skipOidc:i.options?.skipOidc})},{verbose:s?.verbose,infraOnly:s?.infraOnly},i.orgConfig),p=a.frameworkRegistry.resolve({appPath:r.path});let w;const k=!!s?.imageTag;if(p&&!k){w=p.builder.plan({appPath:r.path},p.detection);const e=fe(t),o=await p.builder.build(r.path,w,e,{skipBuild:s?.skipBuild,infraOnly:s?.infraOnly});if(!o.success){const n=new Error(S(o.error.message));return t.onError?.(n),m(n)}}if(s?.deployOnly||k){t.onLog?.(k?`Rollback mode \u2014 rolling to image tag ${s?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=$(r.path,"cdk.out"),o=J(e),n=o.length>0,l=p?.detection.hasDockerfile===!0,d=n||l;E.debug(R,"Deploy-only branch entered",{isRollback:k,imageTag:s?.imageTag,appName:r.appName,appPath:r.path,cdkOutPath:e,dockerProviderAvailable:i.dockerProvider!==void 0,builderName:p?.builder.name,hasDockerfileFromManifest:n,hasDockerfileFromDisk:l,hasDockerfile:d,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(u=>u.docker.path)}),!n&&!l&&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");let f={};if(!k&&i.dockerProvider!==void 0&&d){E.debug(R,"Running Docker build before code-only deploy",{source:n?"manifest":"disk"});const u=await ce(i,a,r,t);if(!u.success)return m(u.error);f=u.data}else E.debug(R,"Skipping Docker build",{reason:k?"rollback":i.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return ge(i,a,r,f)}t.onLog?.("Analysing infrastructure\u2026","info");const T=await oe(r,a,D,t);if(!T.success){const e=new Error(S(T.error.message));return t.onError?.(e),m(e)}const c=T.data;try{await t.onDetectionComplete?.({...c,builderName:p?.builder.name??"unknown"})}catch(e){const o=e instanceof Error?e.message:String(e),n=new Error(S(o));return t.onError?.(n),m(n)}const K={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:s?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...p&&{builderName:p.builder.name}},N=ne.getSteps(K),A=N.findIndex(e=>e.id===O.TARGET_READINESS),I=await se(t,{stepId:O.TARGET_READINESS,stepName:re.TARGET_READINESS,...A>=0&&{stepIndex:A,totalSteps:N.length}},async()=>{if(s?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const e=a.awsProvider.getAccountId(),o=i.orgConfig?.providerAccounts.find(f=>f.id===e)?.name,n=a.awsProvider.getCredentials(),l=ie(i.orgConfig),d=await ae({cloudFormation:a.awsProvider.getClient(q),ec2:new z({region:l,...n!==void 0&&{credentials:n}})},{id:e,...o!==void 0&&{name:o}},a.awsProvider.getRegion(),i.orgConfig,i.abortSignal);return d.success?d.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(S(d.data.advisory))}:{kind:"error",error:new Error(S(d.error.message))}});if(!I.success)return I;const g=w?w.deployOrder:V({pattern:c.pattern,resources:c.resources}),h=g.length;if(!c.hasDifferences&&!s?.force){t.onLog?.("No infrastructure changes detected","info");const e=c.hasDockerfile&&i.dockerProvider!==void 0&&g.includes(j.COMPUTE);for(let n=0;n<g.length;n++){const l=g[n];e&&l===j.COMPUTE&&(t.onStepStart?.(O.DOCKER_OPERATIONS,G),t.onStepComplete?.(O.DOCKER_OPERATIONS,G,"skipped"));const d=H(l,"deploy"),f=F(l,"deploy");t.onStepStart?.(d,f,n,h),t.onStepComplete?.(d,f,"skipped",n,h)}const o=await a.stackService.resolveWebsiteUrl(r.appName);return U({target:r.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,durationMs:Date.now()-b})}const v=await te(a,D,t);if(!v.success)return v;const C={},P=new Map;for(let e=0;e<g.length;e++){const o=g[e],n=X(r.appName,o),l=H(o,"deploy"),d=F(o,"deploy");if(!(c.stackChanges.get(n)??!0)&&!s?.force){t.onStepStart?.(l,d,e,h),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(l,d,"skipped",e,h);continue}const u=de(g,e,c.stackChanges,r.appName,s?.force);if(u.length>=2){const B=await le(u,r,a,D,t,e,h,c,C,P);if(!B.success)return m(B.error);e+=u.length-1;continue}const y=await ue(o,i,a,r,t,c.hasDockerfile);if(y!==null&&!y.success)return m(y.error);const W=y!==null&&y.success?y.data.contentHashTagsByService:{},L=me(W),x=await pe(o,a,D,t,e,h,C,L!==void 0?{parameters:L}:void 0);if(!x.success)return m(x.error);const M=c.currentHashes.get(n);M&&P.set(n,M)}if(P.size>0){const e=await a.hashService.updateStateAfterDeploy(r.path,P);if(!e.success){const o=S(e.error.message);E.debug(R,"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 _=await a.stackService.resolveWebsiteUrl(r.appName);return _&&(C.websiteUrl=_),U({target:r.appName,deploymentType:"application",outputs:Object.keys(C).length>0?C:void 0,durationMs:Date.now()-b})}export{Me as deployApplication};
@@ -1 +1 @@
1
- import{success as w,failure as b}from"@fjall/generator";import{getErrorMessage as y}from"@fjall/util";import{logger as h}from"@fjall/util/logger";import{getApplicationDestroyOrder as N,getApplicationStepName as S,getApplicationStepId as P}from"../types/operations.js";import{stubCallerIdentity as x}from"../types/deployment/index.js";import{deriveResourcesFromManifestStacks as D}from"../types/patternDetection.js";import{CdkContextBuilder as R}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as I}from"./contextHelpers.js";import{deleteStateFile as O,readStateFile as A}from"../types/FjallState.js";async function q(s,c,r){const{callbacks:o}=s,C=Date.now(),f=c.frameworkRegistry.resolve({appPath:r.path})?.detection.pattern??null;let p;try{const e=await A(r.path);if(e!==null){const t=Object.keys(e.templateHashes);t.length>0&&(p=D(t))}}catch(e){h.debug("applicationDestroy","Could not read state file for resource detection",{error:y(e)}),o.onLog?.("Could not read state file for resource detection \u2014 falling back to pattern detection","warn")}const k=R.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:c.awsProvider.getRegion(),callerIdentity:x(c.awsProvider.getAccountId()),...I({orgConfig:s.orgConfig,identity:s.identity})},{verbose:s.options?.verbose},s.orgConfig),d=N({pattern:f,resources:p}),n=d.length;o.onLog?.(`Destroying ${r.appName} (${n} stacks, ${f??"standard"} pattern)`,"info");const g=[],u=[],m=await c.stackService.destroyAllStacks(k,{onOutput:e=>{o.onOutput?.(e)},onResourceProgress:(e,t)=>{o.onResourceProgress?.(e),t&&o.onParallelStackResourceProgress?.(t,e)},onStackStart:(e,t)=>{const a=P(e,"destroy"),l=S(e,"destroy"),i=d.indexOf(e);o.onStepStart?.(a,l,i,n)},onStackComplete:async(e,t)=>{const a=P(e,"destroy"),l=S(e,"destroy"),i=d.indexOf(e);t.success?t.data?.skipped?(u.push(t.data.stackName||e),o.onStepComplete?.(a,l,"skipped",i,n)):(g.push(t.data?.stackName||e),o.onStepComplete?.(a,l,"completed",i,n)):(o.onStepComplete?.(a,l,"error",i,n),o.onError?.(t.error))},onParallelPhaseStart:(e,t)=>{o.onLog?.(`Parallel phase: ${t}`,"info"),o.onParallelPhaseStart?.(e,t)},onParallelPhaseComplete:e=>{const t=e.filter(a=>!a.success);t.length>0&&o.onLog?.(`Parallel phase completed with ${t.length} failure(s)`,"warn"),o.onParallelPhaseComplete?.(e)}},p);if(!m.success)return o.onError?.(m.error),b(m.error);try{await O(r.path)}catch(e){h.debug("applicationDestroy","Failed to delete state file (non-critical)",{error:y(e)}),o.onLog?.("Failed to delete state file (non-critical)","warn")}return w({target:r.appName,deploymentType:"application",stacksDestroyed:g,skippedStacks:u,durationMs:Date.now()-C})}export{q as destroyApplication};
1
+ import{success as w,failure as b}from"@fjall/generator";import{getErrorMessage as y}from"@fjall/util";import{logger as S}from"@fjall/util/logger";import{getApplicationDestroyOrder as N,getApplicationStepName as h,getApplicationStepId as P}from"../types/operations.js";import{stubCallerIdentity as x}from"../types/deployment/index.js";import{deriveResourcesFromManifestStacks as D}from"../types/patternDetection.js";import{CdkContextBuilder as R}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as I}from"./contextHelpers.js";import{deleteStateFile as L,readStateFile as O}from"../types/FjallState.js";async function q(n,c,r){const{callbacks:t}=n,C=Date.now(),m=c.frameworkRegistry.resolve({appPath:r.path})?.detection.pattern??null;let p;try{const e=await O(r.path);if(e!==null){const o=Object.keys(e.templateHashes);o.length>0&&(p=D(o))}}catch(e){S.debug("applicationDestroy","Could not read state file for resource detection",{error:y(e)}),t.onLog?.("Could not read state file for resource detection \u2014 falling back to pattern detection","warn")}const k=R.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:c.awsProvider.getRegion(),callerIdentity:x(c.awsProvider.getAccountId()),...I({orgConfig:n.orgConfig,identity:n.identity})},{verbose:n.options?.verbose},n.orgConfig),d=N({pattern:m,resources:p}),s=d.length;t.onLog?.(`Destroying ${r.appName} (${s} stacks, ${m??"standard"} pattern)`,"info");const f=[],u=[],g=await c.stackService.destroyAllStacks(k,{onOutput:e=>{t.onOutput?.(e)},onLog:t.onLog,onStackCleanupProgress:t.onStackCleanupProgress,onResourceProgress:(e,o)=>{t.onResourceProgress?.(e),o&&t.onParallelStackResourceProgress?.(o,e)},onStackStart:(e,o)=>{const a=P(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);t.onStepStart?.(a,l,i,s)},onStackComplete:async(e,o)=>{const a=P(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);o.success?o.data?.skipped?(u.push(o.data.stackName||e),t.onStepComplete?.(a,l,"skipped",i,s)):(f.push(o.data?.stackName||e),t.onStepComplete?.(a,l,"completed",i,s)):(t.onStepComplete?.(a,l,"error",i,s),t.onError?.(o.error))},onParallelPhaseStart:(e,o)=>{t.onLog?.(`Parallel phase: ${o}`,"info"),t.onParallelPhaseStart?.(e,o)},onParallelPhaseComplete:e=>{const o=e.filter(a=>!a.success);o.length>0&&t.onLog?.(`Parallel phase completed with ${o.length} failure(s)`,"warn"),t.onParallelPhaseComplete?.(e)}},p,n.abortSignal);if(!g.success)return t.onError?.(g.error),b(g.error);try{await L(r.path)}catch(e){S.debug("applicationDestroy","Failed to delete state file (non-critical)",{error:y(e)}),t.onLog?.("Failed to delete state file (non-critical)","warn")}return w({target:r.appName,deploymentType:"application",stacksDestroyed:f,skippedStacks:u,durationMs:Date.now()-C})}export{q as destroyApplication};
@@ -17,6 +17,17 @@ export interface CascadeDestroyAccountResult {
17
17
  error?: string;
18
18
  skipped?: boolean;
19
19
  }
20
+ export interface DestroyCascadeAccountOptions {
21
+ /**
22
+ * Resolved primary (home) region of the cascade. buildParamsContext derives
23
+ * the account-globals skip from it, mirroring deployCascadeAccount: a
24
+ * non-home (secondary/DR) region destroy synthesises WITHOUT the fixed-name
25
+ * org-global IAM resources, matching the deployed stack shape so the destroy
26
+ * can never identify the primary's globals for teardown. Omitted ⇒ the
27
+ * globals stay in the synthesised template (home-region semantics).
28
+ */
29
+ primaryRegion?: string;
30
+ }
20
31
  /**
21
32
  * Destroy a single cascade account (platform or member) in a specific region.
22
33
  *
@@ -27,4 +38,4 @@ export declare function destroyCascadeAccount(params: DestroyParams, services: D
27
38
  id: string;
28
39
  name: string;
29
40
  environment: string | null;
30
- }, deployType: "platform" | "account", region: string, callbacks: DeployCallbacks): Promise<CascadeDestroyAccountResult>;
41
+ }, deployType: "platform" | "account", region: string, callbacks: DeployCallbacks, options?: DestroyCascadeAccountOptions): Promise<CascadeDestroyAccountResult>;
@@ -1 +1 @@
1
- import{logger as f}from"@fjall/util/logger";import{maskSensitiveOutput as s,getErrorMessage as y}from"@fjall/util";import{stubCallerIdentity as E}from"../types/deployment/index.js";import{CloudFormationClient as I,DescribeStacksCommand as R}from"@aws-sdk/client-cloudformation";import{BackupClient as $}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as g,describeSurvivingBackupVault as O,formatSurvivingVaultWarning as _}from"../aws/organisations/backup.js";import{NodeHttpHandler as B}from"@smithy/node-http-handler";import{ORGANISATION_TYPES as k,getOrganisationStackName as F}from"../types/operations.js";import{CdkContextBuilder as x}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as L,assumeCascadeRole as M,forwardOutput as T}from"./contextHelpers.js";import{cleanupFailedStack as H}from"./stackCleanup.js";import{STACK_NOT_FOUND_PATTERN as K,STACK_FAILED_STATE_PATTERN as V}from"../types/constants.js";async function re(n,o,i,e,c,t,r){const d=Date.now(),a=`${e.name} (${t})`;r.onCascadeAccountStart?.(a,e.id,t,c);const l=await M(o.awsProvider,e.id,t,`fjall-cascade-destroy-${e.name}`);if(!l.success)return r.onCascadeAccountComplete?.(a,!1,s(l.error.message),t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-d,error:`AssumeRole failed: ${s(l.error.message)}`};const{provider:w,credentials:p}=l.data,S=x.buildDeploymentContext({deployType:c,target:i.target,path:i.path,region:t,accountName:e.name,callerIdentity:E(e.id),...L({orgConfig:n.orgConfig,identity:n.identity})},{verbose:n.options?.verbose},n.orgConfig);r.onCascadeAccountPhaseChange?.(a,"synth",t);const A=await o.cdkService.runCdkSynth(S,T(r));if(!A.success){const u=s(`Synth failed: ${A.error}`);return r.onCascadeAccountComplete?.(a,!1,u,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-d,error:u}}r.onCascadeAccountPhaseChange?.(a,"destroy",t);const m=F(c==="platform"?k.PLATFORM:k.ACCOUNT);g(e.environment,n.orgConfig?.disasterRecoveryRegion)&&await q(w.getClient($),t,r);const D=await o.cdkService.runCdkDestroy(S,m,T(r),u=>r.onCascadeAccountResourceProgress?.(a,u,t),w,!0,p);if(!D.success){const u=D.error;if(u.includes(V)){f.warn("cascadeDestroy",`CDK destroy failed on ${m} in failed state, retrying via CloudFormation API`,{region:t,account:e.name});try{await H(m,t,p,void 0,r)}catch(C){const h=`cleanupFailedStack threw for ${m}: ${s(y(C))}`;f.warn("cascadeDestroy",h),r.onLog?.(h,"warn")}const v=await U(m,t,p);if(v.deleted)return r.onCascadeAccountComplete?.(a,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-d};if(v.error){const C=s(v.error);return r.onCascadeAccountComplete?.(a,!1,C,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-d,error:C}}const P=s(`Stack ${m} cleanup attempted but stack still exists in ${t}`);return r.onCascadeAccountComplete?.(a,!1,P,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-d,error:P}}const N=s(u);return r.onCascadeAccountComplete?.(a,!1,N,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-d,error:N}}return r.onCascadeAccountComplete?.(a,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-d}}async function q(n,o,i){try{const e=await O(n,o);if(!e.success){f.debug("cascadeDestroy","Backup-vault survival probe failed",{region:o,error:s(e.error.message)});return}if(e.data===null)return;i.onProgress?.({type:"warning",message:_(e.data),metadata:{source:"backup-vault-survival"}}),f.warn("cascadeDestroy","Backup vault survives destroy",{region:o,vaultName:e.data.vaultName,recoveryPointCount:e.data.recoveryPointCount,lockPermanent:e.data.lockPermanent})}catch(e){f.debug("cascadeDestroy","Backup-vault survival probe threw",{region:o,error:s(y(e))})}}async function U(n,o,i){try{const t=(await new I({region:o,credentials:i,requestHandler:new B({requestTimeout:15e3})}).send(new R({StackName:n}))).Stacks?.[0]?.StackStatus;return!t||t==="DELETE_COMPLETE"?{deleted:!0}:{deleted:!1,error:`Stack still in ${t} after cleanup attempt`}}catch(e){if(e instanceof Error&&e.message?.includes(K))return{deleted:!0};const c=s(y(e));return f.debug("cascadeDestroy","Stack verification failed",{error:c}),{deleted:!1,error:`Stack verification failed: ${c}`}}}export{re as destroyCascadeAccount};
1
+ import{join as I}from"path";import{logger as f}from"@fjall/util/logger";import{maskSensitiveOutput as i,getErrorMessage as v}from"@fjall/util";import{stubCallerIdentity as b}from"../types/deployment/index.js";import{CloudFormationClient as P,DescribeStacksCommand as _}from"@aws-sdk/client-cloudformation";import{S3Client as g}from"@aws-sdk/client-s3";import{BackupClient as x}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as F,describeSurvivingBackupVault as M,formatSurvivingVaultWarning as K}from"../aws/organisations/backup.js";import{composeSdkAbortSignal as L}from"../aws/organisations/types.js";import{ORGANISATION_TYPES as R,getOrganisationStackName as V}from"../types/operations.js";import{CdkContextBuilder as j}from"../services/supporting/CdkContextBuilder.js";import{regionSuffix as U}from"../types/FjallState.js";import{buildParamsContext as G,assumeCascadeRole as H,forwardOutput as E}from"./contextHelpers.js";import{cascadeOperationKey as W}from"./cascadeHelpers.js";import{capturedSweepBuckets as Y,cleanupFailedStack as q,preEmptyStackBuckets as z,sweepOrphanedDestroyBuckets as N}from"./stackCleanup.js";import{STACK_NOT_FOUND_PATTERN as J,STACK_FAILED_STATE_PATTERN as Q}from"../types/constants.js";async function ye(a,o,c,e,n,t,r,T){const y=Date.now(),u=W(e.name,t),l=(s,p=s)=>(r.onCascadeAccountComplete?.(u,!1,p,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-y,error:s});r.onCascadeAccountStart?.(u,e.id,t,n);const S=await H(o.awsProvider,e.id,t,`fjall-cascade-destroy-${e.name}`);if(!S.success){const s=i(S.error.message);return l(`AssumeRole failed: ${s}`,s)}const{provider:d,credentials:w}=S.data,O=I(c.path,`cdk.out.${e.id}.${U(t)}`),k=j.buildDeploymentContext({deployType:n,target:c.target,path:c.path,assemblyDir:O,region:t,accountName:e.name,callerIdentity:b(e.id),...G({orgConfig:a.orgConfig,identity:a.identity,region:t,primaryRegion:T?.primaryRegion})},{verbose:a.options?.verbose},a.orgConfig);r.onCascadeAccountPhaseChange?.(u,"synth",t);const A=await o.cdkService.runCdkSynth(k,E(r));if(!A.success)return l(i(`Synth failed: ${A.error}`));r.onCascadeAccountPhaseChange?.(u,"destroy",t);const m=V(n==="platform"?R.PLATFORM:R.ACCOUNT);F(e.environment,a.orgConfig?.disasterRecoveryRegion)&&await X(d.getClient(x),t,r);const $=await z(d.getClient(P),d.getClient(g),m,r,a.abortSignal),C=Y($),D=await o.cdkService.runCdkDestroy(k,m,E(r),s=>r.onCascadeAccountResourceProgress?.(u,s,t),d,!0,w);if(!D.success){const s=D.error;if(s.includes(Q)){f.warn("cascadeDestroy",`CDK destroy failed on ${m} in failed state, retrying via CloudFormation API`,{region:t,account:e.name});try{await q(m,t,w,{accountId:e.id,abortSignal:a.abortSignal},r)}catch(B){const h=`cleanupFailedStack threw for ${m}: ${i(v(B))}`;f.warn("cascadeDestroy",h),r.onLog?.(h,"warn")}const p=await Z(m,d.getClient(P),a.abortSignal);return p.deleted?(C.length>0&&await N(d.getClient(g),C,r,a.abortSignal),r.onCascadeAccountComplete?.(u,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-y}):p.error?l(i(p.error)):l(i(`Stack ${m} cleanup attempted but stack still exists in ${t}`))}return l(i(s))}return C.length>0&&await N(d.getClient(g),C,r,a.abortSignal),r.onCascadeAccountComplete?.(u,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-y}}async function X(a,o,c){try{const e=await M(a,o);if(!e.success){f.debug("cascadeDestroy","Backup-vault survival probe failed",{region:o,error:i(e.error.message)});return}if(e.data===null)return;c.onProgress?.({type:"warning",message:K(e.data),metadata:{source:"backup-vault-survival"}}),f.warn("cascadeDestroy","Backup vault survives destroy",{region:o,vaultName:e.data.vaultName,recoveryPointCount:e.data.recoveryPointCount,lockPermanent:e.data.lockPermanent})}catch(e){f.debug("cascadeDestroy","Backup-vault survival probe threw",{region:o,error:i(v(e))})}}async function Z(a,o,c){try{const n=(await o.send(new _({StackName:a}),{abortSignal:L(c)})).Stacks?.[0]?.StackStatus;return!n||n==="DELETE_COMPLETE"?{deleted:!0}:{deleted:!1,error:`Stack still in ${n} after cleanup attempt`}}catch(e){if(e instanceof Error&&e.message?.includes(J))return{deleted:!0};const n=i(v(e));return f.debug("cascadeDestroy","Stack verification failed",{error:n}),{deleted:!1,error:`Stack verification failed: ${n}`}}}export{ye as destroyCascadeAccount};
@@ -34,11 +34,41 @@ export interface AccountRegionPair {
34
34
  * on this set, else destroy sweeps regions deploy never created (or vice versa).
35
35
  */
36
36
  export declare function buildRegionList(orgConfig: OrgConfig | undefined): string[];
37
+ /**
38
+ * The cascade's home (anchor) region — first entry of buildRegionList, i.e.
39
+ * the configured primary (or DEFAULT_REGION when none is set). Deploy and
40
+ * destroy fan-outs MUST share this derivation so a destroy synth mirrors the
41
+ * account-globals decision of the deploy that created each region's stack.
42
+ */
43
+ export declare function cascadeHomeRegion(orgConfig: OrgConfig | undefined): string;
44
+ /**
45
+ * Operation key identifying one account x region task across the cascade
46
+ * lifecycle. Deploy events, destroy events, and the Ink pre-seeded pills
47
+ * (CLI `stackOperationUtils.fetchCascadeAccounts`) MUST share this shape so
48
+ * every pill matches its engine events one-for-one.
49
+ */
50
+ export declare function cascadeOperationKey(accountName: string, region: string): string;
37
51
  /**
38
52
  * Build all account x region pairs for parallel cascade fan-out.
39
53
  */
40
54
  export declare function buildAccountRegionPairs(accounts: ProviderAccount[], regions: string[]): AccountRegionPair[];
41
55
  export { buildCascadeRoleArn } from "./contextHelpers.js";
56
+ /** A single pre-flight probe failure: the cascade role could not be assumed. */
57
+ export interface CascadeRoleProbeFailure {
58
+ accountId: string;
59
+ accountName: string;
60
+ error: string;
61
+ }
62
+ /**
63
+ * Pre-flight check that OrganizationAccountAccessRole is assumable in every
64
+ * cascade target, run BEFORE any synth/bootstrap/deploy work. An imported
65
+ * (invite-joined) member never gets the role auto-created, so without this
66
+ * probe a broken role only surfaces mid-cascade — after the org root has
67
+ * already deployed — one account at a time. Read-only: the assumed
68
+ * credentials are discarded. Inherits assumeCascadeRole's retry budget, so
69
+ * role propagation on freshly created accounts does not false-fail.
70
+ */
71
+ export declare function probeCascadeRoles(services: DeployServices, accounts: ProviderAccount[], callbacks: DeployCallbacks, signal?: AbortSignal): Promise<CascadeRoleProbeFailure[]>;
42
72
  /**
43
73
  * Deploy a single cascade account (platform or member).
44
74
  * Assumes the target account's role, sets env credentials, and deploys.
@@ -62,11 +92,12 @@ export interface DeployCascadeAccountOptions {
62
92
  /** Region to deploy in; falls back to account.region then the provider region. */
63
93
  region?: string;
64
94
  /**
65
- * True for non-home (secondary/DR) cascade regions the synthesised account
66
- * stack then skips the fixed-name org-global IAM resources that would collide
67
- * across regions. Unset/false for the home (primary) region.
95
+ * Resolved primary (home) region of the cascade. buildParamsContext derives
96
+ * the account-globals skip from it: non-home (secondary/DR) regions skip the
97
+ * fixed-name org-global IAM resources that would collide across regions.
98
+ * Omitted ⇒ globals are never skipped.
68
99
  */
69
- skipAccountGlobals?: boolean;
100
+ primaryRegion?: string;
70
101
  }
71
102
  export declare function deployCascadeAccount(params: DeployParams, services: DeployServices, operation: OrganisationOperation, account: ProviderAccount, deployType: "platform" | "account", callbacks: DeployCallbacks, options?: DeployCascadeAccountOptions): Promise<Result<CascadeAccountResult>>;
72
103
  /**
@@ -78,7 +109,7 @@ export declare function deployCascadeAccount(params: DeployParams, services: Dep
78
109
  export declare function readPlatformIpamPoolIds(services: DeployServices, platformAccount: {
79
110
  id: string;
80
111
  name: string;
81
- }, callbacks: DeployCallbacks): Promise<Map<string, string>>;
112
+ }, callbacks: DeployCallbacks, signal?: AbortSignal): Promise<Map<string, string>>;
82
113
  /**
83
114
  * Deploy configured domains: apex domains sequentially, then delegated
84
115
  * domains in parallel. Delegates to the caller-provided DomainDeployProvider.
@@ -1 +1 @@
1
- import{join as N}from"path";import{success as B,failure as C}from"@fjall/generator";import{logger as R}from"@fjall/util/logger";import{maskSensitiveOutput as m}from"@fjall/util";import{ORGANISATION_TYPES as h,getOrganisationStackName as L}from"../types/operations.js";import{CdkContextBuilder as U}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as V}from"../types/deployment/index.js";import{CloudFormationService as M}from"../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as _}from"../types/FjallState.js";import{BackupClient as z}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as K,describeBackupVaultExists as Y}from"../aws/organisations/backup.js";import{buildParamsContext as X,collectStackOutputs as T,assumeCascadeRole as b,forwardOutput as k}from"./contextHelpers.js";import{accountTier as j}from"@fjall/util";import{DEFAULT_REGION as q}from"../aws/utils/regions.js";const de=4;function pe(r){const n=r.find(e=>j(e)==="platform"),i=r.filter(e=>j(e)==="account");return{platformAccount:n,memberAccounts:i}}function me(r){const n=r?.primaryRegion??q,i=r?.secondaryRegions??[],e=[n,...i],a=r?.disasterRecoveryRegion;return a&&!e.includes(a)&&e.push(a),e}function fe(r,n){const i=[];for(const e of n)for(const a of r)i.push({account:a,region:e});return i}import{buildCascadeRoleArn as ye}from"./contextHelpers.js";async function le(r,n,i,e,a,s,u){const t=u?.region??e.region??n.awsProvider.getRegion(),o=`${e.name} (${t})`,c=u?.orgConfig??r.orgConfig,f=u?.ipamPoolId;s.onCascadeAccountStart?.(o,e.id,t,a);const d=await b(n.awsProvider,e.id,t,`fjall-cascade-${e.name}`);if(!d.success)return s.onCascadeAccountComplete?.(o,!1,m(d.error.message),t),C(new Error(`Failed to assume role for ${e.name}: ${m(d.error.message)}`));const{provider:g,credentials:A}=d.data,P=N(r.workingDirectory,"fjall",a==="platform"?h.PLATFORM:h.ACCOUNT),H=t.replace(/-/g,""),D=N(P,`cdk.out.${e.id}.${H}`),y=U.buildDeploymentContext({deployType:a,target:i.target,path:P,assemblyDir:D,environment:e.environment??void 0,region:t,accountName:e.name,callerIdentity:V(e.id),ipamPoolId:f,...X({orgConfig:c,identity:r.identity,skipOidc:r.options?.skipOidc,skipAccountGlobals:u?.skipAccountGlobals})},{verbose:r.options?.verbose},c);if(K(e.environment,c?.disasterRecoveryRegion)){const p=await Y(g.getClient(z));if(!p.success)return s.onCascadeAccountComplete?.(o,!1,m(p.error.message),t),C(new Error(`Backup vault probe failed for ${e.name}: ${m(p.error.message)}`));y.fjallAdoptBackupVault=p.data}s.onCascadeAccountPhaseChange?.(o,"synth",t);const w=await n.cdkService.runCdkSynth(y,k(s),A);if(!w.success)return s.onCascadeAccountComplete?.(o,!1,m(`Synth failed: ${w.error}`),t),C(new Error(`Synth failed for ${e.name}: ${m(w.error)}`));const l=L(a==="platform"?h.PLATFORM:h.ACCOUNT),v=_(P,e.id,t),$=new M(g),{changed:G,currentHash:x}=await n.hashService.compareCascadeStack(D,l,v);if(!G&&r.options?.force!==!0&&await $.stackExists(l)){const p=await $.getStackOutputs(l);p.success||R.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:l,account:e.name});const F=T(p);return s.onLog?.(`${e.name}: no infrastructure changes \u2014 skipping deploy`,"info"),s.onCascadeAccountComplete?.(o,!0,void 0,t,F,!0),B({outputs:F,skipped:!0})}s.onCascadeAccountPhaseChange?.(o,"bootstrap",t);const S=await n.cdkService.runCdkBootstrap(y,k(s),A);if(!S.success)return s.onCascadeAccountComplete?.(o,!1,m(`Bootstrap failed: ${S.error}`),t),C(new Error(`Bootstrap failed for ${e.name}: ${m(S.error)}`));s.onCascadeAccountPhaseChange?.(o,"deploy",t);const O=await n.cdkService.runCdkDeploy(y,l,k(s),p=>s.onCascadeAccountResourceProgress?.(o,p,t),g,A);if(!O.success)return s.onCascadeAccountComplete?.(o,!1,m(O.error),t),C(new Error(m(O.error)));const I=await $.getStackOutputs(l);I.success||R.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:l,account:e.name});const E=T(I);return x!==void 0&&((await n.hashService.persistCascadeStack(v,l,x)).success||R.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:l,account:e.name})),s.onCascadeAccountComplete?.(o,!0,void 0,t,E,!1),B({outputs:E,skipped:!1})}async function ge(r,n,i){const e=new Map,a=r.awsProvider.getRegion(),s=await b(r.awsProvider,n.id,a,`fjall-ipam-read-${n.name}`);if(!s.success)return R.debug("organisationDeploy",`Cannot read Platform outputs: ${s.error.message}`),e;const u=new M(s.data.provider),t=L(h.PLATFORM),o=await u.getStackOutputs(t);if(!o.success)return R.debug("organisationDeploy",`Failed to read Platform stack outputs: ${o.error.message}`),e;const c=/^IpamPoolId(\d{12})(\w+)$/;for(const f of o.data){const d=f.OutputKey?.match(c);if(d&&f.OutputValue){const g=`${d[1]}-${d[2]}`;e.set(g,f.OutputValue)}}return e.size>0&&i.onLog?.(`Read ${e.size} IPAM pool ID(s) from Platform stack`,"info"),e}async function Ce(r,n){const i=r.getDomains();if(i.length===0)return{domainsDeployed:0,errors:[]};n.onCascadePhaseStart?.("domains");const e=i.filter(t=>t.type==="apex"),a=i.filter(t=>t.type==="delegated");let s=0;const u=[];for(const t of e){const o=await r.deployDomain(t.name,n);o.success?s++:u.push(`${t.name}: ${o.error.message}`)}if(a.length>0){const t=await Promise.allSettled(a.map(o=>r.deployDomain(o.name,n)));for(let o=0;o<t.length;o++){const c=t[o],f=a[o];if(!(!c||!f))if(c.status==="fulfilled")c.value.success?s++:u.push(`${f.name}: ${c.value.error.message}`);else{const d=c.reason instanceof Error?c.reason.message:String(c.reason);u.push(`${f.name}: ${d}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:s,errors:u}}export{de as CASCADE_MAX_CONCURRENCY,fe as buildAccountRegionPairs,ye as buildCascadeRoleArn,me as buildRegionList,le as deployCascadeAccount,Ce as deployDomains,pe as partitionAccounts,ge as readPlatformIpamPoolIds};
1
+ import{join as L}from"path";import{success as B,failure as h}from"@fjall/generator";import{logger as y}from"@fjall/util/logger";import{maskSensitiveOutput as p,mapSettledWithConcurrency as U}from"@fjall/util";import{ORGANISATION_TYPES as R,getOrganisationStackName as j}from"../types/operations.js";import{CdkContextBuilder as _}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as K}from"../types/deployment/index.js";import{CloudFormationService as H}from"../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as z,regionSuffix as G}from"../types/FjallState.js";import{BackupClient as Y}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as W,describeBackupVaultExists as X}from"../aws/organisations/backup.js";import{buildParamsContext as q,collectStackOutputs as M,assumeCascadeRole as v,forwardOutput as x}from"./contextHelpers.js";import{accountTier as T}from"@fjall/util";import{DEFAULT_REGION as V}from"../aws/utils/regions.js";const J=4;function le(o){const n=o.find(e=>T(e)==="platform"),c=o.filter(e=>T(e)==="account");return{platformAccount:n,memberAccounts:c}}function Q(o){const n=o?.primaryRegion??V,c=o?.secondaryRegions??[],e=new Set([n,...c]),i=o?.disasterRecoveryRegion;return i&&e.add(i),[...e]}function ge(o){return Q(o)[0]??V}function Z(o,n){return`${o} (${n})`}function Ce(o,n){const c=[];for(const e of n)for(const i of o)c.push({account:i,region:e});return c}import{buildCascadeRoleArn as Se}from"./contextHelpers.js";async function he(o,n,c,e){c.onLog?.(`Verifying cascade role access for ${n.length} account(s)\u2026`,"info");const i=await U(n,J,async a=>v(o.awsProvider,a.id,a.region??o.awsProvider.getRegion(),`fjall-preflight-${a.name}`,e)),s=[];return i.forEach((a,t)=>{const r=n[t];if(r){if(a.status==="rejected"){s.push({accountId:r.id,accountName:r.name,error:a.reason instanceof Error?a.reason.message:String(a.reason)});return}a.value.success||s.push({accountId:r.id,accountName:r.name,error:a.value.error.message})}}),s.length===0&&c.onLog?.(`Cascade role access verified for ${n.length} account(s)`,"info"),s}async function ye(o,n,c,e,i,s,a){const t=a?.region??e.region??n.awsProvider.getRegion(),r=Z(e.name,t),u=a?.orgConfig??o.orgConfig,l=a?.ipamPoolId;s.onCascadeAccountStart?.(r,e.id,t,i);const d=await v(n.awsProvider,e.id,t,`fjall-cascade-${e.name}`,o.abortSignal);if(!d.success)return s.onCascadeAccountComplete?.(r,!1,p(d.error.message),t),h(new Error(`Failed to assume role for ${e.name}: ${p(d.error.message)}`));const{provider:g,credentials:C}=d.data,P=L(o.workingDirectory,"fjall",i==="platform"?R.PLATFORM:R.ACCOUNT),D=L(P,`cdk.out.${e.id}.${G(t)}`),w=_.buildDeploymentContext({deployType:i,target:c.target,path:P,assemblyDir:D,environment:e.environment??void 0,region:t,accountName:e.name,callerIdentity:K(e.id),ipamPoolId:l,...q({orgConfig:u,identity:o.identity,skipOidc:o.options?.skipOidc,region:t,primaryRegion:a?.primaryRegion,trailLifecycle:e.trailLifecycle})},{verbose:o.options?.verbose},u);if(W(e.environment,u?.disasterRecoveryRegion)){const f=await X(g.getClient(Y));if(!f.success)return s.onCascadeAccountComplete?.(r,!1,p(f.error.message),t),h(new Error(`Backup vault probe failed for ${e.name}: ${p(f.error.message)}`));w.fjallAdoptBackupVault=f.data}s.onCascadeAccountPhaseChange?.(r,"synth",t);const $=await n.cdkService.runCdkSynth(w,x(s),C);if(!$.success)return s.onCascadeAccountComplete?.(r,!1,p(`Synth failed: ${$.error}`),t),h(new Error(`Synth failed for ${e.name}: ${p($.error)}`));const m=j(i==="platform"?R.PLATFORM:R.ACCOUNT),k=z(P,e.id,t),S=new H(g),{changed:b,currentHash:I}=await n.hashService.compareCascadeStack(D,m,k);if(!b&&o.options?.force!==!0&&await S.stackExists(m)){const f=await S.getStackOutputs(m);f.success||y.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:m,account:e.name});const F=M(f);return s.onLog?.(`${e.name}: no infrastructure changes \u2014 skipping deploy`,"info"),s.onCascadeAccountComplete?.(r,!0,void 0,t,F,!0),B({outputs:F,skipped:!0})}s.onCascadeAccountPhaseChange?.(r,"bootstrap",t);const A=await n.cdkService.runCdkBootstrap(w,x(s),C);if(!A.success)return s.onCascadeAccountComplete?.(r,!1,p(`Bootstrap failed: ${A.error}`),t),h(new Error(`Bootstrap failed for ${e.name}: ${p(A.error)}`));s.onCascadeAccountPhaseChange?.(r,"deploy",t);const O=await n.cdkService.runCdkDeploy(w,m,x(s),f=>s.onCascadeAccountResourceProgress?.(r,f,t),g,C);if(!O.success)return s.onCascadeAccountComplete?.(r,!1,p(O.error),t),h(new Error(p(O.error)));const E=await S.getStackOutputs(m);E.success||y.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:m,account:e.name});const N=M(E);return I!==void 0&&((await n.hashService.persistCascadeStack(k,m,I)).success||y.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:m,account:e.name})),s.onCascadeAccountComplete?.(r,!0,void 0,t,N,!1),B({outputs:N,skipped:!1})}async function Re(o,n,c,e){const i=new Map,s=o.awsProvider.getRegion(),a=await v(o.awsProvider,n.id,s,`fjall-ipam-read-${n.name}`,e);if(!a.success)return y.debug("organisationDeploy",`Cannot read Platform outputs: ${a.error.message}`),i;const t=new H(a.data.provider),r=j(R.PLATFORM),u=await t.getStackOutputs(r);if(!u.success)return y.debug("organisationDeploy",`Failed to read Platform stack outputs: ${u.error.message}`),i;const l=/^IpamPoolId(\d{12})(\w+)$/;for(const d of u.data){const g=d.OutputKey?.match(l);if(g&&d.OutputValue){const C=`${g[1]}-${g[2]}`;i.set(C,d.OutputValue)}}return i.size>0&&c.onLog?.(`Read ${i.size} IPAM pool ID(s) from Platform stack`,"info"),i}async function we(o,n){const c=o.getDomains();if(c.length===0)return{domainsDeployed:0,errors:[]};n.onCascadePhaseStart?.("domains");const e=c.filter(t=>t.type==="apex"),i=c.filter(t=>t.type==="delegated");let s=0;const a=[];for(const t of e){const r=await o.deployDomain(t.name,n);r.success?s++:a.push(`${t.name}: ${r.error.message}`)}if(i.length>0){const t=await Promise.allSettled(i.map(r=>o.deployDomain(r.name,n)));for(let r=0;r<t.length;r++){const u=t[r],l=i[r];if(!(!u||!l))if(u.status==="fulfilled")u.value.success?s++:a.push(`${l.name}: ${u.value.error.message}`);else{const d=u.reason instanceof Error?u.reason.message:String(u.reason);a.push(`${l.name}: ${d}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:s,errors:a}}export{J as CASCADE_MAX_CONCURRENCY,Ce as buildAccountRegionPairs,Se as buildCascadeRoleArn,Q as buildRegionList,ge as cascadeHomeRegion,Z as cascadeOperationKey,ye as deployCascadeAccount,we as deployDomains,le as partitionAccounts,he as probeCascadeRoles,Re as readPlatformIpamPoolIds};
@@ -1,4 +1,5 @@
1
1
  import { type Result } from "@fjall/generator";
2
+ import type { TrailLifecycleState } from "@fjall/util/config";
2
3
  import type { ResourceEvent } from "@fjall/util/aws";
3
4
  import type { OrgConfig } from "../types/orgConfig.js";
4
5
  import type { DeployIdentity } from "../types/credentials.js";
@@ -7,20 +8,37 @@ import { SimpleAwsProvider } from "../aws/SimpleAwsProvider.js";
7
8
  import type { DeploymentContext } from "../types/deployment/DeploymentTypes.js";
8
9
  import type { DeployCallbacks } from "../types/callbacks.js";
9
10
  import type { DeployServices } from "./serviceFactory.js";
11
+ /**
12
+ * Whether a deploy targets a region other than the resolved primary. Shared
13
+ * predicate for the synth-time globals skip (buildParamsContext) AND the
14
+ * pre-deploy globals existence probe (deploySingleComponent) — the two MUST
15
+ * agree or a skip can pass unprobed. No primary configured (config never
16
+ * synced) ⇒ false: the deploy region anchors a first-ever deploy.
17
+ */
18
+ export declare function targetsNonPrimaryRegion(region: string | undefined, primaryRegion: string | undefined): boolean;
10
19
  /**
11
20
  * Build the orgConfig/identity context fields shared by all deployment paths.
12
21
  * CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string.
22
+ *
23
+ * Sole derivation site for `fjallAccountGlobalsConfigured`: a deploy skips the
24
+ * fixed-name account globals iff it targets a region other than the resolved
25
+ * primary (targetsNonPrimaryRegion above).
13
26
  */
14
27
  export declare function buildParamsContext(params: {
15
28
  orgConfig?: OrgConfig;
16
29
  identity?: DeployIdentity;
17
30
  skipOidc?: boolean;
18
- skipAccountGlobals?: boolean;
31
+ /** Region this synth targets. Omitted ⇒ no globals flag. */
32
+ region?: string;
33
+ /** Resolved primary (home) region; callers pass their resolved anchor, never re-derive. */
34
+ primaryRegion?: string;
35
+ trailLifecycle?: TrailLifecycleState;
19
36
  }): {
20
37
  orgConfig?: string;
21
38
  fjallOrgId?: string;
22
39
  fjallOidcConfigured?: boolean;
23
40
  fjallAccountGlobalsConfigured?: boolean;
41
+ fjallAccountTrailState?: string;
24
42
  };
25
43
  /** Forward onOutput callback — reduces lambda repetition across orchestration files. */
26
44
  export declare function forwardOutput(callbacks: DeployCallbacks): (chunk: string) => void;
@@ -42,7 +60,7 @@ export interface CascadeAssumedRole {
42
60
  * Callers handle failure (callbacks, logging, return type) — this helper
43
61
  * only owns the assume + provider construction.
44
62
  */
45
- export declare function assumeCascadeRole(awsProvider: SimpleAwsProvider, accountId: string, region: string, sessionName: string): Promise<Result<CascadeAssumedRole, Error>>;
63
+ export declare function assumeCascadeRole(awsProvider: SimpleAwsProvider, accountId: string, region: string, sessionName: string, signal?: AbortSignal): Promise<Result<CascadeAssumedRole, Error>>;
46
64
  /**
47
65
  * Run CDK synth and return failure with masked error if it fails.
48
66
  * Calls `onError` on the callbacks so callers only need to handle
@@ -1 +1 @@
1
- import{success as f,failure as u}from"@fjall/generator";import{getErrorMessage as y,maskSensitiveOutput as d,sleep as E}from"@fjall/util";import{logger as O}from"@fjall/util/logger";import{SimpleAwsProvider as R}from"../aws/SimpleAwsProvider.js";function k(e){return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{},...e.skipAccountGlobals?{fjallAccountGlobalsConfigured:!0}:{}}}function C(e){return r=>e.onOutput?.(r)}function M(e){return r=>e.onResourceProgress?.(r)}function T(e){if(!e.success||e.data.length===0)return;const r={};for(const t of e.data)t.OutputKey&&t.OutputValue!==void 0&&(r[t.OutputKey]=t.OutputValue);return Object.keys(r).length>0?r:void 0}const g="OrganizationAccountAccessRole",l=5,S=5e3,w=3e4;function $(e){return`arn:aws:iam::${e}:role/${g}`}async function D(e,r,t,s){if(!e.assumeRole)return u(new Error("AwsProvider does not support assumeRole"));const n=$(r),i=e.assumeRole.bind(e);let o;for(let c=0;c<=l;c++)try{o=await i(n,s);break}catch(a){const p=a instanceof Error?a.name:void 0;if(p==="AccessDenied"||p==="AccessDeniedException")return u(new Error(`Access denied assuming ${g} in account ${r}. The role may not exist or may not trust the management account.`));if(c<l){const m=Math.min(S*2**c,w);O.debug("assumeCascadeRole",`Attempt ${c+1} failed for account ${r}, retrying in ${Math.round(m/1e3)}s`,{error:d(y(a))}),await E(m);continue}return u(new Error(`Failed to assume role in account ${r} after ${l+1} attempts: ${d(y(a))}`))}if(!o)return u(new Error(`Failed to assume role in account ${r}`));const A=new R({accessKeyId:o.accessKeyId,secretAccessKey:o.secretAccessKey,sessionToken:o.sessionToken,region:t,accountId:r});return f({provider:A,credentials:{accessKeyId:o.accessKeyId,secretAccessKey:o.secretAccessKey,sessionToken:o.sessionToken}})}async function b(e,r,t,s){const n=await e.cdkService.runCdkSynth(r,i=>t.onCdkOutput?.(i,"synth"));if(!n.success){const i=new Error(d(`${s}: ${n.error}`));return t.onError?.(i),u(i)}return f(void 0)}async function B(e,r,t){t.onCDKBootstrap?.("bootstrapping");const s=await e.cdkService.runCdkBootstrap(r,C(t));if(!s.success){t.onCDKBootstrap?.("failed");const n=new Error(d(`Bootstrap failed: ${s.error}`));return t.onError?.(n),u(n)}return t.onCDKBootstrap?.("complete"),f(void 0)}export{D as assumeCascadeRole,B as bootstrapOrFail,$ as buildCascadeRoleArn,k as buildParamsContext,T as collectStackOutputs,C as forwardOutput,M as forwardResourceProgress,b as synthOrFail};
1
+ import{success as f,failure as i}from"@fjall/generator";import{getErrorMessage as m,maskSensitiveOutput as d,sleep as y}from"@fjall/util";import{logger as O}from"@fjall/util/logger";import{SimpleAwsProvider as R}from"../aws/SimpleAwsProvider.js";const S={account:"active",draining:"draining",org:"removed"};function w(e,r){return e!==void 0&&e!==""&&r!==void 0&&r!==""&&e!==r}function M(e){const r=w(e.region,e.primaryRegion);return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{},...r?{fjallAccountGlobalsConfigured:!0}:{},...e.trailLifecycle!==void 0?{fjallAccountTrailState:S[e.trailLifecycle]}:{}}}function _(e){return r=>e.onOutput?.(r)}function L(e){return r=>e.onResourceProgress?.(r)}function D(e){if(!e.success||e.data.length===0)return;const r={};for(const t of e.data)t.OutputKey&&t.OutputValue!==void 0&&(r[t.OutputKey]=t.OutputValue);return Object.keys(r).length>0?r:void 0}const E="OrganizationAccountAccessRole",l=5,$=5e3,b=3e4;function K(e){return`arn:aws:iam::${e}:role/${E}`}function T(e,r){return r===void 0?y(e):r.aborted?Promise.resolve():new Promise(t=>{const n=()=>{t()};r.addEventListener("abort",n,{once:!0}),y(e).then(()=>{r.removeEventListener("abort",n),t()})})}async function P(e,r,t,n,o){if(!e.assumeRole)return i(new Error("AwsProvider does not support assumeRole"));const u=K(r),g=e.assumeRole.bind(e);let s;for(let c=0;c<=l;c++)try{s=await g(u,n);break}catch(a){const p=a instanceof Error?a.name:void 0;if(p==="AccessDenied"||p==="AccessDeniedException")return i(new Error(`Access denied assuming ${E} in account ${r}. The role may not exist or may not trust the management account.`));if(c<l){const A=Math.min($*2**c,b);if(O.debug("assumeCascadeRole",`Attempt ${c+1} failed for account ${r}, retrying in ${Math.round(A/1e3)}s`,{error:d(m(a))}),await T(A,o),o?.aborted)return i(new Error(`Aborted while retrying assume-role for account ${r}`));continue}return i(new Error(`Failed to assume role in account ${r} after ${l+1} attempts: ${d(m(a))}`))}if(!s)return i(new Error(`Failed to assume role in account ${r}`));const C=new R({accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken,region:t,accountId:r});return f({provider:C,credentials:{accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken}})}async function j(e,r,t,n){const o=await e.cdkService.runCdkSynth(r,u=>t.onCdkOutput?.(u,"synth"));if(!o.success){const u=new Error(d(`${n}: ${o.error}`));return t.onError?.(u),i(u)}return f(void 0)}async function B(e,r,t){t.onCDKBootstrap?.("bootstrapping");const n=await e.cdkService.runCdkBootstrap(r,_(t));if(!n.success){t.onCDKBootstrap?.("failed");const o=new Error(d(`Bootstrap failed: ${n.error}`));return t.onError?.(o),i(o)}return t.onCDKBootstrap?.("complete"),f(void 0)}export{P as assumeCascadeRole,B as bootstrapOrFail,K as buildCascadeRoleArn,M as buildParamsContext,D as collectStackOutputs,_ as forwardOutput,L as forwardResourceProgress,j as synthOrFail,w as targetsNonPrimaryRegion};
@@ -2,21 +2,30 @@ export { deploy } from "./deploy.js";
2
2
  export { destroy } from "./destroy.js";
3
3
  export { deployOrganisation } from "./organisationDeploy.js";
4
4
  export { destroyOrganisation } from "./organisationDestroy.js";
5
- export { cleanupFailedStack, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
5
+ export { cleanupFailedStack, emptyS3Bucket, preEmptyStackBuckets, formatQuarantineSuspectedMessage, formatRetainedBucketsMessage, isQuarantineDetail, isRetainedBucketsDetail, PRE_EMPTY_TAG_KEYS, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
6
+ export type { PreEmptyBucketsSummary, EmptyBucketOutcome } from "./stackCleanup.js";
6
7
  export type { CascadeDestroyAccountResult } from "./cascadeDestroyHelpers.js";
7
- export { partitionAccounts, buildRegionList, buildAccountRegionPairs } from "./cascadeHelpers.js";
8
+ export { partitionAccounts, buildRegionList, buildAccountRegionPairs, cascadeHomeRegion, cascadeOperationKey } from "./cascadeHelpers.js";
8
9
  export type { AccountRegionPair } from "./cascadeHelpers.js";
9
10
  export { projectScalarSummary, projectAccountRows } from "./cascadeSummary.js";
10
11
  export type { CascadeOutcomeResult, CascadeMemberOutcome, CascadePlatformOutcome, CascadeLedger, CascadeAccountRow, CascadePlatformRow, CascadeAccountProjection } from "./cascadeSummary.js";
11
12
  export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./reconcileProviderAccounts.js";
12
13
  export type { ReconcileResult } from "./reconcileProviderAccounts.js";
13
- export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./accountsConfig.js";
14
+ export { decideNextTransition, reconcileTrailMigration, ORG_TRAIL_BUCKET_OUTPUT_KEY, TRAIL_BUCKET_OUTPUT_KEY, TRAIL_KEY_ARN_OUTPUT_KEY } from "./trailMigration/trailMigration.js";
15
+ export type { MemberTrailFacts, TrailMigrationTransition, TrailMigrationOutcome } from "./trailMigration/trailMigration.js";
16
+ export { decommissionMemberTrailStorage } from "./trailMigration/memberTrailCleanup.js";
17
+ export type { DecommissionClients, DecommissionInput, DecommissionOutcome } from "./trailMigration/memberTrailCleanup.js";
18
+ export { unlockBucket } from "./unlock/unlockBucket.js";
19
+ export type { UnlockBucketInput, UnlockBucketReport } from "./unlock/unlockBucket.js";
20
+ export { unlockQueue } from "./unlock/unlockQueue.js";
21
+ export type { UnlockQueueInput, UnlockQueueReport } from "./unlock/unlockQueue.js";
22
+ export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig, isOuOnlyAccountBucket, OU_ONLY_ACCOUNT_BUCKETS } from "./accountsConfig.js";
14
23
  export type { AccountsConfig } from "./accountsConfig.js";
15
24
  export type { DockerProvider, DockerProgressCallback, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams } from "./dockerInterface.js";
16
25
  export type { DomainDeployProvider, DomainConfig, DomainDeployResult } from "./domainInterface.js";
17
26
  export type { DeployServices } from "./serviceFactory.js";
18
27
  export type { DetectionResult } from "./detectionPipeline.js";
19
28
  export { runOpenNextBuild } from "./openNextBuild.js";
20
- export { runOrganisationSetup } from "./organisationSetup.js";
29
+ export { runOrganisationSetup, ORG_SETUP_PHASES } from "./organisationSetup.js";
21
30
  export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult } from "./organisationSetup.js";
22
31
  export * from "./builders/index.js";
@@ -1 +1 @@
1
- import{deploy as t}from"./deploy.js";import{destroy as n}from"./destroy.js";import{deployOrganisation as i}from"./organisationDeploy.js";import{destroyOrganisation as p}from"./organisationDestroy.js";import{cleanupFailedStack as u,isCleanableState as m,SAFE_CLEANUP_STATES as f}from"./stackCleanup.js";import{partitionAccounts as x,buildRegionList as A,buildAccountRegionPairs as d}from"./cascadeHelpers.js";import{projectScalarSummary as S,projectAccountRows as y}from"./cascadeSummary.js";import{reconcileProviderAccounts as O,mergeReconciledProviderAccounts as T}from"./reconcileProviderAccounts.js";import{parseAccountsConfiguration as P,flattenAccountsToEnvironments as R,extractAllAccountNames as b,accountsConfigToOUTree as v,isStringArray as N,isAccountsConfig as j}from"./accountsConfig.js";import{runOpenNextBuild as L}from"./openNextBuild.js";import{runOrganisationSetup as _}from"./organisationSetup.js";export*from"./builders/index.js";export{f as SAFE_CLEANUP_STATES,v as accountsConfigToOUTree,d as buildAccountRegionPairs,A as buildRegionList,u as cleanupFailedStack,t as deploy,i as deployOrganisation,n as destroy,p as destroyOrganisation,b as extractAllAccountNames,R as flattenAccountsToEnvironments,j as isAccountsConfig,m as isCleanableState,N as isStringArray,T as mergeReconciledProviderAccounts,P as parseAccountsConfiguration,x as partitionAccounts,y as projectAccountRows,S as projectScalarSummary,O as reconcileProviderAccounts,L as runOpenNextBuild,_ as runOrganisationSetup};
1
+ import{deploy as r}from"./deploy.js";import{destroy as n}from"./destroy.js";import{deployOrganisation as i}from"./organisationDeploy.js";import{destroyOrganisation as s}from"./organisationDestroy.js";import{cleanupFailedStack as p,emptyS3Bucket as m,preEmptyStackBuckets as T,formatQuarantineSuspectedMessage as l,formatRetainedBucketsMessage as f,isQuarantineDetail as A,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as d,isCleanableState as x,SAFE_CLEANUP_STATES as S}from"./stackCleanup.js";import{partitionAccounts as O,buildRegionList as g,buildAccountRegionPairs as R,cascadeHomeRegion as U,cascadeOperationKey as P}from"./cascadeHelpers.js";import{projectScalarSummary as y,projectAccountRows as B}from"./cascadeSummary.js";import{reconcileProviderAccounts as K,mergeReconciledProviderAccounts as N}from"./reconcileProviderAccounts.js";import{decideNextTransition as L,reconcileTrailMigration as M,ORG_TRAIL_BUCKET_OUTPUT_KEY as b,TRAIL_BUCKET_OUTPUT_KEY as v,TRAIL_KEY_ARN_OUTPUT_KEY as G}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as Q}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as D}from"./unlock/unlockBucket.js";import{unlockQueue as H}from"./unlock/unlockQueue.js";import{parseAccountsConfiguration as h,flattenAccountsToEnvironments as q,extractAllAccountNames as z,accountsConfigToOUTree as J,isStringArray as V,isAccountsConfig as W,isOuOnlyAccountBucket as X,OU_ONLY_ACCOUNT_BUCKETS as Z}from"./accountsConfig.js";import{runOpenNextBuild as ee}from"./openNextBuild.js";import{runOrganisationSetup as re,ORG_SETUP_PHASES as te}from"./organisationSetup.js";export*from"./builders/index.js";export{te as ORG_SETUP_PHASES,b as ORG_TRAIL_BUCKET_OUTPUT_KEY,Z as OU_ONLY_ACCOUNT_BUCKETS,d as PRE_EMPTY_TAG_KEYS,S as SAFE_CLEANUP_STATES,v as TRAIL_BUCKET_OUTPUT_KEY,G as TRAIL_KEY_ARN_OUTPUT_KEY,J as accountsConfigToOUTree,R as buildAccountRegionPairs,g as buildRegionList,U as cascadeHomeRegion,P as cascadeOperationKey,p as cleanupFailedStack,L as decideNextTransition,Q as decommissionMemberTrailStorage,r as deploy,i as deployOrganisation,n as destroy,s as destroyOrganisation,m as emptyS3Bucket,z as extractAllAccountNames,q as flattenAccountsToEnvironments,l as formatQuarantineSuspectedMessage,f as formatRetainedBucketsMessage,W as isAccountsConfig,x as isCleanableState,X as isOuOnlyAccountBucket,A as isQuarantineDetail,_ as isRetainedBucketsDetail,V as isStringArray,N as mergeReconciledProviderAccounts,h as parseAccountsConfiguration,O as partitionAccounts,T as preEmptyStackBuckets,B as projectAccountRows,y as projectScalarSummary,K as reconcileProviderAccounts,M as reconcileTrailMigration,ee as runOpenNextBuild,re as runOrganisationSetup,D as unlockBucket,H as unlockQueue};
@@ -0,0 +1,28 @@
1
+ import type { ProviderAccount } from "@fjall/util/config";
2
+ import type { DeployParams } from "../../types/params.js";
3
+ import type { OrgConfig } from "../../types/orgConfig.js";
4
+ import type { OrganisationOperation } from "../../types/operations.js";
5
+ import type { DeployServices } from "../serviceFactory.js";
6
+ /**
7
+ * Execute the cascade phases: platform deploy, IPAM pool read, domains, and
8
+ * the member-account fan-out, finishing with the ledger callbacks.
9
+ *
10
+ * Pushes per-target failures onto `cascadeErrors` and captured stack outputs
11
+ * onto `allCascadeOutputs` (both caller-owned). Returns whether any cascade
12
+ * target actually deployed (vs every target skipping as unchanged).
13
+ */
14
+ export declare function executeCascade(params: DeployParams, services: DeployServices, operation: OrganisationOperation, args: {
15
+ providerAccounts: ProviderAccount[];
16
+ effectiveOrgConfig: OrgConfig | undefined;
17
+ totalSteps: number;
18
+ cascadeErrors: Array<{
19
+ accountId: string;
20
+ error: string;
21
+ }>;
22
+ allCascadeOutputs: Array<{
23
+ accountId: string;
24
+ outputs: Record<string, string>;
25
+ }>;
26
+ }): Promise<{
27
+ anyCascadeDeployHappened: boolean;
28
+ }>;
@@ -0,0 +1 @@
1
+ import{failure as U}from"@fjall/generator";import{regionSuffix as j}from"../../types/FjallState.js";import{partitionAccounts as W,deployCascadeAccount as v,readPlatformIpamPoolIds as $,deployDomains as X,buildRegionList as Y,buildAccountRegionPairs as z,cascadeHomeRegion as B,CASCADE_MAX_CONCURRENCY as G}from"../cascadeHelpers.js";import{projectScalarSummary as x}from"../cascadeSummary.js";import{getErrorMessage as J,maskSensitiveOutput as C,mapSettledWithConcurrency as K}from"@fjall/util";import{INFRA_STEPS as E}from"./infraSteps.js";async function re(d,D,N,T){const{callbacks:e}=d,{providerAccounts:q,effectiveOrgConfig:R,totalSteps:p,cascadeErrors:l,allCascadeOutputs:b}=T;let S=!1;e.onCascadeStart?.();const H=Date.now();let i=2,P=!1,g,w=!1;const h=[],M=t=>({members:h,...g!==void 0?{platform:g}:{},domainsDeployed:w,errors:l,totalDurationMs:t}),{platformAccount:a,memberAccounts:A}=W(q),O=B(d.orgConfig);if(a){const{id:t,name:o}=E.CASCADE_PLATFORM;e.onStepStart?.(t,o,i,p),e.onCascadePhaseStart?.("platform");let s;const I=Date.now();try{s=await v(d,D,N,a,"platform",e,{orgConfig:R,primaryRegion:O})}catch(c){const r=C(J(c));l.push({accountId:a.id,error:r}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,"error",i,p),s=U(new Error(r))}const k=Date.now()-I;if(s.success){P=!0;const c=s.data.skipped===!0;c||(S=!0),s.data.outputs&&b.push({accountId:a.id,outputs:s.data.outputs}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,c?"skipped":"completed",i,p),g={accountId:a.id,result:c?"skipped":"succeeded",durationMs:k}}else l.some(c=>c.accountId===a.id)||(l.push({accountId:a.id,error:C(s.error.message)}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,"error",i,p)),g={accountId:a.id,result:"failed",durationMs:k,error:C(s.error.message)};i++}let _=new Map;if(P&&a&&(_=await $(D,a,e,d.abortSignal)),d.domainProvider){const t=await X(d.domainProvider,e);w=t.domainsDeployed>0,w&&(S=!0);for(const o of t.errors)l.push({accountId:"domains",error:C(o)})}if(a!==void 0&&!P&&A.length>0){const{id:t,name:o}=E.CASCADE_ACCOUNTS;e.onStepStart?.(t,o,i,p),e.onStepComplete?.(t,o,"skipped",i,p),e.onLog?.("Skipping account cascade \u2014 platform deployment failed; platform is a prerequisite for member accounts.","warn")}else if(A.length>0){const{id:t,name:o}=E.CASCADE_ACCOUNTS;e.onStepStart?.(t,o,i,p),e.onCascadePhaseStart?.("accounts");const s=Y(d.orgConfig),I=z(A,s);(await K(I,G,async({account:r,region:y})=>{const m=_.get(`${r.id}-${j(y)}`),n=Date.now();return{result:await v(d,D,N,r,"account",e,{ipamPoolId:m,orgConfig:R,region:y,primaryRegion:O}),durationMs:Date.now()-n}})).forEach((r,y)=>{const m=I[y];if(!m)return;const n=m.account;if(r.status==="rejected"){const u=C(r.reason instanceof Error?r.reason.message:String(r.reason));h.push({accountId:n.id,accountName:n.name,region:m.region,result:"failed",durationMs:0,error:u}),l.push({accountId:n.id,error:u});return}const{result:f,durationMs:L}=r.value;if(f.success){const u=f.data.skipped===!0;u||(S=!0),f.data.outputs&&b.push({accountId:n.id,outputs:f.data.outputs}),h.push({accountId:n.id,accountName:n.name,region:m.region,result:u?"skipped":"succeeded",durationMs:L})}else{const u=C(f.error.message);h.push({accountId:n.id,accountName:n.name,region:m.region,result:"failed",durationMs:L,error:u}),l.push({accountId:n.id,error:u})}});const c=x(M(0));e.onCascadePhaseComplete?.("accounts"),e.onStepComplete?.(t,o,c.accountsFailed>0?"error":c.accountsSkipped===A.length?"skipped":"completed",i,p)}const F=M(Date.now()-H);return e.onCascadeComplete?.(x(F)),e.onCascadeLedger?.(F),{anyCascadeDeployHappened:S}}export{re as executeCascade};