@fjall/deploy-core 0.89.5 → 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 (198) 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.js +0 -1
  9. package/dist/src/aws/SimpleAwsProvider.js +1 -70
  10. package/dist/src/aws/index.d.ts +4 -2
  11. package/dist/src/aws/index.js +1 -3
  12. package/dist/src/aws/organisations/accounts.js +10 -10
  13. package/dist/src/aws/organisations/backup.js +4 -2
  14. package/dist/src/aws/organisations/costAllocation.js +4 -2
  15. package/dist/src/aws/organisations/delegatedAdmin.d.ts +9 -0
  16. package/dist/src/aws/organisations/delegatedAdmin.js +43 -0
  17. package/dist/src/aws/organisations/identityCentre.d.ts +1 -1
  18. package/dist/src/aws/organisations/identityCentre.js +6 -2
  19. package/dist/src/aws/organisations/index.d.ts +4 -3
  20. package/dist/src/aws/organisations/index.js +1 -12
  21. package/dist/src/aws/organisations/ipam.js +4 -2
  22. package/dist/src/aws/organisations/organisation.js +27 -18
  23. package/dist/src/aws/organisations/organisationalUnits.d.ts +26 -6
  24. package/dist/src/aws/organisations/organisationalUnits.js +149 -35
  25. package/dist/src/aws/organisations/policies.js +4 -3
  26. package/dist/src/aws/organisations/ram.js +6 -2
  27. package/dist/src/aws/organisations/serviceAccess.js +12 -6
  28. package/dist/src/aws/organisations/trustedAccess.js +6 -2
  29. package/dist/src/aws/organisations/types.d.ts +23 -1
  30. package/dist/src/aws/organisations/types.js +1 -16
  31. package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.d.ts +6 -0
  32. package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.js +1 -0
  33. package/dist/src/aws/utils/cloudformationEventHelpers.d.ts +48 -0
  34. package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -0
  35. package/dist/src/aws/utils/cloudformationEventTypes.d.ts +45 -0
  36. package/dist/src/aws/utils/cloudformationEventTypes.js +1 -0
  37. package/dist/src/aws/utils/cloudformationEvents.d.ts +8 -54
  38. package/dist/src/aws/utils/cloudformationEvents.js +1 -596
  39. package/dist/src/aws/utils/index.d.ts +5 -0
  40. package/dist/src/aws/utils/index.js +1 -0
  41. package/dist/src/aws/utils/stackStatus.js +1 -90
  42. package/dist/src/events/index.d.ts +13 -0
  43. package/dist/src/events/index.js +1 -0
  44. package/dist/src/index.d.ts +34 -17
  45. package/dist/src/index.js +41 -21
  46. package/dist/src/orchestration/__tests__/cascadeTestHelpers.d.ts +12 -0
  47. package/dist/src/orchestration/__tests__/cascadeTestHelpers.js +78 -0
  48. package/dist/src/orchestration/activeDeploymentGuard.d.ts +10 -0
  49. package/dist/src/orchestration/activeDeploymentGuard.js +39 -0
  50. package/dist/src/orchestration/applicationDeploy.js +46 -229
  51. package/dist/src/orchestration/applicationDeployHelpers.d.ts +39 -0
  52. package/dist/src/orchestration/applicationDeployHelpers.js +223 -0
  53. package/dist/src/orchestration/applicationDestroy.d.ts +14 -0
  54. package/dist/src/orchestration/applicationDestroy.js +131 -0
  55. package/dist/src/orchestration/builders/dockerBuilder.d.ts +17 -0
  56. package/dist/src/orchestration/builders/dockerBuilder.js +98 -0
  57. package/dist/src/orchestration/builders/frameworkRegistry.d.ts +23 -0
  58. package/dist/src/orchestration/builders/frameworkRegistry.js +1 -0
  59. package/dist/src/orchestration/builders/index.d.ts +4 -0
  60. package/dist/src/orchestration/builders/index.js +1 -0
  61. package/dist/src/orchestration/builders/openNextBuilder.d.ts +21 -0
  62. package/dist/src/orchestration/builders/openNextBuilder.js +144 -0
  63. package/dist/src/orchestration/cascadeDestroyHelpers.d.ts +30 -0
  64. package/dist/src/orchestration/cascadeDestroyHelpers.js +1 -0
  65. package/dist/src/orchestration/cascadeHelpers.d.ts +46 -0
  66. package/dist/src/orchestration/cascadeHelpers.js +160 -0
  67. package/dist/src/orchestration/contextHelpers.d.ts +46 -2
  68. package/dist/src/orchestration/contextHelpers.js +93 -1
  69. package/dist/src/orchestration/destroy.d.ts +13 -0
  70. package/dist/src/orchestration/destroy.js +67 -0
  71. package/dist/src/orchestration/detectionPipeline.d.ts +2 -11
  72. package/dist/src/orchestration/detectionPipeline.js +29 -10
  73. package/dist/src/orchestration/dockerBuildHelper.d.ts +10 -0
  74. package/dist/src/orchestration/dockerBuildHelper.js +49 -0
  75. package/dist/src/orchestration/dockerInterface.d.ts +4 -2
  76. package/dist/src/orchestration/index.d.ts +8 -1
  77. package/dist/src/orchestration/index.js +1 -3
  78. package/dist/src/orchestration/manifestSecretParser.d.ts +11 -0
  79. package/dist/src/orchestration/manifestSecretParser.js +1 -0
  80. package/dist/src/orchestration/openNextBuild.d.ts +28 -0
  81. package/dist/src/orchestration/openNextBuild.js +243 -0
  82. package/dist/src/orchestration/organisationDeploy.js +110 -233
  83. package/dist/src/orchestration/organisationDestroy.d.ts +24 -0
  84. package/dist/src/orchestration/organisationDestroy.js +189 -0
  85. package/dist/src/orchestration/organisationSetup.d.ts +6 -4
  86. package/dist/src/orchestration/organisationSetup.js +28 -8
  87. package/dist/src/orchestration/resolveOperation.js +68 -6
  88. package/dist/src/orchestration/serviceFactory.d.ts +4 -0
  89. package/dist/src/orchestration/serviceFactory.js +1 -16
  90. package/dist/src/orchestration/spawnHelpers.d.ts +47 -0
  91. package/dist/src/orchestration/spawnHelpers.js +1 -0
  92. package/dist/src/orchestration/stackCleanup.d.ts +39 -0
  93. package/dist/src/orchestration/stackCleanup.js +1 -0
  94. package/dist/src/orchestration/welcomeImageHelper.d.ts +15 -0
  95. package/dist/src/orchestration/welcomeImageHelper.js +64 -0
  96. package/dist/src/services/application/ApplicationStackService.d.ts +21 -30
  97. package/dist/src/services/application/ApplicationStackService.js +16 -234
  98. package/dist/src/services/application/applicationStackHelpers.d.ts +46 -0
  99. package/dist/src/services/application/applicationStackHelpers.js +248 -0
  100. package/dist/src/services/application/index.d.ts +1 -0
  101. package/dist/src/services/application/index.js +1 -1
  102. package/dist/src/services/index.d.ts +6 -0
  103. package/dist/src/services/index.js +1 -0
  104. package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -67
  105. package/dist/src/services/infrastructure/CdkCommandRunner.d.ts +10 -2
  106. package/dist/src/services/infrastructure/CdkCommandRunner.js +18 -15
  107. package/dist/src/services/infrastructure/CdkErrorFormatter.js +16 -194
  108. package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -41
  109. package/dist/src/services/infrastructure/CdkOutputAnalyser.js +1 -1
  110. package/dist/src/services/infrastructure/CdkOutputParser.js +2 -33
  111. package/dist/src/services/infrastructure/CdkProcessManager.d.ts +5 -0
  112. package/dist/src/services/infrastructure/CdkProcessManager.js +81 -47
  113. package/dist/src/services/infrastructure/CdkService.d.ts +7 -53
  114. package/dist/src/services/infrastructure/CdkService.js +41 -83
  115. package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +50 -0
  116. package/dist/src/services/infrastructure/CdkServiceTypes.js +0 -0
  117. package/dist/src/services/infrastructure/CloudFormationService.js +9 -10
  118. package/dist/src/services/infrastructure/ICdkProcessManager.d.ts +27 -0
  119. package/dist/src/services/infrastructure/ICdkProcessManager.js +1 -0
  120. package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.d.ts +9 -0
  121. package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.js +1 -0
  122. package/dist/src/services/infrastructure/cdkServiceHelpers.d.ts +9 -0
  123. package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -0
  124. package/dist/src/services/infrastructure/constructMapEnrichment.d.ts +7 -0
  125. package/dist/src/services/infrastructure/constructMapEnrichment.js +1 -0
  126. package/dist/src/services/infrastructure/index.d.ts +3 -1
  127. package/dist/src/services/infrastructure/index.js +1 -7
  128. package/dist/src/services/supporting/TemplateHashService.js +1 -1
  129. package/dist/src/services/supporting/helpers.js +1 -81
  130. package/dist/src/services/supporting/index.js +1 -3
  131. package/dist/src/steps/index.d.ts +1 -0
  132. package/dist/src/steps/index.js +1 -0
  133. package/dist/src/steps/stepRegistry.d.ts +71 -0
  134. package/dist/src/steps/stepRegistry.js +505 -0
  135. package/dist/src/types/FjallState.js +1 -118
  136. package/dist/src/types/ProgressEvent.js +1 -48
  137. package/dist/src/types/application/ApplicationServiceTypes.js +1 -30
  138. package/dist/src/types/application/index.js +1 -1
  139. package/dist/src/types/callbacks.d.ts +76 -4
  140. package/dist/src/types/callbacks.js +0 -1
  141. package/dist/src/types/constants.d.ts +2 -0
  142. package/dist/src/types/constants.js +1 -6
  143. package/dist/src/types/credentials.js +0 -1
  144. package/dist/src/types/deployment/DeploymentServiceTypes.d.ts +5 -2
  145. package/dist/src/types/deployment/DeploymentServiceTypes.js +1 -1
  146. package/dist/src/types/deployment/DeploymentTypes.js +0 -1
  147. package/dist/src/types/deployment/cloudformation.js +0 -1
  148. package/dist/src/types/deployment/index.d.ts +3 -1
  149. package/dist/src/types/deployment/index.js +1 -1
  150. package/dist/src/types/deployment/parallel.js +1 -10
  151. package/dist/src/types/deploymentEventSchema.d.ts +158 -0
  152. package/dist/src/types/deploymentEventSchema.js +1 -0
  153. package/dist/src/types/detection.d.ts +22 -0
  154. package/dist/src/types/detection.js +1 -0
  155. package/dist/src/types/entitlements.d.ts +31 -0
  156. package/dist/src/types/entitlements.js +0 -0
  157. package/dist/src/types/errors/CdkError.js +1 -20
  158. package/dist/src/types/errors/ServiceError.d.ts +2 -1
  159. package/dist/src/types/errors/ServiceError.js +1 -119
  160. package/dist/src/types/errors/index.d.ts +2 -0
  161. package/dist/src/types/errors/index.js +1 -0
  162. package/dist/src/types/events.d.ts +3 -9
  163. package/dist/src/types/events.js +0 -5
  164. package/dist/src/types/frameworkBuilder.d.ts +96 -0
  165. package/dist/src/types/frameworkBuilder.js +8 -0
  166. package/dist/src/types/index.d.ts +19 -4
  167. package/dist/src/types/index.js +1 -9
  168. package/dist/src/types/operations.d.ts +3 -2
  169. package/dist/src/types/operations.js +1 -285
  170. package/dist/src/types/orgConfig.d.ts +2 -10
  171. package/dist/src/types/orgConfig.js +0 -11
  172. package/dist/src/types/params.d.ts +60 -1
  173. package/dist/src/types/patternDetection.d.ts +14 -16
  174. package/dist/src/types/patternDetection.js +14 -18
  175. package/dist/src/types/patternTypes.d.ts +19 -0
  176. package/dist/src/types/patternTypes.js +1 -0
  177. package/dist/src/types/stepDefinitions.d.ts +163 -0
  178. package/dist/src/types/stepDefinitions.js +98 -0
  179. package/dist/src/types/validation.js +0 -1
  180. package/dist/src/util/dockerfileDetection.d.ts +5 -0
  181. package/dist/src/util/dockerfileDetection.js +1 -0
  182. package/dist/src/util/index.d.ts +4 -3
  183. package/dist/src/util/index.js +1 -3
  184. package/dist/src/util/sequencedCallbacks.d.ts +44 -0
  185. package/dist/src/util/sequencedCallbacks.js +1 -0
  186. package/package.json +49 -8
  187. package/dist/src/aws/utils/CloudFormationFailureAnalyser.d.ts +0 -32
  188. package/dist/src/aws/utils/CloudFormationFailureAnalyser.js +0 -228
  189. package/dist/src/aws/utils/errors.d.ts +0 -26
  190. package/dist/src/aws/utils/errors.js +0 -59
  191. package/dist/src/util/fsHelpers.d.ts +0 -4
  192. package/dist/src/util/fsHelpers.js +0 -16
  193. package/dist/src/util/securityHelpers.d.ts +0 -31
  194. package/dist/src/util/securityHelpers.js +0 -124
  195. package/dist/src/util/singleton.d.ts +0 -2
  196. package/dist/src/util/singleton.js +0 -9
  197. package/dist/src/util/sleep.d.ts +0 -4
  198. package/dist/src/util/sleep.js +0 -4
