@fjall/deploy-core 0.89.5 → 0.94.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 (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,160 @@
1
+ import { success, failure } from "@fjall/generator";
2
+ import { logger } from "@fjall/util/logger";
3
+ import { maskSensitiveOutput } from "@fjall/util";
4
+ import { ORGANISATION_TYPES, getOrganisationStackName } from "../types/operations.js";
5
+ import { CdkContextBuilder } from "../services/supporting/CdkContextBuilder.js";
6
+ import { stubCallerIdentity } from "../types/deployment/index.js";
7
+ import { CloudFormationService } from "../services/infrastructure/CloudFormationService.js";
8
+ import { buildParamsContext, collectStackOutputs, assumeCascadeRole, forwardOutput } from "./contextHelpers.js";
9
+ import { STRUCTURAL_ENVIRONMENTS } from "@fjall/util";
10
+ /**
11
+ * Partition provider accounts into platform and member accounts.
12
+ * Used by both deploy and destroy orchestration.
13
+ */
14
+ export function partitionAccounts(providerAccounts) {
15
+ const platformAccount = providerAccounts.find((acc) => acc.environment === STRUCTURAL_ENVIRONMENTS.PLATFORM);
16
+ const memberAccounts = providerAccounts.filter((acc) => acc.environment !== STRUCTURAL_ENVIRONMENTS.ROOT &&
17
+ acc.environment !== STRUCTURAL_ENVIRONMENTS.PLATFORM);
18
+ return { platformAccount, memberAccounts };
19
+ }
20
+ // Re-export for backwards compatibility (canonical definition in contextHelpers)
21
+ export { buildCascadeRoleArn } from "./contextHelpers.js";
22
+ export async function deployCascadeAccount(params, services, operation, account, deployType, callbacks, ipamPoolId) {
23
+ const operationKey = `${account.name}-${account.id}`;
24
+ const region = services.awsProvider.getRegion();
25
+ callbacks.onCascadeAccountStart?.(operationKey, account.id, region, deployType);
26
+ // Assume role in target account
27
+ const roleResult = await assumeCascadeRole(services.awsProvider, account.id, region, `fjall-cascade-${account.name}`);
28
+ if (!roleResult.success) {
29
+ callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(roleResult.error.message), region);
30
+ return failure(new Error(`Failed to assume role for ${account.name}: ${maskSensitiveOutput(roleResult.error.message)}`));
31
+ }
32
+ const { provider: accountProvider, credentials: cascadeCredentials } = roleResult.data;
33
+ // Build context for this account (includes IPAM pool ID if available)
34
+ const accountContext = CdkContextBuilder.buildDeploymentContext({
35
+ deployType,
36
+ target: operation.target,
37
+ path: operation.path,
38
+ region,
39
+ accountName: account.name,
40
+ callerIdentity: stubCallerIdentity(account.id),
41
+ ipamPoolId,
42
+ ...buildParamsContext({
43
+ orgConfig: params.orgConfig,
44
+ identity: params.identity,
45
+ skipOidc: params.options?.skipOidc
46
+ })
47
+ }, { verbose: params.options?.verbose }, params.orgConfig);
48
+ // Bootstrap the target account
49
+ callbacks.onCascadeAccountPhaseChange?.(operationKey, "bootstrap", region);
50
+ const bootstrapResult = await services.cdkService.runCdkBootstrap(accountContext, forwardOutput(callbacks), cascadeCredentials);
51
+ if (!bootstrapResult.success) {
52
+ callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(`Bootstrap failed: ${bootstrapResult.error}`), region);
53
+ return failure(new Error(`Bootstrap failed for ${account.name}: ${maskSensitiveOutput(bootstrapResult.error)}`));
54
+ }
55
+ // Deploy the account stack
56
+ callbacks.onCascadeAccountPhaseChange?.(operationKey, "deploy", region);
57
+ const stackName = getOrganisationStackName(deployType === "platform"
58
+ ? ORGANISATION_TYPES.PLATFORM
59
+ : ORGANISATION_TYPES.ACCOUNT);
60
+ const deployResult = await services.cdkService.runCdkDeploy(accountContext, stackName, forwardOutput(callbacks), (event) => callbacks.onCascadeAccountResourceProgress?.(operationKey, event, region), accountProvider, cascadeCredentials);
61
+ if (!deployResult.success) {
62
+ callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(deployResult.error), region);
63
+ return failure(new Error(maskSensitiveOutput(deployResult.error)));
64
+ }
65
+ // Capture stack outputs (OIDC role ARN, etc.) from the target account
66
+ const accountCfn = new CloudFormationService(accountProvider);
67
+ const outputsResult = await accountCfn.getStackOutputs(stackName);
68
+ if (!outputsResult.success) {
69
+ logger.debug("cascadeHelpers", "Failed to read cascade account stack outputs (non-critical)", { stackName, account: account.name });
70
+ }
71
+ const outputs = collectStackOutputs(outputsResult);
72
+ callbacks.onCascadeAccountComplete?.(operationKey, true, undefined, region, outputs);
73
+ return success({ outputs });
74
+ }
75
+ /**
76
+ * Read Platform stack outputs to extract IPAM pool IDs for member accounts.
77
+ * Output keys follow the pattern `IpamPoolId{12-digit-accountId}{regionSuffix}`.
78
+ * Returns a map keyed by `{accountId}-{regionSuffix}` → pool ID.
79
+ * Non-fatal: returns empty map on any error.
80
+ */
81
+ export async function readPlatformIpamPoolIds(services, platformAccount, callbacks) {
82
+ const poolIds = new Map();
83
+ // Assume role in platform account to read its stack outputs
84
+ const region = services.awsProvider.getRegion();
85
+ const roleResult = await assumeCascadeRole(services.awsProvider, platformAccount.id, region, `fjall-ipam-read-${platformAccount.name}`);
86
+ if (!roleResult.success) {
87
+ logger.debug("organisationDeploy", `Cannot read Platform outputs: ${roleResult.error.message}`);
88
+ return poolIds;
89
+ }
90
+ const platformCfn = new CloudFormationService(roleResult.data.provider);
91
+ const platformStackName = getOrganisationStackName(ORGANISATION_TYPES.PLATFORM);
92
+ const outputsResult = await platformCfn.getStackOutputs(platformStackName);
93
+ if (!outputsResult.success) {
94
+ logger.debug("organisationDeploy", `Failed to read Platform stack outputs: ${outputsResult.error.message}`);
95
+ return poolIds;
96
+ }
97
+ const ipamPattern = /^IpamPoolId(\d{12})(\w+)$/;
98
+ for (const output of outputsResult.data) {
99
+ const match = output.OutputKey?.match(ipamPattern);
100
+ if (match && output.OutputValue) {
101
+ const key = `${match[1]}-${match[2]}`;
102
+ poolIds.set(key, output.OutputValue);
103
+ }
104
+ }
105
+ if (poolIds.size > 0) {
106
+ callbacks.onLog?.(`Read ${poolIds.size} IPAM pool ID(s) from Platform stack`, "info");
107
+ }
108
+ return poolIds;
109
+ }
110
+ /**
111
+ * Deploy configured domains: apex domains sequentially, then delegated
112
+ * domains in parallel. Delegates to the caller-provided DomainDeployProvider.
113
+ */
114
+ export async function deployDomains(provider, callbacks) {
115
+ const domains = provider.getDomains();
116
+ if (domains.length === 0) {
117
+ return { domainsDeployed: 0, errors: [] };
118
+ }
119
+ callbacks.onCascadePhaseStart?.("domains");
120
+ const apexDomains = domains.filter((d) => d.type === "apex");
121
+ const delegatedDomains = domains.filter((d) => d.type === "delegated");
122
+ let domainsDeployed = 0;
123
+ const errors = [];
124
+ // Phase A: Apex domains (sequential — delegation roles must exist first)
125
+ for (const domain of apexDomains) {
126
+ const result = await provider.deployDomain(domain.name, callbacks);
127
+ if (result.success) {
128
+ domainsDeployed++;
129
+ }
130
+ else {
131
+ errors.push(`${domain.name}: ${result.error.message}`);
132
+ }
133
+ }
134
+ // Phase B: Delegated subdomains (parallel — independent of each other)
135
+ if (delegatedDomains.length > 0) {
136
+ const subdomainResults = await Promise.allSettled(delegatedDomains.map((domain) => provider.deployDomain(domain.name, callbacks)));
137
+ for (let i = 0; i < subdomainResults.length; i++) {
138
+ const settled = subdomainResults[i];
139
+ const domain = delegatedDomains[i];
140
+ if (!settled || !domain)
141
+ continue;
142
+ if (settled.status === "fulfilled") {
143
+ if (settled.value.success) {
144
+ domainsDeployed++;
145
+ }
146
+ else {
147
+ errors.push(`${domain.name}: ${settled.value.error.message}`);
148
+ }
149
+ }
150
+ else {
151
+ const message = settled.reason instanceof Error
152
+ ? settled.reason.message
153
+ : String(settled.reason);
154
+ errors.push(`${domain.name}: ${message}`);
155
+ }
156
+ }
157
+ }
158
+ callbacks.onCascadePhaseComplete?.("domains");
159
+ return { domainsDeployed, errors };
160
+ }
@@ -1,10 +1,54 @@
1
- import type { DeployParams } from "../types/params.js";
1
+ import { type Result } from "@fjall/generator";
2
+ import type { ResourceEvent } from "@fjall/util/aws";
3
+ import type { OrgConfig } from "../types/orgConfig.js";
4
+ import type { DeployIdentity } from "../types/credentials.js";
5
+ import type { AwsProviderCredentials } from "../aws/AwsProvider.js";
6
+ import { SimpleAwsProvider } from "../aws/SimpleAwsProvider.js";
7
+ import type { DeploymentContext } from "../types/deployment/DeploymentTypes.js";
8
+ import type { DeployCallbacks } from "../types/callbacks.js";
9
+ import type { DeployServices } from "./serviceFactory.js";
2
10
  /**
3
11
  * Build the orgConfig/identity context fields shared by all deployment paths.
4
12
  * CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string.
5
13
  */
