@fjall/deploy-core 0.89.4 → 0.89.6

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 (202) hide show
  1. package/LICENSE +50 -21
  2. package/README.md +25 -0
  3. package/dist/.minified +1 -0
  4. package/dist/src/__test-utils__/awsMockHelpers.d.ts +20 -0
  5. package/dist/src/__test-utils__/awsMockHelpers.js +1 -0
  6. package/dist/src/__test-utils__/index.d.ts +1 -0
  7. package/dist/src/__test-utils__/index.js +1 -0
  8. package/dist/src/aws/AwsProvider.d.ts +2 -2
  9. package/dist/src/aws/AwsProvider.js +0 -1
  10. package/dist/src/aws/SimpleAwsProvider.d.ts +2 -3
  11. package/dist/src/aws/SimpleAwsProvider.js +1 -73
  12. package/dist/src/aws/index.d.ts +4 -2
  13. package/dist/src/aws/index.js +1 -3
  14. package/dist/src/aws/organisations/accounts.js +10 -10
  15. package/dist/src/aws/organisations/backup.js +4 -2
  16. package/dist/src/aws/organisations/costAllocation.js +4 -2
  17. package/dist/src/aws/organisations/delegatedAdmin.d.ts +9 -0
  18. package/dist/src/aws/organisations/delegatedAdmin.js +43 -0
  19. package/dist/src/aws/organisations/identityCentre.d.ts +1 -1
  20. package/dist/src/aws/organisations/identityCentre.js +6 -2
  21. package/dist/src/aws/organisations/index.d.ts +4 -3
  22. package/dist/src/aws/organisations/index.js +1 -12
  23. package/dist/src/aws/organisations/ipam.js +4 -2
  24. package/dist/src/aws/organisations/organisation.js +27 -18
  25. package/dist/src/aws/organisations/organisationalUnits.d.ts +26 -6
  26. package/dist/src/aws/organisations/organisationalUnits.js +149 -35
  27. package/dist/src/aws/organisations/policies.js +4 -3
  28. package/dist/src/aws/organisations/ram.js +6 -2
  29. package/dist/src/aws/organisations/serviceAccess.js +12 -6
  30. package/dist/src/aws/organisations/trustedAccess.js +6 -2
  31. package/dist/src/aws/organisations/types.d.ts +23 -1
  32. package/dist/src/aws/organisations/types.js +1 -16
  33. package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.d.ts +6 -0
  34. package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.js +1 -0
  35. package/dist/src/aws/utils/cloudformationEventHelpers.d.ts +48 -0
  36. package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -0
  37. package/dist/src/aws/utils/cloudformationEventTypes.d.ts +45 -0
  38. package/dist/src/aws/utils/cloudformationEventTypes.js +1 -0
  39. package/dist/src/aws/utils/cloudformationEvents.d.ts +8 -54
  40. package/dist/src/aws/utils/cloudformationEvents.js +1 -596
  41. package/dist/src/aws/utils/index.d.ts +5 -0
  42. package/dist/src/aws/utils/index.js +1 -0
  43. package/dist/src/aws/utils/stackStatus.js +1 -90
  44. package/dist/src/events/index.d.ts +13 -0
  45. package/dist/src/events/index.js +1 -0
  46. package/dist/src/index.d.ts +34 -17
  47. package/dist/src/index.js +41 -21
  48. package/dist/src/orchestration/__tests__/cascadeTestHelpers.d.ts +12 -0
  49. package/dist/src/orchestration/__tests__/cascadeTestHelpers.js +78 -0
  50. package/dist/src/orchestration/activeDeploymentGuard.d.ts +10 -0
  51. package/dist/src/orchestration/activeDeploymentGuard.js +39 -0
  52. package/dist/src/orchestration/applicationDeploy.js +46 -224
  53. package/dist/src/orchestration/applicationDeployHelpers.d.ts +39 -0
  54. package/dist/src/orchestration/applicationDeployHelpers.js +223 -0
  55. package/dist/src/orchestration/applicationDestroy.d.ts +14 -0
  56. package/dist/src/orchestration/applicationDestroy.js +131 -0
  57. package/dist/src/orchestration/builders/dockerBuilder.d.ts +17 -0
  58. package/dist/src/orchestration/builders/dockerBuilder.js +98 -0
  59. package/dist/src/orchestration/builders/frameworkRegistry.d.ts +23 -0
  60. package/dist/src/orchestration/builders/frameworkRegistry.js +1 -0
  61. package/dist/src/orchestration/builders/index.d.ts +4 -0
  62. package/dist/src/orchestration/builders/index.js +1 -0
  63. package/dist/src/orchestration/builders/openNextBuilder.d.ts +21 -0
  64. package/dist/src/orchestration/builders/openNextBuilder.js +144 -0
  65. package/dist/src/orchestration/cascadeDestroyHelpers.d.ts +30 -0
  66. package/dist/src/orchestration/cascadeDestroyHelpers.js +1 -0
  67. package/dist/src/orchestration/cascadeHelpers.d.ts +46 -0
  68. package/dist/src/orchestration/cascadeHelpers.js +160 -0
  69. package/dist/src/orchestration/contextHelpers.d.ts +47 -2
  70. package/dist/src/orchestration/contextHelpers.js +94 -1
  71. package/dist/src/orchestration/destroy.d.ts +13 -0
  72. package/dist/src/orchestration/destroy.js +67 -0
  73. package/dist/src/orchestration/detectionPipeline.d.ts +2 -11
  74. package/dist/src/orchestration/detectionPipeline.js +29 -10
  75. package/dist/src/orchestration/dockerBuildHelper.d.ts +10 -0
  76. package/dist/src/orchestration/dockerBuildHelper.js +49 -0
  77. package/dist/src/orchestration/dockerInterface.d.ts +4 -2
  78. package/dist/src/orchestration/index.d.ts +8 -1
  79. package/dist/src/orchestration/index.js +1 -3
  80. package/dist/src/orchestration/manifestSecretParser.d.ts +11 -0
  81. package/dist/src/orchestration/manifestSecretParser.js +1 -0
  82. package/dist/src/orchestration/openNextBuild.d.ts +28 -0
  83. package/dist/src/orchestration/openNextBuild.js +243 -0
  84. package/dist/src/orchestration/organisationDeploy.js +130 -228
  85. package/dist/src/orchestration/organisationDestroy.d.ts +24 -0
  86. package/dist/src/orchestration/organisationDestroy.js +189 -0
  87. package/dist/src/orchestration/organisationSetup.d.ts +6 -4
  88. package/dist/src/orchestration/organisationSetup.js +28 -8
  89. package/dist/src/orchestration/resolveOperation.js +68 -6
  90. package/dist/src/orchestration/serviceFactory.d.ts +4 -0
  91. package/dist/src/orchestration/serviceFactory.js +1 -16
  92. package/dist/src/orchestration/spawnHelpers.d.ts +47 -0
  93. package/dist/src/orchestration/spawnHelpers.js +1 -0
  94. package/dist/src/orchestration/stackCleanup.d.ts +39 -0
  95. package/dist/src/orchestration/stackCleanup.js +1 -0
  96. package/dist/src/orchestration/welcomeImageHelper.d.ts +15 -0
  97. package/dist/src/orchestration/welcomeImageHelper.js +64 -0
  98. package/dist/src/services/application/ApplicationStackService.d.ts +21 -30
  99. package/dist/src/services/application/ApplicationStackService.js +16 -234
  100. package/dist/src/services/application/applicationStackHelpers.d.ts +46 -0
  101. package/dist/src/services/application/applicationStackHelpers.js +248 -0
  102. package/dist/src/services/application/index.d.ts +1 -0
  103. package/dist/src/services/application/index.js +1 -1
  104. package/dist/src/services/index.d.ts +6 -0
  105. package/dist/src/services/index.js +1 -0
  106. package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -67
  107. package/dist/src/services/infrastructure/CdkCommandRunner.d.ts +10 -2
  108. package/dist/src/services/infrastructure/CdkCommandRunner.js +18 -15
  109. package/dist/src/services/infrastructure/CdkErrorFormatter.js +16 -194
  110. package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -41
  111. package/dist/src/services/infrastructure/CdkOutputAnalyser.js +1 -1
  112. package/dist/src/services/infrastructure/CdkOutputParser.js +2 -33
  113. package/dist/src/services/infrastructure/CdkProcessManager.d.ts +5 -0
  114. package/dist/src/services/infrastructure/CdkProcessManager.js +81 -47
  115. package/dist/src/services/infrastructure/CdkService.d.ts +7 -52
  116. package/dist/src/services/infrastructure/CdkService.js +41 -82
  117. package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +50 -0
  118. package/dist/src/services/infrastructure/CdkServiceTypes.js +0 -0
  119. package/dist/src/services/infrastructure/CloudFormationService.js +9 -10
  120. package/dist/src/services/infrastructure/ICdkProcessManager.d.ts +27 -0
  121. package/dist/src/services/infrastructure/ICdkProcessManager.js +1 -0
  122. package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.d.ts +9 -0
  123. package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.js +1 -0
  124. package/dist/src/services/infrastructure/cdkServiceHelpers.d.ts +9 -0
  125. package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -0
  126. package/dist/src/services/infrastructure/constructMapEnrichment.d.ts +7 -0
  127. package/dist/src/services/infrastructure/constructMapEnrichment.js +1 -0
  128. package/dist/src/services/infrastructure/index.d.ts +3 -1
  129. package/dist/src/services/infrastructure/index.js +1 -7
  130. package/dist/src/services/supporting/TemplateHashService.js +1 -1
  131. package/dist/src/services/supporting/helpers.js +1 -81
  132. package/dist/src/services/supporting/index.js +1 -3
  133. package/dist/src/steps/index.d.ts +1 -0
  134. package/dist/src/steps/index.js +1 -0
  135. package/dist/src/steps/stepRegistry.d.ts +71 -0
  136. package/dist/src/steps/stepRegistry.js +505 -0
  137. package/dist/src/types/FjallState.js +1 -118
  138. package/dist/src/types/ProgressEvent.js +1 -48
  139. package/dist/src/types/application/ApplicationServiceTypes.js +1 -30
  140. package/dist/src/types/application/index.js +1 -1
  141. package/dist/src/types/callbacks.d.ts +76 -4
  142. package/dist/src/types/callbacks.js +0 -1
  143. package/dist/src/types/constants.d.ts +2 -0
  144. package/dist/src/types/constants.js +1 -6
  145. package/dist/src/types/credentials.d.ts +1 -1
  146. package/dist/src/types/credentials.js +0 -1
  147. package/dist/src/types/deployment/DeploymentServiceTypes.d.ts +5 -2
  148. package/dist/src/types/deployment/DeploymentServiceTypes.js +1 -1
  149. package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
  150. package/dist/src/types/deployment/DeploymentTypes.js +0 -1
  151. package/dist/src/types/deployment/cloudformation.js +0 -1
  152. package/dist/src/types/deployment/index.d.ts +3 -1
  153. package/dist/src/types/deployment/index.js +1 -1
  154. package/dist/src/types/deployment/parallel.js +1 -10
  155. package/dist/src/types/deploymentEventSchema.d.ts +158 -0
  156. package/dist/src/types/deploymentEventSchema.js +1 -0
  157. package/dist/src/types/detection.d.ts +22 -0
  158. package/dist/src/types/detection.js +1 -0
  159. package/dist/src/types/entitlements.d.ts +31 -0
  160. package/dist/src/types/entitlements.js +0 -0
  161. package/dist/src/types/errors/CdkError.js +1 -20
  162. package/dist/src/types/errors/ServiceError.d.ts +2 -1
  163. package/dist/src/types/errors/ServiceError.js +1 -119
  164. package/dist/src/types/errors/index.d.ts +2 -0
  165. package/dist/src/types/errors/index.js +1 -0
  166. package/dist/src/types/events.d.ts +3 -9
  167. package/dist/src/types/events.js +0 -5
  168. package/dist/src/types/frameworkBuilder.d.ts +96 -0
  169. package/dist/src/types/frameworkBuilder.js +8 -0
  170. package/dist/src/types/index.d.ts +19 -4
  171. package/dist/src/types/index.js +1 -9
  172. package/dist/src/types/operations.d.ts +3 -2
  173. package/dist/src/types/operations.js +1 -285
  174. package/dist/src/types/orgConfig.d.ts +2 -10
  175. package/dist/src/types/orgConfig.js +0 -11
  176. package/dist/src/types/params.d.ts +61 -0
  177. package/dist/src/types/patternDetection.d.ts +14 -16
  178. package/dist/src/types/patternDetection.js +14 -18
  179. package/dist/src/types/patternTypes.d.ts +19 -0
  180. package/dist/src/types/patternTypes.js +1 -0
  181. package/dist/src/types/stepDefinitions.d.ts +163 -0
  182. package/dist/src/types/stepDefinitions.js +98 -0
  183. package/dist/src/types/validation.js +0 -1
  184. package/dist/src/util/dockerfileDetection.d.ts +5 -0
  185. package/dist/src/util/dockerfileDetection.js +1 -0
  186. package/dist/src/util/index.d.ts +4 -3
  187. package/dist/src/util/index.js +1 -3
  188. package/dist/src/util/sequencedCallbacks.d.ts +44 -0
  189. package/dist/src/util/sequencedCallbacks.js +1 -0
  190. package/package.json +49 -8
  191. package/dist/src/aws/utils/CloudFormationFailureAnalyser.d.ts +0 -32
  192. package/dist/src/aws/utils/CloudFormationFailureAnalyser.js +0 -228
  193. package/dist/src/aws/utils/errors.d.ts +0 -26
  194. package/dist/src/aws/utils/errors.js +0 -59
  195. package/dist/src/util/fsHelpers.d.ts +0 -4
  196. package/dist/src/util/fsHelpers.js +0 -16
  197. package/dist/src/util/securityHelpers.d.ts +0 -31
  198. package/dist/src/util/securityHelpers.js +0 -124
  199. package/dist/src/util/singleton.d.ts +0 -2
  200. package/dist/src/util/singleton.js +0 -9
  201. package/dist/src/util/sleep.d.ts +0 -4
  202. package/dist/src/util/sleep.js +0 -4
