@fjall/deploy-core 7.0.1 → 7.2.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.
@@ -9,7 +9,7 @@
9
9
  "package.json",
10
10
  "tsconfig.json"
11
11
  ],
12
- "hash": "3ed6b740d75832c192b54c10150afb348a91118a0788afb5db2ff383ee1958d2",
12
+ "hash": "d471f35f40c7c92d371201481e61e9fd32faf7b081dc9f87779382598575b4f5",
13
13
  "files": {
14
14
  "src/__test-utils__/awsMockHelpers.ts": "fea371c1b1869678",
15
15
  "src/__test-utils__/index.ts": "e03cf35821157dc4",
@@ -52,7 +52,7 @@
52
52
  "src/aws/utils/regions.ts": "3a5191f4d2242a60",
53
53
  "src/aws/utils/stackStatus.ts": "55fb23ba5514bc13",
54
54
  "src/events/index.ts": "e55c022f0d9013e6",
55
- "src/index.ts": "246666b0fa119a1c",
55
+ "src/index.ts": "138bc00a3dedc54f",
56
56
  "src/orchestration/activeDeploymentGuard.ts": "e8dde742c00ea728",
57
57
  "src/orchestration/application/applicationDeploy.ts": "2da75f7e60474a5a",
58
58
  "src/orchestration/application/applicationDeployHelpers.ts": "a8023472f95d3f05",
@@ -147,7 +147,7 @@
147
147
  "src/orchestration/remediation/postFailureRepairOffer.ts": "934b3fbd1c4d34d1",
148
148
  "src/orchestration/remediation/recreatePreFlight.ts": "654ef18cabe97777",
149
149
  "src/orchestration/remediation/recreateResource.ts": "a9d97c47e0af68a7",
150
- "src/orchestration/remediation/remediationJournal.ts": "3ff3e7e2e5cbfae6",
150
+ "src/orchestration/remediation/remediationJournal.ts": "9c37f66a07ce9912",
151
151
  "src/orchestration/remediation/removalUpdate.ts": "805be54eb0ac1623",
152
152
  "src/orchestration/remediation/retainFlip.ts": "8799a03380cb8f45",
153
153
  "src/orchestration/resolveOperation.ts": "9bcd66ba59808104",
@@ -177,16 +177,16 @@
177
177
  "src/services/application/ApplicationStackService.ts": "45824be79c1b82aa",
178
178
  "src/services/application/applicationStackHelpers.ts": "04e1758b97ffc5fc",
179
179
  "src/services/application/index.ts": "ee810be803dfda9c",
180
- "src/services/index.ts": "cc84cbd35873b20d",
180
+ "src/services/index.ts": "8c441c1b37540650",
181
181
  "src/services/infrastructure/CdkArgumentBuilder.ts": "e1b866b72e619bf5",
182
- "src/services/infrastructure/CdkCommandRunner.ts": "e33d4e529298f518",
182
+ "src/services/infrastructure/CdkCommandRunner.ts": "427a9c1171902e42",
183
183
  "src/services/infrastructure/CdkErrorFormatter.ts": "e2e017136108a5d1",
184
184
  "src/services/infrastructure/CdkEventMonitoring.ts": "2b0ecd84f76c54c1",
185
185
  "src/services/infrastructure/CdkOutputAnalyser.ts": "37114ecae45d7017",
186
186
  "src/services/infrastructure/CdkOutputParser.ts": "eed9f3475324118e",
187
187
  "src/services/infrastructure/CdkProcessManager.ts": "f68ae4e44adee2fa",
188
- "src/services/infrastructure/CdkService.ts": "1ec1718dfc3cb587",
189
- "src/services/infrastructure/CdkServiceTypes.ts": "6dc7429857b2efee",
188
+ "src/services/infrastructure/CdkService.ts": "9b5883d5362be9b0",
189
+ "src/services/infrastructure/CdkServiceTypes.ts": "c4f3075238952728",
190
190
  "src/services/infrastructure/CfnRegistryService.ts": "bd4f57d97982cdda",
191
191
  "src/services/infrastructure/CloudFormationService.ts": "82ce03614feae2db",
192
192
  "src/services/infrastructure/EcrImageInspectorService.ts": "e784440b5680df74",
@@ -195,13 +195,14 @@
195
195
  "src/services/infrastructure/ICdkProcessManager.ts": "7e7bfe7dacc999f6",
196
196
  "src/services/infrastructure/LambdaService.ts": "5b21e34185e77a4c",
197
197
  "src/services/infrastructure/SsmParameterMetadataService.ts": "3ff0bb42219cec3d",
198
+ "src/services/infrastructure/bootstrapConvergence.ts": "ca11f0bf9552ba8c",
198
199
  "src/services/infrastructure/cancelInFlightStack.ts": "8388a16a23db725a",
199
200
  "src/services/infrastructure/cdkServiceHelpers.ts": "2db30686382e568a",
200
201
  "src/services/infrastructure/cfnRegistrySeed.ts": "b1232b66883d6f90",
201
202
  "src/services/infrastructure/changeSetProbe.ts": "5f3e100db396cf9b",
202
203
  "src/services/infrastructure/constructMapEnrichment.ts": "7400af9bcfaa0da3",
203
204
  "src/services/infrastructure/dependencyIntegrityGate.ts": "c984bb1620f73f38",
204
- "src/services/infrastructure/index.ts": "1241c6dc24d925fe",
205
+ "src/services/infrastructure/index.ts": "c45e43711bf64fa9",
205
206
  "src/services/infrastructure/infraTypecheckPreflight.ts": "09e423a8283e3b8b",
206
207
  "src/services/infrastructure/transientNetworkRecovery.ts": "44a9333ce963bf50",
207
208
  "src/services/supporting/CdkContextBuilder.ts": "dee16f4302f7312a",
@@ -255,9 +256,9 @@
255
256
  "src/util/sleepAbortable.ts": "f9cac3bf8a79a476",
256
257
  "src/version.ts": "1811a4475823b27e",
257
258
  "../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
258
- "package.json": "e44e24f88fe8f78c",
259
+ "package.json": "05b055cd30867fc1",
259
260
  "tsconfig.json": "1929cf3f99fe9b1e"
260
261
  },