6
- export declare function buildParamsContext(params: DeployParams): {
14
+ export declare function buildParamsContext(params: {
15
+ orgConfig?: OrgConfig;
16
+ identity?: DeployIdentity;
17
+ skipOidc?: boolean;
18
+ }): {
7
19
  orgConfig?: string;
8
20
  fjallOrgId?: string;
9
21
  fjallOidcConfigured?: boolean;
10
22
  };
23
+ /** Forward onOutput callback — reduces lambda repetition across orchestration files. */
24
+ export declare function forwardOutput(callbacks: DeployCallbacks): (chunk: string) => void;
25
+ /** Forward onResourceProgress callback — reduces lambda repetition across orchestration files. */
26
+ export declare function forwardResourceProgress(callbacks: DeployCallbacks): (event: ResourceEvent) => void;
27
+ /** Convert CloudFormation getStackOutputs result to Record<string, string>. */
28
+ export declare function collectStackOutputs(result: Result<Array<{
29
+ OutputKey?: string;
30
+ OutputValue?: string;
31
+ }>, unknown>): Record<string, string> | undefined;
32
+ /** Build the IAM role ARN used for cross-account cascade operations. */
33
+ export declare function buildCascadeRoleArn(accountId: string): string;
34
+ export interface CascadeAssumedRole {
35
+ provider: SimpleAwsProvider;
36
+ credentials: AwsProviderCredentials;
37
+ }
38
+ /**
39
+ * Assume a cross-account cascade role and create a scoped provider.
40
+ * Callers handle failure (callbacks, logging, return type) — this helper
41
+ * only owns the assume + provider construction.
42
+ */
43
+ export declare function assumeCascadeRole(awsProvider: SimpleAwsProvider, accountId: string, region: string, sessionName: string): Promise<Result<CascadeAssumedRole, Error>>;
44
+ /**
45
+ * Run CDK synth and return failure with masked error if it fails.
46
+ * Calls `onError` on the callbacks so callers only need to handle
47
+ * step-specific reporting (e.g. onStepComplete) before returning.
48
+ */
49
+ export declare function synthOrFail(services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks, errorPrefix: string): Promise<Result<void>>;
50
+ /**
51
+ * Run CDK bootstrap and return failure with masked error if it fails.
52
+ * Emits onCDKBootstrap status callbacks and calls onError on failure.
53
+ */
54
+ export declare function bootstrapOrFail(services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks): Promise<Result<void>>;
@@ -1,3 +1,6 @@
1
+ import { success, failure } from "@fjall/generator";
2
+ import { getErrorMessage, maskSensitiveOutput } from "@fjall/util";
3
+ import { SimpleAwsProvider } from "../aws/SimpleAwsProvider.js";
1
4
  /**
2
5
  * Build the orgConfig/identity context fields shared by all deployment paths.
3
6
  * CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string.
@@ -10,6 +13,95 @@ export function buildParamsContext(params) {
10
13
  ...(params.identity !== undefined
11
14
  ? { fjallOrgId: params.identity.fjallOrgId }
12
15
  : {}),
13
- ...(params.options?.skipOidc ? { fjallOidcConfigured: true } : {})
16
+ ...(params.skipOidc ? { fjallOidcConfigured: true } : {})
14
17
  };
15
18
  }
19
+ /** Forward onOutput callback — reduces lambda repetition across orchestration files. */
20
+ export function forwardOutput(callbacks) {
21
+ return (chunk) => callbacks.onOutput?.(chunk);
22
+ }
23
+ /** Forward onResourceProgress callback — reduces lambda repetition across orchestration files. */
24
+ export function forwardResourceProgress(callbacks) {
25
+ return (event) => callbacks.onResourceProgress?.(event);
26
+ }
27
+ /** Convert CloudFormation getStackOutputs result to Record<string, string>. */
28
+ export function collectStackOutputs(result) {
29
+ if (!result.success || result.data.length === 0)
30
+ return undefined;
31
+ const record = {};
32
+ for (const output of result.data) {
33
+ if (output.OutputKey && output.OutputValue !== undefined) {
34
+ record[output.OutputKey] = output.OutputValue;
35
+ }
36
+ }
37
+ return Object.keys(record).length > 0 ? record : undefined;
38
+ }
39
+ /** Role name created by the Account CDK stack for cross-account cascade operations. */
40
+ const CASCADE_DEPLOYMENT_ROLE = "fjall-deployment-role";
41
+ /** Build the IAM role ARN used for cross-account cascade operations. */
42
+ export function buildCascadeRoleArn(accountId) {
43
+ return `arn:aws:iam::${accountId}:role/${CASCADE_DEPLOYMENT_ROLE}`;
44
+ }
45
+ /**
46
+ * Assume a cross-account cascade role and create a scoped provider.
47
+ * Callers handle failure (callbacks, logging, return type) — this helper
48
+ * only owns the assume + provider construction.
49
+ */
50
+ export async function assumeCascadeRole(awsProvider, accountId, region, sessionName) {
51
+ if (!awsProvider.assumeRole) {
52
+ return failure(new Error("AwsProvider does not support assumeRole"));
53
+ }
54
+ const roleArn = buildCascadeRoleArn(accountId);
55
+ let assumed;
56
+ try {
57
+ assumed = await awsProvider.assumeRole(roleArn, sessionName);
58
+ }
59
+ catch (err) {
60
+ return failure(new Error(getErrorMessage(err)));
61
+ }
62
+ const provider = new SimpleAwsProvider({
63
+ accessKeyId: assumed.accessKeyId,
64
+ secretAccessKey: assumed.secretAccessKey,
65
+ sessionToken: assumed.sessionToken,
66
+ region,
67
+ accountId
68
+ });
69
+ return success({
70
+ provider,
71
+ credentials: {
72
+ accessKeyId: assumed.accessKeyId,
73
+ secretAccessKey: assumed.secretAccessKey,
74
+ sessionToken: assumed.sessionToken
75
+ }
76
+ });
77
+ }
78
+ /**
79
+ * Run CDK synth and return failure with masked error if it fails.
80
+ * Calls `onError` on the callbacks so callers only need to handle
81
+ * step-specific reporting (e.g. onStepComplete) before returning.
82
+ */
83
+ export async function synthOrFail(services, context, callbacks, errorPrefix) {
84
+ const synthResult = await services.cdkService.runCdkSynth(context, (chunk) => callbacks.onCdkOutput?.(chunk, "synth"));
85
+ if (!synthResult.success) {
86
+ const error = new Error(maskSensitiveOutput(`${errorPrefix}: ${synthResult.error}`));
87
+ callbacks.onError?.(error);
88
+ return failure(error);
89
+ }
90
+ return success(undefined);
91
+ }
92
+ /**
93
+ * Run CDK bootstrap and return failure with masked error if it fails.
94
+ * Emits onCDKBootstrap status callbacks and calls onError on failure.
95
+ */
96
+ export async function bootstrapOrFail(services, context, callbacks) {
97
+ callbacks.onCDKBootstrap?.("bootstrapping");
98
+ const bootstrapResult = await services.cdkService.runCdkBootstrap(context, forwardOutput(callbacks));
99
+ if (!bootstrapResult.success) {
100
+ callbacks.onCDKBootstrap?.("failed");
101
+ const error = new Error(maskSensitiveOutput(`Bootstrap failed: ${bootstrapResult.error}`));
102
+ callbacks.onError?.(error);
103
+ return failure(error);
104
+ }
105
+ callbacks.onCDKBootstrap?.("complete");
106
+ return success(undefined);
107
+ }
@@ -0,0 +1,13 @@
1
+ import { type Result } from "@fjall/generator";
2
+ import type { DestroyParams, DestroyResult } from "../types/params.js";
3
+ /**
4
+ * Destroy infrastructure for a target.
5
+ *
6
+ * Primary entry point for both CLI and webapp worker. Determines whether
7
+ * the target is an application or organisation and routes to the
8
+ * appropriate orchestrator.
9
+ *
10
+ * Mirrors the deploy() entry point -- same resolution, same service
11
+ * factory, same callback contract.
12
+ */
13
+ export declare function destroy(params: DestroyParams): Promise<Result<DestroyResult>>;
@@ -0,0 +1,67 @@
1
+ import { success, failure } from "@fjall/generator";
2
+ import { isApplicationOperation, isOrganisationOperation, getApplicationStackName, getOrganisationStackName, APPLICATION_STACKS } from "../types/operations.js";
3
+ import { createDeployServices } from "./serviceFactory.js";
4
+ import { resolveOperation } from "./resolveOperation.js";
5
+ import { destroyApplication } from "./applicationDestroy.js";
6
+ import { destroyOrganisation } from "./organisationDestroy.js";
7
+ import { checkActiveDeployments } from "./activeDeploymentGuard.js";
8
+ /**
9
+ * Destroy infrastructure for a target.
10
+ *
11
+ * Primary entry point for both CLI and webapp worker. Determines whether
12
+ * the target is an application or organisation and routes to the
13
+ * appropriate orchestrator.
14
+ *
15
+ * Mirrors the deploy() entry point -- same resolution, same service
16
+ * factory, same callback contract.
17
+ */
18
+ export async function destroy(params) {
19
+ const startTime = Date.now();
20
+ const services = createDeployServices({
21
+ target: params.target,
22
+ workingDirectory: params.workingDirectory,
23
+ awsCredentials: params.awsCredentials,
24
+ callbacks: params.callbacks,
25
+ options: {
26
+ verbose: params.options?.verbose,
27
+ force: params.options?.force,
28
+ cascade: params.options?.cascade,
29
+ skipConfirmation: params.options?.skipConfirmation
30
+ },
31
+ orgConfig: params.orgConfig,
32
+ identity: params.identity
33
+ });
34
+ const opResult = await resolveOperation(params.target, params.workingDirectory);
35
+ if (!opResult.success) {
36
+ params.callbacks.onError?.(opResult.error);
37
+ return opResult;
38
+ }
39
+ const operation = opResult.data;
40
+ // Pre-flight: reject if any target stacks are mid-operation (unless --force)
41
+ if (!params.options?.force) {
42
+ const stackNames = isApplicationOperation(operation)
43
+ ? Object.values(APPLICATION_STACKS).map((stack) => getApplicationStackName(operation.appName, stack))
44
+ : [getOrganisationStackName(operation.type)];
45
+ const guardResult = await checkActiveDeployments(stackNames, services.cfnService);
46
+ if (!guardResult.success) {
47
+ params.callbacks.onError?.(guardResult.error);
48
+ return failure(guardResult.error);
49
+ }
50
+ }
51
+ if (isApplicationOperation(operation)) {
52
+ const result = await destroyApplication(params, services, operation);
53
+ if (result.success && result.data.durationMs === undefined) {
54
+ return success({
55
+ ...result.data,
56
+ durationMs: Date.now() - startTime
57
+ });
58
+ }
59
+ return result;
60
+ }
61
+ if (isOrganisationOperation(operation)) {
62
+ return destroyOrganisation(params, services, operation);
63
+ }
64
+ const error = new Error(`Unsupported destroy target: ${params.target}`);
65
+ params.callbacks.onError?.(error);
66
+ return failure(error);
67
+ }
@@ -2,18 +2,9 @@ import { type Result } from "@fjall/generator";
2
2
  import type { DeployCallbacks } from "../types/callbacks.js";
3
3
  import type { DeploymentContext } from "../types/deployment/DeploymentTypes.js";
4
4
  import type { ApplicationOperation } from "../types/operations.js";
5
- import { type AppResourceFlags } from "../types/patternDetection.js";
6
- import type { PatternType } from "@fjall/generator";
7
5
  import type { DeployServices } from "./serviceFactory.js";
8
- export interface DetectionResult {
9
- pattern: PatternType | null;
10
- hasDatabase: boolean;
11
- hasDifferences: boolean;
12
- stackChanges: Map<string, boolean>;
13
- currentHashes: Map<string, string>;
14
- resources: AppResourceFlags;
15
- hasDockerfile: boolean;
16
- }
6
+ import type { DetectionResult } from "../types/detection.js";
7
+ export type { DetectionResult };
17
8
  /**
18
9
  * Pre-deployment analysis pipeline.
19
10
  *
@@ -1,8 +1,11 @@
1
1
  import { join } from "path";
2
2
  import { success, failure } from "@fjall/generator";
3
- import { logger } from "@fjall/util";
4
- import { detectPattern, deriveResourcesFromManifestStacks } from "../types/patternDetection.js";
3
+ import { logger } from "@fjall/util/logger";
4
+ import { maskSensitiveOutput } from "@fjall/util";
5
+ import { hasDockerfile } from "../util/dockerfileDetection.js";
6
+ import { deriveResourcesFromManifestStacks } from "../types/patternDetection.js";
5
7
  import { emitProgress, PROGRESS_MESSAGES } from "../services/supporting/helpers.js";
8
+ import { parseRequiredSecretsFromManifest } from "./manifestSecretParser.js";
6
9
  /**
7
10
  * Pre-deployment analysis pipeline.
8
11
  *
@@ -11,28 +14,43 @@ import { emitProgress, PROGRESS_MESSAGES } from "../services/supporting/helpers.
11
14
  */
12
15
  export async function runDetectionPipeline(operation, services, context, callbacks) {
13
16
  // 1. Detect application pattern
14
- const { pattern, hasDatabase } = detectPattern(operation.path);
17
+ callbacks.onDetectionPhaseChange?.("detect", "started");
18
+ const resolved = services.frameworkRegistry.resolve({
19
+ appPath: operation.path
20
+ });
21
+ const pattern = resolved?.detection.pattern ?? null;
22
+ const hasDatabase = resolved?.detection.hasDatabase ?? false;
15
23
  callbacks.onLog?.(`Pattern detected: ${pattern ?? "standard"}`, "info");
24
+ callbacks.onDetectionPhaseChange?.("detect", "completed");
25
+ const cdkOutPath = join(operation.path, "cdk.out");
16
26
  // 2. Synthesise infrastructure
27
+ callbacks.onDetectionPhaseChange?.("synth", "started");
17
28
  emitProgress(callbacks, PROGRESS_MESSAGES.SYNTH);
18
29
  const synthResult = await services.cdkService.runCdkSynth(context, (chunk) => callbacks.onCdkOutput?.(chunk, "synth"));
19
30
  if (!synthResult.success) {
20
- return failure(new Error(`CDK synthesis failed: ${synthResult.error}`));
31
+ return failure(new Error(maskSensitiveOutput(`CDK synthesis failed: ${synthResult.error}`)));
32
+ }
33
+ callbacks.onDetectionPhaseChange?.("synth", "completed");
34
+ // 2b. Parse required secrets from manifest (pure data, no AWS calls)
35
+ const requiredSecrets = parseRequiredSecretsFromManifest(cdkOutPath);
36
+ if (requiredSecrets.length > 0) {
37
+ callbacks.onLog?.(`Found ${requiredSecrets.length} required secret path(s) in manifest`, "info");
21
38
  }
22
39
  // 3. Compute template hashes
40
+ callbacks.onDetectionPhaseChange?.("hash", "started");
23
41
  emitProgress(callbacks, PROGRESS_MESSAGES.HASH);
24
- const cdkOutPath = join(operation.path, "cdk.out");
25
42
  const hashResult = await services.hashService.getTemplateHashes(cdkOutPath);
26
43
  if (!hashResult.success) {
27
- return failure(new Error(`Template hash computation failed: ${hashResult.error.message}`));
44
+ return failure(new Error(maskSensitiveOutput(`Template hash computation failed: ${hashResult.error.message}`)));
28
45
  }
29
46
  const currentHashes = hashResult.data;
30
47
  // 4. Compare with stored state
31
48
  const comparisonResult = await services.hashService.compareWithState(currentHashes, operation.path);
32
49
  if (!comparisonResult.success) {
33
- return failure(new Error(`Hash comparison failed: ${comparisonResult.error.message}`));
50
+ return failure(new Error(maskSensitiveOutput(`Hash comparison failed: ${comparisonResult.error.message}`)));
34
51
  }
35
52
  const comparison = comparisonResult.data;
53
+ callbacks.onDetectionPhaseChange?.("hash", "completed");
36
54
  // 5. Stale hash detection: unchanged templates whose stacks don't exist in CFN
37
55
  const stackChanges = new Map(comparison.stackChanges);
38
56
  for (const [stackName, hasChanged] of comparison.stackChanges) {
@@ -49,8 +67,8 @@ export async function runDetectionPipeline(operation, services, context, callbac
49
67
  // 6. Derive resource flags from manifest stacks
50
68
  const stackNames = Array.from(currentHashes.keys());
51
69
  const resources = deriveResourcesFromManifestStacks(stackNames);
52
- // 7. Detect Dockerfile (Compute stack presence implies Docker)
53
- const hasDockerfile = resources.hasCompute;
70
+ // 7. Detect Dockerfile on disk (Compute stack alone is not sufficient)
71
+ const dockerfilePresent = resources.hasCompute && hasDockerfile(operation.path);
54
72
  const hasDifferences = Array.from(stackChanges.values()).some(Boolean);
55
73
  callbacks.onLog?.(`Detection complete: ${comparison.changedCount} changed, ${comparison.unchangedCount} unchanged`, "info");
56
74
  return success({
@@ -60,6 +78,7 @@ export async function runDetectionPipeline(operation, services, context, callbac
60
78
  stackChanges,
61
79
  currentHashes,
62
80
  resources,
63
- hasDockerfile
81
+ hasDockerfile: dockerfilePresent,
82
+ requiredSecrets
64
83
  });
65
84
  }
@@ -0,0 +1,10 @@
1
+ import { type Result } from "@fjall/generator";
2
+ import type { DeployParams } from "../types/params.js";
3
+ import type { DeployCallbacks } from "../types/callbacks.js";
4
+ import type { ApplicationOperation } from "../types/operations.js";
5
+ import type { DeployServices } from "./serviceFactory.js";
6
+ /**
7
+ * Run Docker build and push before deploying the Compute stack.
8
+ * Initialises ECR if needed, then builds and pushes the image.
9
+ */
10
+ export declare function runDockerBuild(params: DeployParams, services: DeployServices, operation: ApplicationOperation, callbacks: DeployCallbacks): Promise<Result<void>>;
@@ -0,0 +1,49 @@
1
+ import { success, failure } from "@fjall/generator";
2
+ import { maskSensitiveOutput } from "@fjall/util";
3
+ import { STEP_IDS } from "../types/stepDefinitions.js";
4
+ const DOCKER_BUILD_STEP_NAME = "Building and pushing Docker image";
5
+ /**
6
+ * Run Docker build and push before deploying the Compute stack.
7
+ * Initialises ECR if needed, then builds and pushes the image.
8
+ */
9
+ export async function runDockerBuild(params, services, operation, callbacks) {
10
+ const dockerProvider = params.dockerProvider;
11
+ if (!dockerProvider)
12
+ return success(undefined);
13
+ const accountId = services.awsProvider.getAccountId();
14
+ if (!accountId) {
15
+ callbacks.onLog?.("Skipping Docker build — account ID not available", "warn");
16
+ return success(undefined);
17
+ }
18
+ const region = services.awsProvider.getRegion();
19
+ callbacks.onStepStart?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME);
20
+ // Initialise ECR repository (non-fatal — CDK creates it if needed)
21
+ callbacks.onLog?.("Initialising ECR repository…", "info");
22
+ const ecrResult = await dockerProvider.initialiseECR({
23
+ appName: operation.appName,
24
+ region,
25
+ accountId
26
+ });
27
+ if (!ecrResult.success) {
28
+ callbacks.onLog?.(`ECR initialisation failed: ${ecrResult.error.message}`, "warn");
29
+ }
30
+ // Build and push Docker image
31
+ callbacks.onLog?.("Building and pushing Docker image…", "info");
32
+ const buildResult = await dockerProvider.buildAndPush({
33
+ appName: operation.appName,
34
+ appPath: operation.path,
35
+ region,
36
+ accountId
37
+ }, (message, percentage, layerId, status) => {
38
+ callbacks.onDockerProgress?.(maskSensitiveOutput(message), percentage, layerId, status);
39
+ });
40
+ if (!buildResult.success) {
41
+ callbacks.onStepComplete?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME, "error");
42
+ const error = new Error(maskSensitiveOutput(buildResult.error.message));
43
+ callbacks.onError?.(error);
44
+ return failure(error);
45
+ }
46
+ callbacks.onStepComplete?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME, "completed");
47
+ callbacks.onLog?.(`Docker image pushed: ${buildResult.data.imageUri}`, "info");
48
+ return success(undefined);
49
+ }
@@ -43,14 +43,16 @@ export interface TagImagesParams {
43
43
  export interface TagImagesResult {
44
44
  taggedServices: string[];
45
45
  }
46
+ /** Callback for Docker build/push progress reporting. */
47
+ export type DockerProgressCallback = (message: string, percentage?: number, layerId?: string, status?: string) => void;
46
48
  /**
47
49
  * Interface for Docker operations (build, push, ECR initialisation).
48
50
  * CLI provides CliDockerProvider (wraps dockerode-based services).
49
51
  * Worker passes undefined (Docker ops skipped until container deployments enabled).
50
52
  */
51
53
  export interface DockerProvider {
52
- buildAndPush(params: DockerBuildParams): Promise<Result<DockerBuildResult>>;
54
+ buildAndPush(params: DockerBuildParams, onProgress?: DockerProgressCallback): Promise<Result<DockerBuildResult>>;
53
55
  initialiseECR(params: ECRInitParams): Promise<Result<ECRInitResult>>;
54
56
  /** Tag existing ECR images for ECS services (non-Dockerfile apps). Optional. */
55
- tagImages?(params: TagImagesParams): Promise<Result<TagImagesResult>>;
57
+ tagImages?(params: TagImagesParams, onProgress?: DockerProgressCallback): Promise<Result<TagImagesResult>>;
56
58
  }
@@ -1,8 +1,15 @@
1
1
  export { deploy } from "./deploy.js";
2
+ export { destroy } from "./destroy.js";
2
3
  export { deployOrganisation } from "./organisationDeploy.js";
3
- export type { DockerProvider, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult } from "./dockerInterface.js";
4
+ export { destroyOrganisation } from "./organisationDestroy.js";
5
+ export { cleanupFailedStack, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
6
+ export type { CascadeDestroyAccountResult } from "./cascadeDestroyHelpers.js";
7
+ export { partitionAccounts } from "./cascadeHelpers.js";
8
+ export type { DockerProvider, DockerProgressCallback, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult } from "./dockerInterface.js";
4
9
  export type { DomainDeployProvider, DomainConfig, DomainDeployResult } from "./domainInterface.js";
5
10
  export type { DeployServices } from "./serviceFactory.js";
6
11
  export type { DetectionResult } from "./detectionPipeline.js";
12
+ export { runOpenNextBuild } from "./openNextBuild.js";
7
13
  export { runOrganisationSetup } from "./organisationSetup.js";
8
14
  export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult } from "./organisationSetup.js";
15
+ export * from "./builders/index.js";
@@ -1,3 +1 @@
1
- export { deploy } from "./deploy.js";
2
- export { deployOrganisation } from "./organisationDeploy.js";
3
- export { runOrganisationSetup } from "./organisationSetup.js";
1
+ import{deploy as t}from"./deploy.js";import{destroy as p}from"./destroy.js";import{deployOrganisation as n}from"./organisationDeploy.js";import{destroyOrganisation as x}from"./organisationDestroy.js";import{cleanupFailedStack as m,isCleanableState as l,SAFE_CLEANUP_STATES as s}from"./stackCleanup.js";import{partitionAccounts as u}from"./cascadeHelpers.js";import{runOpenNextBuild as c}from"./openNextBuild.js";import{runOrganisationSetup as A}from"./organisationSetup.js";export*from"./builders/index.js";export{s as SAFE_CLEANUP_STATES,m as cleanupFailedStack,t as deploy,n as deployOrganisation,p as destroy,x as destroyOrganisation,l as isCleanableState,u as partitionAccounts,c as runOpenNextBuild,A as runOrganisationSetup};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Extract required SSM secret paths from the Fjall manifest.
3
+ *
4
+ * Reads `fjall-manifest.json` from the given cdk.out directory and
5
+ * collects all `ssmSecretsPath/secretName` entries from ECS services
6
+ * and Lambda functions.
7
+ *
8
+ * Returns an empty array if the manifest does not exist, cannot be
9
+ * parsed, or contains no secrets. Never throws.
10
+ */
11
+ export declare function parseRequiredSecretsFromManifest(cdkOutPath: string): string[];
@@ -0,0 +1 @@
1
+ import{readFileSync as m}from"fs";import{join as y}from"path";import{FJALL_MANIFEST_FILENAME as p}from"@fjall/util/constructMap";import{logger as f}from"@fjall/util/logger";function a(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function l(t){const n=y(t,p);let i;try{i=m(n,"utf-8")}catch{return f.debug("manifestSecretParser","Manifest file not readable \u2014 no secrets extracted",{path:n}),[]}let r;try{r=JSON.parse(i)}catch{return f.debug("manifestSecretParser","Manifest is not valid JSON \u2014 no secrets extracted",{path:n}),[]}if(!a(r))return[];const o=[];if(Array.isArray(r.services))for(const s of r.services){if(!a(s))continue;const e=s;if(!(!Array.isArray(e.secrets)||e.secrets.length===0||typeof e.ssmSecretsPath!="string"))for(const c of e.secrets)typeof c=="string"&&o.push(`${e.ssmSecretsPath}/${c}`)}if(Array.isArray(r.lambdas))for(const s of r.lambdas){if(!a(s))continue;const e=s;if(!(!Array.isArray(e.secrets)||e.secrets.length===0||typeof e.ssmSecretsPath!="string"))for(const c of e.secrets)typeof c=="string"&&o.push(`${e.ssmSecretsPath}/${c}`)}return o}export{l as parseRequiredSecretsFromManifest};