@@ -5,9 +5,18 @@ import type { ResourceEvent } from "../../types/events.js";
5
5
  import type { DeploymentContext } from "../../types/deployment/DeploymentTypes.js";
6
6
  import { type ApplicationStack } from "../../types/operations.js";
7
7
  import type { ParallelDeploymentResult } from "../../types/deployment/parallel.js";
8
- import { type AppResourceFlags } from "../../types/patternDetection.js";
8
+ import type { AppResourceFlags } from "../../types/patternDetection.js";
9
9
  import { type Result } from "@fjall/generator";
10
10
  import { ApplicationError, type StackDeploymentData } from "../../types/application/ApplicationServiceTypes.js";
11
+ interface StackCallbacks {
12
+ onOutput?: (chunk: string) => void;
13
+ onResourceProgress?: (event: ResourceEvent) => void;
14
+ }
15
+ interface ParallelStackCallbacks {
16
+ onOutput?: (chunk: string, stackId: ApplicationStack) => void;
17
+ onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
18
+ onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
19
+ }
11
20
  /**
12
21
  * Service for deploying and destroying individual CloudFormation stacks.
13
22
  *
@@ -20,33 +29,26 @@ import { ApplicationError, type StackDeploymentData } from "../../types/applicat
20
29
  export declare class ApplicationStackService {
21
30
  private cdkService;
22
31
  private cloudFormationService;
23
- private aws;
24
- constructor(cdkService: CdkService, cloudFormationService: CloudFormationService);
25
- setAwsContext(aws: AwsProvider | undefined): void;
32
+ private aws?;
33
+ constructor(cdkService: CdkService, cloudFormationService: CloudFormationService, aws?: AwsProvider | undefined);
34
+ /**
35
+ * Delegate to CdkService.runCdkSynth — exposed for destroyAllStacks orchestration.
36
+ */
37
+ runCdkSynth(context: DeploymentContext): ReturnType<CdkService["runCdkSynth"]>;
26
38
  /**
27
39
  * Deploy a specific stack type for an application
28
40
  */
