@fjall/deploy-core 3.0.0 → 3.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.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 226 files minified at 2026-07-18T01:56:25.593Z
1
+ 226 files minified at 2026-07-18T06:59:22.431Z
@@ -53,17 +53,20 @@ export declare function buildTargetUnreadyAdvisory(params: {
53
53
  providerAccounts?: OrgConfig["providerAccounts"];
54
54
  }): string;
55
55
  /**
56
- * Probe whether the `Account` CloudFormation stack exists and is provisioned
57
- * in `region`. `success(true)` = a usable stack; `success(false)` = absent or
56
+ * Probe whether the governance CloudFormation stack (`Account` by default;
57
+ * pass `stackName` for the platform-tier `Platform` shape) exists and is
58
+ * provisioned in `region`. `success(true)` = a usable stack; `success(false)` = absent or
58
59
  * an unprovisioned shell (failed/rolled-back create, unexecuted change-set,
59
60
  * in-flight/failed delete); `failure` = the describe itself errored. Exported
60
61
  * so connect-time deploy-state probes reuse the same unprovisioned-status
61
62
  * interpretation as the deploy readiness path rather than re-deriving it.
62
63
  */
63
- export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal): Promise<Result<boolean>>;
64
+ export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal, stackName?: string): Promise<Result<boolean>>;
64
65
  /**
65
66
  * Probe whether a deploy target is provisioned before any AWS mutation:
66
- * the fixed-name Account stack must exist in the target region (both shapes),
67
+ * the governance stack must exist in the target region (`Account` for member
68
+ * and solo accounts, `Platform` for a platform-tier target — its substrate
69
+ * deploys under that name via `Platform extends Account`),
67
70
  * and for org estates (a tier-organisation account in providerAccounts) the
68
71
  * (account, region) IPAM pool must exist. Bootstrap is excluded — both deploy
69
72
  * paths self-heal it.
@@ -1 +1 @@
1
- var S=Object.defineProperty;var s=(e,t)=>S(e,"name",{value:t,configurable:!0});import{DescribeStacksCommand as g}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as y}from"@aws-sdk/client-ec2";import{success as c,failure as d}from"@fjall/generator";import{accountTier as E,generateTargetName as O,getErrorMessage as T}from"@fjall/util";import{formatIpamPairTagValue as k,IPAM_OPERATIONS_POOL_TAG_KEY as _,STACK_NOT_FOUND_PATTERN as R}from"@fjall/util/aws";import{getOrganisationStackName as P,ORGANISATION_TYPES as w}from"../types/operations.js";import{hasOrganisationTierAccount as I}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as h,isAborted as $}from"./organisations/types.js";const p=P(w.ACCOUNT),L=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function b(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}s(b,"describeTargetReadinessReason");function C(e){const t=e.accountName??e.accountId,n=e.reasons.map(b).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${O(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=I(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}s(C,"buildTargetUnreadyAdvisory");function x(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(R)}s(x,"isStackNotFoundError");async function v(e,t,n){try{const r=(await e.send(new g({StackName:p}),{abortSignal:h(n)})).Stacks?.[0]?.StackStatus;return c(r!==void 0&&!L.has(r))}catch(o){return x(o)?c(!1):d(new Error(`Failed to probe the ${p} stack in ${t}: ${T(o)}`))}}s(v,"probeAccountStackExists");function D(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(r=>r.Key===_&&r.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}s(D,"matchesIpamPair");async function F(e,t,n,o,r){try{let a;do{if($(r))return d(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const i=await e.send(new y({...a!==void 0&&{NextToken:a}}),{abortSignal:h(r)});if((i.IpamPools??[]).some(f=>D(f,t,n,o)))return c(!0);a=i.NextToken}while(a!==void 0);return c(!1)}catch(a){return d(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${T(a)}`))}}s(F,"probeIpamPoolExists");async function q(e,t,n,o,r){const a=[],i=await v(e.cloudFormation,n,r);if(!i.success)return d(i.error);i.data||a.push({kind:"missing-account-stack",stackName:p,region:n});const u=o?.providerAccounts;if(I(u)){const f=(u??[]).find(m=>m.id===t.id),A=(u??[]).find(m=>E(m)==="platform")?.id,N=f!==void 0&&E(f)==="platform"?{side:"owner",pairTagValue:k(t.id,n)}:{side:"consumer",...A!==void 0&&{platformAccountId:A}},l=await F(e.ec2,t.id,n,N,r);if(!l.success)return d(l.error);l.data||a.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return a.length===0?c({ready:!0}):c({ready:!1,reasons:a,advisory:C({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:a,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}s(q,"checkTargetReadiness");export{p as ACCOUNT_STACK_NAME,C as buildTargetUnreadyAdvisory,q as checkTargetReadiness,b as describeTargetReadinessReason,v as probeAccountStackExists};
1
+ var _=Object.defineProperty;var s=(e,t)=>_(e,"name",{value:t,configurable:!0});import{DescribeStacksCommand as P}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as R}from"@aws-sdk/client-ec2";import{success as c,failure as d}from"@fjall/generator";import{accountTier as E,generateTargetName as k,getErrorMessage as T}from"@fjall/util";import{formatIpamPairTagValue as w,IPAM_OPERATIONS_POOL_TAG_KEY as $,STACK_NOT_FOUND_PATTERN as L}from"@fjall/util/aws";import{getOrganisationStackName as I,ORGANISATION_TYPES as S}from"../types/operations.js";import{hasOrganisationTierAccount as h}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as N,isAborted as C}from"./organisations/types.js";const g=I(S.ACCOUNT),b=I(S.PLATFORM),v=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function x(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}s(x,"describeTargetReadinessReason");function M(e){const t=e.accountName??e.accountId,n=e.reasons.map(x).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${k(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",a=h(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${a}.`}s(M,"buildTargetUnreadyAdvisory");function F(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(L)}s(F,"isStackNotFoundError");async function D(e,t,n,o=g){try{const r=(await e.send(new P({StackName:o}),{abortSignal:N(n)})).Stacks?.[0]?.StackStatus;return c(r!==void 0&&!v.has(r))}catch(a){return F(a)?c(!1):d(new Error(`Failed to probe the ${o} stack in ${t}: ${T(a)}`))}}s(D,"probeAccountStackExists");function K(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(a=>a.Key===$&&a.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}s(K,"matchesIpamPair");async function j(e,t,n,o,a){try{let r;do{if(C(a))return d(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const i=await e.send(new R({...r!==void 0&&{NextToken:r}}),{abortSignal:N(a)});if((i.IpamPools??[]).some(u=>K(u,t,n,o)))return c(!0);r=i.NextToken}while(r!==void 0);return c(!1)}catch(r){return d(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${T(r)}`))}}s(j,"probeIpamPoolExists");async function J(e,t,n,o,a){const r=[],i=o?.providerAccounts,l=(i??[]).find(f=>f.id===t.id),u=l!==void 0&&E(l)==="platform",A=u?b:g,m=await D(e.cloudFormation,n,a,A);if(!m.success)return d(m.error);if(m.data||r.push({kind:"missing-account-stack",stackName:A,region:n}),h(i)){const f=(i??[]).find(O=>E(O)==="platform")?.id,y=u?{side:"owner",pairTagValue:w(t.id,n)}:{side:"consumer",...f!==void 0&&{platformAccountId:f}},p=await j(e.ec2,t.id,n,y,a);if(!p.success)return d(p.error);p.data||r.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return r.length===0?c({ready:!0}):c({ready:!1,reasons:r,advisory:M({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:r,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}s(J,"checkTargetReadiness");export{g as ACCOUNT_STACK_NAME,M as buildTargetUnreadyAdvisory,J as checkTargetReadiness,x as describeTargetReadinessReason,D as probeAccountStackExists};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "3.0.0",
3
+ "version": "3.1.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",
@@ -87,8 +87,8 @@
87
87
  "@aws-sdk/client-ssm": "^3.1038.0",
88
88
  "@aws-sdk/client-sso-admin": "^3.1038.0",
89
89
  "@aws-sdk/client-sts": "^3.1038.0",
90
- "@fjall/generator": "^3.0.0",
91
- "@fjall/util": "^3.0.0",
90
+ "@fjall/generator": "^3.1.0",
91
+ "@fjall/util": "^3.1.0",
92
92
  "@smithy/node-http-handler": "^4.6.1",
93
93
  "aws-cdk": "^2.1128.1",
94
94
  "tsx": "^4.21.0",
@@ -99,5 +99,5 @@
99
99
  "typescript": "^6.0.3",
100
100
  "vitest": "^4.1.5"
101
101
  },
102
- "gitHead": "848a386e3e2244401b76faabd02818c682d231e8"
102
+ "gitHead": "0f27cc5e260fdf218d33de546ba8610be89e1d81"
103
103
  }