261
- "outputHash": "d65f44a3d1c787fb4b04534f4fd227ff81049a51daa7a7fa18036b4bf1070f3e",
262
- "outputFileCount": 479
262
+ "outputHash": "de61f3e9e5353ea996e61e068fb1054c7460b0e48216a23ea707c046bf775001",
263
+ "outputFileCount": 481
263
264
  }
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 239 files minified at 2026-08-03T05:01:32.712Z
1
+ 240 files minified at 2026-08-06T02:34:18.572Z
@@ -40,7 +40,7 @@ export { readDomainState, writeDomainState, recordDomainDeployState, deleteDomai
40
40
  export { CloudFormationEventMonitor } from "./aws/index.js";
41
41
  export type { AwsClientLike, EventLogWriter, EventFailureAnalyser, EventLogWriterFactory, EventMonitorDeps } from "./aws/index.js";
42
42
  export { CdkService, CdkArgumentBuilder, CdkProcessManager, ensureDependencyIntegrity, clearDependencyIntegrityCache, DEPENDENCY_SYNC_TIMEOUT_MS, CdkEventMonitor, startStackMonitoring, DEFAULT_DEPLOY_TIMEOUT_MS, isCdkError, formatInfrastructureError, getStructuralHint, getSourceContext, hasCdkDifferences, parseDiffOutput, CloudFormationService, CloudFormationError, EcsService, EcsError, EcsServiceResolver, type CfnExportsClient, TemplateHashService, TemplateHashError, type TemplateComparisonResult, CdkContextBuilder, emitProgress, PROGRESS_MESSAGES, parseBuildPhase, buildStepContextBuildConfig, convertCloudFormationOutputsToRecord, type CloudFormationOutput, ApplicationStackService } from "./services/index.js";
43
- export type { CdkContext, CdkOptions, CdkOutput, CheckDifferencesResult, CdkServiceOptions, ICdkProcessManager, DependencyIntegrityOutcome, DependencyIntegrityStatus, EnsureDependencyIntegrityOptions, DiffDetails, CloudFormationCallbacks, ECSServiceInfo, ECSClusterInfo, DeploymentStatus, ECSDeploymentOptions } from "./services/index.js";
43
+ export type { BootstrapRunFailure, CdkContext, CdkOptions, CdkOutput, CheckDifferencesResult, CdkServiceOptions, ICdkProcessManager, DependencyIntegrityOutcome, DependencyIntegrityStatus, EnsureDependencyIntegrityOptions, DiffDetails, CloudFormationCallbacks, ECSServiceInfo, ECSClusterInfo, DeploymentStatus, ECSDeploymentOptions } from "./services/index.js";
44
44
  export { CdkError } from "./types/errors/index.js";
45
45
  export { type ServiceErrorDetails, type ServiceError, BaseServiceError, ValidationError, AuthError, AwsError, DeploymentError, NetworkError, FileSystemError, ConfigError, toServiceError } from "./types/errors/index.js";
46
46
  export { filterDangerousEnvVars, maskSensitiveOutput, parseShellArgs, sleep } from "@fjall/util";
@@ -1,2 +1,2 @@
1
1
  var b=Object.defineProperty;var c=(e,r)=>b(e,"name",{value:r,configurable:!0});import{createHash as S}from"node:crypto";import{mkdir as T,readdir as $,readFile as g,rename as E,rm as F,rmdir as O,writeFile as _}from"node:fs/promises";import{homedir as k}from"node:os";import{basename as I,dirname as J,join as h}from"node:path";import{success as w,failure as D}from"@fjall/generator";import{getErrorMessage as p,maskSensitiveOutput as l}from"@fjall/util";import{logger as d}from"@fjall/util/logger";import{RemediationJournalRecordSchema as C}from"../../types/remediation.js";const f="RemediationJournal",M=90;function y(){return h(k(),".fjall","state","remediation")}c(y,"defaultRemediationJournalDir");function X(e,r){const n=S("sha256");n.update(e);for(const t of[...r].sort())n.update(`
2
- `),n.update(t);return n.digest("hex").slice(0,16)}c(X,"computeRemediationOpId");function P(e){return`${K(e.verb)} operation ${e.opId} already completed`}c(P,"formatCompletedOperationNotice");function ee(e){return`Archived completed ${e.verb} operation ${e.opId} from a previous incarnation \u2014 starting a fresh operation`}c(ee,"formatArchivedOperationNotice");function K(e){return e.charAt(0).toUpperCase()+e.slice(1)}c(K,"capitaliseVerb");const R=/^[A-Za-z0-9][A-Za-z0-9-]*$/,v=/^[a-f0-9]{8,64}$/;function A(e,r,n,t,a){for(const[o,u,i]of[["accountId",r,R],["region",n,R],["stackName",t,R],["opId",a,v]])if(!i.test(u))return D(new Error(`Refusing remediation journal path: unsafe ${o} segment "${l(u)}"`));return w(h(e,r,n,t,`${a}.json`))}c(A,"journalPath");async function te(e,r){const n=r?.journalDir??y(),t=A(n,e.accountId,e.region,e.stackName,e.opId);if(!t.success)return t;try{await T(J(t.data),{recursive:!0});const a=`${t.data}.tmp-${process.pid}`;return await _(a,JSON.stringify(e,null,2),"utf8"),await E(a,t.data),w(t.data)}catch(a){return D(new Error(`Failed to write remediation journal for ${e.stackName}/${e.opId}: ${l(p(a))}`))}}c(te,"writeRemediationJournal");async function re(e,r,n,t,a){const o=a?.journalDir??y(),u=A(o,e,r,n,t);if(!u.success)return null;let i;try{i=await g(u.data,"utf8")}catch{return null}return x(i,`${n}/${t}`)}c(re,"readRemediationJournal");function x(e,r){let n;try{n=JSON.parse(e)}catch(a){return d.warn(f,"Corrupt remediation journal ignored",{label:r,error:l(p(a))}),null}const t=C.safeParse(n);return t.success?t.data:(d.warn(f,"Schema-drifted remediation journal ignored",{label:r,error:l(t.error.message)}),null)}c(x,"parseJournal");async function ne(e,r){const n=r?.journalDir??y(),t=A(n,e.accountId,e.region,e.stackName,e.opId);if(!t.success)return t;const a=(e.completedAt??e.updatedAt).replace(/[^0-9A-Za-z.-]/g,""),o=t.data.replace(/\.json$/,`.done-${a}.json`);try{return await E(t.data,o),w(o)}catch(u){return D(new Error(`Failed to archive the completed remediation journal for ${e.stackName}/${e.opId}: ${l(p(u))}`))}}c(ne,"archiveCompletedRemediationJournal");async function ae(e,r,n){if(!v.test(e))return w(null);const t=n?.journalDir??y();let a;try{a=await N(t,3)}catch{return w(null)}const o=[];for(const i of a.filter(s=>s.endsWith(`/${e}.json`))){let s;try{s=await g(i,"utf8")}catch{continue}const m=x(s,e);m!==null&&o.push(m)}if(o.length===0)return w(null);if(o.length===1)return w(o[0]??null);const u=o.find(i=>i.accountId===r?.accountId&&i.region===r?.region);return u!==void 0?w(u):D(new Error(`Remediation operation ${e} exists in more than one context: ${o.map(i=>`${i.accountId}/${i.region}`).join(", ")}${r!==void 0?` \u2014 none matches the current credentials (${r.accountId}/${r.region}). Switch to the matching account/region and re-run 'fjall drift resume ${e}'.`:"."}`))}c(ae,"findRemediationJournalByOpId");async function z(e,r,n,t){const a=t?.journalDir??y(),o=h(a,e,r,n);if(!R.test(n))return[];let u;try{u=await $(o)}catch{return[]}const i=[];for(const s of u){if(!s.endsWith(".json"))continue;let m;try{m=await g(h(o,s),"utf8")}catch{continue}const j=x(m,`${n}/${s}`);j!==null&&j.phase!=="done"&&i.push(j)}return i}c(z,"listActiveRemediationOps");async function oe(e,r,n){const t=n?.journalDir??y();let a;try{a=await $(h(t,e,r))}catch{return[]}const o=[];for(const u of a)o.push(...await z(e,r,u,n));return o}c(oe,"listActiveRemediationOpsForContext");async function ie(e,r=()=>new Date){const n=e?.journalDir??y(),t=r().getTime()-M*24*60*60*1e3;let a;try{a=await N(n,3)}catch{return}for(const o of a){let u;try{u=await g(o,"utf8")}catch{continue}const i=x(u,o);if(i===null||i.phase!=="done")continue;const s=Date.parse(i.completedAt??i.updatedAt);if(Number.isNaN(s)||s>=t)continue;let m=!0;await F(o,{force:!0}).catch(j=>{m=!1,d.debug(f,"Failed to sweep expired remediation journal",{file:o,error:l(p(j))})}),!(!m||i.forensicsPath===void 0)&&await B(i.forensicsPath,i.opId)}}c(ie,"sweepExpiredRemediationJournals");async function B(e,r){const n=J(e),t=I(e)===r;if(!v.test(r)||!t&&I(n)!==r){d.debug(f,"Kept forensics bundle \u2014 unexpected path shape",{forensicsPath:e});return}if(t){await G(e);return}let a=!0;await F(e,{recursive:!0,force:!0}).catch(o=>{a=!1,d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:e,error:l(p(o))})}),a&&await O(n).catch(o=>{d.debug(f,"Kept forensics opId directory",{opDir:n,error:l(p(o))})})}c(B,"sweepForensicsBundle");async function G(e){let r;try{r=await $(e,{withFileTypes:!0})}catch(t){d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:e,error:l(p(t))});return}let n=!1;for(const t of r){if(!t.isFile()){n=!0;continue}await F(h(e,t.name),{force:!0}).catch(a=>{n=!0,d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:h(e,t.name),error:l(p(a))})})}if(n){d.debug(f,"Kept flat forensics directory \u2014 nested bundles live",{forensicsPath:e});return}await O(e).catch(t=>{d.debug(f,"Kept forensics opId directory",{forensicsPath:e,error:l(p(t))})})}c(G,"sweepFlatBundleFiles");async function N(e,r){const n=await $(e,{withFileTypes:!0}),t=[];for(const a of n){const o=h(e,a.name);a.isFile()&&a.name.endsWith(".json")?t.push(o):a.isDirectory()&&r>0&&t.push(...await N(o,r-1).catch(()=>[]))}return t}c(N,"listFilesRecursive");export{M as REMEDIATION_JOURNAL_RETENTION_DAYS,ne as archiveCompletedRemediationJournal,X as computeRemediationOpId,y as defaultRemediationJournalDir,ae as findRemediationJournalByOpId,ee as formatArchivedOperationNotice,P as formatCompletedOperationNotice,z as listActiveRemediationOps,oe as listActiveRemediationOpsForContext,re as readRemediationJournal,ie as sweepExpiredRemediationJournals,te as writeRemediationJournal};
2
+ `),n.update(t);return n.digest("hex").slice(0,16)}c(X,"computeRemediationOpId");function P(e){return`${K(e.verb)} operation ${e.opId} already completed`}c(P,"formatCompletedOperationNotice");function ee(e){return`Archived completed ${e.verb} operation ${e.opId} from a previous incarnation \u2014 starting a fresh operation`}c(ee,"formatArchivedOperationNotice");function K(e){return e.charAt(0).toUpperCase()+e.slice(1)}c(K,"capitaliseVerb");const R=/^[A-Za-z0-9][A-Za-z0-9-]*$/,v=/^[a-f0-9]{8,64}$/;function A(e,r,n,t,a){for(const[o,u,i]of[["accountId",r,R],["region",n,R],["stackName",t,R],["opId",a,v]])if(!i.test(u))return D(new Error(`Refusing remediation journal path: unsafe ${o} segment "${l(u)}"`));return w(h(e,r,n,t,`${a}.json`))}c(A,"journalPath");async function te(e,r){const n=r?.journalDir??y(),t=A(n,e.accountId,e.region,e.stackName,e.opId);if(!t.success)return t;try{await T(J(t.data),{recursive:!0});const a=`${t.data}.tmp-${process.pid}`;return await _(a,JSON.stringify(e,null,2),"utf8"),await E(a,t.data),w(t.data)}catch(a){return D(new Error(`Failed to write remediation journal for ${e.stackName}/${e.opId}: ${l(p(a))}`))}}c(te,"writeRemediationJournal");async function re(e,r,n,t,a){const o=a?.journalDir??y(),u=A(o,e,r,n,t);if(!u.success)return null;let i;try{i=await g(u.data,"utf8")}catch{return null}return x(i,`${n}/${t}`)}c(re,"readRemediationJournal");function x(e,r){let n;try{n=JSON.parse(e)}catch(a){return d.warn(f,"Corrupt remediation journal ignored",{label:r,error:l(p(a))}),null}const t=C.safeParse(n);return t.success?t.data:(d.warn(f,"Schema-drifted remediation journal ignored",{label:r,error:l(t.error.message)}),null)}c(x,"parseJournal");async function ne(e,r){const n=r?.journalDir??y(),t=A(n,e.accountId,e.region,e.stackName,e.opId);if(!t.success)return t;const a=(e.completedAt??e.updatedAt).replace(/[^0-9A-Za-z.-]/g,""),o=t.data.replace(/\.json$/,()=>`.done-${a}.json`);try{return await E(t.data,o),w(o)}catch(u){return D(new Error(`Failed to archive the completed remediation journal for ${e.stackName}/${e.opId}: ${l(p(u))}`))}}c(ne,"archiveCompletedRemediationJournal");async function ae(e,r,n){if(!v.test(e))return w(null);const t=n?.journalDir??y();let a;try{a=await N(t,3)}catch{return w(null)}const o=[];for(const i of a.filter(s=>s.endsWith(`/${e}.json`))){let s;try{s=await g(i,"utf8")}catch{continue}const m=x(s,e);m!==null&&o.push(m)}if(o.length===0)return w(null);if(o.length===1)return w(o[0]??null);const u=o.find(i=>i.accountId===r?.accountId&&i.region===r?.region);return u!==void 0?w(u):D(new Error(`Remediation operation ${e} exists in more than one context: ${o.map(i=>`${i.accountId}/${i.region}`).join(", ")}${r!==void 0?` \u2014 none matches the current credentials (${r.accountId}/${r.region}). Switch to the matching account/region and re-run 'fjall drift resume ${e}'.`:"."}`))}c(ae,"findRemediationJournalByOpId");async function z(e,r,n,t){const a=t?.journalDir??y(),o=h(a,e,r,n);if(!R.test(n))return[];let u;try{u=await $(o)}catch{return[]}const i=[];for(const s of u){if(!s.endsWith(".json"))continue;let m;try{m=await g(h(o,s),"utf8")}catch{continue}const j=x(m,`${n}/${s}`);j!==null&&j.phase!=="done"&&i.push(j)}return i}c(z,"listActiveRemediationOps");async function oe(e,r,n){const t=n?.journalDir??y();let a;try{a=await $(h(t,e,r))}catch{return[]}const o=[];for(const u of a)o.push(...await z(e,r,u,n));return o}c(oe,"listActiveRemediationOpsForContext");async function ie(e,r=()=>new Date){const n=e?.journalDir??y(),t=r().getTime()-M*24*60*60*1e3;let a;try{a=await N(n,3)}catch{return}for(const o of a){let u;try{u=await g(o,"utf8")}catch{continue}const i=x(u,o);if(i===null||i.phase!=="done")continue;const s=Date.parse(i.completedAt??i.updatedAt);if(Number.isNaN(s)||s>=t)continue;let m=!0;await F(o,{force:!0}).catch(j=>{m=!1,d.debug(f,"Failed to sweep expired remediation journal",{file:o,error:l(p(j))})}),!(!m||i.forensicsPath===void 0)&&await B(i.forensicsPath,i.opId)}}c(ie,"sweepExpiredRemediationJournals");async function B(e,r){const n=J(e),t=I(e)===r;if(!v.test(r)||!t&&I(n)!==r){d.debug(f,"Kept forensics bundle \u2014 unexpected path shape",{forensicsPath:e});return}if(t){await G(e);return}let a=!0;await F(e,{recursive:!0,force:!0}).catch(o=>{a=!1,d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:e,error:l(p(o))})}),a&&await O(n).catch(o=>{d.debug(f,"Kept forensics opId directory",{opDir:n,error:l(p(o))})})}c(B,"sweepForensicsBundle");async function G(e){let r;try{r=await $(e,{withFileTypes:!0})}catch(t){d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:e,error:l(p(t))});return}let n=!1;for(const t of r){if(!t.isFile()){n=!0;continue}await F(h(e,t.name),{force:!0}).catch(a=>{n=!0,d.warn(f,"Failed to sweep the expired journal's forensics",{forensicsPath:h(e,t.name),error:l(p(a))})})}if(n){d.debug(f,"Kept flat forensics directory \u2014 nested bundles live",{forensicsPath:e});return}await O(e).catch(t=>{d.debug(f,"Kept forensics opId directory",{forensicsPath:e,error:l(p(t))})})}c(G,"sweepFlatBundleFiles");async function N(e,r){const n=await $(e,{withFileTypes:!0}),t=[];for(const a of n){const o=h(e,a.name);a.isFile()&&a.name.endsWith(".json")?t.push(o):a.isDirectory()&&r>0&&t.push(...await N(o,r-1).catch(()=>[]))}return t}c(N,"listFilesRecursive");export{M as REMEDIATION_JOURNAL_RETENTION_DAYS,ne as archiveCompletedRemediationJournal,X as computeRemediationOpId,y as defaultRemediationJournalDir,ae as findRemediationJournalByOpId,ee as formatArchivedOperationNotice,P as formatCompletedOperationNotice,z as listActiveRemediationOps,oe as listActiveRemediationOpsForContext,re as readRemediationJournal,ie as sweepExpiredRemediationJournals,te as writeRemediationJournal};
@@ -1,5 +1,5 @@
1
1
  export { CdkService, CdkArgumentBuilder, CdkProcessManager, ensureDependencyIntegrity, clearDependencyIntegrityCache, DEPENDENCY_SYNC_TIMEOUT_MS, CdkEventMonitor, startStackMonitoring, DEFAULT_DEPLOY_TIMEOUT_MS, isCdkError, formatInfrastructureError, getStructuralHint, getSourceContext, hasCdkDifferences, parseDiffOutput, CloudFormationService, CloudFormationError, CfnRegistryService, CFN_REGISTRY_SEED, EcsService, EcsError, EcsServiceResolver, type CfnExportsClient } from "./infrastructure/index.js";
2
- export type { CdkContext, CdkOptions, CdkOutput, CheckDifferencesResult, CdkServiceOptions, ICdkProcessManager, DependencyIntegrityOutcome, DependencyIntegrityStatus, EnsureDependencyIntegrityOptions, DiffDetails, CloudFormationCallbacks, CfnRegistrySchemaSummary, CfnRegistryServiceOptions, CfnRegistrySummaryReader, ECSServiceInfo, ECSClusterInfo, DeploymentStatus, ECSDeploymentOptions } from "./infrastructure/index.js";
2
+ export type { BootstrapRunFailure, CdkContext, CdkOptions, CdkOutput, CheckDifferencesResult, CdkServiceOptions, ICdkProcessManager, DependencyIntegrityOutcome, DependencyIntegrityStatus, EnsureDependencyIntegrityOptions, DiffDetails, CloudFormationCallbacks, CfnRegistrySchemaSummary, CfnRegistryServiceOptions, CfnRegistrySummaryReader, ECSServiceInfo, ECSClusterInfo, DeploymentStatus, ECSDeploymentOptions } from "./infrastructure/index.js";
3
3
  export { TemplateHashService, TemplateHashError, type TemplateComparisonResult } from "./supporting/index.js";
4
4
  export { CdkContextBuilder } from "./supporting/index.js";
5
5
  export { emitProgress, PROGRESS_MESSAGES, parseBuildPhase, buildStepContextBuildConfig, convertCloudFormationOutputsToRecord, type CloudFormationOutput } from "./supporting/index.js";
@@ -2,6 +2,7 @@ import type { Result } from "@fjall/generator";
2
2
  import { CdkError } from "../../types/errors/CdkError.js";
3
3
  import { type DiffDetails } from "./CdkOutputParser.js";
4
4
  import type { ICdkProcessManager } from "./ICdkProcessManager.js";
5
+ import { type BootstrapRunFailure } from "./bootstrapConvergence.js";
5
6
  import type { CdkOptions, CdkOutput } from "./CdkService.js";
6
7
  /** Synth is CPU-bound template generation — 5 minutes is generous */
7
8
  export declare const SYNTH_TIMEOUT_MS = 300000;
@@ -35,5 +36,5 @@ export declare class CdkCommandRunner {
35
36
  */
36
37
  runImport(path: string, resourceMappingFile?: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
37
38
  synth(path: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
38
- bootstrap(accountId: string, region: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
39
+ bootstrap(accountId: string, region: string, options?: CdkOptions): Promise<Result<CdkOutput, BootstrapRunFailure>>;
39
40
  }
@@ -1,2 +1,2 @@
1
- var E=Object.defineProperty;var f=(m,s)=>E(m,"name",{value:s,configurable:!0});import{tmpdir as O}from"os";import{resolve as A}from"path";import{logger as l}from"@fjall/util/logger";import{success as p,failure as i}from"@fjall/generator";import{CdkError as h}from"../../types/errors/CdkError.js";import{DEFAULT_REGION as P}from"../../aws/utils/regions.js";import{CdkArgumentBuilder as g}from"./CdkArgumentBuilder.js";import{isCdkError as _,formatInfrastructureError as c}from"./CdkErrorFormatter.js";import{hasCdkDifferences as L,parseDiffOutput as R}from"./CdkOutputParser.js";import{DEFAULT_DEPLOY_TIMEOUT_MS as C}from"./CdkEventMonitoring.js";import{analyseBootstrapError as y}from"./CdkOutputAnalyser.js";const k=3e5,S=18e4,D=12e5,b="cdk.out",o=Object.freeze({APP:"--app",CI:"--ci",REQUIRE_APPROVAL:"--require-approval",APPROVAL_NEVER:"never",VERBOSE:"--verbose",NO_LOOKUPS:"--no-lookups",ALL:"--all",EXCLUSIVELY:"--exclusively"});class F{static{f(this,"CdkCommandRunner")}processManager;argumentBuilder=new g;constructor(s){this.processManager=s}dispose(){this.processManager.dispose()}async checkDifferences(s,t,r){const e=["diff"];t?e.push(t):e.push(o.ALL),e.push("--no-color"),r?.noLookups&&e.push(o.NO_LOOKUPS);const u=await this.processManager.runCdkCommand(s,e,{...r,ignoreExitCode:!0,combineOutput:!0});if(!u.success)return i(new h(c(u.error,s),"diff_failed",void 0,void 0,u.error,void 0,!1));const a=u.data.output||"";if(_(a))return i(new h(c(a,s),"diff_failed",void 0,void 0,a,void 0,!1));const n=L(a),d=R(a);return p({hasDifferences:n,output:a,details:d})}async deploy(s,t,r){const e=["deploy"];r?.appDir?e.push(o.APP,r.appDir):r?.useCdkOut&&e.push(o.APP,"cdk.out"),t?(e.push(t),r?.exclusively&&e.push(o.EXCLUSIVELY)):e.push(o.ALL),e.push(o.REQUIRE_APPROVAL,o.APPROVAL_NEVER),e.push(o.CI),e.push("--no-version-reporting"),e.push("--no-path-metadata"),e.push("--no-asset-metadata"),r?.verbose?e.push(o.VERBOSE):e.push("--progress","events"),e.push(...this.argumentBuilder.buildParameterArgs(r?.parameters,t));const u={...r,timeout:r?.timeout||C};return r?.passThroughCDK?this.processManager.runCdkCommandPassthrough(s,e,u):this.processManager.runCdkCommand(s,e,u)}async destroy(s,t,r){const e=["destroy"];r?.appDir?e.push(o.APP,r.appDir):r?.useCdkOut&&e.push(o.APP,"cdk.out"),t?e.push(t):e.push(o.ALL),e.push("--force"),r?.verbose&&e.push(o.VERBOSE);const u={...r,timeout:r?.timeout||C};return this.processManager.runCdkCommand(s,e,u)}async runImport(s,t,r){const e=["import"];r?.stacks&&r.stacks.length>0&&e.push(...r.stacks),t&&e.push("--resource-mapping",t),e.push(o.REQUIRE_APPROVAL,o.APPROVAL_NEVER),e.push(o.CI),r?.noLookups&&e.push(o.NO_LOOKUPS),r?.verbose&&(e.push(o.VERBOSE),e.push("--trace"),e.push("--debug"));const u={...r,timeout:r?.timeout||D};return r?.passThroughCDK?this.processManager.runCdkCommandPassthrough(s,e,u):this.processManager.runCdkCommand(s,e,u)}async synth(s,t){t?.outputCallback?.(`Synthesising CloudFormation template...
2
- `);const r=t?.noLookups?[o.NO_LOOKUPS]:[],u=["--output",A(s,t?.outputDir??b)],a=t?.context?.region||P,n=await this.processManager.runCdkCommand(s,["synth",...r,...u,o.CI,"--quiet"],{...t,context:{...t?.context,region:a},timeout:t?.timeout||k});if(!n.success){const d=n.error?c(n.error,s):"Failed to synthesise CloudFormation template";return i(d)}return n}async bootstrap(s,t,r){const e=O();l.debug("CdkService","Starting CDK bootstrap",{accountId:s,region:t,bootstrapPath:e,target:`aws://${s}/${t}`});const u=await this.processManager.runCdkCommand(e,["bootstrap",`aws://${s}/${t}`,"--cloudformation-execution-policies","arn:aws:iam::aws:policy/AdministratorAccess",...r?.permissionsBoundary!==void 0&&r.permissionsBoundary!==""?["--custom-permissions-boundary",r.permissionsBoundary]:[],...Object.entries(r?.bootstrapTags??{}).filter(([a,n])=>a!==""&&n!=="").flatMap(([a,n])=>["--tags",`${a}=${n}`]),"-c",`accountId=${s}`,o.REQUIRE_APPROVAL,o.APPROVAL_NEVER,o.CI,"--quiet","--force"],{...r,timeout:r?.timeout||S,context:{region:t,accountId:s},credentials:r?.credentials,skipProjectCheck:!0,extraEnv:{TERM:"dumb",CDK_DISABLE_NOTICES:"true",CDK_DISABLE_PROGRESS_BAR:"true"}});return l.debug("CdkService",u.success?"Bootstrap completed successfully":"Bootstrap exited with non-zero code",{accountId:s,region:t,exitCode:u.success?u.data.exitCode:void 0,output:u.success&&u.data.output?.trim()||"(empty)",error:u.success?"(empty)":u.error.trim()||"(empty)"}),u.success?p({output:"AWS environment bootstrapped",exitCode:0}):u.error.includes("already bootstrapped")?p({output:"Environment is already bootstrapped",exitCode:0}):i(y(u.error))}}export{S as BOOTSTRAP_TIMEOUT_MS,F as CdkCommandRunner,D as IMPORT_TIMEOUT_MS,k as SYNTH_TIMEOUT_MS};
1
+ var E=Object.defineProperty;var f=(m,s)=>E(m,"name",{value:s,configurable:!0});import{tmpdir as O}from"os";import{resolve as g}from"path";import{logger as h}from"@fjall/util/logger";import{maskSensitiveOutput as A}from"@fjall/util";import{success as d,failure as n}from"@fjall/generator";import{CdkError as l}from"../../types/errors/CdkError.js";import{DEFAULT_REGION as P}from"../../aws/utils/regions.js";import{CdkArgumentBuilder as _}from"./CdkArgumentBuilder.js";import{isCdkError as L,formatInfrastructureError as c}from"./CdkErrorFormatter.js";import{hasCdkDifferences as R,parseDiffOutput as y}from"./CdkOutputParser.js";import{DEFAULT_DEPLOY_TIMEOUT_MS as C}from"./CdkEventMonitoring.js";import{analyseBootstrapError as k}from"./CdkOutputAnalyser.js";import{filterBootstrapTagEntries as S,isBootstrapLostRaceError as D}from"./bootstrapConvergence.js";const b=3e5,M=18e4,T=12e5,v="cdk.out",u=Object.freeze({APP:"--app",CI:"--ci",REQUIRE_APPROVAL:"--require-approval",APPROVAL_NEVER:"never",VERBOSE:"--verbose",NO_LOOKUPS:"--no-lookups",ALL:"--all",EXCLUSIVELY:"--exclusively"});class G{static{f(this,"CdkCommandRunner")}processManager;argumentBuilder=new _;constructor(s){this.processManager=s}dispose(){this.processManager.dispose()}async checkDifferences(s,t,r){const e=["diff"];t?e.push(t):e.push(u.ALL),e.push("--no-color"),r?.noLookups&&e.push(u.NO_LOOKUPS);const o=await this.processManager.runCdkCommand(s,e,{...r,ignoreExitCode:!0,combineOutput:!0});if(!o.success)return n(new l(c(o.error,s),"diff_failed",void 0,void 0,o.error,void 0,!1));const a=o.data.output||"";if(L(a))return n(new l(c(a,s),"diff_failed",void 0,void 0,a,void 0,!1));const i=R(a),p=y(a);return d({hasDifferences:i,output:a,details:p})}async deploy(s,t,r){const e=["deploy"];r?.appDir?e.push(u.APP,r.appDir):r?.useCdkOut&&e.push(u.APP,"cdk.out"),t?(e.push(t),r?.exclusively&&e.push(u.EXCLUSIVELY)):e.push(u.ALL),e.push(u.REQUIRE_APPROVAL,u.APPROVAL_NEVER),e.push(u.CI),e.push("--no-version-reporting"),e.push("--no-path-metadata"),e.push("--no-asset-metadata"),r?.verbose?e.push(u.VERBOSE):e.push("--progress","events"),e.push(...this.argumentBuilder.buildParameterArgs(r?.parameters,t));const o={...r,timeout:r?.timeout||C};return r?.passThroughCDK?this.processManager.runCdkCommandPassthrough(s,e,o):this.processManager.runCdkCommand(s,e,o)}async destroy(s,t,r){const e=["destroy"];r?.appDir?e.push(u.APP,r.appDir):r?.useCdkOut&&e.push(u.APP,"cdk.out"),t?e.push(t):e.push(u.ALL),e.push("--force"),r?.verbose&&e.push(u.VERBOSE);const o={...r,timeout:r?.timeout||C};return this.processManager.runCdkCommand(s,e,o)}async runImport(s,t,r){const e=["import"];r?.stacks&&r.stacks.length>0&&e.push(...r.stacks),t&&e.push("--resource-mapping",t),e.push(u.REQUIRE_APPROVAL,u.APPROVAL_NEVER),e.push(u.CI),r?.noLookups&&e.push(u.NO_LOOKUPS),r?.verbose&&(e.push(u.VERBOSE),e.push("--trace"),e.push("--debug"));const o={...r,timeout:r?.timeout||T};return r?.passThroughCDK?this.processManager.runCdkCommandPassthrough(s,e,o):this.processManager.runCdkCommand(s,e,o)}async synth(s,t){t?.outputCallback?.(`Synthesising CloudFormation template...
2
+ `);const r=t?.noLookups?[u.NO_LOOKUPS]:[],o=["--output",g(s,t?.outputDir??v)],a=t?.context?.region||P,i=await this.processManager.runCdkCommand(s,["synth",...r,...o,u.CI,"--quiet"],{...t,context:{...t?.context,region:a},timeout:t?.timeout||b});if(!i.success){const p=i.error?c(i.error,s):"Failed to synthesise CloudFormation template";return n(p)}return i}async bootstrap(s,t,r){const e=O();h.debug("CdkService","Starting CDK bootstrap",{accountId:s,region:t,bootstrapPath:e,target:`aws://${s}/${t}`});const o=await this.processManager.runCdkCommand(e,["bootstrap",`aws://${s}/${t}`,"--cloudformation-execution-policies","arn:aws:iam::aws:policy/AdministratorAccess",...r?.permissionsBoundary!==void 0&&r.permissionsBoundary!==""?["--custom-permissions-boundary",r.permissionsBoundary]:[],...Object.entries(S(r?.bootstrapTags)).flatMap(([a,i])=>["--tags",`${a}=${i}`]),"-c",`accountId=${s}`,u.REQUIRE_APPROVAL,u.APPROVAL_NEVER,u.CI,"--quiet",...r?.bootstrapForce!==!1?["--force"]:[]],{...r,timeout:r?.timeout||M,context:{region:t,accountId:s},credentials:r?.credentials,skipProjectCheck:!0,extraEnv:{TERM:"dumb",CDK_DISABLE_NOTICES:"true",CDK_DISABLE_PROGRESS_BAR:"true"}});return h.debug("CdkService",o.success?"Bootstrap completed successfully":"Bootstrap exited with non-zero code",{accountId:s,region:t,exitCode:o.success?o.data.exitCode:void 0,output:o.success&&o.data.output?.trim()||"(empty)",error:o.success?"(empty)":A(o.error.trim())||"(empty)"}),o.success?d({output:"AWS environment bootstrapped",exitCode:0}):o.error.includes("already bootstrapped")?d({output:"Environment is already bootstrapped",exitCode:0}):D(o.error)?n({kind:"lost-race",message:`Another process is bootstrapping aws://${s}/${t} at the same time. Wait for it to finish, then retry.`}):n({kind:"other",message:k(o.error)})}}export{M as BOOTSTRAP_TIMEOUT_MS,G as CdkCommandRunner,T as IMPORT_TIMEOUT_MS,b as SYNTH_TIMEOUT_MS};
@@ -4,6 +4,7 @@ import type { AwsProvider } from "../../aws/AwsProvider.js";
4
4
  import type { Result } from "@fjall/generator";