29
- deployStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: {
30
- onOutput?: (chunk: string) => void;
31
- onResourceProgress?: (event: ResourceEvent) => void;
32
- }): Promise<Result<StackDeploymentData, ApplicationError>>;
41
+ deployStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: StackCallbacks): Promise<Result<StackDeploymentData, ApplicationError>>;
33
42
  /**
34
43
  * Deploy multiple stacks in parallel within a deployment phase.
35
44
  *
36
45
  * Uses Promise.allSettled to ensure all stacks are attempted even if some fail.
37
46
  */
38
- deployStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: {
39
- onOutput?: (chunk: string, stackId: ApplicationStack) => void;
40
- onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
41
- onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
42
- }): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
47
+ deployStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: ParallelStackCallbacks): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
43
48
  /**
44
49
  * Destroy a specific stack type
45
50
  */
46
- destroyStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: {
47
- onOutput?: (chunk: string) => void;
48
- onResourceProgress?: (event: ResourceEvent) => void;
49
- }, useCdkOut?: boolean): Promise<Result<StackDeploymentData, ApplicationError>>;
51
+ destroyStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: StackCallbacks, useCdkOut?: boolean): Promise<Result<StackDeploymentData, ApplicationError>>;
50
52
  /**
51
53
  * Destroy multiple stacks in parallel within a destroy phase.
52
54
  *
@@ -54,11 +56,7 @@ export declare class ApplicationStackService {
54
56
  * be run beforehand. Multiple CDK commands cannot run in parallel because they
55
57
  * all try to synthesise to the same cdk.out directory.
56
58
  */
