@fjall/deploy-core 3.6.0 → 3.7.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
- 230 files minified at 2026-07-19T21:13:20.771Z
1
+ 231 files minified at 2026-07-20T02:13:12.085Z
@@ -1,3 +1,3 @@
1
- var K=Object.defineProperty;var M=(I,e)=>K(I,"name",{value:e,configurable:!0});import{existsSync as T}from"fs";import{join as F}from"path";import{logger as R}from"@fjall/util/logger";import{success as A,failure as o}from"@fjall/generator";import{DEFAULT_REGION as k}from"../../aws/utils/regions.js";import{getErrorMessage as D,maskSensitiveOutput as g}from"@fjall/util";import{CdkEventMonitor as L,settleMonitoringAnalysis as P,startStackMonitoring as W}from"./CdkEventMonitoring.js";import{analyseDeployOutput as $,analyseDestroyResult as _,createEnhancedOutputCallback as j,formatAnalysedDeployFailure as G}from"./CdkOutputAnalyser.js";import{recordDriftSuspects as q}from"../../orchestration/drift/driftSuspectJournal.js";import{CdkCommandRunner as B}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as U}from"./CdkArgumentBuilder.js";import{CdkProcessManager as z}from"./CdkProcessManager.js";import{cancelInFlightStack as H}from"./cancelInFlightStack.js";import{isAborted as w}from"../../aws/organisations/types.js";import{wrapWithConstructMapEnrichment as J}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as Q,resolveStackName as N,getFallbackStackName as V,buildDeploymentCdkContext as C}from"./cdkServiceHelpers.js";class fe{static{M(this,"CdkService")}commandRunner;eventMonitor;abortSignal;constructor(e){const t=e?.processManager??new z(new U,e?.abortSignal);this.commandRunner=new B(t),this.eventMonitor=new L({eventLogWriterFactory:e?.eventLogWriterFactory,...e?.onFailureAnalysis!==void 0?{onFailureAnalysis:e.onFailureAnalysis}:{}}),this.abortSignal=e?.abortSignal}dispose(){this.commandRunner.dispose()}async checkDifferences(e,t,r){return this.commandRunner.checkDifferences(e,t,r)}async deploy(e,t,r){return this.commandRunner.deploy(e,t,r)}async destroy(e,t,r){return this.commandRunner.destroy(e,t,r)}async runImport(e,t,r){return this.commandRunner.runImport(e,t,r)}async synth(e,t){return this.commandRunner.synth(e,t)}async bootstrap(e,t,r){return this.commandRunner.bootstrap(e,t,r)}async runCdkSynth(e,t,r){const n=e.callerIdentity?.Account;try{const a=await this.synth(e.path,{outputCallback:t,context:C(e,n,e.region||k),...e.assemblyDir!==void 0?{outputDir:e.assemblyDir}:{},...r!==void 0?{credentials:r}:{}});return a.success?A({message:"CloudFormation template synthesised",details:a.data.output?{synthesisTime:a.data.output}:void 0}):o(a.error||"Failed to synthesise CloudFormation template")}catch(a){return o(`CDK synth failed: ${g(D(a))}`)}}async runCdkBootstrap(e,t,r,n,a){const m=e.callerIdentity?.Account,f=e.region||k;try{if(!m)return o("No AWS account ID available");const d=F(e.path,"node_modules");if(!T(d))return o(`Dependencies not installed. Please run 'npm install' in ${e.path} before deploying.`);const l=await this.bootstrap(m,f,{outputCallback:t,credentials:r,...n!==void 0&&n!==""?{permissionsBoundary:n}:{},...a!==void 0&&Object.keys(a).length>0?{bootstrapTags:a}:{}});return l.success?A({message:"AWS environment bootstrapped"}):o(l.error||"Failed to bootstrap AWS environment")}catch(d){return o(`CDK bootstrap failed: ${g(D(d))}`)}}async runCdkDiff(e,t){const r=e.callerIdentity?.Account;try{const n=await this.checkDifferences(e.path,void 0,{verbose:e.options?.verbose,outputCallback:t,context:C(e,r,e.region||k)});return n.success?A({message:"Diff check complete",details:{hasDifferences:n.data.hasDifferences,details:n.data.details}}):o(`CDK diff failed: ${n.error.message}`)}catch(n){return o(`CDK diff failed: ${g(D(n))}`)}}async runCdkDeploy(e,t,r,n,a,m,f,d){const l=e.callerIdentity?.Account,c=e.region||k;if(!l)return o("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!a)return o("AwsProvider is required for deployment monitoring.");const y=e.assemblyDir??F(e.path,"cdk.out"),b=J(y,n);let s=null,S;try{const u=N(t,e)??V(e);s=await this.eventMonitor.createEventMonitor("deploy",u,c,e,a),r&&(r(g(`Starting CloudFormation deployment of ${u}...
1
+ var O=Object.defineProperty;var M=(I,e)=>O(I,"name",{value:e,configurable:!0});import{existsSync as T}from"fs";import{join as F}from"path";import{logger as R}from"@fjall/util/logger";import{success as A,failure as a}from"@fjall/generator";import{DEFAULT_REGION as k}from"../../aws/utils/regions.js";import{getErrorMessage as b,maskSensitiveOutput as g}from"@fjall/util";import{CdkEventMonitor as L,settleMonitoringAnalysis as P,startStackMonitoring as W}from"./CdkEventMonitoring.js";import{analyseDeployOutput as $,analyseDestroyResult as _,createEnhancedOutputCallback as j,formatAnalysedDeployFailure as G}from"./CdkOutputAnalyser.js";import{recordDriftSuspects as q}from"../../orchestration/drift/driftSuspectJournal.js";import{CdkCommandRunner as B}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as U}from"./CdkArgumentBuilder.js";import{CdkProcessManager as z}from"./CdkProcessManager.js";import{cancelInFlightStack as H}from"./cancelInFlightStack.js";import{isAborted as w}from"../../aws/organisations/types.js";import{wrapWithConstructMapEnrichment as J}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as Q,resolveStackName as N,getFallbackStackName as V,buildDeploymentCdkContext as S}from"./cdkServiceHelpers.js";import{ensureSynthDependencies as X}from"./synthDependencies.js";class ye{static{M(this,"CdkService")}commandRunner;eventMonitor;abortSignal;constructor(e){const t=e?.processManager??new z(new U,e?.abortSignal);this.commandRunner=new B(t),this.eventMonitor=new L({eventLogWriterFactory:e?.eventLogWriterFactory,...e?.onFailureAnalysis!==void 0?{onFailureAnalysis:e.onFailureAnalysis}:{}}),this.abortSignal=e?.abortSignal}dispose(){this.commandRunner.dispose()}async checkDifferences(e,t,r){return this.commandRunner.checkDifferences(e,t,r)}async deploy(e,t,r){return this.commandRunner.deploy(e,t,r)}async destroy(e,t,r){return this.commandRunner.destroy(e,t,r)}async runImport(e,t,r){return this.commandRunner.runImport(e,t,r)}async synth(e,t){return this.commandRunner.synth(e,t)}async bootstrap(e,t,r){return this.commandRunner.bootstrap(e,t,r)}async runCdkSynth(e,t,r){const n=e.callerIdentity?.Account;try{const o=await X(e.path,{...t!==void 0?{onOutput:t}:{},...this.abortSignal!==void 0?{abortSignal:this.abortSignal}:{}});if(!o.success)return a(o.error);const s=await this.synth(e.path,{outputCallback:t,context:S(e,n,e.region||k),...e.assemblyDir!==void 0?{outputDir:e.assemblyDir}:{},...r!==void 0?{credentials:r}:{}});return s.success?A({message:"CloudFormation template synthesised",details:s.data.output?{synthesisTime:s.data.output}:void 0}):a(s.error||"Failed to synthesise CloudFormation template")}catch(o){return a(`CDK synth failed: ${g(b(o))}`)}}async runCdkBootstrap(e,t,r,n,o){const s=e.callerIdentity?.Account,f=e.region||k;try{if(!s)return a("No AWS account ID available");const m=F(e.path,"node_modules");if(!T(m))return a(`Dependencies not installed. Please run 'npm install' in ${e.path} before deploying.`);const u=await this.bootstrap(s,f,{outputCallback:t,credentials:r,...n!==void 0&&n!==""?{permissionsBoundary:n}:{},...o!==void 0&&Object.keys(o).length>0?{bootstrapTags:o}:{}});return u.success?A({message:"AWS environment bootstrapped"}):a(u.error||"Failed to bootstrap AWS environment")}catch(m){return a(`CDK bootstrap failed: ${g(b(m))}`)}}async runCdkDiff(e,t){const r=e.callerIdentity?.Account;try{const n=await this.checkDifferences(e.path,void 0,{verbose:e.options?.verbose,outputCallback:t,context:S(e,r,e.region||k)});return n.success?A({message:"Diff check complete",details:{hasDifferences:n.data.hasDifferences,details:n.data.details}}):a(`CDK diff failed: ${n.error.message}`)}catch(n){return a(`CDK diff failed: ${g(b(n))}`)}}async runCdkDeploy(e,t,r,n,o,s,f,m){const u=e.callerIdentity?.Account,l=e.region||k;if(!u)return a("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!o)return a("AwsProvider is required for deployment monitoring.");const p=e.assemblyDir??F(e.path,"cdk.out"),D=J(p,n);let c=null,C;try{const d=N(t,e)??V(e);c=await this.eventMonitor.createEventMonitor("deploy",d,l,e,o),r&&(r(g(`Starting CloudFormation deployment of ${d}...
2
2
  `)),r(`Monitoring CloudFormation events (CDK process running in background)...
3
- `));const i={cdkOutput:"",actualStackName:u,stackDetected:!1,monitoringPromise:null},E=j(i,r,s,b);S=setTimeout(()=>{!i.stackDetected&&s&&!i.monitoringPromise&&(R.debug("CdkService","Fallback monitoring STARTING",{targetStackName:u,stackDetected:i.stackDetected,hasOnResourceProgress:!!n}),i.monitoringPromise=W(s,u,b))},Q);const O=await this.deploy(e.path,u,{verbose:e.options?.verbose,outputCallback:E,...e.assemblyDir!==void 0?{appDir:e.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:s?.getEventLogger()??void 0,context:C(e,l,c),credentials:m,...f!==void 0&&Object.keys(f).length>0&&{parameters:f},...d===!0&&{exclusively:!0}});if(w(this.abortSignal))return await H(u,c,m,r),o("Deployment cancelled");const v=$(i.cdkOutput,O,i.actualStackName);if(v.success)return v;const h=await P(s,i.monitoringPromise);return h===null?v:(h.driftSuspects!==void 0&&h.driftSuspects.length>0&&await q({accountId:l,region:c,stackName:i.actualStackName,recordedAt:new Date().toISOString(),suspects:h.driftSuspects.map(p=>({stackName:i.actualStackName,logicalId:p.logicalId,resourceType:p.resourceType,...p.physicalId!==void 0?{physicalId:p.physicalId}:{},failedStatus:p.failedStatus,statusReason:p.statusReason}))}),o(G(h)))}catch(u){const i=`CDK deploy failed: ${g(D(u))}`;return R.error("CdkService","CDK deployment exception",{error:i}),o(i)}finally{clearTimeout(S),s&&s.stopMonitoring()}}async runCdkDestroy(e,t,r,n,a,m,f){const d=e.callerIdentity?.Account,l=e.region||k;let c=null;try{const y=N(t,e);d&&y&&a&&(c=await this.eventMonitor.createEventMonitor("destroy",y,l,e,a),c.startMonitoring(y,s=>{n?.(s)},(s,S)=>{}));const b=await this.destroy(e.path,t,{verbose:e.options?.verbose,outputCallback:r,useCdkOut:m,cdkOutputLogger:c?.getEventLogger()??void 0,context:C(e,d,l),credentials:f});return w(this.abortSignal)?o("Destroy cancelled"):_(b)}catch(y){return o(`CDK destroy failed: ${g(D(y))}`)}finally{c&&c.stopMonitoring()}}}export{fe as CdkService};
3
+ `));const i={cdkOutput:"",actualStackName:d,stackDetected:!1,monitoringPromise:null},E=j(i,r,c,D);C=setTimeout(()=>{!i.stackDetected&&c&&!i.monitoringPromise&&(R.debug("CdkService","Fallback monitoring STARTING",{targetStackName:d,stackDetected:i.stackDetected,hasOnResourceProgress:!!n}),i.monitoringPromise=W(c,d,D))},Q);const K=await this.deploy(e.path,d,{verbose:e.options?.verbose,outputCallback:E,...e.assemblyDir!==void 0?{appDir:e.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:c?.getEventLogger()??void 0,context:S(e,u,l),credentials:s,...f!==void 0&&Object.keys(f).length>0&&{parameters:f},...m===!0&&{exclusively:!0}});if(w(this.abortSignal))return await H(d,l,s,r),a("Deployment cancelled");const v=$(i.cdkOutput,K,i.actualStackName);if(v.success)return v;const h=await P(c,i.monitoringPromise);return h===null?v:(h.driftSuspects!==void 0&&h.driftSuspects.length>0&&await q({accountId:u,region:l,stackName:i.actualStackName,recordedAt:new Date().toISOString(),suspects:h.driftSuspects.map(y=>({stackName:i.actualStackName,logicalId:y.logicalId,resourceType:y.resourceType,...y.physicalId!==void 0?{physicalId:y.physicalId}:{},failedStatus:y.failedStatus,statusReason:y.statusReason}))}),a(G(h)))}catch(d){const i=`CDK deploy failed: ${g(b(d))}`;return R.error("CdkService","CDK deployment exception",{error:i}),a(i)}finally{clearTimeout(C),c&&c.stopMonitoring()}}async runCdkDestroy(e,t,r,n,o,s,f){const m=e.callerIdentity?.Account,u=e.region||k;let l=null;try{const p=N(t,e);m&&p&&o&&(l=await this.eventMonitor.createEventMonitor("destroy",p,u,e,o),l.startMonitoring(p,c=>{n?.(c)},(c,C)=>{}));const D=await this.destroy(e.path,t,{verbose:e.options?.verbose,outputCallback:r,useCdkOut:s,cdkOutputLogger:l?.getEventLogger()??void 0,context:S(e,m,u),credentials:f});return w(this.abortSignal)?a("Destroy cancelled"):_(D)}catch(p){return a(`CDK destroy failed: ${g(b(p))}`)}finally{l&&l.stopMonitoring()}}}export{ye as CdkService};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Ensures a synth folder's npm dependencies are installed before the CDK
3
+ * app is executed.
4
+ *
5
+ * Fresh CI checkouts (and fresh clones generally) have no
6
+ * `fjall/<App>/node_modules`, so synthesis dies with a raw
7
+ * ERR_MODULE_NOT_FOUND from tsx before the deploy engine can produce an
8
+ * actionable error. The engine owns its inputs: when the synth folder has
9
+ * its own package.json and no node_modules, install the dependencies
10
+ * rather than asking every consumer (CI pipelines, local clones, the
11
+ * deployment worker) to remember an install step.
12
+ *
13
+ * Only the fully-missing node_modules case is handled — a present but
14
+ * incomplete install proceeds to synthesis as before, where npm's own
15
+ * resolution errors surface.
16
+ */
17
+ import type { Result } from "@fjall/generator";
18
+ /** Timeout for the synth-folder dependency install (5 minutes). */
19
+ export declare const SYNTH_DEPS_INSTALL_TIMEOUT_MS: number;
20
+ export interface EnsureSynthDependenciesOptions {
21
+ onOutput?: (chunk: string) => void;
22
+ abortSignal?: AbortSignal;
23
+ }
24
+ export interface SynthDependenciesOutcome {
25
+ /** True when an install was performed; false when nothing was needed. */
26
+ installed: boolean;
27
+ }
28
+ /**
29
+ * Install the synth folder's dependencies when they are missing.
30
+ *
31
+ * No-op (success, installed: false) when the folder has no package.json of
32
+ * its own — dependencies then resolve from an ancestor node_modules — or
33
+ * when node_modules already exists.
34
+ */
35
+ export declare function ensureSynthDependencies(path: string, options?: EnsureSynthDependenciesOptions): Promise<Result<SynthDependenciesOutcome, string>>;
@@ -0,0 +1,2 @@
1
+ var g=Object.defineProperty;var o=(e,n)=>g(e,"name",{value:n,configurable:!0});import{existsSync as i}from"fs";import{join as a}from"path";import{filterDangerousEnvVars as S,maskSensitiveOutput as d}from"@fjall/util";import{logger as l}from"@fjall/util/logger";import{success as c,failure as s}from"@fjall/generator";import{spawnWithTimeout as $}from"../../orchestration/spawnHelpers.js";const u=300*1e3,m=1500;function w(e){const n=d(e.trim());return n.length>m?`\u2026${n.slice(-m)}`:n}o(w,"maskedTail");async function A(e,n={}){if(!i(a(e,"package.json")))return c({installed:!1});if(i(a(e,"node_modules")))return c({installed:!1});const t=i(a(e,"package-lock.json"))?"ci":"install",f=[t,"--no-audit","--no-fund"];n.onOutput?.(`Installing infrastructure dependencies (npm ${t}) in ${e}...
2
+ `),l.info("CdkService","Installing synth-folder dependencies",{path:e,subcommand:t});const p=Date.now(),r=await $({command:"npm",args:f,cwd:e,env:S(process.env),timeout:u,...n.abortSignal!==void 0?{abortSignal:n.abortSignal}:{}});switch(r.type){case"success":return r.code===0?(l.info("CdkService","Synth-folder dependencies installed",{path:e,durationMs:Date.now()-p}),c({installed:!0})):s(`npm ${t} failed (exit ${r.code}) in ${e}: ${w(r.stderr||r.stdout)}`);case"timeout":return s(`npm ${t} timed out after ${u}ms in ${e}`);case"aborted":return s("Dependency installation was cancelled");case"enoent":return s("npm was not found on PATH. Install Node.js 22+ (which bundles npm) on this machine or CI agent, then retry.");case"spawn_error":return s(`Failed to start npm ${t} in ${e}: ${d(r.error.message)}`)}}o(A,"ensureSynthDependencies");export{u as SYNTH_DEPS_INSTALL_TIMEOUT_MS,A as ensureSynthDependencies};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "3.6.0",
3
+ "version": "3.7.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",
@@ -88,8 +88,8 @@
88
88
  "@aws-sdk/client-ssm": "^3.1038.0",
89
89
  "@aws-sdk/client-sso-admin": "^3.1038.0",
90
90
  "@aws-sdk/client-sts": "^3.1038.0",
91
- "@fjall/generator": "^3.6.0",
92
- "@fjall/util": "^3.6.0",
91
+ "@fjall/generator": "^3.7.0",
92
+ "@fjall/util": "^3.7.0",
93
93
  "@smithy/node-http-handler": "^4.6.1",
94
94
  "aws-cdk": "^2.1128.1",
95
95
  "tsx": "^4.21.0",
@@ -100,5 +100,5 @@
100
100
  "typescript": "^6.0.3",
101
101
  "vitest": "^4.1.5"
102
102
  },
103
- "gitHead": "10366db602d7e80669e22825a52071a47e05c3b7"
103
+ "gitHead": "2ee5e51bc6aa45e34ec70160b36ddf976371ccd5"
104
104
  }