5
5
  import type { CdkError } from "../../types/errors/CdkError.js";
6
6
  import { type CheckDifferencesResult } from "./CdkCommandRunner.js";
7
+ import { type BootstrapRunFailure } from "./bootstrapConvergence.js";
7
8
  import type { CdkOptions, CdkOutput, CdkServiceOptions } from "./CdkServiceTypes.js";
8
9
  export type { CdkContext, CdkOptions, CdkOutput, CdkServiceOptions } from "./CdkServiceTypes.js";
9
10
  export { type CheckDifferencesResult } from "./CdkCommandRunner.js";
@@ -18,8 +19,17 @@ export declare class CdkService {
18
19
  destroy(path: string, stackName?: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
19
20
  runImport(path: string, resourceMappingFile?: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
20
21
  synth(path: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
21
- bootstrap(accountId: string, region: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
22
+ bootstrap(accountId: string, region: string, options?: CdkOptions): Promise<Result<CdkOutput, BootstrapRunFailure>>;
22
23
  runCdkSynth(context: DeploymentContext, onOutput?: (chunk: string) => void, credentials?: CdkOptions["credentials"]): Promise<Result<StepOutput, string>>;
24
+ /**
25
+ * Convergence-first bootstrap protocol: probe the CDKToolkit stack, wait
26
+ * out any in-flight bootstrap instead of racing its fixed-name changeset,
27
+ * force only when the desired tag set cannot be confirmed live, and retry
28
+ * a bounded number of lost races after the winner settles. Retry rather
29
+ * than declare success on a lost race — the winner may have run a
30
+ * different CDK version or stamped a different tag set; a no-force retry
31
+ * short-circuits in seconds when the toolkit already converged.
32
+ */
23
33
  runCdkBootstrap(context: DeploymentContext, onOutput?: (chunk: string) => void, credentials?: CdkOptions["credentials"], permissionsBoundary?: string, bootstrapTags?: CdkOptions["bootstrapTags"]): Promise<Result<StepOutput, string>>;
24
34
  runCdkDiff(context: DeploymentContext, onOutput?: (chunk: string) => void): Promise<Result<StepOutput, string>>;
25
35
  runCdkDeploy(context: DeploymentContext, stackPattern?: string, onOutput?: (chunk: string) => void, onResourceProgress?: (event: ResourceEvent) => void, aws?: AwsProvider, credentials?: CdkOptions["credentials"], parameters?: Record<string, string>, exclusively?: boolean): Promise<Result<StepOutput, string>>;
@@ -1,13 +1,15 @@
1
- var q=Object.defineProperty;var M=(P,t)=>q(P,"name",{value:t,configurable:!0});import{existsSync as H}from"fs";import{join as K}from"path";import{logger as T}from"@fjall/util/logger";import{isSuccessState as L,isTerminalState as W}from"../../aws/utils/cloudformationEventHelpers.js";import{success as y,failure as s}from"@fjall/generator";import{DEFAULT_REGION as k}from"../../aws/utils/regions.js";import{getErrorMessage as b,maskSensitiveOutput as p}from"@fjall/util";import{CdkEventMonitor as X,DEFAULT_DEPLOY_TIMEOUT_MS as z,settleMonitoringAnalysis as j,startStackMonitoring as J}from"./CdkEventMonitoring.js";import{analyseDeployOutput as Q,analyseDestroyResult as V,createEnhancedOutputCallback as Z,formatAnalysedDeployFailure as $}from"./CdkOutputAnalyser.js";import{recordDriftSuspects as x}from"../../orchestration/drift/driftSuspectJournal.js";import{CdkCommandRunner as tt}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as et}from"./CdkArgumentBuilder.js";import{CdkProcessManager as rt}from"./CdkProcessManager.js";import{cancelInFlightStack as U}from"./cancelInFlightStack.js";import{isAborted as A}from"../../aws/organisations/types.js";import{wrapWithConstructMapEnrichment as nt}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as at,resolveStackName as G,getFallbackStackName as ot,buildDeploymentCdkContext as N}from"./cdkServiceHelpers.js";import{ensureDependencyIntegrity as it}from"./dependencyIntegrityGate.js";import{runInfraTypecheckPreflight as st}from"./infraTypecheckPreflight.js";import{isTransientNetworkFailure as ct,isReattachableInProgressStatus as ut,MAX_TRANSIENT_NETWORK_RETRIES as E,TRANSIENT_RETRY_BACKOFF_MS as lt}from"./transientNetworkRecovery.js";import{sleepAbortable as dt}from"../../util/sleepAbortable.js";class Pt{static{M(this,"CdkService")}commandRunner;eventMonitor;abortSignal;constructor(t){const e=t?.processManager??new rt(new et,t?.abortSignal);this.commandRunner=new tt(e),this.eventMonitor=new X({eventLogWriterFactory:t?.eventLogWriterFactory,...t?.onFailureAnalysis!==void 0?{onFailureAnalysis:t.onFailureAnalysis}:{}}),this.abortSignal=t?.abortSignal}dispose(){this.commandRunner.dispose()}async checkDifferences(t,e,r){return this.commandRunner.checkDifferences(t,e,r)}async deploy(t,e,r){return this.commandRunner.deploy(t,e,r)}async destroy(t,e,r){return this.commandRunner.destroy(t,e,r)}async runImport(t,e,r){return this.commandRunner.runImport(t,e,r)}async synth(t,e){return this.commandRunner.synth(t,e)}async bootstrap(t,e,r){return this.commandRunner.bootstrap(t,e,r)}async runCdkSynth(t,e,r){const a=t.callerIdentity?.Account;try{const n=await it(t.path,{...e!==void 0?{onOutput:e}:{},...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});if(!n.success)return s(n.error);const o=await st(t.path,{...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});o.status==="failed"&&(e?.(`WARNING: TypeScript errors in ${t.path} \u2014 the synthesised template may be wrong. This check will block deploys in a future release.
1
+ var H=Object.defineProperty;var M=(_,t)=>H(_,"name",{value:t,configurable:!0});import{existsSync as z}from"fs";import{join as K}from"path";import{logger as I}from"@fjall/util/logger";import{isSuccessState as L,isTerminalState as W}from"../../aws/utils/cloudformationEventHelpers.js";import{success as b,failure as a}from"@fjall/generator";import{DEFAULT_REGION as C}from"../../aws/utils/regions.js";import{getErrorMessage as D,maskSensitiveOutput as S}from"@fjall/util";import{CdkEventMonitor as J,DEFAULT_DEPLOY_TIMEOUT_MS as Q,settleMonitoringAnalysis as B,startStackMonitoring as V}from"./CdkEventMonitoring.js";import{analyseDeployOutput as Z,analyseDestroyResult as x,createEnhancedOutputCallback as tt,formatAnalysedDeployFailure as N}from"./CdkOutputAnalyser.js";import{recordDriftSuspects as et}from"../../orchestration/drift/driftSuspectJournal.js";import{CdkCommandRunner as rt}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as nt}from"./CdkArgumentBuilder.js";import{CdkProcessManager as ot}from"./CdkProcessManager.js";import{cancelInFlightStack as j}from"./cancelInFlightStack.js";import{awaitToolkitSettled as U,filterBootstrapTagEntries as at,probeToolkitStack as it,resolveBootstrapForce as st,MAX_BOOTSTRAP_LOST_RACE_RETRIES as G,TOOLKIT_SETTLE_BUDGET_MS as ct}from"./bootstrapConvergence.js";import{isAborted as k}from"../../aws/organisations/types.js";import{wrapWithConstructMapEnrichment as lt}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as ut,resolveStackName as X,getFallbackStackName as dt,buildDeploymentCdkContext as $}from"./cdkServiceHelpers.js";import{ensureDependencyIntegrity as ft}from"./dependencyIntegrityGate.js";import{runInfraTypecheckPreflight as mt}from"./infraTypecheckPreflight.js";import{isTransientNetworkFailure as pt,isReattachableInProgressStatus as gt,MAX_TRANSIENT_NETWORK_RETRIES as E,TRANSIENT_RETRY_BACKOFF_MS as ht}from"./transientNetworkRecovery.js";import{sleepAbortable as yt}from"../../util/sleepAbortable.js";class jt{static{M(this,"CdkService")}commandRunner;eventMonitor;abortSignal;constructor(t){const e=t?.processManager??new ot(new nt,t?.abortSignal);this.commandRunner=new rt(e),this.eventMonitor=new J({eventLogWriterFactory:t?.eventLogWriterFactory,...t?.onFailureAnalysis!==void 0?{onFailureAnalysis:t.onFailureAnalysis}:{}}),this.abortSignal=t?.abortSignal}dispose(){this.commandRunner.dispose()}async checkDifferences(t,e,r){return this.commandRunner.checkDifferences(t,e,r)}async deploy(t,e,r){return this.commandRunner.deploy(t,e,r)}async destroy(t,e,r){return this.commandRunner.destroy(t,e,r)}async runImport(t,e,r){return this.commandRunner.runImport(t,e,r)}async synth(t,e){return this.commandRunner.synth(t,e)}async bootstrap(t,e,r){return this.commandRunner.bootstrap(t,e,r)}async runCdkSynth(t,e,r){const i=t.callerIdentity?.Account;try{const s=await ft(t.path,{...e!==void 0?{onOutput:e}:{},...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});if(!s.success)return a(s.error);const o=await mt(t.path,{...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});o.status==="failed"&&(e?.(`WARNING: TypeScript errors in ${t.path} \u2014 the synthesised template may be wrong. This check will block deploys in a future release.
2
2
  ${o.details}
3
- `),T.warn("CdkService","Infrastructure typecheck failed",{path:t.path}));const i=await this.synth(t.path,{outputCallback:e,context:N(t,a,t.region||k),...t.assemblyDir!==void 0?{outputDir:t.assemblyDir}:{},...r!==void 0?{credentials:r}:{}});return i.success?y({message:"CloudFormation template synthesised",details:i.data.output?{synthesisTime:i.data.output}:void 0}):s(i.error||"Failed to synthesise CloudFormation template")}catch(n){return s(`CDK synth failed: ${p(b(n))}`)}}async runCdkBootstrap(t,e,r,a,n){const o=t.callerIdentity?.Account,i=t.region||k;try{if(!o)return s("No AWS account ID available");const l=K(t.path,"node_modules");if(!H(l))return s(`Dependencies not installed. Please run 'npm install' in ${t.path} before deploying.`);const d=await this.bootstrap(o,i,{outputCallback:e,credentials:r,...a!==void 0&&a!==""?{permissionsBoundary:a}:{},...n!==void 0&&Object.keys(n).length>0?{bootstrapTags:n}:{}});return d.success?y({message:"AWS environment bootstrapped"}):s(d.error||"Failed to bootstrap AWS environment")}catch(l){return s(`CDK bootstrap failed: ${p(b(l))}`)}}async runCdkDiff(t,e){const r=t.callerIdentity?.Account;try{const a=await this.checkDifferences(t.path,void 0,{verbose:t.options?.verbose,outputCallback:e,context:N(t,r,t.region||k)});return a.success?y({message:"Diff check complete",details:{hasDifferences:a.data.hasDifferences,details:a.data.details}}):s(`CDK diff failed: ${a.error.message}`)}catch(a){return s(`CDK diff failed: ${p(b(a))}`)}}async runCdkDeploy(t,e,r,a,n,o,i,l){const d=t.callerIdentity?.Account,u=t.region||k;if(!d)return s("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!n)return s("AwsProvider is required for deployment monitoring.");const g=t.assemblyDir??K(t.path,"cdk.out"),S=nt(g,a);let h=null,D;try{const m=G(e,t)??ot(t),f=await this.eventMonitor.createEventMonitor("deploy",m,u,t,n);h=f,r&&(r(p(`Starting CloudFormation deployment of ${m}...
3
+ `),I.warn("CdkService","Infrastructure typecheck failed",{path:t.path}));const n=await this.synth(t.path,{outputCallback:e,context:$(t,i,t.region||C),...t.assemblyDir!==void 0?{outputDir:t.assemblyDir}:{},...r!==void 0?{credentials:r}:{}});return n.success?b({message:"CloudFormation template synthesised",details:n.data.output?{synthesisTime:n.data.output}:void 0}):a(n.error||"Failed to synthesise CloudFormation template")}catch(s){return a(`CDK synth failed: ${S(D(s))}`)}}async runCdkBootstrap(t,e,r,i,s){const o=t.callerIdentity?.Account,n=t.region||C;try{if(!o)return a("No AWS account ID available");const u=K(t.path,"node_modules");if(!z(u))return a(`Dependencies not installed. Please run 'npm install' in ${t.path} before deploying.`);const d=at(s),c=Object.keys(d).length>0,m={region:n,...r!==void 0?{credentials:r}:{},...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}};for(let p=0;p<=G;p++){if(k(this.abortSignal))return a("Bootstrap cancelled before completion");let f=await it(m);if(f.kind==="in-progress"&&(e?.(`Another deployment is updating the CDK toolkit in ${o}/${n} \u2014 waiting for it to finish...
4
+ `),f=await U(m)),k(this.abortSignal))return a("Bootstrap cancelled before completion");if(f.kind==="in-progress")return a(`The CDKToolkit stack in ${o}/${n} has been ${f.status} for over ${Math.round(ct/6e4)} minutes. Another deployment may be stuck bootstrapping this environment \u2014 wait for it to finish (or cancel it in the CloudFormation console), then retry.`);const y=await this.bootstrap(o,n,{outputCallback:e,credentials:r,bootstrapForce:st(f,c?d:void 0),...i!==void 0&&i!==""?{permissionsBoundary:i}:{},...c?{bootstrapTags:d}:{}});if(y.success)return b({message:"AWS environment bootstrapped"});if(k(this.abortSignal))return a("Bootstrap cancelled before completion");if(y.error.kind==="lost-race"&&p<G){I.warn("CdkService","Concurrent bootstrap detected \u2014 waiting for it to settle before retrying",{accountId:o,region:n,attempt:p+1}),e?.(`Another deployment is bootstrapping ${o}/${n} \u2014 waiting for it to finish before retrying...
5
+ `),await U(m);continue}return a(y.error.message||"Failed to bootstrap AWS environment")}return a("Failed to bootstrap AWS environment")}catch(u){return a(`CDK bootstrap failed: ${S(D(u))}`)}}async runCdkDiff(t,e){const r=t.callerIdentity?.Account;try{const i=await this.checkDifferences(t.path,void 0,{verbose:t.options?.verbose,outputCallback:e,context:$(t,r,t.region||C)});return i.success?b({message:"Diff check complete",details:{hasDifferences:i.data.hasDifferences,details:i.data.details}}):a(`CDK diff failed: ${i.error.message}`)}catch(i){return a(`CDK diff failed: ${S(D(i))}`)}}async runCdkDeploy(t,e,r,i,s,o,n,u){const d=t.callerIdentity?.Account,c=t.region||C;if(!d)return a("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!s)return a("AwsProvider is required for deployment monitoring.");const m=t.assemblyDir??K(t.path,"cdk.out"),p=lt(m,i);let f=null,y;try{const h=X(e,t)??dt(t),g=await this.eventMonitor.createEventMonitor("deploy",h,c,t,s);f=g,r&&(r(S(`Starting CloudFormation deployment of ${h}...
4
6
  `)),r(`Monitoring CloudFormation events (CDK process running in background)...
5
- `));const c={cdkOutput:"",actualStackName:m,stackDetected:!1,monitoringPromise:null},B=Z(c,r,h,S);D=setTimeout(()=>{!c.stackDetected&&!c.monitoringPromise&&(T.debug("CdkService","Fallback monitoring STARTING",{targetStackName:m,stackDetected:c.stackDetected,hasOnResourceProgress:!!a}),c.monitoringPromise=J(f,m,S))},at);for(let C=0;;C++){const R=await this.deploy(t.path,m,{verbose:t.options?.verbose,outputCallback:B,...t.assemblyDir!==void 0?{appDir:t.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:f.getEventLogger()??void 0,context:N(t,d,u),credentials:o,...i!==void 0&&Object.keys(i).length>0&&{parameters:i},...l===!0&&{exclusively:!0}});if(A(this.abortSignal))return await U(m,u,o,r),s("Deployment cancelled");const w=Q(c.cdkOutput,R,c.actualStackName);if(w.success)return await this.reconcileDeploySuccessAgainstStack({successOutput:w.data,stackName:c.actualStackName,monitor:f,monitoringPromise:c.monitoringPromise,accountId:d,region:u,onOutput:r});const Y=[c.cdkOutput,R.success?R.data.output??"":R.error,w.error].join(`
6
- `);if(ct(Y)){r?.(`Transient network failure while running CDK for ${c.actualStackName} \u2014 probing CloudFormation for an in-flight operation...
7
- `);const I=await f.getStackStatus(c.actualStackName,this.abortSignal);if(I!==null&&ut(I.status))return clearTimeout(D),await this.reattachToInFlightOperation({stackName:c.actualStackName,currentStatus:I.status,region:u,accountId:d,monitor:f,state:c,credentials:o,onOutput:r,onResourceProgress:S});const v=I?.status,O=v!==void 0&&W(v)&&!L(v);if(!O&&C<E){const _=lt[C]??15e3;if(r?.(`No CloudFormation operation in progress \u2014 nothing was mutated. Retrying the CDK deploy in ${_/1e3}s (retry ${C+1} of ${E})...
8
- `),await dt(_,this.abortSignal),A(this.abortSignal))return s("Deployment cancelled");c.cdkOutput="";continue}r?.(O?`CloudFormation settled at ${v} for ${c.actualStackName} \u2014 the stack was rolled back, not cleanly interrupted. Not retrying.
7
+ `));const l={cdkOutput:"",actualStackName:h,stackDetected:!1,monitoringPromise:null},Y=tt(l,r,f,p);y=setTimeout(()=>{!l.stackDetected&&!l.monitoringPromise&&(I.debug("CdkService","Fallback monitoring STARTING",{targetStackName:h,stackDetected:l.stackDetected,hasOnResourceProgress:!!i}),l.monitoringPromise=V(g,h,p))},ut);for(let w=0;;w++){const T=await this.deploy(t.path,h,{verbose:t.options?.verbose,outputCallback:Y,...t.assemblyDir!==void 0?{appDir:t.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:g.getEventLogger()??void 0,context:$(t,d,c),credentials:o,...n!==void 0&&Object.keys(n).length>0&&{parameters:n},...u===!0&&{exclusively:!0}});if(k(this.abortSignal))return await j(h,c,o,r),a("Deployment cancelled");const R=Z(l.cdkOutput,T,l.actualStackName);if(R.success)return await this.reconcileDeploySuccessAgainstStack({successOutput:R.data,stackName:l.actualStackName,monitor:g,monitoringPromise:l.monitoringPromise,accountId:d,region:c,onOutput:r});const q=[l.cdkOutput,T.success?T.data.output??"":T.error,R.error].join(`
8
+ `);if(pt(q)){r?.(`Transient network failure while running CDK for ${l.actualStackName} \u2014 probing CloudFormation for an in-flight operation...
9
+ `);const A=await g.getStackStatus(l.actualStackName,this.abortSignal);if(A!==null&&gt(A.status))return clearTimeout(y),await this.reattachToInFlightOperation({stackName:l.actualStackName,currentStatus:A.status,region:c,accountId:d,monitor:g,state:l,credentials:o,onOutput:r,onResourceProgress:p});const v=A?.status,P=v!==void 0&&W(v)&&!L(v);if(!P&&w<E){const O=ht[w]??15e3;if(r?.(`No CloudFormation operation in progress \u2014 nothing was mutated. Retrying the CDK deploy in ${O/1e3}s (retry ${w+1} of ${E})...
10
+ `),await yt(O,this.abortSignal),k(this.abortSignal))return a("Deployment cancelled");l.cdkOutput="";continue}r?.(P?`CloudFormation settled at ${v} for ${l.actualStackName} \u2014 the stack was rolled back, not cleanly interrupted. Not retrying.
9
11
  `:`No CloudFormation operation in progress and the retry budget (${E}) is exhausted \u2014 giving up.
10
- `)}const F=await j(f,c.monitoringPromise);return F===null?w:(await this.journalDriftSuspects(d,u,c.actualStackName,F),s($(F)))}}catch(m){const f=`CDK deploy failed: ${p(b(m))}`;return T.error("CdkService","CDK deployment exception",{error:f}),s(f)}finally{clearTimeout(D),h&&h.stopMonitoring()}}async reattachToInFlightOperation(t){const{stackName:e,monitor:r,state:a,onOutput:n}=t;n?.(`CloudFormation reports ${t.currentStatus} for ${e} \u2014 the operation survived the network failure. Re-attaching and monitoring it to completion...
11
- `),r.stopMonitoring(),a.monitoringPromise=null;const o=M(()=>{r.stopMonitoring()},"onAbort");this.abortSignal?.addEventListener("abort",o,{once:!0});const i=await r.waitForStackComplete(e,{timeout:z,...t.onResourceProgress!==void 0?{onResourceUpdate:t.onResourceProgress}:{}}).finally(()=>{this.abortSignal?.removeEventListener("abort",o)});if(A(this.abortSignal))return await U(e,t.region,t.credentials,n),s("Deployment cancelled");if(i.success){const u=i.status??"COMPLETE";return n?.(`${e} reached ${u} after re-attach.
12
- `),y({message:`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${e} reached ${u}`,details:{reattached:!0,status:u}})}const l=r.getFailureAnalysis();if(l!==null)return await this.journalDriftSuspects(t.accountId,t.region,e,l),s($(l));const d=i.failureReason!==void 0&&i.failureReason!==""?p(i.failureReason):"no failure reason reported";return s(`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${e} reached ${i.status??"UNKNOWN"}: ${d}`)}async reconcileDeploySuccessAgainstStack(t){const{successOutput:e,stackName:r,monitor:a,onOutput:n}=t;if(e.status==="NO_CHANGES"||e.status==="SKIPPED"||e.details?.skipped===!0)return y(e);const o=await a.getStackStatus(r,this.abortSignal);if(o===null||!W(o.status)||L(o.status))return y(e);n?.(`CDK reported success but CloudFormation settled at ${o.status} for ${r} \u2014 the deployment did not converge. Reporting failure.
13
- `);const i=await j(a,t.monitoringPromise);if(i!==null)return await this.journalDriftSuspects(t.accountId,t.region,r,i),s($(i));const l=o.statusReason!==void 0&&o.statusReason!==""?p(o.statusReason):"no failure reason reported";return s(`Deployment of ${r} reported success but CloudFormation settled at ${o.status}: ${l}`)}async journalDriftSuspects(t,e,r,a){a.driftSuspects===void 0||a.driftSuspects.length===0||await x({accountId:t,region:e,stackName:r,recordedAt:new Date().toISOString(),suspects:a.driftSuspects.map(n=>({stackName:r,logicalId:n.logicalId,resourceType:n.resourceType,...n.physicalId!==void 0?{physicalId:n.physicalId}:{},failedStatus:n.failedStatus,statusReason:n.statusReason}))})}async runCdkDestroy(t,e,r,a,n,o,i){const l=t.callerIdentity?.Account,d=t.region||k;let u=null;try{const g=G(e,t);l&&g&&n&&(u=await this.eventMonitor.createEventMonitor("destroy",g,d,t,n),u.startMonitoring(g,h=>{a?.(h)},(h,D)=>{}));const S=await this.destroy(t.path,e,{verbose:t.options?.verbose,outputCallback:r,useCdkOut:o,cdkOutputLogger:u?.getEventLogger()??void 0,context:N(t,l,d),credentials:i});return A(this.abortSignal)?s("Destroy cancelled"):V(S)}catch(g){return s(`CDK destroy failed: ${p(b(g))}`)}finally{u&&u.stopMonitoring()}}}export{Pt as CdkService};
12
+ `)}const F=await B(g,l.monitoringPromise);return F===null?R:(await this.journalDriftSuspects(d,c,l.actualStackName,F),a(N(F)))}}catch(h){const g=`CDK deploy failed: ${S(D(h))}`;return I.error("CdkService","CDK deployment exception",{error:g}),a(g)}finally{clearTimeout(y),f&&f.stopMonitoring()}}async reattachToInFlightOperation(t){const{stackName:e,monitor:r,state:i,onOutput:s}=t;s?.(`CloudFormation reports ${t.currentStatus} for ${e} \u2014 the operation survived the network failure. Re-attaching and monitoring it to completion...
13
+ `),r.stopMonitoring(),i.monitoringPromise=null;const o=M(()=>{r.stopMonitoring()},"onAbort");this.abortSignal?.addEventListener("abort",o,{once:!0});const n=await r.waitForStackComplete(e,{timeout:Q,...t.onResourceProgress!==void 0?{onResourceUpdate:t.onResourceProgress}:{}}).finally(()=>{this.abortSignal?.removeEventListener("abort",o)});if(k(this.abortSignal))return await j(e,t.region,t.credentials,s),a("Deployment cancelled");if(n.success){const c=n.status??"COMPLETE";return s?.(`${e} reached ${c} after re-attach.
14
+ `),b({message:`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${e} reached ${c}`,details:{reattached:!0,status:c}})}const u=r.getFailureAnalysis();if(u!==null)return await this.journalDriftSuspects(t.accountId,t.region,e,u),a(N(u));const d=n.failureReason!==void 0&&n.failureReason!==""?S(n.failureReason):"no failure reason reported";return a(`Re-attached to the in-flight CloudFormation operation after a transient network failure; ${e} reached ${n.status??"UNKNOWN"}: ${d}`)}async reconcileDeploySuccessAgainstStack(t){const{successOutput:e,stackName:r,monitor:i,onOutput:s}=t;if(e.status==="NO_CHANGES"||e.status==="SKIPPED"||e.details?.skipped===!0)return b(e);const o=await i.getStackStatus(r,this.abortSignal);if(o===null||!W(o.status)||L(o.status))return b(e);s?.(`CDK reported success but CloudFormation settled at ${o.status} for ${r} \u2014 the deployment did not converge. Reporting failure.
15
+ `);const n=await B(i,t.monitoringPromise);if(n!==null)return await this.journalDriftSuspects(t.accountId,t.region,r,n),a(N(n));const u=o.statusReason!==void 0&&o.statusReason!==""?S(o.statusReason):"no failure reason reported";return a(`Deployment of ${r} reported success but CloudFormation settled at ${o.status}: ${u}`)}async journalDriftSuspects(t,e,r,i){i.driftSuspects===void 0||i.driftSuspects.length===0||await et({accountId:t,region:e,stackName:r,recordedAt:new Date().toISOString(),suspects:i.driftSuspects.map(s=>({stackName:r,logicalId:s.logicalId,resourceType:s.resourceType,...s.physicalId!==void 0?{physicalId:s.physicalId}:{},failedStatus:s.failedStatus,statusReason:s.statusReason}))})}async runCdkDestroy(t,e,r,i,s,o,n){const u=t.callerIdentity?.Account,d=t.region||C;let c=null;try{const m=X(e,t);u&&m&&s&&(c=await this.eventMonitor.createEventMonitor("destroy",m,d,t,s),c.startMonitoring(m,f=>{i?.(f)},(f,y)=>{}));const p=await this.destroy(t.path,e,{verbose:t.options?.verbose,outputCallback:r,useCdkOut:o,cdkOutputLogger:c?.getEventLogger()??void 0,context:$(t,u,d),credentials:n});return k(this.abortSignal)?a("Destroy cancelled"):x(p)}catch(m){return a(`CDK destroy failed: ${S(D(m))}`)}finally{c&&c.stopMonitoring()}}}export{jt as CdkService};
@@ -108,6 +108,15 @@ export interface CdkOptions {
108
108
  * `--tags` args (today's behaviour).
109
109
  */
110
110
  bootstrapTags?: Record<string, string>;
111
+ /**
112
+ * Force decision for `cdk bootstrap`. `--force` defeats CDK's own
113
+ * "toolkit already at version N" short-circuit and exists ONLY to serve
114
+ * the bootstrapTags REPLACE contract, so the convergence protocol
115
+ * (`resolveBootstrapForce`) emits it iff the desired tag set cannot be
116
+ * confirmed present on the live CDKToolkit stack. `undefined` preserves
117
+ * the historical always-force behaviour for direct callers.
118
+ */
119
+ bootstrapForce?: boolean;
111
120
  /**
112
121
  * Pass `--exclusively` to `cdk deploy <stack>`: deploy only the named
113
122
  * stack, never the dependency stacks cdk would otherwise pull in. Set by
@@ -0,0 +1,95 @@
1
+ import type { CdkOptions } from "./CdkServiceTypes.js";
2
+ /** CDK's fixed toolkit stack name for the default qualifier. */
3
+ export declare const TOOLKIT_STACK_NAME = "CDKToolkit";
4
+ /** Poll cadence while waiting for a concurrent bootstrap to settle. */
5
+ export declare const TOOLKIT_SETTLE_POLL_INTERVAL_MS = 5000;
6
+ /**
7
+ * Overall budget for waiting on a concurrent bootstrap. The toolkit stack is
8
+ * a handful of resources — a healthy bootstrap settles well inside this.
9
+ */
10
+ export declare const TOOLKIT_SETTLE_BUDGET_MS = 240000;
11
+ /**
12
+ * Lost-race retries after the initial attempt. A no-force retry
13
+ * short-circuits in seconds when the winner already converged the toolkit,
14
+ * and a forced retry covers the winner having stamped a different tag set.
15
+ */
16
+ export declare const MAX_BOOTSTRAP_LOST_RACE_RETRIES = 2;
17
+ export type ToolkitStackState = {
18
+ kind: "absent";
19
+ } | {
20
+ kind: "in-progress";
21
+ status: string;
22
+ } | {
23
+ kind: "healthy";
24
+ status: string;
25
+ tags: Record<string, string>;
26
+ } | {
27
+ kind: "unhealthy";
28
+ status: string;
29
+ } | {
30
+ kind: "unknown";
31
+ };
32
+ export interface ToolkitProbeParams {
33
+ region: string;
34
+ credentials?: CdkOptions["credentials"];
35
+ abortSignal?: AbortSignal;
36
+ }
37
+ /**
38
+ * Describe the CDKToolkit stack and classify its state. Never throws — a
39
+ * probe failure (network, permissions) classifies as `unknown`, which the
40
+ * force decision treats conservatively.
41
+ */
42
+ export declare function probeToolkitStack(params: ToolkitProbeParams): Promise<ToolkitStackState>;
43
+ export interface ToolkitSettleOptions {
44
+ pollIntervalMs?: number;
45
+ budgetMs?: number;
46
+ /** Injectable clock for budget tests. */
47
+ now?: () => number;
48
+ }
49
+ /**
50
+ * Poll the toolkit stack until it reaches a definitively settled state
51
+ * (absent, healthy, unhealthy), the budget expires, or the abort signal
52
+ * fires. An `unknown` probe does NOT exit the wait — DescribeStacks
53
+ * throttling is likeliest under exactly the concurrent load this protocol
54
+ * targets, and a single throttled poll must not vacate the settle gate.
55
+ * Returns the final observed state — still `in-progress` (or `unknown`) on
56
+ * budget exhaustion, so callers decide how to surface that.
57
+ */
58
+ export declare function awaitToolkitSettled(params: ToolkitProbeParams, options?: ToolkitSettleOptions): Promise<ToolkitStackState>;
59
+ /**
60
+ * Drop empty-key/empty-value tag entries. The CDK CLI parses degenerate
61
+ * `--tags =v` / `k=` args fine — it is CloudFormation that rejects them.
62
+ * Shared by the runner's argv emission AND the service's force decision so
63
+ * both sites compare the same emitted set (coupled at 2 occurrences).
64
+ */
65
+ export declare function filterBootstrapTagEntries(tags: Record<string, string> | undefined): Record<string, string>;
66
+ /**
67
+ * The force decision: `--force` is emitted iff there are tags to stamp AND
68
+ * the live stack cannot be confirmed to already carry exactly that set.
69
+ * Parity is exact record equality — an extra live tag forces a restamp,
70
+ * preserving the REPLACE contract's "complete set or nothing" semantics.
71
+ *
72
+ * Without desired tags there is nothing `--force` buys: creation ignores it,
73
+ * and CDK upgrades an outdated toolkit without it.
74
+ */
75
+ export declare function resolveBootstrapForce(state: ToolkitStackState, desiredTags: Record<string, string> | undefined): boolean;
76
+ /**
77
+ * Discriminated failure for CdkCommandRunner.bootstrap: `lost-race` means a
78
+ * concurrent bootstrap won the fixed-name changeset and the protocol should
79
+ * wait for it to settle and retry; `other` carries the analysed error.
80
+ * Messages are pre-masked at construction: the lost-race arm interpolates
81
+ * identifiers only, and the `other` arm analyses runCdkCommand's failure
82
+ * string, whose sole stderr-bearing arm masks at source (the close-handler
83
+ * `maskSensitiveOutput(fullError)` in CdkProcessManager) — every other arm
84
+ * is identifier-only.
85
+ */
86
+ export interface BootstrapRunFailure {
87
+ kind: "lost-race" | "other";
88
+ message: string;
89
+ }
90
+ /**
91
+ * Classify RAW bootstrap stderr as a lost concurrent-bootstrap race. Must run
92
+ * BEFORE analyseBootstrapError, which reformats the output and destroys the
93
+ * signatures.
94
+ */
95
+ export declare function isBootstrapLostRaceError(stderr: string): boolean;
@@ -0,0 +1 @@
1
+ var _=Object.defineProperty;var o=(t,e)=>_(t,"name",{value:e,configurable:!0});import{CloudFormationClient as f,DescribeStacksCommand as S}from"@aws-sdk/client-cloudformation";import{NodeHttpHandler as O}from"@smithy/node-http-handler";import{logger as g}from"@fjall/util/logger";import{getErrorMessage as k,maskSensitiveOutput as b}from"@fjall/util";import{composeSdkAbortSignal as p,isAborted as T}from"../../aws/organisations/types.js";import{STACK_NOT_FOUND_PATTERN as L}from"../../types/constants.js";import{sleepAbortable as R}from"../../util/sleepAbortable.js";const A="BootstrapConvergence",C="CDKToolkit",E=15e3,h=5e3,w=24e4,U=2,M=new Set(["CREATE_COMPLETE","UPDATE_COMPLETE","UPDATE_ROLLBACK_COMPLETE","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE"]);function d(t){return new f({region:t.region,...t.credentials!==void 0?{credentials:t.credentials}:{},requestHandler:new O({requestTimeout:E})})}o(d,"createProbeClient");async function j(t){return l(d(t),t)}o(j,"probeToolkitStack");async function l(t,e){const{region:r,abortSignal:a}=e;try{const c=(await t.send(new S({StackName:C}),{abortSignal:p(a,E)})).Stacks?.[0],n=c?.StackStatus;if(c===void 0||n===void 0)return{kind:"absent"};if(n==="DELETE_COMPLETE")return{kind:"absent"};if(n.endsWith("_IN_PROGRESS")&&n!=="REVIEW_IN_PROGRESS")return{kind:"in-progress",status:n};if(M.has(n)){const s={};for(const u of c.Tags??[])u.Key!==void 0&&u.Value!==void 0&&(s[u.Key]=u.Value);return{kind:"healthy",status:n,tags:s}}return{kind:"unhealthy",status:n}}catch(i){return i instanceof Error&&i.message.includes(L)?{kind:"absent"}:(g.debug(A,"Toolkit probe failed \u2014 treating state as unknown",{region:r,error:b(k(i))}),{kind:"unknown"})}}o(l,"probeWithClient");async function G(t,e){const r=e?.pollIntervalMs??h,a=e?.budgetMs??w,i=e?.now??(()=>Date.now()),c=i(),n=d(t);try{let s=await l(n,t);for(;(s.kind==="in-progress"||s.kind==="unknown")&&!T(t.abortSignal)&&i()-c<a&&(await R(r,t.abortSignal),!T(t.abortSignal));)s=await l(n,t);return s}finally{n.destroy()}}o(G,"awaitToolkitSettled");function H(t){return Object.fromEntries(Object.entries(t??{}).filter(([e,r])=>e!==""&&r!==""))}o(H,"filterBootstrapTagEntries");function P(t,e){const r=Object.keys(t);return r.length===Object.keys(e).length&&r.every(a=>e[a]===t[a])}o(P,"tagRecordsEqual");function F(t,e){if(e===void 0||Object.keys(e).length===0)return!1;switch(t.kind){case"absent":return!1;case"healthy":return!P(t.tags,e);case"in-progress":case"unhealthy":case"unknown":return!0}}o(F,"resolveBootstrapForce");const m=[/ChangeSetNotFoundException/,/mismatch with existing attribute ClientToken/i,/is in [A-Z_]*IN_PROGRESS state and can ?not be updated/i];function V(t){return m.some(e=>e.test(t))}o(V,"isBootstrapLostRaceError");export{U as MAX_BOOTSTRAP_LOST_RACE_RETRIES,w as TOOLKIT_SETTLE_BUDGET_MS,h as TOOLKIT_SETTLE_POLL_INTERVAL_MS,C as TOOLKIT_STACK_NAME,G as awaitToolkitSettled,H as filterBootstrapTagEntries,V as isBootstrapLostRaceError,j as probeToolkitStack,F as resolveBootstrapForce};
@@ -1,5 +1,6 @@
1
1
  export { CdkService } from "./CdkService.js";
2
2
  export type { CheckDifferencesResult } from "./CdkCommandRunner.js";
3
+ export { isBootstrapLostRaceError, probeToolkitStack, awaitToolkitSettled, resolveBootstrapForce, TOOLKIT_STACK_NAME, type BootstrapRunFailure, type ToolkitStackState, type ToolkitProbeParams } from "./bootstrapConvergence.js";
3
4
  export type { CdkContext, CdkOptions, CdkOutput, CdkServiceOptions } from "./CdkServiceTypes.js";
4
5
  export type { ICdkProcessManager } from "./ICdkProcessManager.js";
5
6
  export { CdkArgumentBuilder } from "./CdkArgumentBuilder.js";
@@ -1 +1 @@
1
- import{CdkService as o}from"./CdkService.js";import{CdkArgumentBuilder as c}from"./CdkArgumentBuilder.js";import{CdkProcessManager as n}from"./CdkProcessManager.js";import{ensureDependencyIntegrity as p,clearDependencyIntegrityCache as E,DEPENDENCY_SYNC_TIMEOUT_MS as m}from"./dependencyIntegrityGate.js";import{CdkEventMonitor as a,startStackMonitoring as x,DEFAULT_DEPLOY_TIMEOUT_MS as C}from"./CdkEventMonitoring.js";import{isCdkError as u,formatInfrastructureError as d,getStructuralHint as g,getSourceContext as D}from"./CdkErrorFormatter.js";import{hasCdkDifferences as I,parseDiffOutput as _}from"./CdkOutputParser.js";import{CloudFormationService as M,CloudFormationError as T}from"./CloudFormationService.js";import{CfnRegistryService as y}from"./CfnRegistryService.js";import{CFN_REGISTRY_SEED as F}from"./cfnRegistrySeed.js";import{EcsService as R,EcsError as Y}from"./EcsService.js";import{EcsServiceResolver as U}from"./EcsServiceResolver.js";import{EcrImageInspectorService as A,stripTagOrDigest as L}from"./EcrImageInspectorService.js";export{F as CFN_REGISTRY_SEED,c as CdkArgumentBuilder,a as CdkEventMonitor,n as CdkProcessManager,o as CdkService,y as CfnRegistryService,T as CloudFormationError,M as CloudFormationService,C as DEFAULT_DEPLOY_TIMEOUT_MS,m as DEPENDENCY_SYNC_TIMEOUT_MS,A as EcrImageInspectorService,Y as EcsError,R as EcsService,U as EcsServiceResolver,E as clearDependencyIntegrityCache,p as ensureDependencyIntegrity,d as formatInfrastructureError,D as getSourceContext,g as getStructuralHint,I as hasCdkDifferences,u as isCdkError,_ as parseDiffOutput,x as startStackMonitoring,L as stripTagOrDigest};
1
+ import{CdkService as o}from"./CdkService.js";import{isBootstrapLostRaceError as i,probeToolkitStack as c,awaitToolkitSettled as p,resolveBootstrapForce as s,TOOLKIT_STACK_NAME as a}from"./bootstrapConvergence.js";import{CdkArgumentBuilder as n}from"./CdkArgumentBuilder.js";import{CdkProcessManager as m}from"./CdkProcessManager.js";import{ensureDependencyIntegrity as x,clearDependencyIntegrityCache as C,DEPENDENCY_SYNC_TIMEOUT_MS as d}from"./dependencyIntegrityGate.js";import{CdkEventMonitor as T,startStackMonitoring as g,DEFAULT_DEPLOY_TIMEOUT_MS as k}from"./CdkEventMonitoring.js";import{isCdkError as D,formatInfrastructureError as _,getStructuralHint as v,getSourceContext as I}from"./CdkErrorFormatter.js";import{hasCdkDifferences as O,parseDiffOutput as y}from"./CdkOutputParser.js";import{CloudFormationService as N,CloudFormationError as R}from"./CloudFormationService.js";import{CfnRegistryService as L}from"./CfnRegistryService.js";import{CFN_REGISTRY_SEED as B}from"./cfnRegistrySeed.js";import{EcsService as U,EcsError as h}from"./EcsService.js";import{EcsServiceResolver as b}from"./EcsServiceResolver.js";import{EcrImageInspectorService as G,stripTagOrDigest as H}from"./EcrImageInspectorService.js";export{B as CFN_REGISTRY_SEED,n as CdkArgumentBuilder,T as CdkEventMonitor,m as CdkProcessManager,o as CdkService,L as CfnRegistryService,R as CloudFormationError,N as CloudFormationService,k as DEFAULT_DEPLOY_TIMEOUT_MS,d as DEPENDENCY_SYNC_TIMEOUT_MS,G as EcrImageInspectorService,h as EcsError,U as EcsService,b as EcsServiceResolver,a as TOOLKIT_STACK_NAME,p as awaitToolkitSettled,C as clearDependencyIntegrityCache,x as ensureDependencyIntegrity,_ as formatInfrastructureError,I as getSourceContext,v as getStructuralHint,O as hasCdkDifferences,i as isBootstrapLostRaceError,D as isCdkError,y as parseDiffOutput,c as probeToolkitStack,s as resolveBootstrapForce,g as startStackMonitoring,H as stripTagOrDigest};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "7.0.1",
3
+ "version": "7.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -95,8 +95,8 @@
95
95
  "@aws-sdk/client-ssm": "^3.1098.0",
96
96
  "@aws-sdk/client-sso-admin": "^3.1098.0",
97
97
  "@aws-sdk/client-sts": "^3.1098.0",
98
- "@fjall/generator": "^7.0.1",
99
- "@fjall/util": "^7.0.1",
98
+ "@fjall/generator": "^7.2.0",
99
+ "@fjall/util": "^7.2.0",
100
100
  "@smithy/node-http-handler": "^4.9.13",
101
101
  "aws-cdk": "^2.1134.0",
102
102
  "tsx": "^4.23.1",