57
- destroyStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: {
58
- onOutput?: (chunk: string, stackId: ApplicationStack) => void;
59
- onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
60
- onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
61
- }, useCdkOut?: boolean): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
59
+ destroyStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: ParallelStackCallbacks, useCdkOut?: boolean): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
62
60
  /**
63
61
  * Get stack outputs for a deployed stack
64
62
  */
@@ -82,12 +80,5 @@ export declare class ApplicationStackService {
82
80
  }, resources?: AppResourceFlags): Promise<Result<{
83
81
  message: string;
84
82
  }, ApplicationError>>;
85
- /**
86
- * Convert "doesn't exist" errors to success for destroy operations.
87
- */
88
- private convertDestroyResultIfNotExists;
89
- /**
90
- * Handle destroy errors and determine if we should continue or fail
91
- */
92
- private handleDestroyError;
93
83
  }
84
+ export {};
@@ -1,10 +1,10 @@
1
- import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME } from "../../types/constants.js";
2
- import { APPLICATION_STACKS, getApplicationStackName, getApplicationDestroyOrder, getParallelDestroyGroups } from "../../types/operations.js";
3
- import { detectPattern, isOpenNextPattern } from "../../types/patternDetection.js";
4
- import { success, failure, isFailure } from "@fjall/generator";
1
+ import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH } from "../../types/constants.js";
2
+ import { getApplicationStackName } from "../../types/operations.js";
3
+ import { success, failure } from "@fjall/generator";
5
4
  import { ApplicationError } from "../../types/application/ApplicationServiceTypes.js";
6
- import { logger } from "@fjall/util";
5
+ import { logger } from "@fjall/util/logger";
7
6
  import { convertCloudFormationOutputsToRecord } from "../supporting/helpers.js";
7
+ import { destroyAllStacks, mapSettledResults, resolveWebsiteUrl } from "./applicationStackHelpers.js";
8
8
  /**
9
9
  * Service for deploying and destroying individual CloudFormation stacks.
10
10
  *
@@ -18,13 +18,17 @@ export class ApplicationStackService {
18
18
  cdkService;
19
19
  cloudFormationService;
20
20
  aws;
21
- constructor(cdkService, cloudFormationService) {
21
+ constructor(cdkService, cloudFormationService, aws) {
22
22
  this.cdkService = cdkService;
23
23
  this.cloudFormationService = cloudFormationService;
24
- }
25
- setAwsContext(aws) {
26
24
  this.aws = aws;
27
25
  }
26
+ /**
27
+ * Delegate to CdkService.runCdkSynth — exposed for destroyAllStacks orchestration.
28
+ */
29
+ async runCdkSynth(context) {
30
+ return this.cdkService.runCdkSynth(context);
31
+ }
28
32
  /**
29
33
  * Deploy a specific stack type for an application
30
34
  */
@@ -111,20 +115,7 @@ export class ApplicationStackService {
111
115
  onStackCompleteCallback?.(stack, result.success, duration, result.success ? undefined : result.error);
112
116
  return deploymentResult;
113
117
  }));
114
- const deployResults = results.map((settledResult, index) => {
115
- if (settledResult.status === "fulfilled") {
116
- return settledResult.value;
117
- }
118
- return {
119
- stack: stacks[index],
120
- success: false,
121
- error: settledResult.reason instanceof Error
122
- ? settledResult.reason
123
- : new Error(String(settledResult.reason)),
124
- duration: 0
125
- };
126
- });
127
- return success(deployResults);
118
+ return success(mapSettledResults(results, stacks));
128
119
  }
129
120
  /**
130
121
  * Destroy a specific stack type
@@ -191,20 +182,7 @@ export class ApplicationStackService {
191
182
  onStackCompleteCallback?.(stack, destroyResult.success, duration, destroyResult.success ? undefined : destroyResult.error);
192
183
  return destroyResult;
193
184
  }));
194
- const destroyResults = results.map((settledResult, index) => {
195
- if (settledResult.status === "fulfilled") {
196
- return settledResult.value;
197
- }
198
- return {
199
- stack: stacks[index],
200
- success: false,
201
- error: settledResult.reason instanceof Error
202
- ? settledResult.reason
203
- : new Error(String(settledResult.reason)),
204
- duration: 0
205
- };
206
- });
207
- return success(destroyResults);
185
+ return success(mapSettledResults(results, stacks));
208
186
  }
209
187
  /**
210
188
  * Get stack outputs for a deployed stack
@@ -228,209 +206,13 @@ export class ApplicationStackService {
228
206
  * Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
229
207
  */
