@fjall/deploy-core 7.0.1 → 7.1.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": "3ac67fa1123461c35ead0b130bd78ff3854de09e21665058101ab80eb6b8f76a",
13
13
  "files": {
14
14
  "src/__test-utils__/awsMockHelpers.ts": "fea371c1b1869678",
15
15
  "src/__test-utils__/index.ts": "e03cf35821157dc4",
@@ -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",
@@ -255,9 +255,9 @@
255
255
  "src/util/sleepAbortable.ts": "f9cac3bf8a79a476",
256
256
  "src/version.ts": "1811a4475823b27e",
257
257
  "../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
258
- "package.json": "e44e24f88fe8f78c",
258
+ "package.json": "4a2daca4713da152",
259
259
  "tsconfig.json": "1929cf3f99fe9b1e"
260
260
  },
261
- "outputHash": "d65f44a3d1c787fb4b04534f4fd227ff81049a51daa7a7fa18036b4bf1070f3e",
261
+ "outputHash": "c2b4d1981a0a0e4b3f626776d932d77f5525dc0457909bb0390ec0620974c394",
262
262
  "outputFileCount": 479
263
263
  }
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 239 files minified at 2026-08-03T05:01:32.712Z
1
+ 239 files minified at 2026-08-05T08:58:52.836Z
@@ -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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "7.0.1",
3
+ "version": "7.1.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.1.0",
99
+ "@fjall/util": "^7.1.0",
100
100
  "@smithy/node-http-handler": "^4.9.13",
101
101
  "aws-cdk": "^2.1134.0",
102
102
  "tsx": "^4.23.1",