@fjall/deploy-core 2.4.8 → 2.5.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.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 116 files minified at 2026-05-27T22:11:30.371Z
1
+ 118 files minified at 2026-05-29T04:05:16.658Z
@@ -43,6 +43,10 @@ export { type Result, success, failure, isSuccess, isFailure } from "@fjall/gene
43
43
  export { deploy } from "./orchestration/index.js";
44
44
  export { destroy } from "./orchestration/index.js";
45
45
  export { partitionAccounts } from "./orchestration/index.js";
46
+ export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./orchestration/index.js";
47
+ export type { ReconcileResult } from "./orchestration/index.js";
48
+ export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./orchestration/index.js";
49
+ export type { AccountsConfig } from "./orchestration/index.js";
46
50
  export { runOpenNextBuild } from "./orchestration/index.js";
47
51
  export { runOrganisationSetup } from "./orchestration/index.js";
48
52
  export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
package/dist/src/index.js CHANGED
@@ -1 +1 @@
1
- import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,CASCADE_PHASES as i,CASCADE_ACCOUNT_STATUSES as E}from"./types/index.js";import{SimpleAwsProvider as S}from"./aws/index.js";import{ensureOrganisationExists as p,describeOrganisation as c,enablePolicyTypes as l,enableServiceAccess as A,enableRamSharing as T,activateTrustedAccess as u,enableIpamDelegatedAdmin as O,updateBackupGlobalSettings as m,listAccounts as P,findAccount as _,createAccount as d,ensureOrganisationalUnitsExist as R,placeAccountsInOUs as C,buildAccountToOUMap as N,activateCostAllocationTags as f,checkIdentityCentreStatus as g,extractErrorName as x,isOULeaf as D,registerSecurityDelegates as I,SECURITY_SERVICE_PRINCIPALS as L}from"./aws/index.js";import{STEP_IDS as F,STEP_NAMES as v,INFRASTRUCTURE_STEP_NAMES as y,INFRA_STEP_NAME as U}from"./types/index.js";import{ProgressReporter as h,APPLICATION_STACKS as Y,ORGANISATION_TYPES as b,APPLICATION_DEPLOY_ORDER as G,APPLICATION_DESTROY_ORDER as B,OPENNEXT_DEPLOY_ORDER as w,OPENNEXT_DESTROY_ORDER as H,PARALLEL_DEPLOY_GROUPS as K,PARALLEL_DESTROY_GROUPS as V,OPENNEXT_PARALLEL_GROUPS as X,PARALLEL_OPERATION_TYPES as j,isApplicationOperation as q,isOrganisationOperation as z,getParallelDeployGroups as J,getParallelDestroyGroups as Q,getApplicationDeployOrder as W,getApplicationDestroyOrder as Z,getApplicationStackName as $,getOrganisationStackName as ee,isApplicationStack as re,getApplicationStepName as te,getApplicationStepId as oe,toPascalCase as ae,isOpenNextPattern as ie,OPENNEXT_PATTERNS as Ee,deriveResourcesFromManifestStacks as se,STACK_NOT_FOUND_PATTERN as Se,STACK_FAILED_STATE_PATTERN as ne,CDK_NO_STACKS_MATCH as pe,INFRASTRUCTURE_FILENAME as ce,ApplicationError as le,wrapApplicationError as Ae,FjallStateFileSchema as Te,readStateFile as ue,writeStateFile as Oe,createEmptyState as me,deleteStateFile as Pe,updateTemplateHash as _e,getStateFilePath as de,stubCallerIdentity as Re}from"./types/index.js";import{detectPattern as Ne}from"./types/index.js";import{detectPayloadPattern as ge}from"./types/index.js";import{detectDatabase as De}from"./types/index.js";import{CloudFormationEventMonitor as Le}from"./aws/index.js";import{CdkService as Fe,CdkArgumentBuilder as ve,CdkProcessManager as ye,CdkEventMonitor as Ue,startStackMonitoring as Me,DEFAULT_DEPLOY_TIMEOUT_MS as he,isCdkError as Ye,formatInfrastructureError as be,getStructuralHint as Ge,getSourceContext as Be,hasCdkDifferences as we,parseDiffOutput as He,CloudFormationService as Ke,CloudFormationError as Ve,EcsService as Xe,EcsError as je,EcsServiceResolver as qe,TemplateHashService as ze,TemplateHashError as Je,CdkContextBuilder as Qe,emitProgress as We,PROGRESS_MESSAGES as Ze,parseBuildPhase as $e,buildStepContextBuildConfig as er,convertCloudFormationOutputsToRecord as rr,ApplicationStackService as tr}from"./services/index.js";import{CdkError as ar}from"./types/errors/index.js";import{BaseServiceError as Er,ValidationError as sr,AuthError as Sr,AwsError as nr,DeploymentError as pr,NetworkError as cr,FileSystemError as lr,ConfigError as Ar,toServiceError as Tr}from"./types/errors/index.js";import{filterDangerousEnvVars as Or,maskSensitiveOutput as mr,parseShellArgs as Pr,sleep as _r}from"@fjall/util";import{hasDockerfile as Rr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as Nr}from"./util/sequencedCallbacks.js";import{fileExists as gr}from"@fjall/util/fsHelpers";import{success as Dr,failure as Ir,isSuccess as Lr,isFailure as kr}from"@fjall/generator";import{deploy as vr}from"./orchestration/index.js";import{destroy as Ur}from"./orchestration/index.js";import{partitionAccounts as hr}from"./orchestration/index.js";import{runOpenNextBuild as br}from"./orchestration/index.js";import{runOrganisationSetup as Br}from"./orchestration/index.js";import{FrameworkRegistry as Hr}from"./orchestration/index.js";import{openNextBuilder as Vr,dockerBuilder as Xr}from"./orchestration/index.js";import{StepRegistry as qr,getDestroyStepId as zr}from"./steps/index.js";export{G as APPLICATION_DEPLOY_ORDER,B as APPLICATION_DESTROY_ORDER,Y as APPLICATION_STACKS,le as ApplicationError,tr as ApplicationStackService,Sr as AuthError,nr as AwsError,Er as BaseServiceError,E as CASCADE_ACCOUNT_STATUSES,i as CASCADE_PHASES,pe as CDK_NO_STACKS_MATCH,ve as CdkArgumentBuilder,Qe as CdkContextBuilder,ar as CdkError,Ue as CdkEventMonitor,ye as CdkProcessManager,Fe as CdkService,Ve as CloudFormationError,Le as CloudFormationEventMonitor,Ke as CloudFormationService,Ar as ConfigError,he as DEFAULT_DEPLOY_TIMEOUT_MS,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,o as DEPLOYMENT_EVENT_TYPES,pr as DeploymentError,t as DeploymentEventSchema,je as EcsError,Xe as EcsService,qe as EcsServiceResolver,lr as FileSystemError,Te as FjallStateFileSchema,Hr as FrameworkRegistry,ce as INFRASTRUCTURE_FILENAME,y as INFRASTRUCTURE_STEP_NAMES,U as INFRA_STEP_NAME,cr as NetworkError,w as OPENNEXT_DEPLOY_ORDER,H as OPENNEXT_DESTROY_ORDER,X as OPENNEXT_PARALLEL_GROUPS,Ee as OPENNEXT_PATTERNS,b as ORGANISATION_TYPES,K as PARALLEL_DEPLOY_GROUPS,V as PARALLEL_DESTROY_GROUPS,j as PARALLEL_OPERATION_TYPES,Ze as PROGRESS_MESSAGES,h as ProgressReporter,L as SECURITY_SERVICE_PRINCIPALS,ne as STACK_FAILED_STATE_PATTERN,Se as STACK_NOT_FOUND_PATTERN,F as STEP_IDS,v as STEP_NAMES,S as SimpleAwsProvider,qr as StepRegistry,Je as TemplateHashError,ze as TemplateHashService,sr as ValidationError,f as activateCostAllocationTags,u as activateTrustedAccess,N as buildAccountToOUMap,er as buildStepContextBuildConfig,g as checkIdentityCentreStatus,rr as convertCloudFormationOutputsToRecord,d as createAccount,me as createEmptyState,Nr as createSequencedCallbacks,Pe as deleteStateFile,vr as deploy,se as deriveResourcesFromManifestStacks,c as describeOrganisation,Ur as destroy,De as detectDatabase,Ne as detectPattern,ge as detectPayloadPattern,Xr as dockerBuilder,We as emitProgress,O as enableIpamDelegatedAdmin,l as enablePolicyTypes,T as enableRamSharing,A as enableServiceAccess,p as ensureOrganisationExists,R as ensureOrganisationalUnitsExist,x as extractErrorName,Ir as failure,gr as fileExists,Or as filterDangerousEnvVars,_ as findAccount,be as formatInfrastructureError,W as getApplicationDeployOrder,Z as getApplicationDestroyOrder,$ as getApplicationStackName,oe as getApplicationStepId,te as getApplicationStepName,zr as getDestroyStepId,ee as getOrganisationStackName,J as getParallelDeployGroups,Q as getParallelDestroyGroups,Be as getSourceContext,de as getStateFilePath,Ge as getStructuralHint,we as hasCdkDifferences,Rr as hasDockerfile,q as isApplicationOperation,re as isApplicationStack,Ye as isCdkError,kr as isFailure,D as isOULeaf,ie as isOpenNextPattern,z as isOrganisationOperation,Lr as isSuccess,P as listAccounts,mr as maskSensitiveOutput,Vr as openNextBuilder,$e as parseBuildPhase,He as parseDiffOutput,Pr as parseShellArgs,hr as partitionAccounts,C as placeAccountsInOUs,ue as readStateFile,I as registerSecurityDelegates,br as runOpenNextBuild,Br as runOrganisationSetup,_r as sleep,Me as startStackMonitoring,Re as stubCallerIdentity,Dr as success,ae as toPascalCase,Tr as toServiceError,m as updateBackupGlobalSettings,_e as updateTemplateHash,Ae as wrapApplicationError,Oe as writeStateFile};
1
+ import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,CASCADE_PHASES as i,CASCADE_ACCOUNT_STATUSES as s}from"./types/index.js";import{SimpleAwsProvider as c}from"./aws/index.js";import{ensureOrganisationExists as p,describeOrganisation as S,enablePolicyTypes as l,enableServiceAccess as A,enableRamSharing as u,activateTrustedAccess as T,enableIpamDelegatedAdmin as m,updateBackupGlobalSettings as O,listAccounts as P,findAccount as d,createAccount as _,ensureOrganisationalUnitsExist as C,placeAccountsInOUs as f,buildAccountToOUMap as R,activateCostAllocationTags as N,checkIdentityCentreStatus as g,extractErrorName as x,isOULeaf as D,registerSecurityDelegates as I,SECURITY_SERVICE_PRINCIPALS as L}from"./aws/index.js";import{STEP_IDS as k,STEP_NAMES as F,INFRASTRUCTURE_STEP_NAMES as y,INFRA_STEP_NAME as U}from"./types/index.js";import{ProgressReporter as h,APPLICATION_STACKS as Y,ORGANISATION_TYPES as b,APPLICATION_DEPLOY_ORDER as G,APPLICATION_DESTROY_ORDER as B,OPENNEXT_DEPLOY_ORDER as w,OPENNEXT_DESTROY_ORDER as H,PARALLEL_DEPLOY_GROUPS as K,PARALLEL_DESTROY_GROUPS as V,OPENNEXT_PARALLEL_GROUPS as X,PARALLEL_OPERATION_TYPES as j,isApplicationOperation as q,isOrganisationOperation as z,getParallelDeployGroups as J,getParallelDestroyGroups as Q,getApplicationDeployOrder as W,getApplicationDestroyOrder as Z,getApplicationStackName as $,getOrganisationStackName as ee,isApplicationStack as re,getApplicationStepName as te,getApplicationStepId as oe,toPascalCase as ae,isOpenNextPattern as ie,OPENNEXT_PATTERNS as se,deriveResourcesFromManifestStacks as ne,STACK_NOT_FOUND_PATTERN as ce,STACK_FAILED_STATE_PATTERN as Ee,CDK_NO_STACKS_MATCH as pe,INFRASTRUCTURE_FILENAME as Se,ApplicationError as le,wrapApplicationError as Ae,FjallStateFileSchema as ue,readStateFile as Te,writeStateFile as me,createEmptyState as Oe,deleteStateFile as Pe,updateTemplateHash as de,getStateFilePath as _e,stubCallerIdentity as Ce}from"./types/index.js";import{detectPattern as Re}from"./types/index.js";import{detectPayloadPattern as ge}from"./types/index.js";import{detectDatabase as De}from"./types/index.js";import{CloudFormationEventMonitor as Le}from"./aws/index.js";import{CdkService as ke,CdkArgumentBuilder as Fe,CdkProcessManager as ye,CdkEventMonitor as Ue,startStackMonitoring as Me,DEFAULT_DEPLOY_TIMEOUT_MS as he,isCdkError as Ye,formatInfrastructureError as be,getStructuralHint as Ge,getSourceContext as Be,hasCdkDifferences as we,parseDiffOutput as He,CloudFormationService as Ke,CloudFormationError as Ve,EcsService as Xe,EcsError as je,EcsServiceResolver as qe,TemplateHashService as ze,TemplateHashError as Je,CdkContextBuilder as Qe,emitProgress as We,PROGRESS_MESSAGES as Ze,parseBuildPhase as $e,buildStepContextBuildConfig as er,convertCloudFormationOutputsToRecord as rr,ApplicationStackService as tr}from"./services/index.js";import{CdkError as ar}from"./types/errors/index.js";import{BaseServiceError as sr,ValidationError as nr,AuthError as cr,AwsError as Er,DeploymentError as pr,NetworkError as Sr,FileSystemError as lr,ConfigError as Ar,toServiceError as ur}from"./types/errors/index.js";import{filterDangerousEnvVars as mr,maskSensitiveOutput as Or,parseShellArgs as Pr,sleep as dr}from"@fjall/util";import{hasDockerfile as Cr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as Rr}from"./util/sequencedCallbacks.js";import{fileExists as gr}from"@fjall/util/fsHelpers";import{success as Dr,failure as Ir,isSuccess as Lr,isFailure as vr}from"@fjall/generator";import{deploy as Fr}from"./orchestration/index.js";import{destroy as Ur}from"./orchestration/index.js";import{partitionAccounts as hr}from"./orchestration/index.js";import{reconcileProviderAccounts as br,mergeReconciledProviderAccounts as Gr}from"./orchestration/index.js";import{parseAccountsConfiguration as wr,flattenAccountsToEnvironments as Hr,extractAllAccountNames as Kr,accountsConfigToOUTree as Vr,isStringArray as Xr,isAccountsConfig as jr}from"./orchestration/index.js";import{runOpenNextBuild as zr}from"./orchestration/index.js";import{runOrganisationSetup as Qr}from"./orchestration/index.js";import{FrameworkRegistry as Zr}from"./orchestration/index.js";import{openNextBuilder as et,dockerBuilder as rt}from"./orchestration/index.js";import{StepRegistry as ot,getDestroyStepId as at}from"./steps/index.js";export{G as APPLICATION_DEPLOY_ORDER,B as APPLICATION_DESTROY_ORDER,Y as APPLICATION_STACKS,le as ApplicationError,tr as ApplicationStackService,cr as AuthError,Er as AwsError,sr as BaseServiceError,s as CASCADE_ACCOUNT_STATUSES,i as CASCADE_PHASES,pe as CDK_NO_STACKS_MATCH,Fe as CdkArgumentBuilder,Qe as CdkContextBuilder,ar as CdkError,Ue as CdkEventMonitor,ye as CdkProcessManager,ke as CdkService,Ve as CloudFormationError,Le as CloudFormationEventMonitor,Ke as CloudFormationService,Ar as ConfigError,he as DEFAULT_DEPLOY_TIMEOUT_MS,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,o as DEPLOYMENT_EVENT_TYPES,pr as DeploymentError,t as DeploymentEventSchema,je as EcsError,Xe as EcsService,qe as EcsServiceResolver,lr as FileSystemError,ue as FjallStateFileSchema,Zr as FrameworkRegistry,Se as INFRASTRUCTURE_FILENAME,y as INFRASTRUCTURE_STEP_NAMES,U as INFRA_STEP_NAME,Sr as NetworkError,w as OPENNEXT_DEPLOY_ORDER,H as OPENNEXT_DESTROY_ORDER,X as OPENNEXT_PARALLEL_GROUPS,se as OPENNEXT_PATTERNS,b as ORGANISATION_TYPES,K as PARALLEL_DEPLOY_GROUPS,V as PARALLEL_DESTROY_GROUPS,j as PARALLEL_OPERATION_TYPES,Ze as PROGRESS_MESSAGES,h as ProgressReporter,L as SECURITY_SERVICE_PRINCIPALS,Ee as STACK_FAILED_STATE_PATTERN,ce as STACK_NOT_FOUND_PATTERN,k as STEP_IDS,F as STEP_NAMES,c as SimpleAwsProvider,ot as StepRegistry,Je as TemplateHashError,ze as TemplateHashService,nr as ValidationError,Vr as accountsConfigToOUTree,N as activateCostAllocationTags,T as activateTrustedAccess,R as buildAccountToOUMap,er as buildStepContextBuildConfig,g as checkIdentityCentreStatus,rr as convertCloudFormationOutputsToRecord,_ as createAccount,Oe as createEmptyState,Rr as createSequencedCallbacks,Pe as deleteStateFile,Fr as deploy,ne as deriveResourcesFromManifestStacks,S as describeOrganisation,Ur as destroy,De as detectDatabase,Re as detectPattern,ge as detectPayloadPattern,rt as dockerBuilder,We as emitProgress,m as enableIpamDelegatedAdmin,l as enablePolicyTypes,u as enableRamSharing,A as enableServiceAccess,p as ensureOrganisationExists,C as ensureOrganisationalUnitsExist,Kr as extractAllAccountNames,x as extractErrorName,Ir as failure,gr as fileExists,mr as filterDangerousEnvVars,d as findAccount,Hr as flattenAccountsToEnvironments,be as formatInfrastructureError,W as getApplicationDeployOrder,Z as getApplicationDestroyOrder,$ as getApplicationStackName,oe as getApplicationStepId,te as getApplicationStepName,at as getDestroyStepId,ee as getOrganisationStackName,J as getParallelDeployGroups,Q as getParallelDestroyGroups,Be as getSourceContext,_e as getStateFilePath,Ge as getStructuralHint,we as hasCdkDifferences,Cr as hasDockerfile,jr as isAccountsConfig,q as isApplicationOperation,re as isApplicationStack,Ye as isCdkError,vr as isFailure,D as isOULeaf,ie as isOpenNextPattern,z as isOrganisationOperation,Xr as isStringArray,Lr as isSuccess,P as listAccounts,Or as maskSensitiveOutput,Gr as mergeReconciledProviderAccounts,et as openNextBuilder,wr as parseAccountsConfiguration,$e as parseBuildPhase,He as parseDiffOutput,Pr as parseShellArgs,hr as partitionAccounts,f as placeAccountsInOUs,Te as readStateFile,br as reconcileProviderAccounts,I as registerSecurityDelegates,zr as runOpenNextBuild,Qr as runOrganisationSetup,dr as sleep,Me as startStackMonitoring,Ce as stubCallerIdentity,Dr as success,ae as toPascalCase,ur as toServiceError,O as updateBackupGlobalSettings,de as updateTemplateHash,Ae as wrapApplicationError,me as writeStateFile};
@@ -0,0 +1,34 @@
1
+ import { type Result } from "@fjall/generator";
2
+ import { ConfigError } from "../types/errors/ServiceError.js";
3
+ import type { OUTree } from "../aws/organisations/types.js";
4
+ export type AccountsConfig = {
5
+ readonly [key: string]: string | readonly string[] | AccountsConfig;
6
+ };
7
+ export declare function isStringArray(value: unknown): value is string[];
8
+ export declare function isAccountsConfig(value: unknown): value is AccountsConfig;
9
+ /**
10
+ * Recursively extract account→environment pairs using leaf keys.
11
+ * Parent keys (e.g. "workloads") are skipped — only leaf keys become environments.
12
+ */
13
+ export declare function flattenAccountsToEnvironments(config: AccountsConfig): Array<{
14
+ accountName: string;
15
+ environment: string;
16
+ }>;
17
+ /**
18
+ * Extract all account names from any depth of nesting.
19
+ */
20
+ export declare function extractAllAccountNames(config: AccountsConfig): string[];
21
+ /**
22
+ * Convert AccountsConfig to deploy-core's OUTree format for nested OU creation.
23
+ */
24
+ export declare function accountsConfigToOUTree(config: AccountsConfig): OUTree;
25
+ /**
26
+ * Parse the ACCOUNTS configuration from `<baseDir>/fjall/organisation/infrastructure.ts`.
27
+ * Returns null (not an error) if the file does not exist.
28
+ *
29
+ * `baseDir` is the repository root — `process.cwd()` for the CLI (run from the
30
+ * user's repo) and the prepared-clone `workingDirectory` for the worker. There
31
+ * is intentionally no `process.cwd()` default: in the worker `process.cwd()` is
32
+ * the worker dir, not the clone, so callers must pass the resolved base.
33
+ */
34
+ export declare function parseAccountsConfiguration(baseDir: string): Promise<Result<AccountsConfig | null, ConfigError>>;
@@ -0,0 +1 @@
1
+ import{success as u,failure as a}from"@fjall/generator";import{maskSensitiveOutput as A,getErrorMessage as C}from"@fjall/util";import{ConfigError as f}from"../types/errors/ServiceError.js";const c="infrastructure.ts";function g(n){return Array.isArray(n)&&n.every(t=>typeof t=="string")}function p(n){return typeof n!="object"||n===null?!1:Object.entries(n).every(([t,r])=>typeof t=="string"&&(typeof r=="string"||g(r)||p(r)))}function O(n){const t=[];for(const[r,e]of Object.entries(n))if(typeof e=="string")t.push({accountName:e,environment:r});else if(Array.isArray(e))for(const o of e)t.push({accountName:o,environment:r});else typeof e=="object"&&e!==null&&t.push(...O(e));return t}function d(n){const t=[];for(const r of Object.values(n))typeof r=="string"?t.push(r):Array.isArray(r)?t.push(...r):typeof r=="object"&&r!==null&&t.push(...d(r));return t}function T(n){const t={};for(const[r,e]of Object.entries(n))typeof e=="string"?t[r]=[e]:Array.isArray(e)?t[r]=[...e]:typeof e=="object"&&e!==null&&(t[r]=T(e));return t}async function S(n){try{const t=await import("path"),r=await import("fs"),{pathToFileURL:e}=await import("url"),o=t.join(n,"fjall","organisation",c);if(!r.existsSync(o))return u(null);const{tsImport:l}=await import("tsx/esm/api"),y=e(o).href,s=await l(o,y),m=s.default,i=s.ACCOUNTS||m?.ACCOUNTS;return i?p(i)?u(i):a(new f("ACCOUNTS configuration has invalid structure","ACCOUNTS",o,{providedValue:i})):a(new f(`ACCOUNTS constant not found in organisation/${c}`,"ACCOUNTS",o,{moduleKeys:Object.keys(s),hasDefault:s.default!==void 0}))}catch(t){const r=A(C(t));return a(new f(`Failed to import ACCOUNTS from organisation/${c}: ${r}`,"ACCOUNTS",void 0,{originalError:t}))}}export{T as accountsConfigToOUTree,d as extractAllAccountNames,O as flattenAccountsToEnvironments,p as isAccountsConfig,g as isStringArray,S as parseAccountsConfiguration};
@@ -1 +1 @@
1
- import{isAbsolute as T,join as v,dirname as k,resolve as y}from"node:path";import{success as D,failure as S}from"@fjall/generator";import{deriveContentHashTag as b,maskSensitiveOutput as C}from"@fjall/util";import{logger as E}from"@fjall/util/logger";import{parseDockerServicesFromManifest as w}from"@fjall/util/manifest";import{STEP_IDS as N}from"../types/stepDefinitions.js";const O="dockerBuildHelper",R="Building and pushing Docker image";function I(t,n){const r=t.docker.path,e=t.docker.context,s=T(r)?r:v(n,r);return{buildContext:e?T(e)?y(e):y(n,e):y(k(s)),dockerfilePath:s,target:t.docker.target}}function x(t){return`${t.buildContext}|${t.dockerfilePath}|${t.target??""}`}function A(t){return t.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function L(t,n,r){return`${t}.dkr.ecr.${n}.${A(n)}/${r.toLowerCase()}`}function B(t,n){const r=n.name.toLowerCase(),e=n.docker.target,s=e?`-${e.toLowerCase()}`:"";return[`${t}:${r}${s}-latest`]}function _(t,n,r){const e=new Map;for(const s of t){const a=I(s,n),u=x(a),d=B(r,s),i=e.get(u);i?(i.members.push(s),i.latestTags.push(...d)):e.set(u,{identity:a,members:[s],latestTags:[...d]})}return Array.from(e.values())}async function K(t,n,r,e,s){const a=await t.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:e},(u,d,i,p)=>{s.onDockerProgress?.(C(u),d,i,p)});return a.success?D({imageUri:a.data.imageUri,imageTag:a.data.imageTag}):S(a.error)}async function H(t,n,r,e,s,a,u){const d=t.members[0],i={appName:r.appName,appPath:t.identity.buildContext,region:e,accountId:s,buildContext:t.identity.buildContext,dockerfilePath:t.identity.dockerfilePath,imageTags:t.latestTags,serviceName:d.name,...t.identity.target!==void 0&&{target:t.identity.target}},p=await n.buildAndPush(i,(c,h,g,m)=>{u.onDockerProgress?.(C(c),h,g,m)});if(!p.success)return S(p.error);const f=p.data.imageDigest;if(typeof f!="string"||!f.startsWith("sha256:"))return S(new Error(`Buildx push succeeded but returned an invalid digest: ${C(String(f))}`));const l={},P=[];for(const c of t.members){const h=c.docker.target,g=b(f,c.name,h);if(!g.success)return S(g.error);const m=g.data,$=h?`${c.name}-${h}`:c.name;l[$]=m,P.push(`${a}:${m}`)}const o=await n.tagByDigest({sourceImage:a,digest:f,tags:P});return o.success?D({contentHashTagsByService:l}):S(o.error)}async function W(t,n,r,e){const s=t.dockerProvider;if(!s)return D({});const a=n.awsProvider.getAccountId();if(!a)return e.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),D({});const u=n.awsProvider.getRegion(),d=v(r.path,"cdk.out"),i=w(d),p=L(a,u,r.appName);E.debug(O,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:a,region:u,cdkOutPath:d,manifestServiceCount:i.length,manifestServices:i.map(o=>({name:o.name,path:o.docker.path,context:o.docker.context,target:o.docker.target})),stepId:N.DOCKER_OPERATIONS,stepName:R}),e.onStepStart?.(N.DOCKER_OPERATIONS,R),e.onLog?.("Initialising ECR repository\u2026","info");const f=await s.initialiseECR({appName:r.appName,region:u,accountId:a});if(f.success||e.onLog?.(`ECR initialisation failed: ${C(f.error.message)}`,"warn"),i.length===0){e.onLog?.("Building and pushing Docker image\u2026","info");const o=await K(s,r,u,a,e);if(!o.success){E.debug(O,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:C(o.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const c=new Error(C(o.error.message));return e.onError?.(c),S(c)}return e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker image pushed: ${o.data.imageUri}`,"info"),D({})}const l=_(i,r.path,p);e.onLog?.(`Building and pushing ${i.length} service image(s) in ${l.length} group(s)\u2026`,"info");const P={};for(let o=0;o<l.length;o++){const c=l[o],h=c.members.map(m=>m.name).join(", ");e.onLog?.(`Building group ${o+1}/${l.length} (${h})\u2026`,"info");const g=await H(c,s,r,u,a,p,e);if(!g.success){E.debug(O,"Docker buildAndPush failed",{appName:r.appName,leader:c.members[0]?.name,members:h,error:C(g.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const m=new Error(C(g.error.message));return e.onError?.(m),S(m)}for(const[m,$]of Object.entries(g.data.contentHashTagsByService))P[m]=$}return E.debug(O,"Docker buildAndPush succeeded",{appName:r.appName,services:i.map(o=>o.name),groupCount:l.length,contentHashTags:P}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker images pushed for ${i.length} service(s)`,"info"),D(P)}export{R as DOCKER_BUILD_STEP_NAME,W as runDockerBuild};
1
+ import{isAbsolute as T,join as b,dirname as k,resolve as y}from"node:path";import{success as D,failure as S}from"@fjall/generator";import{deriveContentHashTag as v,maskSensitiveOutput as C}from"@fjall/util";import{logger as E}from"@fjall/util/logger";import{parseDockerServicesFromManifest as A}from"@fjall/util/manifest";import{STEP_IDS as N}from"../types/stepDefinitions.js";const O="dockerBuildHelper",R="Building and pushing Docker image";function w(t,n){const r=t.docker.path,e=t.docker.context,s=T(r)?r:b(n,r);return{buildContext:e?T(e)?y(e):y(n,e):y(k(s)),dockerfilePath:s,target:t.docker.target}}function I(t){return`${t.buildContext}|${t.dockerfilePath}|${t.target??""}`}function x(t){return t.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function L(t,n,r){return`${t}.dkr.ecr.${n}.${x(n)}/${r.toLowerCase()}`}function B(t,n){const r=n.name.toLowerCase(),e=n.docker.target,s=e?`-${e.toLowerCase()}`:"";return[`${t}:${r}${s}-latest`]}function _(t,n,r){const e=new Map;for(const s of t){const a=w(s,n),c=I(a),d=B(r,s),i=e.get(c);i?(i.members.push(s),i.latestTags.push(...d)):e.set(c,{identity:a,members:[s],latestTags:[...d]})}return Array.from(e.values())}async function K(t,n,r,e,s){const a=await t.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:e},(c,d,i,p)=>{s.onDockerProgress?.(C(c),d,i,p)});return a.success?D({imageUri:a.data.imageUri,imageTag:a.data.imageTag}):S(a.error)}async function H(t,n,r,e,s,a,c){const d=t.members[0],i={appName:r.appName,appPath:t.identity.buildContext,region:e,accountId:s,buildContext:t.identity.buildContext,dockerfilePath:t.identity.dockerfilePath,imageTags:t.latestTags,serviceName:d.name,...t.identity.target!==void 0&&{target:t.identity.target},...d.docker.buildArgs!==void 0&&{buildArgs:d.docker.buildArgs}},p=await n.buildAndPush(i,(u,h,m,g)=>{c.onDockerProgress?.(C(u),h,m,g)});if(!p.success)return S(p.error);const f=p.data.imageDigest;if(typeof f!="string"||!f.startsWith("sha256:"))return S(new Error(`Buildx push succeeded but returned an invalid digest: ${C(String(f))}`));const l={},P=[];for(const u of t.members){const h=u.docker.target,m=v(f,u.name,h);if(!m.success)return S(m.error);const g=m.data,$=h?`${u.name}-${h}`:u.name;l[$]=g,P.push(`${a}:${g}`)}const o=await n.tagByDigest({sourceImage:a,digest:f,tags:P});return o.success?D({contentHashTagsByService:l}):S(o.error)}async function W(t,n,r,e){const s=t.dockerProvider;if(!s)return D({});const a=n.awsProvider.getAccountId();if(!a)return e.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),D({});const c=n.awsProvider.getRegion(),d=b(r.path,"cdk.out"),i=A(d),p=L(a,c,r.appName);E.debug(O,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:a,region:c,cdkOutPath:d,manifestServiceCount:i.length,manifestServices:i.map(o=>({name:o.name,path:o.docker.path,context:o.docker.context,target:o.docker.target})),stepId:N.DOCKER_OPERATIONS,stepName:R}),e.onStepStart?.(N.DOCKER_OPERATIONS,R),e.onLog?.("Initialising ECR repository\u2026","info");const f=await s.initialiseECR({appName:r.appName,region:c,accountId:a});if(f.success||e.onLog?.(`ECR initialisation failed: ${C(f.error.message)}`,"warn"),i.length===0){e.onLog?.("Building and pushing Docker image\u2026","info");const o=await K(s,r,c,a,e);if(!o.success){E.debug(O,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:C(o.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const u=new Error(C(o.error.message));return e.onError?.(u),S(u)}return e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker image pushed: ${o.data.imageUri}`,"info"),D({})}const l=_(i,r.path,p);e.onLog?.(`Building and pushing ${i.length} service image(s) in ${l.length} group(s)\u2026`,"info");const P={};for(let o=0;o<l.length;o++){const u=l[o],h=u.members.map(g=>g.name).join(", ");e.onLog?.(`Building group ${o+1}/${l.length} (${h})\u2026`,"info");const m=await H(u,s,r,c,a,p,e);if(!m.success){E.debug(O,"Docker buildAndPush failed",{appName:r.appName,leader:u.members[0]?.name,members:h,error:C(m.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const g=new Error(C(m.error.message));return e.onError?.(g),S(g)}for(const[g,$]of Object.entries(m.data.contentHashTagsByService))P[g]=$}return E.debug(O,"Docker buildAndPush succeeded",{appName:r.appName,services:i.map(o=>o.name),groupCount:l.length,contentHashTags:P}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker images pushed for ${i.length} service(s)`,"info"),D(P)}export{R as DOCKER_BUILD_STEP_NAME,W as runDockerBuild};
@@ -5,6 +5,10 @@ export { destroyOrganisation } from "./organisationDestroy.js";
5
5
  export { cleanupFailedStack, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
6
6
  export type { CascadeDestroyAccountResult } from "./cascadeDestroyHelpers.js";
7
7
  export { partitionAccounts } from "./cascadeHelpers.js";
8
+ export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./reconcileProviderAccounts.js";
9
+ export type { ReconcileResult } from "./reconcileProviderAccounts.js";
10
+ export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./accountsConfig.js";
11
+ export type { AccountsConfig } from "./accountsConfig.js";
8
12
  export type { DockerProvider, DockerProgressCallback, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams } from "./dockerInterface.js";
9
13
  export type { DomainDeployProvider, DomainConfig, DomainDeployResult } from "./domainInterface.js";
10
14
  export type { DeployServices } from "./serviceFactory.js";
@@ -1 +1 @@
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};
1
+ import{deploy as e}from"./deploy.js";import{destroy as n}from"./destroy.js";import{deployOrganisation as i}from"./organisationDeploy.js";import{destroyOrganisation as p}from"./organisationDestroy.js";import{cleanupFailedStack as f,isCleanableState as m,SAFE_CLEANUP_STATES as u}from"./stackCleanup.js";import{partitionAccounts as l}from"./cascadeHelpers.js";import{reconcileProviderAccounts as d,mergeReconciledProviderAccounts as g}from"./reconcileProviderAccounts.js";import{parseAccountsConfiguration as y,flattenAccountsToEnvironments as C,extractAllAccountNames as O,accountsConfigToOUTree as T,isStringArray as E,isAccountsConfig as v}from"./accountsConfig.js";import{runOpenNextBuild as P}from"./openNextBuild.js";import{runOrganisationSetup as U}from"./organisationSetup.js";export*from"./builders/index.js";export{u as SAFE_CLEANUP_STATES,T as accountsConfigToOUTree,f as cleanupFailedStack,e as deploy,i as deployOrganisation,n as destroy,p as destroyOrganisation,O as extractAllAccountNames,C as flattenAccountsToEnvironments,v as isAccountsConfig,m as isCleanableState,E as isStringArray,g as mergeReconciledProviderAccounts,y as parseAccountsConfiguration,l as partitionAccounts,d as reconcileProviderAccounts,P as runOpenNextBuild,U as runOrganisationSetup};
@@ -1,3 +1,3 @@
1
- import{success as _,failure as I}from"@fjall/generator";import{OrganizationsClient as ot}from"@aws-sdk/client-organizations";import{ORGANISATION_TYPES as D,getOrganisationStackName as v}from"../types/operations.js";import{CdkContextBuilder as et}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as nt}from"../types/deployment/index.js";import{ensureOrganisationExists as rt}from"../aws/organisations/organisation.js";import{buildParamsContext as at,collectStackOutputs as z,synthOrFail as B,bootstrapOrFail as V,forwardOutput as W,forwardResourceProgress as q}from"./contextHelpers.js";import{partitionAccounts as st,deployCascadeAccount as H,readPlatformIpamPoolIds as ct,deployDomains as it}from"./cascadeHelpers.js";import{maskSensitiveOutput as g}from"@fjall/util";import{INFRA_STEP_NAME as T,STEP_IDS as w}from"../types/stepDefinitions.js";async function St(r,o,e){const s=Date.now();switch(e.type){case D.ORGANISATION:return ut(r,o,e,s);case D.PLATFORM:return X(r,o,e,"platform",s);case D.ACCOUNT:return X(r,o,e,"account",s);default:{const t=e.type;return I(new Error(`Unsupported organisation type: ${String(t)}`))}}}function J(r,o,e,s,t,a){return et.buildDeploymentContext({deployType:s,target:e.target,path:e.path,region:o.awsProvider.getRegion(),accountName:a,callerIdentity:nt(o.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...at({orgConfig:r.orgConfig,identity:r.identity,skipOidc:r.options?.skipOidc})},{verbose:r.options?.verbose,infraOnly:r.options?.infraOnly},r.orgConfig)}async function Q(r){const o=r.awsProvider.getClient(ot),e=await rt(o);return e.success?_({orgId:e.data.orgId,rootId:e.data.rootId,managementAccountId:e.data.managementAccountId}):I(e.error)}const n={CONNECT:{id:w.CONNECT,name:T.CONNECT},PREPARE:{id:w.PREPARE_ENVIRONMENT,name:T.PREPARE},DEPLOY:{id:w.DEPLOY,name:T.DEPLOY},MONITORING:{id:w.MONITORING,name:T.MONITORING},ORG_DEPLOY:{id:w.ORG_DEPLOY,name:"Deploying organisation infrastructure"}},d=4;async function X(r,o,e,s,t){const{callbacks:a}=r;a.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,d),a.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,d);const f=await Q(o);if(!f.success){a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d);const p=new Error(g(f.error.message));return a.onError?.(p),I(p)}const E=J(r,o,e,s,f.data,s==="account"?e.target:void 0);a.onLog?.(`Synthesising ${s} infrastructure\u2026`,"info");const P=await B(o,E,a,"CDK synthesis failed");if(!P.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d),P;const N=await V(o,E,a);if(!N.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d),N;a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,d);const k=v(e.type);a.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,d);const l=await o.cdkService.runCdkDeploy(E,k,W(a),q(a),o.awsProvider);if(!l.success){a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,d);const p=new Error(g(l.error));return a.onError?.(p),I(p)}a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,d);const O=await o.cfnService.getStackOutputs(k);O.success||a.onLog?.("Failed to read stack outputs (non-critical)","debug");const R=z(O);return a.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,d),a.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,d),_({target:e.target,deploymentType:"organisation",outputs:R,durationMs:Date.now()-t})}async function ut(r,o,e,s){const{callbacks:t,options:a}=r,f=r.orgConfig?.providerAccounts??[],E=await Q(o);if(!E.success){const i=new Error(g(E.error.message));return t.onError?.(i),I(i)}const P=J(r,o,e,"organisation",E.data),N=a?.cascade!==!1,l=2+(N?f.length:0),{id:O,name:R}=n.PREPARE;t.onStepStart?.(O,R,0,l),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const p=await B(o,P,t,"CDK synthesis failed");if(!p.success)return t.onStepComplete?.(O,R,"error",0,l),p;const x=await V(o,P,t);if(!x.success)return t.onStepComplete?.(O,R,"error",0,l),x;t.onStepComplete?.(O,R,"completed",0,l);const{id:L,name:b}=n.ORG_DEPLOY;t.onStepStart?.(L,b,1,l);const $=v(D.ORGANISATION),F=await o.cdkService.runCdkDeploy(P,$,W(t),q(t),o.awsProvider);if(!F.success){t.onStepComplete?.(L,b,"error",1,l);const i=new Error(g(F.error));return t.onError?.(i),I(i)}const j=await o.cfnService.getStackOutputs($);j.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const Z=z(j);t.onStepComplete?.(L,b,"completed",1,l);const c=[],y=[];if(N&&f.length>0){t.onCascadeStart?.();let i=0,G=!1,K=!1;const{platformAccount:S,memberAccounts:M}=st(f);if(S){t.onCascadePhaseStart?.("platform");const u=await H(r,o,e,S,"platform",t);u.success?(G=!0,u.data.outputs&&y.push({accountId:S.id,outputs:u.data.outputs})):c.push({accountId:S.id,error:u.error.message}),t.onCascadePhaseComplete?.("platform")}let U=new Map;if(G&&S&&(U=await ct(o,S,t)),r.domainProvider){const u=await it(r.domainProvider,t);K=u.domainsDeployed>0;for(const A of u.errors)c.push({accountId:"domains",error:g(A)})}if(M.length>0){t.onCascadePhaseStart?.("accounts");const u=o.awsProvider.getRegion();(await Promise.allSettled(M.map(m=>{const Y=u.replace(/-/g,""),C=U.get(`${m.id}-${Y}`);return H(r,o,e,m,"account",t,C)}))).forEach((m,Y)=>{const C=M[Y];if(!C)return;if(m.status==="rejected"){c.push({accountId:C.id,error:g(m.reason instanceof Error?m.reason.message:String(m.reason))});return}const h=m.value;h.success?(i++,h.data.outputs&&y.push({accountId:C.id,outputs:h.data.outputs})):c.push({accountId:C.id,error:h.error.message})}),t.onCascadePhaseComplete?.("accounts")}if(t.onCascadeComplete?.({platformDeployed:G,domainsDeployed:K,accountsDeployed:i,accountsFailed:c.length,errors:c}),c.length>0){const u=c.map(A=>` ${A.accountId}: ${A.error}`).join(`
2
- `);t.onLog?.(g(`Cascade completed with ${c.length} failure(s):
3
- ${u}`),"warn")}}const tt=c.length>0?c.map(i=>g(`${i.accountId}: ${i.error}`)):void 0;return _({target:e.target,deploymentType:"organisation",outputs:Z,...y.length>0?{cascadeOutputs:y}:{},durationMs:Date.now()-s,warnings:tt})}export{St as deployOrganisation};
1
+ import{success as W,failure as R}from"@fjall/generator";import{OrganizationsClient as lt}from"@aws-sdk/client-organizations";import{ORGANISATION_TYPES as Y,getOrganisationStackName as X}from"../types/operations.js";import{CdkContextBuilder as pt}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as mt}from"../types/deployment/index.js";import{ensureOrganisationExists as gt}from"../aws/organisations/organisation.js";import{buildParamsContext as ft,collectStackOutputs as Z,synthOrFail as tt,bootstrapOrFail as et,forwardOutput as ot,forwardResourceProgress as nt}from"./contextHelpers.js";import{partitionAccounts as rt,deployCascadeAccount as at,readPlatformIpamPoolIds as Ct,deployDomains as St}from"./cascadeHelpers.js";import{reconcileProviderAccounts as Et,mergeReconciledProviderAccounts as Ot}from"./reconcileProviderAccounts.js";import{maskSensitiveOutput as i,STRUCTURAL_ENVIRONMENTS as At}from"@fjall/util";import{INFRA_STEP_NAME as F,STEP_IDS as I,STEP_NAMES as j}from"../types/stepDefinitions.js";async function bt(n,e,r){const p=Date.now();switch(r.type){case Y.ORGANISATION:return Pt(n,e,r,p);case Y.PLATFORM:return it(n,e,r,"platform",p);case Y.ACCOUNT:return it(n,e,r,"account",p);default:{const t=r.type;return R(new Error(`Unsupported organisation type: ${String(t)}`))}}}function st(n,e,r,p,t,a){return pt.buildDeploymentContext({deployType:p,target:r.target,path:r.path,region:e.awsProvider.getRegion(),accountName:a,callerIdentity:mt(e.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...ft({orgConfig:n.orgConfig,identity:n.identity,skipOidc:n.options?.skipOidc})},{verbose:n.options?.verbose,infraOnly:n.options?.infraOnly},n.orgConfig)}async function ct(n){const e=n.awsProvider.getClient(lt),r=await gt(e);return r.success?W({orgId:r.data.orgId,rootId:r.data.rootId,managementAccountId:r.data.managementAccountId}):R(r.error)}const o={CONNECT:{id:I.CONNECT,name:F.CONNECT},PREPARE:{id:I.PREPARE_ENVIRONMENT,name:F.PREPARE},DEPLOY:{id:I.DEPLOY,name:F.DEPLOY},MONITORING:{id:I.MONITORING,name:F.MONITORING},ORG_DEPLOY:{id:I.ORG_DEPLOY,name:j.ORG_DEPLOY},CASCADE_PLATFORM:{id:I.CASCADE_PLATFORM,name:j.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:I.CASCADE_ACCOUNTS,name:j.CASCADE_ACCOUNTS}},g=4;async function it(n,e,r,p,t){const{callbacks:a}=n;a.onStepComplete?.(o.CONNECT.id,o.CONNECT.name,"completed",0,g),a.onStepStart?.(o.PREPARE.id,o.PREPARE.name,1,g);const f=await ct(e);if(!f.success){a.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"error",1,g);const O=new Error(i(f.error.message));return a.onError?.(O),R(O)}const D=st(n,e,r,p,f.data,p==="account"?r.target:void 0);a.onLog?.(`Synthesising ${p} infrastructure\u2026`,"info");const N=await tt(e,D,a,"CDK synthesis failed");if(!N.success)return a.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"error",1,g),N;const w=await et(e,D,a);if(!w.success)return a.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"error",1,g),w;a.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"completed",1,g);const y=X(r.type);a.onStepStart?.(o.DEPLOY.id,o.DEPLOY.name,2,g);const L=await e.cdkService.runCdkDeploy(D,y,ot(a),nt(a),e.awsProvider);if(!L.success){a.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"error",2,g);const O=new Error(i(L.error));return a.onError?.(O),R(O)}a.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"completed",2,g);const k=await e.cfnService.getStackOutputs(y);k.success||a.onLog?.("Failed to read stack outputs (non-critical)","debug");const $=Z(k);return a.onStepStart?.(o.MONITORING.id,o.MONITORING.name,3,g),a.onStepComplete?.(o.MONITORING.id,o.MONITORING.name,"completed",3,g),W({target:r.target,deploymentType:"organisation",outputs:$,durationMs:Date.now()-t})}async function Pt(n,e,r,p){const{callbacks:t,options:a}=n;let f=n.orgConfig?.providerAccounts??[];if(f.length===0||f.every(s=>s.environment===At.ROOT)){const s=await Et(e,n.workingDirectory);if(s.success){const{providerAccounts:C,missingAccountNames:A}=s.data;C.length>0&&(f=Ot(n.orgConfig,C).providerAccounts,t.onLog?.(`Reconciled ${C.length} account(s) from AWS Organizations`,"info")),A.length>0&&(t.onCascadeMissingAccounts?.(A),t.onProgress?.({type:"warning",message:i(`Accounts declared in ACCOUNTS but not yet in AWS Organizations (cascade will skip): ${A.join(", ")}`)}))}else t.onProgress?.({type:"warning",message:i(`Could not reconcile accounts from AWS Organizations \u2014 cascade may skip accounts: ${s.error.message}`)})}const N=await ct(e);if(!N.success){const s=new Error(i(N.error.message));return t.onError?.(s),R(s)}const w=st(n,e,r,"organisation",N.data),y=a?.cascade!==!1,{platformAccount:L,memberAccounts:k}=rt(f),$=y&&L!==void 0?1:0,O=y&&k.length>0?1:0,c=2+$+O,{id:_,name:b}=o.PREPARE;t.onStepStart?.(_,b,0,c),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const K=await tt(e,w,t,"CDK synthesis failed");if(!K.success)return t.onStepComplete?.(_,b,"error",0,c),K;const V=await et(e,w,t);if(!V.success)return t.onStepComplete?.(_,b,"error",0,c),V;t.onStepComplete?.(_,b,"completed",0,c);const{id:x,name:U}=o.ORG_DEPLOY;t.onStepStart?.(x,U,1,c);const B=X(Y.ORGANISATION),q=await e.cdkService.runCdkDeploy(w,B,ot(t),nt(t),e.awsProvider);if(!q.success){t.onStepComplete?.(x,U,"error",1,c);const s=new Error(i(q.error));return t.onError?.(s),R(s)}const H=await e.cfnService.getStackOutputs(B);H.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const ut=Z(H);t.onStepComplete?.(x,U,"completed",1,c);const u=[],M=[];if(y&&f.length>0){t.onCascadeStart?.();let s=0,C=0,A=!1,J=!1,P=2;const{platformAccount:S,memberAccounts:v}=rt(f);if(S){const{id:d,name:l}=o.CASCADE_PLATFORM;t.onStepStart?.(d,l,P,c),t.onCascadePhaseStart?.("platform");let E;try{E=await at(n,e,r,S,"platform",t)}catch(T){const m=i(T instanceof Error?T.message:String(T));u.push({accountId:S.id,error:m}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,l,"error",P,c),E=R(new Error(m))}E.success?(A=!0,E.data.outputs&&M.push({accountId:S.id,outputs:E.data.outputs}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,l,"completed",P,c)):u.some(T=>T.accountId===S.id)||(u.push({accountId:S.id,error:i(E.error.message)}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,l,"error",P,c)),P++}let Q=new Map;if(A&&S&&(Q=await Ct(e,S,t)),n.domainProvider){const d=await St(n.domainProvider,t);J=d.domainsDeployed>0;for(const l of d.errors)u.push({accountId:"domains",error:i(l)})}if(v.length>0){const{id:d,name:l}=o.CASCADE_ACCOUNTS;t.onStepStart?.(d,l,P,c),t.onCascadePhaseStart?.("accounts");const E=e.awsProvider.getRegion();(await Promise.allSettled(v.map(m=>{const z=E.replace(/-/g,""),h=Q.get(`${m.id}-${z}`);return at(n,e,r,m,"account",t,h)}))).forEach((m,z)=>{const h=v[z];if(!h)return;if(m.status==="rejected"){C++,u.push({accountId:h.id,error:i(m.reason instanceof Error?m.reason.message:String(m.reason))});return}const G=m.value;G.success?(s++,G.data.outputs&&M.push({accountId:h.id,outputs:G.data.outputs})):(C++,u.push({accountId:h.id,error:i(G.error.message)}))}),t.onCascadePhaseComplete?.("accounts"),t.onStepComplete?.(d,l,C===0?"completed":"error",P,c)}if(t.onCascadeComplete?.({platformDeployed:A,domainsDeployed:J,accountsDeployed:s,accountsFailed:C,errors:u}),u.length>0){const d=u.map(l=>` ${l.accountId}: ${l.error}`).join(`
2
+ `);t.onLog?.(i(`Cascade completed with ${u.length} failure(s):
3
+ ${d}`),"warn")}}const dt=u.length>0?u.map(s=>i(`${s.accountId}: ${s.error}`)):void 0;return W({target:r.target,deploymentType:"organisation",outputs:ut,...M.length>0?{cascadeOutputs:M}:{},durationMs:Date.now()-p,warnings:dt})}export{bt as deployOrganisation};
@@ -1 +1 @@
1
- import{success as y,failure as h}from"@fjall/generator";import{OrganizationsClient as v}from"@aws-sdk/client-organizations";import{RAMClient as x}from"@aws-sdk/client-ram";import{CloudFormationClient as T}from"@aws-sdk/client-cloudformation";import{EC2Client as M}from"@aws-sdk/client-ec2";import{BackupClient as D}from"@aws-sdk/client-backup";import{CostExplorerClient as N}from"@aws-sdk/client-cost-explorer";import{SSOAdminClient as B}from"@aws-sdk/client-sso-admin";import{ensureOrganisationExists as F}from"../aws/organisations/organisation.js";import{enablePolicyTypes as L}from"../aws/organisations/policies.js";import{enableServiceAccess as W}from"../aws/organisations/serviceAccess.js";import{enableRamSharing as j}from"../aws/organisations/ram.js";import{activateTrustedAccess as z}from"../aws/organisations/trustedAccess.js";import{enableIpamDelegatedAdmin as G}from"../aws/organisations/ipam.js";import{updateBackupGlobalSettings as K}from"../aws/organisations/backup.js";import{listAccounts as q,createAccount as H}from"../aws/organisations/accounts.js";import{ensureOrganisationalUnitsExist as J,placeAccountsInOUs as Q,buildAccountToOUMap as V}from"../aws/organisations/organisationalUnits.js";import{activateCostAllocationTags as X}from"../aws/organisations/costAllocation.js";import{checkIdentityCentreStatus as Y}from"../aws/organisations/identityCentre.js";import{registerSecurityDelegates as Z}from"../aws/organisations/delegatedAdmin.js";async function Pe(r,o,e){const n=[],s=[],t=[],c=[];let C;const u=r.getClient(v),A=r.getClient(x),S=r.getClient(T),E=r.getClient(M),I=r.getClient(D),w=r.getClient(N),O=r.getClient(B);e?.onPhaseStart?.("create-organisation"),e?.onProgress?.("Ensuring AWS Organisation exists");const p=await F(u);if(!p.success)return e?.onError?.("create-organisation",p.error),e?.onPhaseComplete?.("create-organisation","error"),h(p.error);const{orgId:R,rootId:f}=p.data;if(e?.onPhaseComplete?.("create-organisation","completed"),n.push("create-organisation"),await a("enable-policies",()=>(e?.onProgress?.("Enabling organisation policy types"),L(u,f)),n,t,e),await a("enable-service-access",()=>(e?.onProgress?.("Enabling AWS service access"),W(u)),n,t,e),await a("enable-ram-sharing",()=>(e?.onProgress?.("Enabling RAM sharing"),j(A)),n,t,e),await a("activate-trusted-access",()=>(e?.onProgress?.("Activating CloudFormation trusted access"),z(S)),n,t,e),o.platformAccountId){const i=o.platformAccountId;await a("enable-ipam",()=>(e?.onProgress?.("Enabling IPAM delegated administrator"),G(E,i)),n,t,e)}await a("configure-backup",()=>(e?.onProgress?.("Updating backup global settings"),K(I)),n,t,e),e?.onPhaseStart?.("create-accounts"),e?.onProgress?.("Checking for missing accounts");const d=await _(u,o.accounts,c);d.success?(n.push("create-accounts"),e?.onPhaseComplete?.("create-accounts","completed")):(t.push({phase:"create-accounts",error:d.error.message}),e?.onError?.("create-accounts",d.error),e?.onPhaseComplete?.("create-accounts","error"));let m={};e?.onPhaseStart?.("create-organisational-units"),e?.onProgress?.("Ensuring organisational units exist");const g=await J(u,f,o.organisationalUnits);if(g.success?(m=g.data,n.push("create-organisational-units"),e?.onPhaseComplete?.("create-organisational-units","completed")):(t.push({phase:"create-organisational-units",error:g.error.message}),e?.onError?.("create-organisational-units",g.error),e?.onPhaseComplete?.("create-organisational-units","error")),Object.keys(m).length===0)s.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else if(o.accountPlacements===void 0){const i=new Error("Account placements not provided despite OUs being created. Caller must populate accountPlacements so accounts can be moved into their target OUs.");t.push({phase:"place-accounts",error:i.message}),e?.onPhaseStart?.("place-accounts"),e?.onError?.("place-accounts",i),e?.onPhaseComplete?.("place-accounts","error")}else if(o.accountPlacements.length===0)s.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else{const i=o.accountPlacements,U=Array.isArray(o.organisationalUnits)?void 0:V(o.organisationalUnits,m);await a("place-accounts",()=>(e?.onProgress?.("Placing accounts in organisational units"),Q(u,m,i,U)),n,t,e)}const P=o.costAllocationTags??[];if(P.length>0?await a("activate-cost-tags",()=>(e?.onProgress?.("Activating cost allocation tags"),X(w,P.map(i=>({TagKey:i})))),n,t,e):(s.push("activate-cost-tags"),e?.onPhaseStart?.("activate-cost-tags"),e?.onPhaseComplete?.("activate-cost-tags","skipped")),o.skipIdentityCentre)s.push("check-identity-centre"),e?.onPhaseStart?.("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","skipped");else{e?.onPhaseStart?.("check-identity-centre"),e?.onProgress?.("Checking Identity Centre status");const i=await Y(O);i.success?(C=i.data.enabled?"enabled":"not-enabled",n.push("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","completed")):(t.push({phase:"check-identity-centre",error:i.error.message}),e?.onError?.("check-identity-centre",i.error),e?.onPhaseComplete?.("check-identity-centre","error"))}const l=o.securityDelegateAccountId;return l?await a("register-security-delegates",()=>(e?.onProgress?.("Registering security service delegated administrators"),Z(u,l)),n,t,e):(s.push("register-security-delegates"),e?.onPhaseStart?.("register-security-delegates"),e?.onPhaseComplete?.("register-security-delegates","skipped")),y({organisationId:R,createdAccounts:c,identityCentreStatus:C,phasesCompleted:n,phasesSkipped:s,errors:t})}async function a(r,o,e,n,s){s?.onPhaseStart?.(r);const t=await o();t.success?(e.push(r),s?.onPhaseComplete?.(r,"completed")):(n.push({phase:r,error:t.error.message}),s?.onError?.(r,t.error),s?.onPhaseComplete?.(r,"error"))}async function _(r,o,e){const n=await q(r);if(!n.success)return h(n.error);const s=new Set(n.data.map(t=>t.Name?.toLowerCase()).filter(t=>t!==void 0));for(const t of o){if(s.has(t.name.toLowerCase()))continue;const c=await H(r,t.name,t.email);if(!c.success)return h(c.error);e.push({name:c.data.accountName,accountId:c.data.accountId})}return y(void 0)}export{Pe as runOrganisationSetup};
1
+ import{success as C,failure as l}from"@fjall/generator";import{OrganizationsClient as L}from"@aws-sdk/client-organizations";import{RAMClient as D}from"@aws-sdk/client-ram";import{CloudFormationClient as F}from"@aws-sdk/client-cloudformation";import{EC2Client as j}from"@aws-sdk/client-ec2";import{BackupClient as B}from"@aws-sdk/client-backup";import{CostExplorerClient as W}from"@aws-sdk/client-cost-explorer";import{SSOAdminClient as z}from"@aws-sdk/client-sso-admin";import{ensureOrganisationExists as G}from"../aws/organisations/organisation.js";import{enablePolicyTypes as K}from"../aws/organisations/policies.js";import{enableServiceAccess as q}from"../aws/organisations/serviceAccess.js";import{enableRamSharing as H}from"../aws/organisations/ram.js";import{activateTrustedAccess as J}from"../aws/organisations/trustedAccess.js";import{enableIpamDelegatedAdmin as Q}from"../aws/organisations/ipam.js";import{updateBackupGlobalSettings as V}from"../aws/organisations/backup.js";import{listAccounts as E,createAccount as X}from"../aws/organisations/accounts.js";import{ensureOrganisationalUnitsExist as Y,placeAccountsInOUs as Z,buildAccountToOUMap as _}from"../aws/organisations/organisationalUnits.js";import{activateCostAllocationTags as $}from"../aws/organisations/costAllocation.js";import{checkIdentityCentreStatus as b}from"../aws/organisations/identityCentre.js";import{registerSecurityDelegates as k}from"../aws/organisations/delegatedAdmin.js";import{isOULeaf as ee}from"../aws/organisations/types.js";async function Ee(r,o,e){const n=[],i=[],t=[],s=[];let u;const p=r.getClient(L),O=r.getClient(D),U=r.getClient(F),R=r.getClient(j),N=r.getClient(B),T=r.getClient(W),x=r.getClient(z);e?.onPhaseStart?.("create-organisation"),e?.onProgress?.("Ensuring AWS Organisation exists");const g=await G(p);if(!g.success)return e?.onError?.("create-organisation",g.error),e?.onPhaseComplete?.("create-organisation","error"),l(g.error);const{orgId:M,rootId:P}=g.data;if(e?.onPhaseComplete?.("create-organisation","completed"),n.push("create-organisation"),await c("enable-policies",()=>(e?.onProgress?.("Enabling organisation policy types"),K(p,P)),n,t,e),await c("enable-service-access",()=>(e?.onProgress?.("Enabling AWS service access"),q(p)),n,t,e),await c("enable-ram-sharing",()=>(e?.onProgress?.("Enabling RAM sharing"),H(O)),n,t,e),await c("activate-trusted-access",()=>(e?.onProgress?.("Activating CloudFormation trusted access"),J(U)),n,t,e),o.platformAccountId){const a=o.platformAccountId;await c("enable-ipam",()=>(e?.onProgress?.("Enabling IPAM delegated administrator"),Q(R,a)),n,t,e)}await c("configure-backup",()=>(e?.onProgress?.("Updating backup global settings"),V(N)),n,t,e),e?.onPhaseStart?.("create-accounts"),e?.onProgress?.("Checking for missing accounts");const d=await te(p,o.accounts,s);let y=[];d.success?(y=d.data,n.push("create-accounts"),e?.onPhaseComplete?.("create-accounts","completed")):(t.push({phase:"create-accounts",error:d.error.message}),e?.onError?.("create-accounts",d.error),e?.onPhaseComplete?.("create-accounts","error"));let h={};e?.onPhaseStart?.("create-organisational-units"),e?.onProgress?.("Ensuring organisational units exist");const f=await Y(p,P,o.organisationalUnits);f.success?(h=f.data,n.push("create-organisational-units"),e?.onPhaseComplete?.("create-organisational-units","completed")):(t.push({phase:"create-organisational-units",error:f.error.message}),e?.onError?.("create-organisational-units",f.error),e?.onPhaseComplete?.("create-organisational-units","error"));const m=Array.isArray(o.organisationalUnits)?void 0:o.organisationalUnits,A=o.accountPlacements??[],S=A.length===0&&m!==void 0?ne(m,y):A;if(Object.keys(h).length===0)i.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else if(o.accountPlacements===void 0&&m===void 0){const a=new Error("Account placements not provided despite OUs being created. Caller must populate accountPlacements (flat-list mode cannot derive placements internally).");t.push({phase:"place-accounts",error:a.message}),e?.onPhaseStart?.("place-accounts"),e?.onError?.("place-accounts",a),e?.onPhaseComplete?.("place-accounts","error")}else if(S.length===0)i.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else{const a=m?_(m,h):void 0;await c("place-accounts",()=>(e?.onProgress?.("Placing accounts in organisational units"),Z(p,h,S,a)),n,t,e)}const w=o.costAllocationTags??[];if(w.length>0?await c("activate-cost-tags",()=>(e?.onProgress?.("Activating cost allocation tags"),$(T,w.map(a=>({TagKey:a})))),n,t,e):(i.push("activate-cost-tags"),e?.onPhaseStart?.("activate-cost-tags"),e?.onPhaseComplete?.("activate-cost-tags","skipped")),o.skipIdentityCentre)i.push("check-identity-centre"),e?.onPhaseStart?.("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","skipped");else{e?.onPhaseStart?.("check-identity-centre"),e?.onProgress?.("Checking Identity Centre status");const a=await b(x);a.success?(u=a.data.enabled?"enabled":"not-enabled",n.push("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","completed")):(t.push({phase:"check-identity-centre",error:a.error.message}),e?.onError?.("check-identity-centre",a.error),e?.onPhaseComplete?.("check-identity-centre","error"))}const I=o.securityDelegateAccountId;return I?await c("register-security-delegates",()=>(e?.onProgress?.("Registering security service delegated administrators"),k(p,I)),n,t,e):(i.push("register-security-delegates"),e?.onPhaseStart?.("register-security-delegates"),e?.onPhaseComplete?.("register-security-delegates","skipped")),C({organisationId:M,createdAccounts:s,identityCentreStatus:u,phasesCompleted:n,phasesSkipped:i,errors:t})}async function c(r,o,e,n,i){i?.onPhaseStart?.(r);const t=await o();t.success?(e.push(r),i?.onPhaseComplete?.(r,"completed")):(n.push({phase:r,error:t.error.message}),i?.onError?.(r,t.error),i?.onPhaseComplete?.(r,"error"))}async function te(r,o,e){const n=await E(r);if(!n.success)return l(n.error);const i=new Set(n.data.map(s=>s.Name?.toLowerCase()).filter(s=>s!==void 0));for(const s of o){if(i.has(s.name.toLowerCase()))continue;const u=await X(r,s.name,s.email);if(!u.success)return l(u.error);e.push({name:u.data.accountName,accountId:u.data.accountId})}if(e.length===0)return C(n.data);const t=await E(r);return t.success?C(t.data):l(t.error)}function v(r){const o=[];for(const e of Object.values(r))ee(e)?o.push(...e):o.push(...v(e));return o}function ne(r,o){const e=v(r),n=new Map;for(const t of o){const s=t.Name?.toLowerCase();s&&t.Id&&n.set(s,t)}const i=[];for(const t of e){const s=n.get(t.toLowerCase());s?.Id&&s.Name&&i.push({id:s.Id,name:s.Name,environment:t})}return i}export{Ee as runOrganisationSetup};
@@ -0,0 +1,29 @@
1
+ import { type Result } from "@fjall/generator";
2
+ import type { ProviderAccount } from "@fjall/util/config";
3
+ import type { OrgConfig } from "../types/orgConfig.js";
4
+ import type { DeployServices } from "./serviceFactory.js";
5
+ export interface ReconcileResult {
6
+ providerAccounts: ProviderAccount[];
7
+ /** Account names declared in ACCOUNTS but not yet present in AWS Organizations. */
8
+ missingAccountNames: string[];
9
+ }
10
+ /**
11
+ * When the orgConfig providerAccounts list is empty or root-only (first-deploy
12
+ * or stale sync), reconstruct the deployable account set by intersecting the
13
+ * live AWS Organizations account list with the ACCOUNTS configuration file.
14
+ * The ACCOUNTS file owns the accountName→environment mapping; AWS Organizations
15
+ * owns account IDs.
16
+ *
17
+ * `workingDirectory` is the prepared repository root (a populated clone in the
18
+ * worker). It MUST be passed through — `parseAccountsConfiguration` `tsImport`s
19
+ * `<workingDirectory>/fjall/organisation/infrastructure.ts`, which can only
20
+ * resolve `@fjall/components-infrastructure` after the clone's node_modules is
21
+ * installed.
22
+ */
23
+ export declare function reconcileProviderAccounts(services: DeployServices, workingDirectory: string): Promise<Result<ReconcileResult, Error>>;
24
+ /**
25
+ * Merge a reconciled providerAccounts list into an existing orgConfig.
26
+ * Additive by account id — the reconciler supplies entries the config is
27
+ * missing, never replacing pre-existing ones.
28
+ */
29
+ export declare function mergeReconciledProviderAccounts(existing: OrgConfig | undefined, reconciled: ProviderAccount[]): OrgConfig;
@@ -0,0 +1 @@
1
+ import{z as u}from"zod";import{success as N,failure as c}from"@fjall/generator";import{STRUCTURAL_ENVIRONMENTS as v}from"@fjall/util";import{OrganizationsClient as w}from"@aws-sdk/client-organizations";import{listAccounts as g}from"../aws/organisations/accounts.js";import{parseAccountsConfiguration as C,flattenAccountsToEnvironments as E}from"./accountsConfig.js";const T=u.object({Id:u.string().min(1),Name:u.string().min(1)});async function L(e,s){const n=await C(s);if(!n.success)return c(new Error(`Failed to parse ACCOUNTS configuration: ${n.error.message}`));if(n.data===null)return c(new Error("ACCOUNTS configuration file not found"));const r=E(n.data),f=new Map;for(const{accountName:o,environment:t}of r)f.set(o.toLowerCase(),{environment:t,displayName:o});let m;try{m=e.awsProvider.getClient(w)}catch(o){return c(o instanceof Error?o:new Error(String(o)))}const a=await g(m);if(!a.success)return c(a.error);const d=new Map;for(const o of a.data){const t=T.safeParse(o);t.success&&d.set(t.data.Name.toLowerCase(),{id:t.data.Id,name:t.data.Name})}const p=[],l=[];for(const[o,{environment:t,displayName:A}]of f){const i=d.get(o);if(!i){l.push(A);continue}t!==v.ROOT&&p.push({id:i.id,name:i.name,environment:t})}return N({providerAccounts:p,missingAccountNames:l})}function M(e,s){const n=new Map;for(const r of e?.providerAccounts??[])n.set(r.id,r);for(const r of s)n.has(r.id)||n.set(r.id,r);return{...e??{},providerAccounts:Array.from(n.values())}}export{M as mergeReconciledProviderAccounts,L as reconcileProviderAccounts};
@@ -1 +1 @@
1
- import{APPLICATION_STACKS as n,APPLICATION_DEPLOY_ORDER as A,getApplicationStepName as a,getApplicationStepId as t}from"../types/index.js";import{STEP_IDS as e,STEP_NAMES as s,INFRA_STEP_NAME as c}from"../types/index.js";const O={Network:e.NETWORK_DESTROY,Storage:e.STORAGE_DESTROY,Messaging:e.MESSAGING_DESTROY,Database:e.DATABASE_DESTROY,Compute:e.COMPUTE_DESTROY,Cdn:e.CDN_DESTROY};function S(f){return O[f]??`${f.toLowerCase()}-destroy`}class g{static DEPLOYMENT_STEPS=new Map([["application-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.PREPARE_DEPLOY},{id:e.BOOTSTRAP,name:s.BOOTSTRAP,conditions:{requiresInfra:!0}},{id:t(n.NETWORK,"deploy"),name:a(n.NETWORK,"deploy"),conditions:{requiresInfra:!0,requiresNetwork:!0}},{id:t(n.STORAGE,"deploy"),name:a(n.STORAGE,"deploy"),conditions:{requiresInfra:!0,requiresStorage:!0}},{id:t(n.MESSAGING,"deploy"),name:a(n.MESSAGING,"deploy"),conditions:{requiresInfra:!0,requiresMessaging:!0}},{id:t(n.DATABASE,"deploy"),name:a(n.DATABASE,"deploy"),conditions:{requiresInfra:!0,requiresDatabase:!0}},{id:e.DOCKER_OPERATIONS,name:"Docker operations",conditions:{requiresDocker:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:e.TAG_ECR_IMAGES,name:"Tagging container images",conditions:{requiresImageTagging:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:t(n.COMPUTE,"deploy"),name:a(n.COMPUTE,"deploy"),conditions:{requiresInfra:!0,requiresCompute:!0}},{id:t(n.CDN,"deploy"),name:a(n.CDN,"deploy"),conditions:{requiresInfra:!0,requiresCdn:!0}}]],["application-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.CHECK_INFRA_STATE},{id:t(n.CDN,"destroy"),name:a(n.CDN,"destroy"),conditions:{requiresCdn:!0}},{id:t(n.COMPUTE,"destroy"),name:a(n.COMPUTE,"destroy")},{id:t(n.DATABASE,"destroy"),name:a(n.DATABASE,"destroy")},{id:t(n.MESSAGING,"destroy"),name:a(n.MESSAGING,"destroy"),conditions:{requiresMessaging:!0}},{id:t(n.STORAGE,"destroy"),name:a(n.STORAGE,"destroy"),conditions:{requiresStorage:!0}},{id:t(n.NETWORK,"destroy"),name:a(n.NETWORK,"destroy")}]],["organisation-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.ORG_SETUP,name:"Configuring organisation"},{id:e.PREPARE,name:s.PREPARE_DEPLOY},{id:e.BOOTSTRAP,name:s.BOOTSTRAP},{id:e.DEPLOY,name:"Deploying organisation",conditions:{requiresOrgChanges:!0}},{id:e.CASCADE_PLATFORM,name:"Deploying platform",conditions:{requiresPlatformAccount:!0,requiresPlatformChanges:!0}},{id:e.CASCADE_DOMAINS,name:"Deploying domains",conditions:{requiresDomainConfiguration:!0,requiresDomainChanges:!0}},{id:e.CASCADE_ACCOUNTS,name:"Deploying accounts",conditions:{requiresMemberAccounts:!0,requiresAccountChanges:!0}}]],["organisation-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:s.CHECK_INFRA_STATE},{id:e.CASCADE_ACCOUNTS,name:"Destroying account infrastructure",conditions:{requiresMemberAccounts:!0}},{id:e.CASCADE_PLATFORM,name:"Destroying platform infrastructure",conditions:{requiresPlatformAccount:!0}},{id:e.DESTROY,name:"Destroying organisation infrastructure"}]],["platform-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:c.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:c.PREPARE},{id:e.DEPLOY,name:c.DEPLOY},{id:e.MONITORING,name:c.MONITORING}]],["platform-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying platform infrastructure"}]],["account-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:c.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:c.PREPARE},{id:e.DEPLOY,name:c.DEPLOY},{id:e.MONITORING,name:c.MONITORING}]],["account-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying account infrastructure"}]]]);static getSteps(r){if(r.deploymentType==="application"&&r.operation==="deploy"&&r.deployOnly)return[{id:e.AUTH,name:s.AUTH},{id:e.DOCKER_OPERATIONS,name:"Build and push Docker container"},{id:t(n.COMPUTE,"deploy"),name:a(n.COMPUTE,"deploy")}];const o=`${r.deploymentType}-${r.operation}`;return(this.DEPLOYMENT_STEPS.get(o)??[]).filter(i=>{if(r.deploymentType==="application"){const E=r.builderName==="opennext";if(i.conditions?.requiresInfra&&r.deployOnly)return!1;if(r.resources){const d=r.resources;if(i.conditions?.requiresNetwork&&!d.hasNetwork||i.conditions?.requiresCompute&&!d.hasCompute||i.conditions?.requiresDatabase&&!d.hasDatabase||i.conditions?.requiresStorage&&!d.hasStorage||i.conditions?.requiresMessaging&&!d.hasMessaging||i.conditions?.requiresCdn&&!d.hasCdn)return!1}else if(r.operation==="deploy"&&!E&&(i.conditions?.requiresNetwork||i.conditions?.requiresCompute||i.conditions?.requiresDatabase||i.conditions?.requiresStorage||i.conditions?.requiresMessaging||i.conditions?.requiresCdn))return!1;if(i.conditions?.requiresDocker||i.id===e.DOCKER_OPERATIONS)return E||r.infraOnly||r.resources&&!r.resources.hasCompute?!1:r.deployOnly?r.hasDockerfile===!0:r.hasDockerfile===!0||r.hasDockerfile===!1&&!r.isManagedAccount;if(i.conditions?.requiresImageTagging)return r.infraOnly||E||r.resources&&!r.resources.hasCompute?!1:r.hasDockerfile===!1}return!(i.conditions?.requiresMemberAccounts&&!r.hasMemberAccounts||i.conditions?.requiresPlatformAccount&&!r.hasPlatformAccount||i.conditions?.requiresOrgChanges&&r.hasOrgChanges!==!0||i.conditions?.requiresPlatformChanges&&r.hasPlatformChanges!==!0||i.conditions?.requiresAccountChanges&&r.hasAccountChanges!==!0||i.conditions?.requiresDomainConfiguration&&!r.hasDomainConfiguration||i.conditions?.requiresDomainChanges&&r.hasDomainChanges!==!0)}).map(i=>i.id===e.DOCKER_OPERATIONS?{...i,name:r.hasDockerfile?"Building and pushing Docker image":"Initialising container repository"}:i)}static getStepNames(r){return this.getSteps(r).map(o=>o.name)}static getStepIndex(r,o){return this.getSteps(o).findIndex(l=>l.id===r)}static getStepById(r,o="application"){const u=`${o}-deploy`,i=(this.DEPLOYMENT_STEPS.get(u)||[]).find(T=>T.id===r);if(i)return i;const E=`${o}-destroy`;return(this.DEPLOYMENT_STEPS.get(E)||[]).find(T=>T.id===r)}static isStepIncluded(r,o){return this.getSteps(o).some(l=>l.id===r)}static createContext(r,o,u){return{deploymentType:r,operation:o,deployOnly:u?.deployOnly??!1,infraOnly:u?.infraOnly??!1,isManagedAccount:u?.isManagedAccount??!1,hasDockerfile:u?.hasDockerfile??!1,pattern:u?.pattern??null,builderName:u?.builderName,resources:u?.resources}}static getDeploymentTypes(){return["application","organisation","platform","account"]}static getStackNames(r){switch(r){case"application":return[...A];case"organisation":return["Organisation"];case"platform":return["Platform"];case"account":return["Account"];default:return[]}}static getInfraStepIds(){return[e.CFN_CHECK,e.BOOTSTRAP,e.DIFF,e.NETWORK,e.STORAGE,e.MESSAGING,e.DATABASE,e.COMPUTE,e.CDN]}static getDockerStepIds(){return[e.ECR_INIT,e.DOCKER_DEPLOY]}}export{g as StepRegistry,S as getDestroyStepId};
1
+ import{APPLICATION_STACKS as n,APPLICATION_DEPLOY_ORDER as A,getApplicationStepName as t,getApplicationStepId as a}from"../types/index.js";import{STEP_IDS as e,STEP_NAMES as s,INFRA_STEP_NAME as d}from"../types/index.js";const O={Network:e.NETWORK_DESTROY,Storage:e.STORAGE_DESTROY,Messaging:e.MESSAGING_DESTROY,Database:e.DATABASE_DESTROY,Compute:e.COMPUTE_DESTROY,Cdn:e.CDN_DESTROY};function C(f){return O[f]??`${f.toLowerCase()}-destroy`}class p{static DEPLOYMENT_STEPS=new Map([["application-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.PREPARE_DEPLOY},{id:e.BOOTSTRAP,name:s.BOOTSTRAP,conditions:{requiresInfra:!0}},{id:a(n.NETWORK,"deploy"),name:t(n.NETWORK,"deploy"),conditions:{requiresInfra:!0,requiresNetwork:!0}},{id:a(n.STORAGE,"deploy"),name:t(n.STORAGE,"deploy"),conditions:{requiresInfra:!0,requiresStorage:!0}},{id:a(n.MESSAGING,"deploy"),name:t(n.MESSAGING,"deploy"),conditions:{requiresInfra:!0,requiresMessaging:!0}},{id:a(n.DATABASE,"deploy"),name:t(n.DATABASE,"deploy"),conditions:{requiresInfra:!0,requiresDatabase:!0}},{id:e.DOCKER_OPERATIONS,name:"Docker operations",conditions:{requiresDocker:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:e.TAG_ECR_IMAGES,name:"Tagging container images",conditions:{requiresImageTagging:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:a(n.COMPUTE,"deploy"),name:t(n.COMPUTE,"deploy"),conditions:{requiresInfra:!0,requiresCompute:!0}},{id:a(n.CDN,"deploy"),name:t(n.CDN,"deploy"),conditions:{requiresInfra:!0,requiresCdn:!0}}]],["application-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.CHECK_INFRA_STATE},{id:a(n.CDN,"destroy"),name:t(n.CDN,"destroy"),conditions:{requiresCdn:!0}},{id:a(n.COMPUTE,"destroy"),name:t(n.COMPUTE,"destroy")},{id:a(n.DATABASE,"destroy"),name:t(n.DATABASE,"destroy")},{id:a(n.MESSAGING,"destroy"),name:t(n.MESSAGING,"destroy"),conditions:{requiresMessaging:!0}},{id:a(n.STORAGE,"destroy"),name:t(n.STORAGE,"destroy"),conditions:{requiresStorage:!0}},{id:a(n.NETWORK,"destroy"),name:t(n.NETWORK,"destroy")}]],["organisation-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.ORG_SETUP,name:s.ORG_SETUP},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.ORG_DEPLOY,name:s.ORG_DEPLOY},{id:e.CASCADE_PLATFORM,name:s.CASCADE_PLATFORM,conditions:{requiresPlatformAccount:!0}},{id:e.CASCADE_ACCOUNTS,name:s.CASCADE_ACCOUNTS,conditions:{requiresMemberAccounts:!0}}]],["organisation-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:s.CHECK_INFRA_STATE},{id:e.CASCADE_ACCOUNTS,name:"Destroying account infrastructure",conditions:{requiresMemberAccounts:!0}},{id:e.CASCADE_PLATFORM,name:"Destroying platform infrastructure",conditions:{requiresPlatformAccount:!0}},{id:e.DESTROY,name:"Destroying organisation infrastructure"}]],["platform-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:d.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.DEPLOY,name:d.DEPLOY},{id:e.MONITORING,name:d.MONITORING}]],["platform-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying platform infrastructure"}]],["account-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:d.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.DEPLOY,name:d.DEPLOY},{id:e.MONITORING,name:d.MONITORING}]],["account-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying account infrastructure"}]]]);static getSteps(r){if(r.deploymentType==="application"&&r.operation==="deploy"&&r.deployOnly)return[{id:e.AUTH,name:s.AUTH},{id:e.DOCKER_OPERATIONS,name:"Build and push Docker container"},{id:a(n.COMPUTE,"deploy"),name:t(n.COMPUTE,"deploy")}];const o=`${r.deploymentType}-${r.operation}`;return(this.DEPLOYMENT_STEPS.get(o)??[]).filter(i=>{if(r.deploymentType==="application"){const l=r.builderName==="opennext";if(i.conditions?.requiresInfra&&r.deployOnly)return!1;if(r.resources){const c=r.resources;if(i.conditions?.requiresNetwork&&!c.hasNetwork||i.conditions?.requiresCompute&&!c.hasCompute||i.conditions?.requiresDatabase&&!c.hasDatabase||i.conditions?.requiresStorage&&!c.hasStorage||i.conditions?.requiresMessaging&&!c.hasMessaging||i.conditions?.requiresCdn&&!c.hasCdn)return!1}else if(r.operation==="deploy"&&!l&&(i.conditions?.requiresNetwork||i.conditions?.requiresCompute||i.conditions?.requiresDatabase||i.conditions?.requiresStorage||i.conditions?.requiresMessaging||i.conditions?.requiresCdn))return!1;if(i.conditions?.requiresDocker||i.id===e.DOCKER_OPERATIONS)return l||r.infraOnly||r.resources&&!r.resources.hasCompute?!1:r.deployOnly?r.hasDockerfile===!0:r.hasDockerfile===!0||r.hasDockerfile===!1&&!r.isManagedAccount;if(i.conditions?.requiresImageTagging)return r.infraOnly||l||r.resources&&!r.resources.hasCompute?!1:r.hasDockerfile===!1}return!(i.conditions?.requiresMemberAccounts&&!r.hasMemberAccounts||i.conditions?.requiresPlatformAccount&&!r.hasPlatformAccount||i.conditions?.requiresOrgChanges&&r.hasOrgChanges!==!0||i.conditions?.requiresPlatformChanges&&r.hasPlatformChanges!==!0||i.conditions?.requiresAccountChanges&&r.hasAccountChanges!==!0||i.conditions?.requiresDomainConfiguration&&!r.hasDomainConfiguration||i.conditions?.requiresDomainChanges&&r.hasDomainChanges!==!0)}).map(i=>i.id===e.DOCKER_OPERATIONS?{...i,name:r.hasDockerfile?"Building and pushing Docker image":"Initialising container repository"}:i)}static getStepNames(r){return this.getSteps(r).map(o=>o.name)}static getStepIndex(r,o){return this.getSteps(o).findIndex(E=>E.id===r)}static getStepById(r,o="application"){const u=`${o}-deploy`,i=(this.DEPLOYMENT_STEPS.get(u)||[]).find(T=>T.id===r);if(i)return i;const l=`${o}-destroy`;return(this.DEPLOYMENT_STEPS.get(l)||[]).find(T=>T.id===r)}static isStepIncluded(r,o){return this.getSteps(o).some(E=>E.id===r)}static createContext(r,o,u){return{deploymentType:r,operation:o,deployOnly:u?.deployOnly??!1,infraOnly:u?.infraOnly??!1,isManagedAccount:u?.isManagedAccount??!1,hasDockerfile:u?.hasDockerfile??!1,pattern:u?.pattern??null,builderName:u?.builderName,resources:u?.resources}}static getDeploymentTypes(){return["application","organisation","platform","account"]}static getStackNames(r){switch(r){case"application":return[...A];case"organisation":return["Organisation"];case"platform":return["Platform"];case"account":return["Account"];default:return[]}}static getInfraStepIds(){return[e.CFN_CHECK,e.BOOTSTRAP,e.DIFF,e.NETWORK,e.STORAGE,e.MESSAGING,e.DATABASE,e.COMPUTE,e.CDN]}static getDockerStepIds(){return[e.ECR_INIT,e.DOCKER_DEPLOY]}}export{p as StepRegistry,C as getDestroyStepId};
@@ -100,6 +100,14 @@ export interface DeployCallbacks {
100
100
  onCdkOutput?: (output: string, type: "synth" | "diff") => void;
101
101
  /** @emittedBy engine */
102
102
  onCascadeStart?: () => void;
103
+ /**
104
+ * @emittedBy engine — fired once after account reconciliation when accounts
105
+ * are declared in ACCOUNTS but not yet present in AWS Organizations (the
106
+ * cascade skips them). Carries the structured list so consumers can render a
107
+ * banner; the same information is also emitted as an `onProgress` warning for
108
+ * log surfaces.
109
+ */
110
+ onCascadeMissingAccounts?: (accountNames: readonly string[]) => void;
103
111
  /** @emittedBy engine */
104
112
  onCascadePhaseStart?: (phase: CascadePhase) => void;
105
113
  /** @emittedBy engine — fired when a cascade phase completes. */
@@ -15,7 +15,7 @@ export declare const DEPLOYMENT_EVENT_RESOURCE_CATEGORIES: readonly ["security",
15
15
  export type DeploymentEventResourceCategory = (typeof DEPLOYMENT_EVENT_RESOURCE_CATEGORIES)[number];
16
16
  export declare const CASCADE_PHASES: readonly ["platform", "domains", "accounts"];
17
17
  export declare const CASCADE_ACCOUNT_STATUSES: readonly ["started", "deploying", "completed", "failed"];
18
- export declare const DEPLOYMENT_EVENT_TYPES: readonly ["step", "resource", "docker", "ecs", "error", "complete", "cascade_phase", "cascade_account", "parallel_phase", "detection", "log"];
18
+ export declare const DEPLOYMENT_EVENT_TYPES: readonly ["step", "resource", "docker", "ecs", "error", "complete", "cascade_phase", "cascade_account", "cascade_missing_accounts", "parallel_phase", "detection", "log"];
19
19
  export type DeploymentEventType = (typeof DEPLOYMENT_EVENT_TYPES)[number];
20
20
  export type DeploymentEventCascadePhase = (typeof CASCADE_PHASES)[number];
21
21
  export type DeploymentEventCascadeAccountStatus = (typeof CASCADE_ACCOUNT_STATUSES)[number];
@@ -29,6 +29,7 @@ export declare const DeploymentEventSchema: z.ZodObject<{
29
29
  complete: "complete";
30
30
  cascade_phase: "cascade_phase";
31
31
  cascade_account: "cascade_account";
32
+ cascade_missing_accounts: "cascade_missing_accounts";
32
33
  parallel_phase: "parallel_phase";
33
34
  detection: "detection";
34
35
  log: "log";
@@ -117,6 +118,9 @@ export declare const DeploymentEventSchema: z.ZodObject<{
117
118
  accounts: "accounts";
118
119
  }>>;
119
120
  }, z.core.$strict>>;
121
+ cascadeMissingAccounts: z.ZodOptional<z.ZodObject<{
122
+ accountNames: z.ZodArray<z.ZodString>;
123
+ }, z.core.$strict>>;
120
124
  parallelPhase: z.ZodOptional<z.ZodObject<{
121
125
  stacks: z.ZodArray<z.ZodString>;
122
126
  status: z.ZodEnum<{
@@ -1 +1 @@
1
- import{z as e}from"zod";const r=["security","network","compute","database","storage","monitoring","dns","identity","bootstrap","events","registry","backup"],n=["platform","domains","accounts"],c=["started","deploying","completed","failed"],i=["step","resource","docker","ecs","error","complete","cascade_phase","cascade_account","parallel_phase","detection","log"],l={step:"step",resource:"resource",docker:"docker",ecs:"ecs",error:"error",complete:null,cascade_phase:"cascadePhase",cascade_account:"cascadeAccount",parallel_phase:"parallelPhase",detection:"detection",log:"log"},m=e.object({type:e.enum(i),timestamp:e.string().max(64),sequence:e.number().int().nonnegative().optional(),step:e.object({id:e.string().max(256),name:e.string().max(256),index:e.number(),total:e.number(),status:e.string().max(64)}).strict().optional(),resource:e.object({logicalId:e.string().max(256),resourceType:e.string().max(256),category:e.enum(r),group:e.string().max(128).optional(),constructPath:e.string().max(512).optional(),displayName:e.string().max(256),status:e.string().max(64),statusReason:e.string().max(2048).optional(),physicalId:e.string().max(2048).optional(),expectedDurationSeconds:e.number().optional(),stack:e.string().max(256).optional()}).strict().optional(),docker:e.object({message:e.string().max(2048),percentage:e.number().optional()}).strict().optional(),ecs:e.object({status:e.string().max(64),message:e.string().max(2048).optional(),percentage:e.number().optional()}).strict().optional(),error:e.object({message:e.string().max(4096),category:e.string().max(128).optional(),remediation:e.array(e.string().max(1024)).max(10).optional()}).strict().optional(),message:e.string().max(2048).optional(),cascadePhase:e.object({phase:e.enum(n),status:e.enum(["started","completed"])}).strict().optional(),cascadeAccount:e.object({accountId:e.string().max(32),region:e.string().max(32),operationKey:e.string().max(256),status:e.enum(c),error:e.string().max(2048).optional(),phase:e.enum(["bootstrap","synth","deploy","destroy"]).optional(),cascadePhase:e.enum(n).optional()}).strict().optional(),parallelPhase:e.object({stacks:e.array(e.string().max(256)).max(20),status:e.enum(["started","completed"]),results:e.array(e.object({stack:e.string().max(256),success:e.boolean(),error:e.string().max(2048).optional()}).strict()).max(20).optional()}).strict().optional(),detection:e.object({pattern:e.string().max(128).nullable(),hasDockerfile:e.boolean(),hasDifferences:e.boolean(),resources:e.object({hasNetwork:e.boolean(),hasCompute:e.boolean(),hasDatabase:e.boolean(),hasStorage:e.boolean(),hasMessaging:e.boolean(),hasCdn:e.boolean()}).strict(),requiredSecrets:e.array(e.string().max(512)).max(100).optional()}).strict().optional(),log:e.object({message:e.string().max(2048),level:e.enum(["info","debug","warn"])}).strict().optional()}).strict().superRefine((t,o)=>{const a=l[t.type],s=a?t[a]:void 0;a&&s==null&&o.addIssue({code:e.ZodIssueCode.custom,message:`"${a}" is required when type is "${t.type}"`,path:[a]}),t.type==="complete"&&!t.message&&o.addIssue({code:e.ZodIssueCode.custom,message:'"message" is required when type is "complete"',path:["message"]})});function u(t){if(t==="platform")return"platform";if(t==="account")return"accounts"}export{c as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,r as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,i as DEPLOYMENT_EVENT_TYPES,m as DeploymentEventSchema,u as toCascadePhase};
1
+ import{z as e}from"zod";const r=["security","network","compute","database","storage","monitoring","dns","identity","bootstrap","events","registry","backup"],n=["platform","domains","accounts"],c=["started","deploying","completed","failed"],i=["step","resource","docker","ecs","error","complete","cascade_phase","cascade_account","cascade_missing_accounts","parallel_phase","detection","log"],m={step:"step",resource:"resource",docker:"docker",ecs:"ecs",error:"error",complete:null,cascade_phase:"cascadePhase",cascade_account:"cascadeAccount",cascade_missing_accounts:"cascadeMissingAccounts",parallel_phase:"parallelPhase",detection:"detection",log:"log"},p=e.object({type:e.enum(i),timestamp:e.string().max(64),sequence:e.number().int().nonnegative().optional(),step:e.object({id:e.string().max(256),name:e.string().max(256),index:e.number(),total:e.number(),status:e.string().max(64)}).strict().optional(),resource:e.object({logicalId:e.string().max(256),resourceType:e.string().max(256),category:e.enum(r),group:e.string().max(128).optional(),constructPath:e.string().max(512).optional(),displayName:e.string().max(256),status:e.string().max(64),statusReason:e.string().max(2048).optional(),physicalId:e.string().max(2048).optional(),expectedDurationSeconds:e.number().optional(),stack:e.string().max(256).optional()}).strict().optional(),docker:e.object({message:e.string().max(2048),percentage:e.number().optional()}).strict().optional(),ecs:e.object({status:e.string().max(64),message:e.string().max(2048).optional(),percentage:e.number().optional()}).strict().optional(),error:e.object({message:e.string().max(4096),category:e.string().max(128).optional(),remediation:e.array(e.string().max(1024)).max(10).optional()}).strict().optional(),message:e.string().max(2048).optional(),cascadePhase:e.object({phase:e.enum(n),status:e.enum(["started","completed"])}).strict().optional(),cascadeAccount:e.object({accountId:e.string().max(32),region:e.string().max(32),operationKey:e.string().max(256),status:e.enum(c),error:e.string().max(2048).optional(),phase:e.enum(["bootstrap","synth","deploy","destroy"]).optional(),cascadePhase:e.enum(n).optional()}).strict().optional(),cascadeMissingAccounts:e.object({accountNames:e.array(e.string().max(256)).max(256)}).strict().optional(),parallelPhase:e.object({stacks:e.array(e.string().max(256)).max(20),status:e.enum(["started","completed"]),results:e.array(e.object({stack:e.string().max(256),success:e.boolean(),error:e.string().max(2048).optional()}).strict()).max(20).optional()}).strict().optional(),detection:e.object({pattern:e.string().max(128).nullable(),hasDockerfile:e.boolean(),hasDifferences:e.boolean(),resources:e.object({hasNetwork:e.boolean(),hasCompute:e.boolean(),hasDatabase:e.boolean(),hasStorage:e.boolean(),hasMessaging:e.boolean(),hasCdn:e.boolean()}).strict(),requiredSecrets:e.array(e.string().max(512)).max(100).optional()}).strict().optional(),log:e.object({message:e.string().max(2048),level:e.enum(["info","debug","warn"])}).strict().optional()}).strict().superRefine((t,s)=>{const a=m[t.type],o=a?t[a]:void 0;a&&o==null&&s.addIssue({code:e.ZodIssueCode.custom,message:`"${a}" is required when type is "${t.type}"`,path:[a]}),t.type==="complete"&&!t.message&&s.addIssue({code:e.ZodIssueCode.custom,message:'"message" is required when type is "complete"',path:["message"]})});function u(t){if(t==="platform")return"platform";if(t==="account")return"accounts"}export{c as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,r as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,i as DEPLOYMENT_EVENT_TYPES,p as DeploymentEventSchema,u as toCascadePhase};
@@ -73,6 +73,10 @@ export declare const STEP_NAMES: {
73
73
  readonly PREPARE_DESTROY: "Preparing destruction";
74
74
  readonly BOOTSTRAP: "Bootstrapping AWS environment";
75
75
  readonly CHECK_INFRA_STATE: "Checking infrastructure state";
76
+ readonly ORG_SETUP: "Configuring organisation";
77
+ readonly ORG_DEPLOY: "Deploying organisation";
78
+ readonly CASCADE_PLATFORM: "Deploying platform";
79
+ readonly CASCADE_ACCOUNTS: "Deploying accounts";
76
80
  };
77
81
  /**
78
82
  * Canonical step names emitted by infrastructure deployments (platform/account).
@@ -1 +1 @@
1
- const E={AUTH:"auth",DETECT_CONFIG:"detect-config",BOOTSTRAP:"bootstrap",CFN_CHECK:"cfn-check",DIFF:"diff",DEPLOY:"deploy",DESTROY:"destroy",VERIFY:"verify",CDK_SYNTH:"cdk-synth",DOCKER_OPERATIONS:"docker-operations",ECR_INIT:"ecr-init",DOCKER_DEPLOY:"docker-deploy",TAG_ECR_IMAGES:"tag-ecr-images",ORG_SETUP:"org-setup",ORG_ENSURE:"org-ensure",ORG_POLICY_TYPES:"policy-types",ORG_SERVICE_ACCESS:"service-access",ORG_CREATE_ACCOUNTS:"create-accounts",ORG_ENSURE_OUS:"ensure-ous",ORG_PLACE_ACCOUNTS:"place-accounts",IDENTITY_CENTRE:"identity-centre",ORG_ACCOUNTS:"accounts",ORG_COST_TAGS:"cost-tags",ORG_PROFILES:"profiles",PREPARE:"prepare",PREPARE_ENVIRONMENT:"prepare-environment",PLATFORM_ACCOUNT:"platform-account",ACCOUNT_CONTEXT:"account-context",CONNECT:"connect",MONITORING:"monitoring",ORG_DEPLOY:"organisation-deploy",ORG_DESTROY:"organisation-destroy",CASCADE_PLATFORM:"cascade-platform",CASCADE_DOMAINS:"cascade-domains",CASCADE_ACCOUNTS:"cascade-accounts",NETWORK:"network",STORAGE:"storage",MESSAGING:"messaging",DATABASE:"database",COMPUTE:"compute",CDN:"cdn",NETWORK_DESTROY:"network-destroy",STORAGE_DESTROY:"storage-destroy",MESSAGING_DESTROY:"messaging-destroy",DATABASE_DESTROY:"database-destroy",COMPUTE_DESTROY:"compute-destroy",CDN_DESTROY:"cdn-destroy"},t={AUTH:"Authenticating with AWS",PREPARE_DEPLOY:"Preparing deployment",PREPARE_DESTROY:"Preparing destruction",BOOTSTRAP:"Bootstrapping AWS environment",CHECK_INFRA_STATE:"Checking infrastructure state"},e=["Connect securely","Prepare environment","Deploy infrastructure","Enable monitoring"],o={CONNECT:e[0],PREPARE:e[1],DEPLOY:e[2],MONITORING:e[3]};export{e as INFRASTRUCTURE_STEP_NAMES,o as INFRA_STEP_NAME,E as STEP_IDS,t as STEP_NAMES};
1
+ const E={AUTH:"auth",DETECT_CONFIG:"detect-config",BOOTSTRAP:"bootstrap",CFN_CHECK:"cfn-check",DIFF:"diff",DEPLOY:"deploy",DESTROY:"destroy",VERIFY:"verify",CDK_SYNTH:"cdk-synth",DOCKER_OPERATIONS:"docker-operations",ECR_INIT:"ecr-init",DOCKER_DEPLOY:"docker-deploy",TAG_ECR_IMAGES:"tag-ecr-images",ORG_SETUP:"org-setup",ORG_ENSURE:"org-ensure",ORG_POLICY_TYPES:"policy-types",ORG_SERVICE_ACCESS:"service-access",ORG_CREATE_ACCOUNTS:"create-accounts",ORG_ENSURE_OUS:"ensure-ous",ORG_PLACE_ACCOUNTS:"place-accounts",IDENTITY_CENTRE:"identity-centre",ORG_ACCOUNTS:"accounts",ORG_COST_TAGS:"cost-tags",ORG_PROFILES:"profiles",PREPARE:"prepare",PREPARE_ENVIRONMENT:"prepare-environment",PLATFORM_ACCOUNT:"platform-account",ACCOUNT_CONTEXT:"account-context",CONNECT:"connect",MONITORING:"monitoring",ORG_DEPLOY:"organisation-deploy",ORG_DESTROY:"organisation-destroy",CASCADE_PLATFORM:"cascade-platform",CASCADE_DOMAINS:"cascade-domains",CASCADE_ACCOUNTS:"cascade-accounts",NETWORK:"network",STORAGE:"storage",MESSAGING:"messaging",DATABASE:"database",COMPUTE:"compute",CDN:"cdn",NETWORK_DESTROY:"network-destroy",STORAGE_DESTROY:"storage-destroy",MESSAGING_DESTROY:"messaging-destroy",DATABASE_DESTROY:"database-destroy",COMPUTE_DESTROY:"compute-destroy",CDN_DESTROY:"cdn-destroy"},t={AUTH:"Authenticating with AWS",PREPARE_DEPLOY:"Preparing deployment",PREPARE_DESTROY:"Preparing destruction",BOOTSTRAP:"Bootstrapping AWS environment",CHECK_INFRA_STATE:"Checking infrastructure state",ORG_SETUP:"Configuring organisation",ORG_DEPLOY:"Deploying organisation",CASCADE_PLATFORM:"Deploying platform",CASCADE_ACCOUNTS:"Deploying accounts"},e=["Connect securely","Prepare environment","Deploy infrastructure","Enable monitoring"],o={CONNECT:e[0],PREPARE:e[1],DEPLOY:e[2],MONITORING:e[3]};export{e as INFRASTRUCTURE_STEP_NAMES,o as INFRA_STEP_NAME,E as STEP_IDS,t as STEP_NAMES};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "2.4.8",
3
+ "version": "2.5.0",
4
4
  "description": "Shared deployment engine for Fjall — used by CLI and webapp worker",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -73,14 +73,15 @@
73
73
  "@aws-sdk/client-s3": "^3.1038.0",
74
74
  "@aws-sdk/client-sso-admin": "^3.1038.0",
75
75
  "@aws-sdk/client-sts": "^3.1038.0",
76
- "@fjall/generator": "^2.4.8",
77
- "@fjall/util": "^0.100.0",
76
+ "@fjall/generator": "^2.5.0",
77
+ "@fjall/util": "^2.5.0",
78
78
  "@smithy/node-http-handler": "^4.6.1",
79
+ "tsx": "^4.21.0",
79
80
  "zod": "^4.4.3"
80
81
  },
81
82
  "devDependencies": {
82
83
  "@types/node": "^25.6.0",
83
84
  "vitest": "^4.1.5"
84
85
  },
85
- "gitHead": "a1e1f954e39ecbe0360ac6124dba4146aa747f36"
86
+ "gitHead": "5c8f0e004f5520c692f2ee2063c3558c2451f2cf"
86
87
  }