230
208
  async resolveWebsiteUrl(appName) {
231
- try {
232
- const computeStackName = getApplicationStackName(appName, APPLICATION_STACKS.COMPUTE);
233
- const computeOutputs = await this.cloudFormationService.getStackOutputs(computeStackName);
234
- if (computeOutputs.success && computeOutputs.data.length > 0) {
235
- const lbUrl = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerUrl"));
236
- if (lbUrl?.OutputValue) {
237
- return lbUrl.OutputValue.toLowerCase();
238
- }
239
- const lbDns = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerDnsName"));
240
- if (lbDns?.OutputValue) {
241
- return `http://${lbDns.OutputValue.toLowerCase()}`;
242
- }
243
- }
244
- const cdnStackName = getApplicationStackName(appName, APPLICATION_STACKS.CDN);
245
- const cdnOutputs = await this.cloudFormationService.getStackOutputs(cdnStackName);
246
- if (cdnOutputs.success && cdnOutputs.data.length > 0) {
247
- const dist = cdnOutputs.data.find((o) => o.OutputKey?.includes("DistributionDomainName"));
248
- if (dist?.OutputValue) {
249
- return `https://${dist.OutputValue.toLowerCase()}`;
250
- }
251
- }
252
- return undefined;
253
- }
254
- catch (error) {
255
- logger.debug("ApplicationStackService", "URL resolution failed", {
256
- error: error instanceof Error ? error.message : String(error)
257
- });
258
- return undefined;
259
- }
209
+ return resolveWebsiteUrl(appName, this.cloudFormationService);
260
210
  }
261
211
  /**
262
212
  * Destroy all stacks for an application in reverse order.
263
213
  * Supports parallel destruction for OpenNext patterns (nextjs/payload).
264
214
  */
265
215
  async destroyAllStacks(context, callbacks, resources) {
266
- const patternResult = detectPattern(context.path);
267
- const pattern = patternResult.pattern;
268
- const appName = context.target;
269
- if (isOpenNextPattern(pattern)) {
270
- const destroyGroups = getParallelDestroyGroups();
271
- for (const group of destroyGroups) {
272
- const stacks = group.stacks;
273
- if (stacks.length === 1) {
274
- const stackType = stacks[0];
275
- const stackName = getApplicationStackName(appName, stackType);
276
- callbacks?.onStackStart?.(stackType, stackName);
277
- const result = await this.destroyStack(stackType, context, {
278
- onOutput: callbacks?.onOutput
279
- ? (chunk) => callbacks.onOutput?.(chunk, stackType)
280
- : undefined,
281
- onResourceProgress: callbacks?.onResourceProgress
282
- ? (event) => callbacks.onResourceProgress?.(event, stackType)
283
- : undefined
284
- });
285
- const finalResult = this.convertDestroyResultIfNotExists(result, stackType, appName);
286
- if (callbacks?.onStackComplete) {
287
- await callbacks.onStackComplete(stackType, finalResult);
288
- }
289
- const errorResult = this.handleDestroyError(finalResult, stackType);
290
- if (errorResult) {
291
- if (errorResult.continue)
292
- continue;
293
- return errorResult.result;
294
- }
295
- }
296
- else {
297
- const synthResult = await this.cdkService.runCdkSynth(context);
298
- if (!synthResult.success) {
299
- return failure(new ApplicationError(synthResult.error, {
300
- errorType: "synth_failed",
301
- appName,
302
- operation: "destroy-parallel-synth"
303
- }));
304
- }
305
- callbacks?.onParallelPhaseStart?.(stacks, group.description);
306
- for (const stackType of stacks) {
307
- callbacks?.onStackStart?.(stackType, getApplicationStackName(appName, stackType));
308
- }
309
- const parallelResult = await this.destroyStacksInParallel(stacks, context, {
310
- onOutput: callbacks?.onOutput,
311
- onResourceProgress: callbacks?.onResourceProgress,
312
- onStackComplete: async (stack, stackSuccess, _duration, error) => {
313
- const result = stackSuccess
314
- ? success({
315
- stackName: `${appName}${stack}`,
316
- stackType: stack,
317
- outputs: {}
318
- })
319
- : failure(error instanceof ApplicationError
320
- ? error
321
- : new ApplicationError(error?.message ?? "Stack destruction failed", {
322
- errorType: "destroy_failed",
323
- appName,
324
- operation: `destroy-${stack}`
325
- }));
326
- await callbacks?.onStackComplete?.(stack, result);
327
- }
328
- }, true);
329
- if (parallelResult.success) {
330
- callbacks?.onParallelPhaseComplete?.(parallelResult.data);
331
- const failures = parallelResult.data.filter((r) => !r.success);
332
- if (failures.length > 0) {
333
- const realFailures = failures.filter((f) => f.error &&
334
- !f.error.message.includes(STACK_NOT_FOUND_PATTERN) &&
335
- !f.error.message.includes(CDK_NO_STACKS_MATCH));
336
- if (realFailures.length > 0) {
337
- const failedStacks = realFailures
338
- .map((f) => f.stack)
339
- .join(", ");
340
- const errorDetails = realFailures
341
- .map((f) => `${f.stack}: ${f.error?.message || "Unknown error"}`)
342
- .join("\n");
343
- return failure(new ApplicationError(`Failed to destroy stacks: ${failedStacks}\n\n${errorDetails}`, {
344
- errorType: "destroy_failed",
345
- appName,
346
- operation: "destroy-parallel"
347
- }));
348
- }
349
- }
350
- }
351
- }
352
- }
353
- }
354
- else {
355
- const stackOrder = getApplicationDestroyOrder({ pattern, resources });
356
- for (const stackType of stackOrder) {
357
- const stackName = getApplicationStackName(appName, stackType);
358
- callbacks?.onStackStart?.(stackType, stackName);
359
- const result = await this.destroyStack(stackType, context, {
360
- onOutput: callbacks?.onOutput
361
- ? (chunk) => callbacks.onOutput?.(chunk, stackType)
362
- : undefined,
363
- onResourceProgress: callbacks?.onResourceProgress
364
- ? (event) => callbacks.onResourceProgress?.(event, stackType)
365
- : undefined
366
- });
367
- const finalResult = this.convertDestroyResultIfNotExists(result, stackType, appName);
368
- if (callbacks?.onStackComplete) {
369
- await callbacks.onStackComplete(stackType, finalResult);
370
- }
371
- const errorResult = this.handleDestroyError(finalResult, stackType);
372
- if (errorResult) {
373
- if (errorResult.continue)
374
- continue;
375
- return errorResult.result;
376
- }
377
- }
378
- }
379
- return success({ message: "All stacks destroyed" });
380
- }
381
- /**
382
- * Convert "doesn't exist" errors to success for destroy operations.
383
- */
384
- convertDestroyResultIfNotExists(result, stackType, appName) {
385
- if (result.success)
386
- return result;
387
- const errorMessage = isFailure(result) ? result.error.message : "";
388
- const isAlreadyDeleted = errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
389
- errorMessage.includes(CDK_NO_STACKS_MATCH);
390
- if (isAlreadyDeleted) {
391
- return success({
392
- stackName: getApplicationStackName(appName, stackType),
393
- stackType,
394
- outputs: {}
395
- });
396
- }
397
- return result;
398
- }
399
- /**
400
- * Handle destroy errors and determine if we should continue or fail
401
- */
402
- handleDestroyError(result, stackType) {
403
- if (result.success)
404
- return null;
405
- const errorMessage = isFailure(result) ? result.error.message : "";
406
- if (errorMessage.includes("TSError") ||
407
- errorMessage.includes("Unable to compile TypeScript") ||
408
- errorMessage.includes("Subprocess exited with error")) {
409
- const tsErrorMatch = errorMessage.match(/infrastructure\.ts\(\d+,\d+\): error TS\d+: .+/);
410
- const errorDetail = tsErrorMatch
411
- ? tsErrorMatch[0]
412
- : `Check ${INFRASTRUCTURE_FILENAME} for syntax errors`;
413
- return {
414
- continue: false,
415
- result: failure(new ApplicationError(`CDK synthesis failed: ${errorDetail}`, {
416
- errorType: "synth_failed",
417
- operation: `destroy-${stackType}`
418
- }))
419
- };
420
- }
421
- if (errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
422
- errorMessage.includes(CDK_NO_STACKS_MATCH)) {
423
- return {
424
- continue: true,
425
- result: success({ message: "Stack already deleted" })
426
- };
427
- }
428
- return {
429
- continue: false,
430
- result: failure(new ApplicationError(`Failed to destroy ${stackType} stack: ${errorMessage}`, {
431
- errorType: "destroy_failed",
432
- operation: `destroy-${stackType}`
433
- }))
434
- };
216
+ return destroyAllStacks(this, context, callbacks, resources);
435
217
  }