@@ -0,0 +1,505 @@
1
+ import { APPLICATION_STACKS, APPLICATION_DEPLOY_ORDER, getApplicationStepName, getApplicationStepId } from "../types/index.js";
2
+ import { STEP_IDS, STEP_NAMES, INFRA_STEP_NAME } from "../types/index.js";
3
+ /**
4
+ * Map from stack type to destroy step ID
5
+ */
6
+ const DESTROY_STEP_MAP = {
7
+ Network: STEP_IDS.NETWORK_DESTROY,
8
+ Storage: STEP_IDS.STORAGE_DESTROY,
9
+ Messaging: STEP_IDS.MESSAGING_DESTROY,
10
+ Database: STEP_IDS.DATABASE_DESTROY,
11
+ Compute: STEP_IDS.COMPUTE_DESTROY,
12
+ Cdn: STEP_IDS.CDN_DESTROY
13
+ };
14
+ /**
15
+ * Get the destroy step ID for a given stack type
16
+ */
17
+ export function getDestroyStepId(stackType) {
18
+ return DESTROY_STEP_MAP[stackType] ?? `${stackType.toLowerCase()}-destroy`;
19
+ }
20
+ /**
21
+ * Step registry providing step filtering and registry logic.
22
+ * Step definitions (STEP_IDS, STEP_NAMES, StepDefinition, StepContext)
23
+ * live in types/stepDefinitions.ts — the single source of truth.
24
+ *
25
+ * This class was moved from CLI to deploy-core so both CLI and webapp
26
+ * can use the same step registry.
27
+ */
28
+ export class StepRegistry {
29
+ /**
30
+ * Unified deployment steps for ALL deployment types
31
+ * Key format: "{deploymentType}-{operation}"
32
+ */
33
+ static DEPLOYMENT_STEPS = new Map([
34
+ [
35
+ "application-deploy",
36
+ [
37
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
38
+ {
39
+ id: STEP_IDS.DETECT_CONFIG,
40
+ name: STEP_NAMES.PREPARE_DEPLOY
41
+ },
42
+ {
43
+ id: STEP_IDS.BOOTSTRAP,
44
+ name: STEP_NAMES.BOOTSTRAP,
45
+ conditions: { requiresInfra: true }
46
+ },
47
+ {
48
+ id: getApplicationStepId(APPLICATION_STACKS.NETWORK, "deploy"),
49
+ name: getApplicationStepName(APPLICATION_STACKS.NETWORK, "deploy"),
50
+ conditions: { requiresInfra: true, requiresNetwork: true }
51
+ },
52
+ {
53
+ id: getApplicationStepId(APPLICATION_STACKS.STORAGE, "deploy"),
54
+ name: getApplicationStepName(APPLICATION_STACKS.STORAGE, "deploy"),
55
+ conditions: { requiresInfra: true, requiresStorage: true }
56
+ },
57
+ {
58
+ id: getApplicationStepId(APPLICATION_STACKS.MESSAGING, "deploy"),
59
+ name: getApplicationStepName(APPLICATION_STACKS.MESSAGING, "deploy"),
60
+ conditions: { requiresInfra: true, requiresMessaging: true }
61
+ },
62
+ {
63
+ id: STEP_IDS.DOCKER_OPERATIONS,
64
+ name: "Docker operations", // Dynamic name will be set by context
65
+ conditions: {
66
+ requiresDocker: true,
67
+ requiresCompute: true,
68
+ skipForInfraOnly: true
69
+ }
70
+ },
71
+ {
72
+ id: STEP_IDS.TAG_ECR_IMAGES,
73
+ name: "Tagging container images",
74
+ conditions: {
75
+ requiresImageTagging: true,
76
+ requiresCompute: true,
77
+ skipForInfraOnly: true
78
+ }
79
+ },
80
+ {
81
+ id: getApplicationStepId(APPLICATION_STACKS.DATABASE, "deploy"),
82
+ name: getApplicationStepName(APPLICATION_STACKS.DATABASE, "deploy"),
83
+ conditions: { requiresInfra: true, requiresDatabase: true }
84
+ },
85
+ {
86
+ id: getApplicationStepId(APPLICATION_STACKS.COMPUTE, "deploy"),
87
+ name: getApplicationStepName(APPLICATION_STACKS.COMPUTE, "deploy"),
88
+ conditions: { requiresInfra: true, requiresCompute: true }
89
+ },
90
+ {
91
+ id: getApplicationStepId(APPLICATION_STACKS.CDN, "deploy"),
92
+ name: getApplicationStepName(APPLICATION_STACKS.CDN, "deploy"),
93
+ conditions: { requiresInfra: true, requiresCdn: true }
94
+ },
95
+ {
96
+ id: STEP_IDS.ECS_UPDATE,
97
+ name: "Updating ECS service",
98
+ conditions: { requiresECS: true, requiresCompute: true }
99
+ },
100
+ {
101
+ id: STEP_IDS.ECS_MONITOR,
102
+ name: "Monitoring deployment",
103
+ conditions: { requiresECS: true, requiresCompute: true }
104
+ }
105
+ ]
106
+ ],
107
+ [
108
+ "application-destroy",
109
+ [
110
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
111
+ { id: STEP_IDS.DETECT_CONFIG, name: STEP_NAMES.CHECK_INFRA_STATE },
112
+ {
113
+ id: getApplicationStepId(APPLICATION_STACKS.CDN, "destroy"),
114
+ name: getApplicationStepName(APPLICATION_STACKS.CDN, "destroy"),
115
+ conditions: { requiresCdn: true }
116
+ },
117
+ {
118
+ id: getApplicationStepId(APPLICATION_STACKS.COMPUTE, "destroy"),
119
+ name: getApplicationStepName(APPLICATION_STACKS.COMPUTE, "destroy")
120
+ },
121
+ {
122
+ id: getApplicationStepId(APPLICATION_STACKS.DATABASE, "destroy"),
123
+ name: getApplicationStepName(APPLICATION_STACKS.DATABASE, "destroy")
124
+ },
125
+ {
126
+ id: getApplicationStepId(APPLICATION_STACKS.MESSAGING, "destroy"),
127
+ name: getApplicationStepName(APPLICATION_STACKS.MESSAGING, "destroy"),
128
+ conditions: { requiresMessaging: true }
129
+ },
130
+ {
131
+ id: getApplicationStepId(APPLICATION_STACKS.STORAGE, "destroy"),
132
+ name: getApplicationStepName(APPLICATION_STACKS.STORAGE, "destroy"),
133
+ conditions: { requiresStorage: true }
134
+ },
135
+ {
136
+ id: getApplicationStepId(APPLICATION_STACKS.NETWORK, "destroy"),
137
+ name: getApplicationStepName(APPLICATION_STACKS.NETWORK, "destroy")
138
+ }
139
+ ]
140
+ ],
141
+ [
142
+ "organisation-deploy",
143
+ [
144
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
145
+ { id: STEP_IDS.ORG_SETUP, name: "Configuring organisation" },
146
+ { id: STEP_IDS.PREPARE, name: STEP_NAMES.PREPARE_DEPLOY },
147
+ { id: STEP_IDS.BOOTSTRAP, name: STEP_NAMES.BOOTSTRAP },
148
+ {
149
+ id: STEP_IDS.DEPLOY,
150
+ name: "Deploying organisation",
151
+ conditions: { requiresOrgChanges: true }
152
+ },
153
+ {
154
+ id: STEP_IDS.CASCADE_PLATFORM,
155
+ name: "Deploying platform",
156
+ conditions: {
157
+ requiresPlatformAccount: true,
158
+ requiresPlatformChanges: true
159
+ }
160
+ },
161
+ {
162
+ id: STEP_IDS.CASCADE_DOMAINS,
163
+ name: "Deploying domains",
164
+ conditions: {
165
+ requiresDomainConfiguration: true,
166
+ requiresDomainChanges: true
167
+ }
168
+ },
169
+ {
170
+ id: STEP_IDS.CASCADE_ACCOUNTS,
171
+ name: "Deploying accounts",
172
+ conditions: {
173
+ requiresMemberAccounts: true,
174
+ requiresAccountChanges: true
175
+ }
176
+ }
177
+ ]
178
+ ],
179
+ [
180
+ "organisation-destroy",
181
+ [
182
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
183
+ { id: STEP_IDS.CFN_CHECK, name: STEP_NAMES.CHECK_INFRA_STATE },
184
+ {
185
+ id: STEP_IDS.CASCADE_ACCOUNTS,
186
+ name: "Destroying account infrastructure",
187
+ conditions: { requiresMemberAccounts: true }
188
+ },
189
+ {
190
+ id: STEP_IDS.CASCADE_PLATFORM,
191
+ name: "Destroying platform infrastructure",
192
+ conditions: { requiresPlatformAccount: true }
193
+ },
194
+ { id: STEP_IDS.DESTROY, name: "Destroying organisation infrastructure" }
195
+ ]
196
+ ],
197
+ [
198
+ "platform-deploy",
199
+ [
200
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
201
+ { id: STEP_IDS.CONNECT, name: INFRA_STEP_NAME.CONNECT },
202
+ { id: STEP_IDS.PREPARE_ENVIRONMENT, name: INFRA_STEP_NAME.PREPARE },
203
+ { id: STEP_IDS.DEPLOY, name: INFRA_STEP_NAME.DEPLOY },
204
+ { id: STEP_IDS.MONITORING, name: INFRA_STEP_NAME.MONITORING }
205
+ ]
206
+ ],
207
+ [
208
+ "platform-destroy",
209
+ [
210
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
211
+ { id: STEP_IDS.CFN_CHECK, name: "Checking infrastructure state" },
212
+ { id: STEP_IDS.ORG_DESTROY, name: "Destroying platform infrastructure" }
213
+ ]
214
+ ],
215
+ [
216
+ "account-deploy",
217
+ [
218
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
219
+ { id: STEP_IDS.CONNECT, name: INFRA_STEP_NAME.CONNECT },
220
+ { id: STEP_IDS.PREPARE_ENVIRONMENT, name: INFRA_STEP_NAME.PREPARE },
221
+ { id: STEP_IDS.DEPLOY, name: INFRA_STEP_NAME.DEPLOY },
222
+ { id: STEP_IDS.MONITORING, name: INFRA_STEP_NAME.MONITORING }
223
+ ]
224
+ ],
225
+ [
226
+ "account-destroy",
227
+ [
228
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
229
+ { id: STEP_IDS.CFN_CHECK, name: "Checking infrastructure state" },
230
+ { id: STEP_IDS.ORG_DESTROY, name: "Destroying account infrastructure" }
231
+ ]
232
+ ]
233
+ ]);
234
+ /**
235
+ * Get filtered steps based on deployment context
236
+ * This is the SINGLE method for getting steps for ANY deployment
237
+ */
238
+ static getSteps(context) {
239
+ // Special handling for deploy-only mode
240
+ if (context.deploymentType === "application" &&
241
+ context.operation === "deploy" &&
242
+ context.deployOnly) {
243
+ // Return simplified steps for deploy-only mode.
244
+ // Stack step IDs are derived from APPLICATION_DEPLOY_ORDER so they
245
+ // match the IDs deploy-core actually emits during deployment.
246
+ return [
247
+ { id: STEP_IDS.AUTH, name: STEP_NAMES.AUTH },
248
+ {
249
+ id: STEP_IDS.DOCKER_OPERATIONS,
250
+ name: "Build and push Docker container"
251
+ },
252
+ ...APPLICATION_DEPLOY_ORDER.map((stack) => ({
253
+ id: getApplicationStepId(stack, "deploy"),
254
+ name: getApplicationStepName(stack, "deploy")
255
+ }))
256
+ ];
257
+ }
258
+ const key = `${context.deploymentType}-${context.operation}`;
259
+ const steps = this.DEPLOYMENT_STEPS.get(key) ?? [];
260
+ // Apply context-based filtering
261
+ const filteredSteps = steps
262
+ .filter((step) => {
263
+ // Application-specific filtering
264
+ if (context.deploymentType === "application") {
265
+ const isOpenNextDeploy = context.builderName === "opennext";
266
+ // Skip infrastructure steps in deploy-only mode
267
+ if (step.conditions?.requiresInfra && context.deployOnly) {
268
+ return false;
269
+ }
270
+ // Resource gating: when resources are known, skip steps for absent resources
271
+ if (context.resources) {
272
+ const r = context.resources;
273
+ if (step.conditions?.requiresNetwork && !r.hasNetwork)
274
+ return false;
275
+ if (step.conditions?.requiresCompute && !r.hasCompute)
276
+ return false;
277
+ if (step.conditions?.requiresDatabase && !r.hasDatabase)
278
+ return false;
279
+ if (step.conditions?.requiresStorage && !r.hasStorage)
280
+ return false;
281
+ if (step.conditions?.requiresMessaging && !r.hasMessaging)
282
+ return false;
283
+ if (step.conditions?.requiresCdn && !r.hasCdn)
284
+ return false;
285
+ }
286
+ else if (context.operation === "deploy" && !isOpenNextDeploy) {
287
+ // Pre-synth (standard deploy): hide ALL resource-specific steps
288
+ // until manifest detection completes post-synth.
289
+ // OpenNext patterns always use all 6 stacks so they keep theirs.
290
+ // Destroy never runs synth so resources stays undefined — show all steps.
291
+ const isResourceStep = step.conditions?.requiresNetwork ||
292
+ step.conditions?.requiresCompute ||
293
+ step.conditions?.requiresDatabase ||
294
+ step.conditions?.requiresStorage ||
295
+ step.conditions?.requiresMessaging ||
296
+ step.conditions?.requiresCdn;
297
+ if (isResourceStep) {
298
+ return false;
299
+ }
300
+ }
301
+ // Handle Docker operations step - skip for OpenNext deployments
302
+ if (step.conditions?.requiresDocker ||
303
+ step.id === STEP_IDS.DOCKER_OPERATIONS) {
304
+ // Skip Docker steps for OpenNext deployments (uses Lambda instead)
305
+ if (isOpenNextDeploy) {
306
+ return false;
307
+ }
308
+ // Skip in infra-only mode
309
+ if (context.infraOnly) {
310
+ return false;
311
+ }
312
+ // Skip when post-synth detection confirms no Compute stack
313
+ if (context.resources && !context.resources.hasCompute) {
314
+ return false;
315
+ }
316
+ // For deploy-only mode, always show if we have a Dockerfile
317
+ if (context.deployOnly) {
318
+ return context.hasDockerfile === true;
319
+ }
320
+ // For normal mode, show if:
321
+ // - We have a Dockerfile (build+push), OR
322
+ // - No Dockerfile + Compute stack (ECR init + welcome image)
323
+ if (context.hasDockerfile === true) {
324
+ return true;
325
+ }
326
+ if (context.hasDockerfile === false && !context.isManagedAccount) {
327
+ return true;
328
+ }
329
+ // Don't show during initial state (hasDockerfile undefined)
330
+ return false;
331
+ }
332
+ // Handle ECS update/monitor steps - skip for OpenNext, only show in deploy-only mode
333
+ if (step.conditions?.requiresECS) {
334
+ if (isOpenNextDeploy) {
335
+ return false;
336
+ }
337
+ return (context.deployOnly === true && context.hasDockerfile === true);
338
+ }
339
+ // Handle image tagging step - show when no Dockerfile + Compute exists
340
+ // ECS expects :<serviceName>-latest tags; initialiseECR only pushes :latest
341
+ if (step.conditions?.requiresImageTagging) {
342
+ if (context.infraOnly) {
343
+ return false;
344
+ }
345
+ if (isOpenNextDeploy) {
346
+ return false;
347
+ }
348
+ // Skip when post-synth detection confirms no Compute stack
349
+ if (context.resources && !context.resources.hasCompute) {
350
+ return false;
351
+ }
352
+ // Show when no Dockerfile (we're not building our own image)
353
+ return context.hasDockerfile === false;
354
+ }
355
+ }
356
+ // Organisation cascade filtering: hide cascade steps when no accounts exist
357
+ if (step.conditions?.requiresMemberAccounts &&
358
+ !context.hasMemberAccounts) {
359
+ return false;
360
+ }
361
+ if (step.conditions?.requiresPlatformAccount &&
362
+ !context.hasPlatformAccount) {
363
+ return false;
364
+ }
365
+ // Organisation change-detection filtering: hide steps until detection confirms changes.
366
+ // Steps only appear when the corresponding flag is explicitly true.
367
+ // Pre-detection (undefined) hides steps to avoid flash-then-remove.
368
+ if (step.conditions?.requiresOrgChanges &&
369
+ context.hasOrgChanges !== true) {
370
+ return false;
371
+ }
372
+ if (step.conditions?.requiresPlatformChanges &&
373
+ context.hasPlatformChanges !== true) {
374
+ return false;
375
+ }
376
+ if (step.conditions?.requiresAccountChanges &&
377
+ context.hasAccountChanges !== true) {
378
+ return false;
379
+ }
380
+ if (step.conditions?.requiresDomainConfiguration &&
381
+ !context.hasDomainConfiguration) {
382
+ return false;
383
+ }
384
+ if (step.conditions?.requiresDomainChanges &&
385
+ context.hasDomainChanges !== true) {
386
+ return false;
387
+ }
388
+ return true;
389
+ })
390
+ .map((step) => {
391
+ // Dynamically set the name for docker-operations based on context
392
+ if (step.id === STEP_IDS.DOCKER_OPERATIONS) {
393
+ return {
394
+ ...step,
395
+ name: context.hasDockerfile
396
+ ? "Building and pushing Docker image"
397
+ : "Initialising container repository"
398
+ };
399
+ }
400
+ return step;
401
+ });
402
+ return filteredSteps;
403
+ }
404
+ /**
405
+ * Get step names for UI display
406
+ */
407
+ static getStepNames(context) {
408
+ return this.getSteps(context).map((s) => s.name);
409
+ }
410
+ /**
411
+ * Get the index of a specific step in the filtered list
412
+ */
413
+ static getStepIndex(stepId, context) {
414
+ const steps = this.getSteps(context);
415
+ return steps.findIndex((s) => s.id === stepId);
416
+ }
417
+ /**
418
+ * Get a step by ID from all steps
419
+ */
420
+ static getStepById(stepId, deploymentType = "application") {
421
+ // Search in deploy steps first
422
+ const deployKey = `${deploymentType}-deploy`;
423
+ const deploySteps = this.DEPLOYMENT_STEPS.get(deployKey) || [];
424
+ const found = deploySteps.find((s) => s.id === stepId);
425
+ if (found)
426
+ return found;
427
+ // Then search in destroy steps
428
+ const destroyKey = `${deploymentType}-destroy`;
429
+ const destroySteps = this.DEPLOYMENT_STEPS.get(destroyKey) || [];
430
+ return destroySteps.find((s) => s.id === stepId);
431
+ }
432
+ /**
433
+ * Check if a step should be included based on context
434
+ */
435
+ static isStepIncluded(stepId, context) {
436
+ const steps = this.getSteps(context);
437
+ return steps.some((s) => s.id === stepId);
438
+ }
439
+ /**
440
+ * Convenience method for creating context
441
+ */
442
+ static createContext(deploymentType, operation, options) {
443
+ return {
444
+ deploymentType,
445
+ operation,
446
+ deployOnly: options?.deployOnly ?? false,
447
+ infraOnly: options?.infraOnly ?? false,
448
+ isManagedAccount: options?.isManagedAccount ?? false,
449
+ hasDockerfile: options?.hasDockerfile ?? false,
450
+ pattern: options?.pattern ?? null,
451
+ builderName: options?.builderName,
452
+ resources: options?.resources
453
+ };
454
+ }
455
+ /**
456
+ * Get all deployment types
457
+ */
458
+ static getDeploymentTypes() {
459
+ return ["application", "organisation", "platform", "account"];
460
+ }
461
+ /**
462
+ * Get stack names for a deployment type
463
+ */
464
+ static getStackNames(deploymentType) {
465
+ switch (deploymentType) {
466
+ case "application":
467
+ return [...APPLICATION_DEPLOY_ORDER];
468
+ case "organisation":
469
+ return ["Organisation"];
470
+ case "platform":
471
+ return ["Platform"];
472
+ case "account":
473
+ return ["Account"];
474
+ default:
475
+ return [];
476
+ }
477
+ }
478
+ /**
479
+ * Get infrastructure step IDs for skipping
480
+ */
481
+ static getInfraStepIds() {
482
+ return [
483
+ STEP_IDS.CFN_CHECK,
484
+ STEP_IDS.BOOTSTRAP,
485
+ STEP_IDS.DIFF,
486
+ STEP_IDS.NETWORK,
487
+ STEP_IDS.STORAGE,
488
+ STEP_IDS.MESSAGING,
489
+ STEP_IDS.DATABASE,
490
+ STEP_IDS.COMPUTE,
491
+ STEP_IDS.CDN
492
+ ];
493
+ }
494
+ /**
495
+ * Get Docker step IDs for skipping
496
+ */
497
+ static getDockerStepIds() {
498
+ return [
499
+ STEP_IDS.ECR_INIT,
500
+ STEP_IDS.DOCKER_DEPLOY,
501
+ STEP_IDS.ECS_UPDATE,
502
+ STEP_IDS.ECS_MONITOR
503
+ ];
504
+ }
505
+ }
@@ -1,118 +1 @@
1
- import { z } from "zod";
2
- import { readFile, writeFile, unlink, rename, mkdir } from "fs/promises";
3
- import { dirname, join } from "path";
4
- import { fileExists } from "../util/fsHelpers.js";
5
- import { logger, getErrorMessage } from "@fjall/util";
6
- const TemplateHashEntrySchema = z
7
- .object({
8
- hash: z.string(),
9
- deployedAt: z.string(),
10
- stackStatus: z.string().optional()
11
- })
12
- .strict();
13
- /**
14
- * State file schema for hash-based change detection.
15
- * Location: fjall/{appName}/.fjall-state.json
16
- */
17
- export const FjallStateFileSchema = z
18
- .object({
19
- version: z.literal(1),
20
- lastDeployedAt: z.string().optional(),
21
- templateHashes: z.record(z.string(), TemplateHashEntrySchema),
22
- metadata: z.record(z.string(), z.unknown()).optional()
23
- })
24
- .strict();
25
- const STATE_FILE_NAME = ".fjall-state.json";
26
- /**
27
- * Get the state file path for an application
28
- */
29
- export function getStateFilePath(appPath) {
30
- return join(appPath, STATE_FILE_NAME);
31
- }
32
- /**
33
- * Read state file from application directory.
34
- * Returns null if file doesn't exist or is corrupt (graceful degradation).
35
- */
36
- export async function readStateFile(appPath) {
37
- const statePath = getStateFilePath(appPath);
38
- if (!(await fileExists(statePath))) {
39
- return null;
40
- }
41
- try {
42
- const content = await readFile(statePath, "utf-8");
43
- const parsed = JSON.parse(content);
44
- const result = FjallStateFileSchema.safeParse(parsed);
45
- if (!result.success) {
46
- return null;
47
- }
48
- return result.data;
49
- }
50
- catch (err) {
51
- logger.debug("FjallState", "Failed to read state file", {
52
- path: statePath,
53
- error: getErrorMessage(err)
54
- });
55
- return null;
56
- }
57
- }
58
- /**
59
- * Write state file atomically using temp file + rename pattern.
60
- * Prevents partial writes from corrupting the state file.
61
- */
62
- export async function writeStateFile(appPath, state) {
63
- const statePath = getStateFilePath(appPath);
64
- const tempPath = `${statePath}.${Date.now()}.tmp`;
65
- await mkdir(dirname(statePath), { recursive: true });
66
- try {
67
- await writeFile(tempPath, JSON.stringify(state, null, 2), "utf-8");
68
- await rename(tempPath, statePath);
69
- }
70
- catch (error) {
71
- try {
72
- await unlink(tempPath);
73
- }
74
- catch {
75
- // Temp file cleanup is non-fatal
76
- }
77
- throw error;
78
- }
79
- }
80
- /**
81
- * Create an empty state file structure
82
- */
83
- export function createEmptyState() {
84
- return {
85
- version: 1,
86
- templateHashes: {}
87
- };
88
- }
89
- /**
90
- * Delete state file for an application directory.
91
- * No-op if the file does not exist.
92
- */
93
- export async function deleteStateFile(appPath) {
94
- const statePath = getStateFilePath(appPath);
95
- try {
96
- await unlink(statePath);
97
- }
98
- catch {
99
- /* file may not exist */
100
- }
101
- }
102
- /**
103
- * Update a single template hash in the state
104
- */
105
- export function updateTemplateHash(state, stackName, hash, stackStatus) {
106
- return {
107
- ...state,
108
- lastDeployedAt: new Date().toISOString(),
109
- templateHashes: {
110
- ...state.templateHashes,
111
- [stackName]: {
112
- hash,
113
- deployedAt: new Date().toISOString(),
114
- ...(stackStatus !== undefined && { stackStatus })
115
- }
116
- }
117
- };
118
- }
1
+ import{z as e}from"zod";import{randomBytes as d}from"crypto";import{readFile as u,writeFile as m,unlink as c,rename as f,mkdir as h}from"fs/promises";import{dirname as S,join as g}from"path";import{fileExists as y}from"@fjall/util/fsHelpers";import{logger as i}from"@fjall/util/logger";import{getErrorMessage as l}from"@fjall/util";const w=e.object({hash:e.string(),deployedAt:e.string(),stackStatus:e.string().optional()}).strict(),F=e.object({version:e.literal(1),lastDeployedAt:e.string().optional(),templateHashes:e.record(e.string(),w),metadata:e.record(e.string(),e.unknown()).optional()}).strict(),j=".fjall-state.json";function s(r){return g(r,j)}async function D(r){const a=s(r);if(!await y(a))return null;try{const t=await u(a,"utf-8"),n=JSON.parse(t),o=F.safeParse(n);return o.success?o.data:null}catch(t){return i.debug("FjallState","Failed to read state file",{path:a,error:l(t)}),null}}async function N(r,a){const t=s(r),n=`${t}.${Date.now()}-${d(4).toString("hex")}.tmp`;await h(S(t),{recursive:!0});try{await m(n,JSON.stringify(a,null,2),"utf-8"),await f(n,t)}catch(o){try{await c(n)}catch(p){i.debug("FjallState","Temp file cleanup failed (non-fatal)",{path:n,error:l(p)})}throw o}}function O(){return{version:1,templateHashes:{}}}async function k(r){const a=s(r);try{await c(a)}catch(t){(typeof t=="object"&&t!==null&&"code"in t?t.code:void 0)!=="ENOENT"&&i.warn("FjallState","Failed to delete state file",{path:a,error:l(t)})}}function v(r,a,t,n){return{...r,lastDeployedAt:new Date().toISOString(),templateHashes:{...r.templateHashes,[a]:{hash:t,deployedAt:new Date().toISOString(),...n!==void 0&&{stackStatus:n}}}}}export{F as FjallStateFileSchema,O as createEmptyState,k as deleteStateFile,s as getStateFilePath,D as readStateFile,v as updateTemplateHash,N as writeStateFile};
@@ -1,48 +1 @@
1
- /**
2
- * Progress callbacks used by infrastructure services.
3
- *
4
- * This is the simpler callback interface used by inner services
5
- * (CdkService, CloudFormationService). The orchestration layer
6
- * bridges between DeployCallbacks and ProgressCallbacks.
7
- */
8
- /**
9
- * Helper to create progress events with optional typing
10
- */
11
- export class ProgressReporter {
12
- callbacks;
13
- stepIndex = 0;
14
- totalSteps;
15
- constructor(callbacks) {
16
- this.callbacks = callbacks;
17
- }
18
- setTotalSteps(total) {
19
- this.totalSteps = total;
20
- }
21
- step(message, options) {
22
- const { metadata, stepIndex, totalSteps } = options ?? {};
23
- this.callbacks?.onProgress?.({
24
- type: "step",
25
- message,
26
- metadata: {
27
- ...metadata,
28
- stepIndex: stepIndex ?? this.stepIndex++,
29
- totalSteps: totalSteps ?? this.totalSteps
30
- }
31
- });
32
- }
33
- info(message, metadata) {
34
- this.callbacks?.onProgress?.({ type: "info", message, metadata });
35
- }
36
- spinner(message, metadata) {
37
- this.callbacks?.onProgress?.({ type: "spinner", message, metadata });
38
- }
39
- warning(message, metadata) {
40
- this.callbacks?.onProgress?.({ type: "warning", message, metadata });
41
- }
42
- debug(message, metadata) {
43
- this.callbacks?.onProgress?.({ type: "debug", message, metadata });
44
- }
45
- error(message, metadata) {
46
- this.callbacks?.onProgress?.({ type: "error", message, metadata });
47
- }
48
- }
1
+ class l{callbacks;stepIndex=0;totalSteps;constructor(s){this.callbacks=s}setTotalSteps(s){this.totalSteps=s}step(s,t){const{metadata:e,stepIndex:a,totalSteps:r}=t??{};this.callbacks?.onProgress?.({type:"step",message:s,metadata:{...e,stepIndex:a??this.stepIndex++,totalSteps:r??this.totalSteps}})}info(s,t){this.callbacks?.onProgress?.({type:"info",message:s,metadata:t})}spinner(s,t){this.callbacks?.onProgress?.({type:"spinner",message:s,metadata:t})}warning(s,t){this.callbacks?.onProgress?.({type:"warning",message:s,metadata:t})}debug(s,t){this.callbacks?.onProgress?.({type:"debug",message:s,metadata:t})}error(s,t){this.callbacks?.onProgress?.({type:"error",message:s,metadata:t})}}export{l as ProgressReporter};