436
218
  }
@@ -0,0 +1,46 @@
1
+ import type { ResourceEvent } from "../../types/events.js";
2
+ import type { DeploymentContext } from "../../types/deployment/DeploymentTypes.js";
3
+ import { type ApplicationStack } from "../../types/operations.js";
4
+ import type { ParallelDeploymentResult } from "../../types/deployment/parallel.js";
5
+ import type { AppResourceFlags } from "../../types/patternDetection.js";
6
+ import { type Result } from "@fjall/generator";
7
+ import { ApplicationError, type StackDeploymentData } from "../../types/application/ApplicationServiceTypes.js";
8
+ import type { ApplicationStackService } from "./ApplicationStackService.js";
9
+ import type { CloudFormationService } from "../infrastructure/CloudFormationService.js";
10
+ /**
11
+ * Convert "doesn't exist" errors to success for destroy operations.
12
+ */
13
+ export declare function convertDestroyResultIfNotExists(result: Result<StackDeploymentData, ApplicationError>, stackType: ApplicationStack, appName: string): Result<StackDeploymentData, ApplicationError>;
14
+ /**
15
+ * Handle destroy errors and determine if we should continue or fail
16
+ */
17
+ export declare function handleDestroyError(result: Result<StackDeploymentData, ApplicationError>, stackType: ApplicationStack): {
18
+ continue: boolean;
19
+ result: Result<{
20
+ message: string;
21
+ }, ApplicationError>;
22
+ } | null;
23
+ /**
24
+ * Destroy all stacks for an application in reverse order.
25
+ * Supports parallel destruction for OpenNext patterns (nextjs/payload).
26
+ */
27
+ export declare function destroyAllStacks(service: ApplicationStackService, context: DeploymentContext, callbacks?: {
28
+ onOutput?: (chunk: string, stackId?: ApplicationStack) => void;
29
+ onResourceProgress?: (event: ResourceEvent, stackId?: ApplicationStack) => void;
30
+ onStackStart?: (stackType: ApplicationStack, stackName: string) => void;
31
+ onStackComplete?: (stackType: ApplicationStack, result: Result<StackDeploymentData, ApplicationError>) => void | Promise<void>;
32
+ onParallelPhaseStart?: (stacks: readonly ApplicationStack[], description: string) => void;
33
+ onParallelPhaseComplete?: (results: ParallelDeploymentResult[]) => void;
34
+ }, resources?: AppResourceFlags): Promise<Result<{
35
+ message: string;
36
+ }, ApplicationError>>;
37
+ /**
38
+ * Map Promise.allSettled results to ParallelDeploymentResult[], handling
39
+ * rejected promises with a fallback stack identity.
40
+ */
41
+ export declare function mapSettledResults(settled: Array<PromiseSettledResult<ParallelDeploymentResult>>, stacks: readonly ApplicationStack[]): ParallelDeploymentResult[];
42
+ /**
43
+ * Resolve the website URL for an application from CloudFormation stack outputs.
44
+ * Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
45
+ */
46
+ export declare function resolveWebsiteUrl(appName: string, cloudFormationService: CloudFormationService): Promise<string | undefined>;
@@ -0,0 +1,248 @@
1
+ import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME } from "../../types/constants.js";
2
+ import { APPLICATION_STACKS, getApplicationStackName, getApplicationDestroyOrder, getParallelDestroyGroups } from "../../types/operations.js";
3
+ import { FrameworkRegistry } from "../../orchestration/builders/frameworkRegistry.js";
4
+ import { success, failure, isFailure } from "@fjall/generator";
5
+ import { ApplicationError } from "../../types/application/ApplicationServiceTypes.js";
6
+ import { logger } from "@fjall/util/logger";
7
+ /**
8
+ * Convert "doesn't exist" errors to success for destroy operations.
9
+ */
10
+ export function convertDestroyResultIfNotExists(result, stackType, appName) {
11
+ if (result.success)
12
+ return result;
13
+ const errorMessage = isFailure(result) ? result.error.message : "";
14
+ const isAlreadyDeleted = errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
15
+ errorMessage.includes(CDK_NO_STACKS_MATCH);
16
+ if (isAlreadyDeleted) {
17
+ return success({
18
+ stackName: getApplicationStackName(appName, stackType),
19
+ stackType,
20
+ outputs: {},
21
+ skipped: true
22
+ });
23
+ }
24
+ return result;
25
+ }
26
+ /**
27
+ * Handle destroy errors and determine if we should continue or fail
28
+ */
29
+ export function handleDestroyError(result, stackType) {
30
+ if (result.success)
31
+ return null;
32
+ const errorMessage = isFailure(result) ? result.error.message : "";
33
+ if (errorMessage.includes("TSError") ||
34
+ errorMessage.includes("Unable to compile TypeScript") ||
35
+ errorMessage.includes("Subprocess exited with error")) {
36
+ const tsErrorMatch = errorMessage.match(/infrastructure\.ts\(\d+,\d+\): error TS\d+: .+/);
37
+ const errorDetail = tsErrorMatch
38
+ ? tsErrorMatch[0]
39
+ : `Check ${INFRASTRUCTURE_FILENAME} for syntax errors`;
40
+ return {
41
+ continue: false,
42
+ result: failure(new ApplicationError(`CDK synthesis failed: ${errorDetail}`, {
43
+ errorType: "synth_failed",
44
+ operation: `destroy-${stackType}`
45
+ }))
46
+ };
47
+ }
48
+ if (errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
49
+ errorMessage.includes(CDK_NO_STACKS_MATCH)) {
50
+ return {
51
+ continue: true,
52
+ result: success({ message: "Stack already deleted" })
53
+ };
54
+ }
55
+ return {
56
+ continue: false,
57
+ result: failure(new ApplicationError(`Failed to destroy ${stackType} stack: ${errorMessage}`, {
58
+ errorType: "destroy_failed",
59
+ operation: `destroy-${stackType}`
60
+ }))
61
+ };
62
+ }
63
+ /**
64
+ * Destroy all stacks for an application in reverse order.
65
+ * Supports parallel destruction for OpenNext patterns (nextjs/payload).
66
+ */
67
+ export async function destroyAllStacks(service, context, callbacks, resources) {
68
+ const appName = context.target;
69
+ // Use FrameworkRegistry to resolve builder and plan
70
+ const registry = FrameworkRegistry.createDefault();
71
+ const resolved = registry.resolve({ appPath: context.path });
72
+ // Determine destroy strategy from builder plan if available, else fall back to pattern detection
73
+ let useParallelDestroy;
74
+ let destroyOrder;
75
+ if (resolved) {
76
+ const plan = resolved.builder.plan({ appPath: context.path }, resolved.detection);
77
+ useParallelDestroy = plan.parallelDestroy;
78
+ destroyOrder = plan.destroyOrder;
79
+ }
80
+ else {
81
+ // No builder matched — infrastructure.ts missing. Use sequential destroy with default order.
82
+ useParallelDestroy = false;
83
+ destroyOrder = getApplicationDestroyOrder({ pattern: null, resources });
84
+ }
85
+ if (useParallelDestroy) {
86
+ const destroyGroups = getParallelDestroyGroups();
87
+ for (const group of destroyGroups) {
88
+ const stacks = group.stacks;
89
+ if (stacks.length === 1) {
90
+ const stackType = stacks[0];
91
+ const stackName = getApplicationStackName(appName, stackType);
92
+ callbacks?.onStackStart?.(stackType, stackName);
93
+ const result = await service.destroyStack(stackType, context, {
94
+ onOutput: callbacks?.onOutput
95
+ ? (chunk) => callbacks.onOutput?.(chunk, stackType)
96
+ : undefined,
97
+ onResourceProgress: callbacks?.onResourceProgress
98
+ ? (event) => callbacks.onResourceProgress?.(event, stackType)
99
+ : undefined
100
+ });
101
+ const finalResult = convertDestroyResultIfNotExists(result, stackType, appName);
102
+ if (callbacks?.onStackComplete) {
103
+ await callbacks.onStackComplete(stackType, finalResult);
104
+ }
105
+ const errorResult = handleDestroyError(finalResult, stackType);
106
+ if (errorResult) {
107
+ if (errorResult.continue)
108
+ continue;
109
+ return errorResult.result;
110
+ }
111
+ }
112
+ else {
113
+ const synthResult = await service.runCdkSynth(context);
114
+ if (!synthResult.success) {
115
+ return failure(new ApplicationError(synthResult.error, {
116
+ errorType: "synth_failed",
117
+ appName,
118
+ operation: "destroy-parallel-synth"
119
+ }));
120
+ }
121
+ callbacks?.onParallelPhaseStart?.(stacks, group.description);
122
+ for (const stackType of stacks) {
123
+ callbacks?.onStackStart?.(stackType, getApplicationStackName(appName, stackType));
124
+ }
125
+ const parallelResult = await service.destroyStacksInParallel(stacks, context, {
126
+ onOutput: callbacks?.onOutput,
127
+ onResourceProgress: callbacks?.onResourceProgress,
128
+ onStackComplete: async (stack, stackSuccess, _duration, error) => {
129
+ const result = stackSuccess
130
+ ? success({
131
+ stackName: getApplicationStackName(appName, stack),
132
+ stackType: stack,
133
+ outputs: {}
134
+ })
135
+ : failure(error instanceof ApplicationError
136
+ ? error
137
+ : new ApplicationError(error?.message ?? "Stack destruction failed", {
138
+ errorType: "destroy_failed",
139
+ appName,
140
+ operation: `destroy-${stack}`
141
+ }));
142
+ await callbacks?.onStackComplete?.(stack, result);
143
+ }
144
+ }, true);
145
+ if (parallelResult.success) {
146
+ callbacks?.onParallelPhaseComplete?.(parallelResult.data);
147
+ const failures = parallelResult.data.filter((r) => !r.success);
148
+ if (failures.length > 0) {
149
+ const realFailures = failures.filter((f) => f.error &&
150
+ !f.error.message.includes(STACK_NOT_FOUND_PATTERN) &&
151
+ !f.error.message.includes(CDK_NO_STACKS_MATCH));
152
+ if (realFailures.length > 0) {
153
+ const failedStacks = realFailures.map((f) => f.stack).join(", ");
154
+ const errorDetails = realFailures
155
+ .map((f) => `${f.stack}: ${f.error?.message || "Unknown error"}`)
156
+ .join("\n");
157
+ return failure(new ApplicationError(`Failed to destroy stacks: ${failedStacks}\n\n${errorDetails}`, {
158
+ errorType: "destroy_failed",
159
+ appName,
160
+ operation: "destroy-parallel"
161
+ }));
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ else {
169
+ for (const stackType of destroyOrder) {
170
+ const stackName = getApplicationStackName(appName, stackType);
171
+ callbacks?.onStackStart?.(stackType, stackName);
172
+ const result = await service.destroyStack(stackType, context, {
173
+ onOutput: callbacks?.onOutput
174
+ ? (chunk) => callbacks.onOutput?.(chunk, stackType)
175
+ : undefined,
176
+ onResourceProgress: callbacks?.onResourceProgress
177
+ ? (event) => callbacks.onResourceProgress?.(event, stackType)
178
+ : undefined
179
+ });
180
+ const finalResult = convertDestroyResultIfNotExists(result, stackType, appName);
181
+ if (callbacks?.onStackComplete) {
182
+ await callbacks.onStackComplete(stackType, finalResult);
183
+ }
184
+ const errorResult = handleDestroyError(finalResult, stackType);
185
+ if (errorResult) {
186
+ if (errorResult.continue)
187
+ continue;
188
+ return errorResult.result;
189
+ }
190
+ }
191
+ }
192
+ return success({ message: "All stacks destroyed" });
193
+ }
194
+ /**
195
+ * Map Promise.allSettled results to ParallelDeploymentResult[], handling
196
+ * rejected promises with a fallback stack identity.
197
+ */
198
+ export function mapSettledResults(settled, stacks) {
199
+ return settled.map((settledResult, index) => {
200
+ if (settledResult.status === "fulfilled") {
201
+ return settledResult.value;
202
+ }
203
+ const stack = stacks[index] ?? APPLICATION_STACKS.NETWORK;
204
+ return {
205
+ stack,
206
+ success: false,
207
+ error: settledResult.reason instanceof Error
208
+ ? settledResult.reason
209
+ : new Error(String(settledResult.reason)),
210
+ duration: 0
211
+ };
212
+ });
213
+ }
214
+ /**
215
+ * Resolve the website URL for an application from CloudFormation stack outputs.
216
+ * Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
217
+ */
218
+ export async function resolveWebsiteUrl(appName, cloudFormationService) {
219
+ try {
220
+ const computeStackName = getApplicationStackName(appName, APPLICATION_STACKS.COMPUTE);
221
+ const computeOutputs = await cloudFormationService.getStackOutputs(computeStackName);
222
+ if (computeOutputs.success && computeOutputs.data.length > 0) {
223
+ const lbUrl = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerUrl"));
224
+ if (lbUrl?.OutputValue) {
225
+ return lbUrl.OutputValue.toLowerCase();
226
+ }
227
+ const lbDns = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerDnsName"));
228
+ if (lbDns?.OutputValue) {
229
+ return `http://${lbDns.OutputValue.toLowerCase()}`;
230
+ }
231
+ }
232
+ const cdnStackName = getApplicationStackName(appName, APPLICATION_STACKS.CDN);
233
+ const cdnOutputs = await cloudFormationService.getStackOutputs(cdnStackName);
234
+ if (cdnOutputs.success && cdnOutputs.data.length > 0) {
235
+ const dist = cdnOutputs.data.find((o) => o.OutputKey?.includes("DistributionDomainName"));
236
+ if (dist?.OutputValue) {
237
+ return `https://${dist.OutputValue.toLowerCase()}`;
238
+ }
239
+ }
240
+ return undefined;
241
+ }
242
+ catch (error) {
243
+ logger.warn("ApplicationStackService", "URL resolution failed", {
244
+ error: error instanceof Error ? error.message : String(error)
245
+ });
246
+ return undefined;
247
+ }
248
+ }