@fjall/util 3.11.0 → 4.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
- 90 files minified at 2026-07-23T21:49:13.499Z
1
+ 92 files minified at 2026-07-24T23:00:42.861Z
package/dist/config.d.ts CHANGED
@@ -186,6 +186,17 @@ export declare const RootConfigReadSchema: z.ZodObject<{
186
186
  }, z.core.$strict>>>;
187
187
  }, z.core.$strip>;
188
188
  export type RootConfigRead = z.infer<typeof RootConfigReadSchema>;
189
+ /**
190
+ * Local, NON-COMMITTED per-checkout state (`.fjall/local.json`, gitignored),
191
+ * co-located with fjall-config.json. Holds the developer's active deployment
192
+ * target: it lives here rather than in the committed fjall-config.json so a
193
+ * committed value can never silently re-aim someone else's deploy or a CI
194
+ * pipeline (the ci-deploy misdeploy fix). A legacy `activeTarget` in
195
+ * fjall-config.json is read only to warn-and-migrate, never to resolve a
196
+ * target.
197
+ */
198
+ export declare const LOCAL_CONFIG_DIRNAME = ".fjall";
199
+ export declare const LOCAL_CONFIG_FILENAME = "local.json";
189
200
  /**
190
201
  * Canonical serialiser for the root fjall-config.json - the single source of
191
202
  * truth for the file's on-disk shape. Both `Config.saveConfig` and the webapp
@@ -221,12 +232,6 @@ export declare class Config {
221
232
  * vanished from memory.
222
233
  */
223
234
  private parseDegraded;
224
- /**
225
- * Top-level keys explicitly cleared this session (clearActiveTarget).
226
- * The disk-preserving merge in saveConfig would otherwise resurrect them
227
- * from the on-disk copy.
228
- */
229
- private readonly clearedKeys;
230
235
  /**
231
236
  * Deep copy of the state this instance LOADED from disk (empty for a
232
237
  * programmatically-constructed Config, whose entire state is session
@@ -306,6 +311,17 @@ export declare class Config {
306
311
  * does not exist.
307
312
  */
308
313
  isContentUnavailable(): boolean;
314
+ private static warnedLegacyActiveTarget;
315
+ /**
316
+ * In-memory view of `.fjall/local.json`, lazily loaded once per instance.
317
+ * Reads serve from here so `set` then `get` is coherent within a process
318
+ * and hot-path resolution never re-hits disk; writes flush through it.
319
+ */
320
+ private localConfigCache;
321
+ private localConfigFile;
322
+ private loadLocalConfig;
323
+ private writeLocalConfig;
324
+ private static warnLegacyActiveTargetOnce;
309
325
  getActiveTarget(): string | undefined;
310
326
  setActiveTarget(name: string): void;
311
327
  clearActiveTarget(): void;
package/dist/config.js CHANGED
@@ -1 +1,2 @@
1
- var k=Object.defineProperty;var h=(g,e)=>k(g,"name",{value:e,configurable:!0});import*as a from"fs";import*as u from"path";import{z as c}from"zod";import{failure as y,success as O}from"./docker/result.js";import{getErrorMessage as T}from"./errorUtils.js";import{logger as C}from"./logger.js";import{maskSensitiveOutput as w}from"./securityHelpers.js";const F=10,E="fjall-config.json",U=["compliance","governance","none"],J=["enforced","off"],B=["centralised","off"],P=["account","draining","org"],G="managementEvents",W="organisationManagementEvents",Y=["active","draining","removed"],Z="FjallTrailBucketName",H="FjallTrailKeyArn",q="OrganisationTrailBucketName",D=c.object({name:c.string(),type:c.enum(["apex","delegated"]),parentDomain:c.string().optional(),account:c.string().optional(),region:c.string().optional()}).strict(),j=c.object({activeTarget:c.string().optional(),domains:c.array(D).optional()}).strict(),A=c.object({activeTarget:c.string().optional(),domains:c.array(D).optional()});function $(g={}){return JSON.stringify(g,null,2)}h($,"serialiseRootConfig");const x=j.keyof().options;function N(g,e,n){const t=e[n];t!==void 0&&(g[n]=t)}h(N,"copyDefinedKey");function _(g,e){if(e===void 0)return!1;const n=new Set([...Object.keys(g),...Object.keys(e)]);for(const t of n)if(g[t]!==e[t])return!1;return!0}h(_,"domainEntriesEqual");class l{static{h(this,"Config")}rootConfig;configPath=null;loadFailed=!1;parseDegraded=!1;clearedKeys=new Set;loadedSnapshot={};constructor(e,n){this.rootConfig=e??{},this.configPath=n??null}static findConfigDirectory(e){let n=e!==void 0&&e!==""?e:process.cwd();for(let t=0;t<F;t++){const r=u.join(n,"fjall"),o=u.join(r,E);if(a.existsSync(o))return r;const s=u.join(n,E);if(a.existsSync(s))return n;const f=u.dirname(n);if(f===n)break;n=f}return null}static loadConfigFile(e){try{return a.accessSync(e,a.constants.R_OK),a.readFileSync(e,{encoding:"utf8"})}catch(n){return C.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:w(T(n))}),null}}static loadConfig(e){const n=l.findConfigDirectory(e);if(!n)return new l;const t=u.join(n,E),r=l.loadConfigFile(t);if(r===null){const d=new l(void 0,t);return d.loadFailed=!0,d}let o,s=!1;if(r!==""){let d;try{d=JSON.parse(r)}catch(i){throw l.formatZodError(i,E)}const p=j.safeParse(d);if(p.success)o=p.data;else{const i=A.safeParse(d);o=i.success?i.data:{},s=!i.success,i.success?C.warn("Config","fjall-config.json contains keys this version does not recognise; they were ignored (only activeTarget and domains are read). If this is an old config, regenerate it with `fjall create ...` or re-run `fjall connect`.",{file:t}):C.warn("Config","fjall-config.json has a malformed activeTarget or domains value, so its contents were ignored for this run. Fix the file (or regenerate it with `fjall create ...` / `fjall connect`).",{file:t})}}const f=new l(o,t);return f.parseDegraded=s,f.loadedSnapshot=structuredClone(f.rootConfig),f}static formatZodError(e,n){if(e instanceof c.ZodError&&e.issues.length>0){const o=e.issues.map(s=>`${s.path.join(".")}: ${s.message}`).join("; ");return new Error(`Failed to parse ${n}: ${o}`)}const r=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${n}: ${r}`)}saveConfig(){let e=this.configPath;if(!e){const s=l.findConfigDirectory()||u.join(process.cwd(),"fjall");e=u.join(s,E)}if(this.loadFailed)return y(new Error(`Refusing to save ${e}: the file exists but could not be read when this config loaded, so saving would replace its contents with state that never included them. Fix the file permissions (e.g. chmod u+rw ${e}) and retry.`));const n=u.dirname(e);try{a.mkdirSync(n,{recursive:!0})}catch(s){return y(new Error(`Cannot create config directory ${n}: ${w(T(s))}`))}const t=l.assertWritable(e,n);if(!t.success)return t;const r=$(this.mergeWithDisk(e)),o=`${e}.tmp-${process.pid}`;try{a.writeFileSync(o,r,{mode:384}),a.renameSync(o,e)}catch(s){return y(new Error(`Failed to save ${e}: ${w(T(s))}`))}return O(void 0)}static assertWritable(e,n){if(a.existsSync(e))try{a.accessSync(e,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the file is read-only. Make it writable (e.g. chmod u+w ${e}) and retry.`))}try{a.accessSync(n,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the directory ${n} is not writable. Make it writable (e.g. chmod u+w ${n}) and retry.`))}return O(void 0)}mergeWithDisk(e){const n=l.readDiskConfigForMerge(e);if(n===void 0)return this.rootConfig;const t={...n.foreign,...n.known};for(const o of x)o!=="domains"&&this.sessionChangedKey(o)&&(this.rootConfig[o]===void 0?delete t[o]:N(t,this.rootConfig,o));const r=this.mergeDomains(n.known.domains);r!==void 0?t.domains=r:delete t.domains;for(const o of this.clearedKeys)delete t[o];return t}sessionChangedKey(e){return JSON.stringify(this.rootConfig[e])!==JSON.stringify(this.loadedSnapshot[e])}mergeDomains(e){const n=this.rootConfig.domains,t=this.loadedSnapshot.domains??[];if(n===void 0&&t.length===0)return e;const r=h(i=>i.toLowerCase(),"norm"),o=n??[],s=new Map(t.map(i=>[r(i.name),i])),f=new Map(o.map(i=>[r(i.name),i])),d=[],p=new Set;for(const i of e??[]){const m=r(i.name);p.add(m);const S=f.get(m),v=s.get(m);if(S!==void 0&&!_(S,v)){d.push(S);continue}S===void 0&&v!==void 0||d.push(i)}for(const i of o){const m=r(i.name);p.has(m)||_(i,s.get(m))||d.push(i)}return d}static readDiskConfigForMerge(e){if(!a.existsSync(e))return;let n;try{n=JSON.parse(a.readFileSync(e,{encoding:"utf8"}))}catch(o){C.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:w(T(o))});return}const t=A.safeParse(n);if(!t.success){C.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:w(t.error.message)});return}const r={};if(typeof n=="object"&&n!==null){const o=x;for(const[s,f]of Object.entries(n))o.includes(s)||(r[s]=f)}return{known:t.data,foreign:r}}static getConfigDirectory(e){return l.findConfigDirectory(e)}getConfigPath(){return this.configPath}isContentUnavailable(){return this.loadFailed||this.parseDegraded}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(e){this.rootConfig.activeTarget=e,this.clearedKeys.delete("activeTarget")}clearActiveTarget(){this.rootConfig.activeTarget=void 0,this.clearedKeys.add("activeTarget")}getDomains(){return this.rootConfig.domains??[]}setDomains(e){this.rootConfig.domains=e}addDomain(e){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(e)}getDomain(e){return this.rootConfig.domains?.find(n=>n.name.toLowerCase()===e.toLowerCase())}removeDomain(e){if(!this.rootConfig.domains)return!1;const n=this.rootConfig.domains.findIndex(t=>t.name.toLowerCase()===e.toLowerCase());return n===-1?!1:(this.rootConfig.domains.splice(n,1),!0)}}export{G as ACCOUNT_TRAIL_NAME,Y as ACCOUNT_TRAIL_STATES,l as Config,D as DomainConfigSchema,W as ORGANISATION_TRAIL_NAME,q as ORG_TRAIL_BUCKET_OUTPUT_KEY,B as ROOT_ACCESS_MANAGEMENT_MODES,E as ROOT_CONFIG_FILENAME,A as RootConfigReadSchema,j as RootConfigSchema,J as S3_BPA_MODES,Z as TRAIL_BUCKET_OUTPUT_KEY,H as TRAIL_KEY_ARN_OUTPUT_KEY,P as TRAIL_LIFECYCLE_STATES,U as VAULT_LOCK_MODES,$ as serialiseRootConfig};
1
+ var x=Object.defineProperty;var h=(u,e)=>x(u,"name",{value:e,configurable:!0});import*as a from"fs";import*as g from"path";import{z as c}from"zod";import{failure as y,success as O}from"./docker/result.js";import{getErrorMessage as S}from"./errorUtils.js";import{logger as p}from"./logger.js";import{maskSensitiveOutput as w}from"./securityHelpers.js";const N=10,v="fjall-config.json",G=["compliance","governance","none"],B=["enforced","off"],W=["centralised","off"],Y=["account","draining","org"],Z="managementEvents",H="organisationManagementEvents",q=["active","draining","removed"],z="FjallTrailBucketName",V="FjallTrailKeyArn",X="OrganisationTrailBucketName",j=c.object({name:c.string(),type:c.enum(["apex","delegated"]),parentDomain:c.string().optional(),account:c.string().optional(),region:c.string().optional()}).strict(),A=c.object({activeTarget:c.string().optional(),domains:c.array(j).optional()}).strict(),L=c.object({activeTarget:c.string().optional(),domains:c.array(j).optional()}),_=".fjall",k="local.json",R=c.object({activeTarget:c.string().optional()});function $(u={}){return JSON.stringify(u,null,2)}h($,"serialiseRootConfig");const D=A.keyof().options;function I(u,e,n){const t=e[n];t!==void 0&&(u[n]=t)}h(I,"copyDefinedKey");function F(u,e){if(e===void 0)return!1;const n=new Set([...Object.keys(u),...Object.keys(e)]);for(const t of n)if(u[t]!==e[t])return!1;return!0}h(F,"domainEntriesEqual");class f{static{h(this,"Config")}rootConfig;configPath=null;loadFailed=!1;parseDegraded=!1;loadedSnapshot={};constructor(e,n){this.rootConfig=e??{},this.configPath=n??null}static findConfigDirectory(e){let n=e!==void 0&&e!==""?e:process.cwd();for(let t=0;t<N;t++){const i=g.join(n,"fjall"),o=g.join(i,v);if(a.existsSync(o))return i;const s=g.join(n,v);if(a.existsSync(s))return n;const l=g.dirname(n);if(l===n)break;n=l}return null}static loadConfigFile(e){try{return a.accessSync(e,a.constants.R_OK),a.readFileSync(e,{encoding:"utf8"})}catch(n){return p.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:w(S(n))}),null}}static loadConfig(e){const n=f.findConfigDirectory(e);if(!n)return new f;const t=g.join(n,v),i=f.loadConfigFile(t);if(i===null){const d=new f(void 0,t);return d.loadFailed=!0,d}let o,s=!1;if(i!==""){let d;try{d=JSON.parse(i)}catch(r){throw f.formatZodError(r,v)}const C=A.safeParse(d);if(C.success)o=C.data;else{const r=L.safeParse(d);o=r.success?r.data:{},s=!r.success,r.success?p.warn("Config","fjall-config.json contains keys this version does not recognise; they were ignored (only activeTarget and domains are read). If this is an old config, regenerate it with `fjall create ...` or re-run `fjall connect`.",{file:t}):p.warn("Config","fjall-config.json has a malformed activeTarget or domains value, so its contents were ignored for this run. Fix the file (or regenerate it with `fjall create ...` / `fjall connect`).",{file:t})}}const l=new f(o,t);return l.parseDegraded=s,l.loadedSnapshot=structuredClone(l.rootConfig),l}static formatZodError(e,n){if(e instanceof c.ZodError&&e.issues.length>0){const o=e.issues.map(s=>`${s.path.join(".")}: ${s.message}`).join("; ");return new Error(`Failed to parse ${n}: ${o}`)}const i=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${n}: ${i}`)}saveConfig(){let e=this.configPath;if(!e){const s=f.findConfigDirectory()||g.join(process.cwd(),"fjall");e=g.join(s,v)}if(this.loadFailed)return y(new Error(`Refusing to save ${e}: the file exists but could not be read when this config loaded, so saving would replace its contents with state that never included them. Fix the file permissions (e.g. chmod u+rw ${e}) and retry.`));const n=g.dirname(e);try{a.mkdirSync(n,{recursive:!0})}catch(s){return y(new Error(`Cannot create config directory ${n}: ${w(S(s))}`))}const t=f.assertWritable(e,n);if(!t.success)return t;const i=$(this.mergeWithDisk(e)),o=`${e}.tmp-${process.pid}`;try{a.writeFileSync(o,i,{mode:384}),a.renameSync(o,e)}catch(s){return y(new Error(`Failed to save ${e}: ${w(S(s))}`))}return O(void 0)}static assertWritable(e,n){if(a.existsSync(e))try{a.accessSync(e,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the file is read-only. Make it writable (e.g. chmod u+w ${e}) and retry.`))}try{a.accessSync(n,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the directory ${n} is not writable. Make it writable (e.g. chmod u+w ${n}) and retry.`))}return O(void 0)}mergeWithDisk(e){const n=f.readDiskConfigForMerge(e);if(n===void 0)return this.rootConfig;const t={...n.foreign,...n.known};for(const o of D)o!=="domains"&&this.sessionChangedKey(o)&&(this.rootConfig[o]===void 0?delete t[o]:I(t,this.rootConfig,o));const i=this.mergeDomains(n.known.domains);return i!==void 0?t.domains=i:delete t.domains,t}sessionChangedKey(e){return JSON.stringify(this.rootConfig[e])!==JSON.stringify(this.loadedSnapshot[e])}mergeDomains(e){const n=this.rootConfig.domains,t=this.loadedSnapshot.domains??[];if(n===void 0&&t.length===0)return e;const i=h(r=>r.toLowerCase(),"norm"),o=n??[],s=new Map(t.map(r=>[i(r.name),r])),l=new Map(o.map(r=>[i(r.name),r])),d=[],C=new Set;for(const r of e??[]){const m=i(r.name);C.add(m);const T=l.get(m),E=s.get(m);if(T!==void 0&&!F(T,E)){d.push(T);continue}T===void 0&&E!==void 0||d.push(r)}for(const r of o){const m=i(r.name);C.has(m)||F(r,s.get(m))||d.push(r)}return d}static readDiskConfigForMerge(e){if(!a.existsSync(e))return;let n;try{n=JSON.parse(a.readFileSync(e,{encoding:"utf8"}))}catch(o){p.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:w(S(o))});return}const t=L.safeParse(n);if(!t.success){p.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:w(t.error.message)});return}const i={};if(typeof n=="object"&&n!==null){const o=D;for(const[s,l]of Object.entries(n))o.includes(s)||(i[s]=l)}return{known:t.data,foreign:i}}static getConfigDirectory(e){return f.findConfigDirectory(e)}getConfigPath(){return this.configPath}isContentUnavailable(){return this.loadFailed||this.parseDegraded}static warnedLegacyActiveTarget=!1;localConfigCache;localConfigFile(){const e=this.configPath!==null?g.dirname(this.configPath):process.cwd();return g.join(e,_,k)}loadLocalConfig(){if(this.localConfigCache===void 0)try{const e=a.readFileSync(this.localConfigFile(),{encoding:"utf8"}),n=R.safeParse(JSON.parse(e));this.localConfigCache=n.success?n.data:{}}catch{this.localConfigCache={}}return this.localConfigCache}writeLocalConfig(e){const n=this.localConfigFile();a.mkdirSync(g.dirname(n),{recursive:!0}),a.writeFileSync(n,JSON.stringify(e,null,2)+`
2
+ `,{encoding:"utf8"}),this.localConfigCache=e}static warnLegacyActiveTargetOnce(){f.warnedLegacyActiveTarget||(f.warnedLegacyActiveTarget=!0,p.warn("Config","`activeTarget` in fjall-config.json is deprecated and ignored: a committed target must never re-aim a deploy or CI pipeline. Run `fjall target set <name>` to store it locally in .fjall/local.json, then remove `activeTarget` from the committed fjall-config.json."))}getActiveTarget(){const e=this.loadLocalConfig().activeTarget;if(e!==void 0)return e;this.rootConfig.activeTarget!==void 0&&f.warnLegacyActiveTargetOnce()}setActiveTarget(e){const n=this.loadLocalConfig();n.activeTarget=e,this.writeLocalConfig(n)}clearActiveTarget(){const e=this.loadLocalConfig();delete e.activeTarget,this.writeLocalConfig(e)}getDomains(){return this.rootConfig.domains??[]}setDomains(e){this.rootConfig.domains=e}addDomain(e){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(e)}getDomain(e){return this.rootConfig.domains?.find(n=>n.name.toLowerCase()===e.toLowerCase())}removeDomain(e){if(!this.rootConfig.domains)return!1;const n=this.rootConfig.domains.findIndex(t=>t.name.toLowerCase()===e.toLowerCase());return n===-1?!1:(this.rootConfig.domains.splice(n,1),!0)}}export{Z as ACCOUNT_TRAIL_NAME,q as ACCOUNT_TRAIL_STATES,f as Config,j as DomainConfigSchema,_ as LOCAL_CONFIG_DIRNAME,k as LOCAL_CONFIG_FILENAME,H as ORGANISATION_TRAIL_NAME,X as ORG_TRAIL_BUCKET_OUTPUT_KEY,W as ROOT_ACCESS_MANAGEMENT_MODES,v as ROOT_CONFIG_FILENAME,L as RootConfigReadSchema,A as RootConfigSchema,B as S3_BPA_MODES,z as TRAIL_BUCKET_OUTPUT_KEY,V as TRAIL_KEY_ARN_OUTPUT_KEY,Y as TRAIL_LIFECYCLE_STATES,G as VAULT_LOCK_MODES,$ as serialiseRootConfig};
@@ -30,6 +30,7 @@ export declare const DevSubstrateSynthPropsSchema: z.ZodObject<{
30
30
  appKebab: z.ZodString;
31
31
  engineVersion: z.ZodOptional<z.ZodString>;
32
32
  adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
33
+ adoptCacheEcr: z.ZodOptional<z.ZodBoolean>;
33
34
  phase: z.ZodOptional<z.ZodEnum<{
34
35
  zone: "zone";
35
36
  full: "full";
@@ -58,6 +59,7 @@ export declare const DevSubstrateParamsSchema: z.ZodObject<{
58
59
  appKebab: z.ZodString;
59
60
  engineVersion: z.ZodOptional<z.ZodString>;
60
61
  adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
62
+ adoptCacheEcr: z.ZodOptional<z.ZodBoolean>;
61
63
  phase: z.ZodOptional<z.ZodEnum<{
62
64
  zone: "zone";
63
65
  full: "full";
@@ -1 +1 @@
1
- import{z as e}from"zod";const o=2,r="dev-substrate-params.json",i=e.object({devDomain:e.string().min(1),hostedZoneId:e.string().min(1).optional(),parentDomain:e.string().min(1).optional(),parentDelegationRoleArn:e.string().min(1).optional()}).strict().superRefine((t,n)=>{if(t.hostedZoneId!==void 0){t.parentDomain!==void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDomain \u2014 the adopted zone was already delegated; omit parentDomain"}),t.parentDelegationRoleArn!==void 0&&n.addIssue({code:"custom",path:["parentDelegationRoleArn"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDelegationRoleArn \u2014 the adopted zone was already delegated; omit parentDelegationRoleArn"});return}t.parentDelegationRoleArn!==void 0&&t.parentDomain===void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDelegationRoleArn requires parentDomain \u2014 set parentDomain to the parent zone the DelegationRole belongs to"}),t.parentDomain!==void 0&&!t.devDomain.endsWith(`.${t.parentDomain}`)&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDomain must be a proper DNS suffix of devDomain \u2014 devDomain must end with `.<parentDomain>`"})}),a={appId:e.string().min(1),appKebab:e.string().min(1),engineVersion:e.string().min(1).optional(),adoptSlotEcr:e.boolean().optional(),phase:e.enum(["zone","full"]).optional(),domain:i.optional()},p=e.object(a).strict(),d=e.object({...a,version:e.literal(o),appName:e.string().min(1)}).strict().refine(t=>t.appName===`fjall-dev-${t.appKebab}`,{message:"appName must equal `fjall-dev-${appKebab}` (single-source identity)",path:["appName"]});export{r as DEV_SUBSTRATE_PARAMS_FILENAME,o as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,d as DevSubstrateParamsSchema,p as DevSubstrateSynthPropsSchema};
1
+ import{z as e}from"zod";const o=2,r="dev-substrate-params.json",i=e.object({devDomain:e.string().min(1),hostedZoneId:e.string().min(1).optional(),parentDomain:e.string().min(1).optional(),parentDelegationRoleArn:e.string().min(1).optional()}).strict().superRefine((t,n)=>{if(t.hostedZoneId!==void 0){t.parentDomain!==void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDomain \u2014 the adopted zone was already delegated; omit parentDomain"}),t.parentDelegationRoleArn!==void 0&&n.addIssue({code:"custom",path:["parentDelegationRoleArn"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDelegationRoleArn \u2014 the adopted zone was already delegated; omit parentDelegationRoleArn"});return}t.parentDelegationRoleArn!==void 0&&t.parentDomain===void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDelegationRoleArn requires parentDomain \u2014 set parentDomain to the parent zone the DelegationRole belongs to"}),t.parentDomain!==void 0&&!t.devDomain.endsWith(`.${t.parentDomain}`)&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDomain must be a proper DNS suffix of devDomain \u2014 devDomain must end with `.<parentDomain>`"})}),a={appId:e.string().min(1),appKebab:e.string().min(1),engineVersion:e.string().min(1).optional(),adoptSlotEcr:e.boolean().optional(),adoptCacheEcr:e.boolean().optional(),phase:e.enum(["zone","full"]).optional(),domain:i.optional()},p=e.object(a).strict(),d=e.object({...a,version:e.literal(o),appName:e.string().min(1)}).strict().refine(t=>t.appName===`fjall-dev-${t.appKebab}`,{message:"appName must equal `fjall-dev-${appKebab}` (single-source identity)",path:["appName"]});export{r as DEV_SUBSTRATE_PARAMS_FILENAME,o as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,d as DevSubstrateParamsSchema,p as DevSubstrateSynthPropsSchema};
@@ -48,8 +48,9 @@ export declare function _buildxBuild(state: DockerCliState, args: BuildxBuildArg
48
48
  * - argv-only spawn (`shell: false` via `runDocker`)
49
49
  * - tag values are validated to be non-empty strings before they enter argv
50
50
  * - stderr is masked and bounded by `makeError(... stderrTail)`
51
- * - timeout: re-uses `DEFAULT_INSPECT_TIMEOUT_MS`; imagetools create is a
52
- * manifest-only operation comparable to inspect in latency
51
+ * - timeout: `DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS`. This is a REGISTRY
52
+ * WRITE — an auth exchange plus one manifest PUT per tag so it must not
53
+ * borrow the local-daemon inspect budget, which it did until 2026-07-24.
53
54
  */
54
55
  export declare function _tagByDigest(state: DockerCliState, sourceImage: string, digest: string, tags: readonly string[]): Promise<Result<void, DockerCliError>>;
55
56
  export declare function _imagetoolsInspect(state: DockerCliState, image: string): Promise<Result<ImagetoolsInspect, DockerCliError>>;
@@ -1 +1 @@
1
- var A=Object.defineProperty;var g=(s,l)=>A(s,"name",{value:l,configurable:!0});import{rm as R}from"node:fs/promises";import{buildxArgvBuilder as j}from"./buildxArgvBuilder.js";import{BuildxBuildArgsSchema as N}from"./dockerCliSchemas.js";import{DEFAULT_INSPECT_TIMEOUT_MS as M,DOCKER_CLI_BUILDX_LOG_CATEGORY as C}from"./dockerCliConstants.js";import{BuildxBuildMonitor as U,buildPhaseTimeoutMessage as G,publishStallTimeoutMessage as J,resolveBuildxBudgets as P}from"./buildxBuildMonitor.js";import{parseMetadataFile as H}from"./metadataFileParser.js";import{parseRawjsonLine as K}from"./rawjsonParser.js";import{rawjsonToVertexEvent as V}from"./rawjsonToVertexEvent.js";import{maskSensitiveOutput as b}from"../securityHelpers.js";import{failure as t,makeError as a,maskOutput as T,runDocker as F,spawnFailureToError as W,streamDocker as X,success as _}from"./DockerCli.js";function O(s){return s instanceof Error?s.message:String(s)}g(O,"getErrorMessage");async function oe(s,l,e,u){const r=N.safeParse(l);if(!r.success){const n=b(r.error.message);return t(a("validation",`Invalid BuildxBuildArgs: ${n}`,{issues:r.error.issues}))}const o=P(s.env);if(!o.success)return t(a("validation",b(o.error.message)));const p=o.data,c=j(r.data);let f=0,k=0;const E=new Set;let h,v;try{const n=new U(p);h=n,v=r.data.metadataFile;const I=g(x=>{const S=K(x);if(S===null){x.trim()!==""&&s.logger.debug(C,"Skipping malformed rawjson line",{line:b(x)});return}const m=V(S);if(m===null)return;const w=n.observe(m);w!==void 0&&e({type:"status",message:T(w.message),...w.percentage!==void 0&&{percentage:w.percentage}});for(const i of m.vertexes)i.completed!==void 0&&!E.has(i.digest)&&(E.add(i.digest),i.cached===!0?f++:k++),e({type:"vertex",message:T(i.name),vertex:i.digest});for(const i of m.logs)e({type:"log",message:T(i.data),vertex:i.vertex});for(const i of m.statuses)e({type:"status",message:T(`${i.name} ${i.current}`),vertex:i.vertex});for(const i of m.warnings)e({type:"warning",message:T(i.short),vertex:i.vertex})},"onStdoutLine"),L=u!==void 0?AbortSignal.any([n.signal,u]):n.signal,d=await X(s,{args:c,abortSignal:L},I);if(d.spawnError!==void 0)return t(a("daemon_unreachable",`Docker CLI is not available: ${d.spawnError}`,{stderrTail:d.stderrTail}));if(d.aborted||d.exitCode===null)return n.timedOutPhase==="build"?t(a("timeout",G(p),{stderrTail:d.stderrTail})):n.timedOutPhase==="publish"?t(a("timeout",J(p,n.progressSummary()),{stderrTail:d.stderrTail})):t(a("abort","docker buildx build was aborted",{stderrTail:d.stderrTail}));if(d.exitCode!==0)return t(a("build_failed",`docker buildx build failed (exit ${d.exitCode})`,{stderrTail:d.stderrTail}));const B=await H(r.data.metadataFile);if(!B.success)return B;const $=B.data,y=$["containerimage.digest"];if(typeof y!="string"||y==="")return t(a("metadata_missing_digest","Buildx metadata file does not contain containerimage.digest",{metadataFile:r.data.metadataFile}));const D={};for(const x of r.data.tags)D[x]=y;return _({digest:y,imageDigests:D,metadata:$,platforms:r.data.platforms,cacheHits:f,cacheMisses:k})}finally{h!==void 0&&h.dispose(),v!==void 0&&await R(v,{force:!0}).catch(n=>{s.logger.warn(C,"Failed to clean up buildx metadata file",{path:v,error:b(O(n))})})}}g(oe,"_buildxBuild");async function ne(s,l,e,u){if(l.trim()==="")return t(a("validation","tagByDigest: sourceImage must be non-empty"));if(!e.startsWith("sha256:"))return t(a("validation",`tagByDigest: expected sha256:... digest, got ${e.slice(0,32)}`));if(u.length===0)return t(a("validation","tagByDigest: tags must be a non-empty list"));for(const f of u)if(typeof f!="string"||f.trim()==="")return t(a("validation","tagByDigest: every tag must be non-empty"));const r=`${l}@${e}`,o=[];for(const f of u)o.push("--tag",f);const p=["buildx","imagetools","create","--prefer-index=false",...o,r],c=await F(s,{args:p,timeoutMs:M});return c.spawnError!==void 0?t(W(c)):c.exitCode===null?t(a("abort",`docker buildx imagetools create for ${r} was aborted`,{stderrTail:c.stderrTail})):c.exitCode!==0?t(a("tag_failed",`docker buildx imagetools create for ${r} failed (exit ${c.exitCode})`,{stderrTail:c.stderrTail})):_(void 0)}g(ne,"_tagByDigest");async function de(s,l){const e=await F(s,{args:["buildx","imagetools","inspect",l,"--raw"],timeoutMs:M});if(e.spawnError!==void 0)return t(a("daemon_unreachable",`Docker CLI is not available: ${e.spawnError}`,{stderrTail:e.stderrTail}));if(e.exitCode===null)return t(a("abort",`docker buildx imagetools inspect ${l} was aborted`,{stderrTail:e.stderrTail}));if(e.exitCode!==0)return t(a("inspect_failed",`docker buildx imagetools inspect ${l} failed (exit ${e.exitCode})`,{stderrTail:e.stderrTail}));let u,r;try{const o=JSON.parse(e.stdout);typeof o.mediaType=="string"&&(u=o.mediaType),typeof o.digest=="string"&&(r=o.digest)}catch(o){s.logger.debug(C,"imagetools inspect output is not JSON; preserving raw",{error:b(O(o))})}return _({raw:e.stdout,...u!==void 0&&{mediaType:u},...r!==void 0&&{digest:r}})}g(de,"_imagetoolsInspect");export{oe as _buildxBuild,de as _imagetoolsInspect,ne as _tagByDigest};
1
+ var N=Object.defineProperty;var g=(e,n)=>N(e,"name",{value:n,configurable:!0});import{rm as U}from"node:fs/promises";import{buildxArgvBuilder as J}from"./buildxArgvBuilder.js";import{BuildxBuildArgsSchema as Y}from"./dockerCliSchemas.js";import{DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS as B,DOCKER_CLI_BUILDX_LOG_CATEGORY as S}from"./dockerCliConstants.js";import{abortOrTimeoutError as I,makeLoggedError as p,spawnFailureToError as k,wasAborted as $}from"./dockerCliDiagnostics.js";import{BuildxBuildMonitor as H,buildPhaseTimeoutMessage as K,publishStallTimeoutMessage as P,resolveBuildxBudgets as V}from"./buildxBuildMonitor.js";import{parseMetadataFile as W}from"./metadataFileParser.js";import{parseRawjsonLine as X}from"./rawjsonParser.js";import{rawjsonToVertexEvent as q}from"./rawjsonToVertexEvent.js";import{maskSensitiveOutput as y}from"../securityHelpers.js";import{failure as r,makeError as m,maskOutput as v,runDocker as L,streamDocker as z,success as D}from"./DockerCli.js";function R(e){return e instanceof Error?e.message:String(e)}g(R,"getErrorMessage");async function le(e,n,t,d){const i=Y.safeParse(n);if(!i.success){const o=y(i.error.message);return r(m("validation",`Invalid BuildxBuildArgs: ${o}`,{issues:i.error.issues}))}const s=V(e.env);if(!s.success)return r(m("validation",y(s.error.message)));const x=s.data,u=J(i.data);let f=0,M=0;const C=new Set;let E,T;try{const o=new H(x);E=o,T=i.data.metadataFile;const j=g(b=>{const A=X(b);if(A===null){b.trim()!==""&&e.logger.debug(S,"Skipping malformed rawjson line",{line:y(b)});return}const c=q(A);if(c===null)return;const h=o.observe(c);h!==void 0&&t({type:"status",message:v(h.message),...h.percentage!==void 0&&{percentage:h.percentage}});for(const a of c.vertexes)a.completed!==void 0&&!C.has(a.digest)&&(C.add(a.digest),a.cached===!0?f++:M++),t({type:"vertex",message:v(a.name),vertex:a.digest});for(const a of c.logs)t({type:"log",message:v(a.data),vertex:a.vertex});for(const a of c.statuses)t({type:"status",message:v(`${a.name} ${a.current}`),vertex:a.vertex});for(const a of c.warnings)t({type:"warning",message:v(a.short),vertex:a.vertex})},"onStdoutLine"),G=d!==void 0?AbortSignal.any([o.signal,d]):o.signal,l=await z(e,{args:u,abortSignal:G},j);if(l.spawnError!==void 0)return r(k(e,l));if($(l))return o.timedOutPhase==="build"?r(p(e,"timeout",K(x),{stderrTail:l.stderrTail})):o.timedOutPhase==="publish"?r(p(e,"timeout",P(x,o.progressSummary()),{stderrTail:l.stderrTail})):r(p(e,"abort","docker buildx build was aborted",{stderrTail:l.stderrTail}));if(l.exitCode!==0)return r(p(e,"build_failed",`docker buildx build failed (exit ${l.exitCode})`,{stderrTail:l.stderrTail}));const _=await W(i.data.metadataFile);if(!_.success)return _;const F=_.data,w=F["containerimage.digest"];if(typeof w!="string"||w==="")return r(m("metadata_missing_digest","Buildx metadata file does not contain containerimage.digest",{metadataFile:i.data.metadataFile}));const O={};for(const b of i.data.tags)O[b]=w;return D({digest:w,imageDigests:O,metadata:F,platforms:i.data.platforms,cacheHits:f,cacheMisses:M})}finally{E!==void 0&&E.dispose(),T!==void 0&&await U(T,{force:!0}).catch(o=>{e.logger.warn(S,"Failed to clean up buildx metadata file",{path:T,error:y(R(o))})})}}g(le,"_buildxBuild");async function fe(e,n,t,d){if(n.trim()==="")return r(m("validation","tagByDigest: sourceImage must be non-empty"));if(!t.startsWith("sha256:"))return r(m("validation",`tagByDigest: expected sha256:... digest, got ${t.slice(0,32)}`));if(d.length===0)return r(m("validation","tagByDigest: tags must be a non-empty list"));for(const f of d)if(typeof f!="string"||f.trim()==="")return r(m("validation","tagByDigest: every tag must be non-empty"));const i=`${n}@${t}`,s=[];for(const f of d)s.push("--tag",f);const x=["buildx","imagetools","create","--prefer-index=false",...s,i],u=await L(e,{args:x,timeoutMs:B});return u.spawnError!==void 0?r(k(e,u)):$(u)?r(I(e,u,`docker buildx imagetools create for ${i}`,B)):u.exitCode!==0?r(p(e,"tag_failed",`docker buildx imagetools create for ${i} failed (exit ${u.exitCode})`,{stderrTail:u.stderrTail})):D(void 0)}g(fe,"_tagByDigest");async function me(e,n){const t=await L(e,{args:["buildx","imagetools","inspect",n,"--raw"],timeoutMs:B});if(t.spawnError!==void 0)return r(k(e,t));if($(t))return r(I(e,t,`docker buildx imagetools inspect ${n}`,B));if(t.exitCode!==0)return r(p(e,"inspect_failed",`docker buildx imagetools inspect ${n} failed (exit ${t.exitCode})`,{stderrTail:t.stderrTail}));let d,i;try{const s=JSON.parse(t.stdout);typeof s.mediaType=="string"&&(d=s.mediaType),typeof s.digest=="string"&&(i=s.digest)}catch(s){e.logger.debug(S,"imagetools inspect output is not JSON; preserving raw",{error:y(R(s))})}return D({raw:t.stdout,...d!==void 0&&{mediaType:d},...i!==void 0&&{digest:i}})}g(me,"_imagetoolsInspect");export{le as _buildxBuild,me as _imagetoolsInspect,fe as _tagByDigest};
@@ -155,22 +155,26 @@ export interface SpawnDockerResult {
155
155
  readonly stderr: string;
156
156
  readonly stderrTail: readonly string[];
157
157
  readonly aborted: boolean;
158
+ /**
159
+ * True when the per-call `timeoutMs` budget is what fired the abort. A
160
+ * caller cancel and a budget expiry compose into ONE signal, so `aborted`
161
+ * alone cannot tell them apart and every timeout would report as a cancel.
162
+ */
163
+ readonly timedOut: boolean;
164
+ readonly spawnError?: string;
165
+ }
166
+ export interface StreamDockerResult {
167
+ readonly exitCode: number | null;
168
+ readonly stderrTail: readonly string[];
169
+ readonly aborted: boolean;
170
+ readonly timedOut: boolean;
158
171
  readonly spawnError?: string;
172
+ readonly child: ChildProcess | null;
159
173
  }
160
174
  export declare function makeError(kind: DockerCliErrorKind, message: string, details?: Record<string, unknown>): DockerCliError;
161
- export declare function spawnFailureToError(result: {
162
- spawnError?: string;
163
- stderrTail: readonly string[];
164
- }): DockerCliError;
165
175
  export declare function tailLines(buffer: string, count: number): string[];
166
176
  export declare function maskOutput(value: string): string;
167
177
  export declare function runDocker(state: DockerCliState, opts: SpawnDockerOptions): Promise<SpawnDockerResult>;
168
- export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<{
169
- exitCode: number | null;
170
- stderrTail: readonly string[];
171
- aborted: boolean;
172
- spawnError?: string;
173
- child: ChildProcess | null;
174
- }>;
178
+ export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<StreamDockerResult>;
175
179
  export { failure, success };
176
180
  export type { Result };
@@ -1 +1 @@
1
- var _=Object.defineProperty;var a=(n,e)=>_(n,"name",{value:e,configurable:!0});import{maskSensitiveOutput as g}from"../securityHelpers.js";import{BUILDX_VERSION_FLOOR as c,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as v,DEFAULT_INSPECT_TIMEOUT_MS as b,DOCKER_CLI_BUILDX_LOG_CATEGORY as T,DOCKER_CLI_LOG_CATEGORY as E}from"./dockerCliConstants.js";import{failure as t,makeError as u,runDocker as d,spawnFailureToError as l,success as f}from"./DockerCli.js";function m(n){const e=n.startsWith("v")?n.slice(1):n,r=/^(\d+)\.(\d+)\.(\d+)/.exec(e);return r===null?null:[parseInt(r[1]??"0",10),parseInt(r[2]??"0",10),parseInt(r[3]??"0",10)]}a(m,"parseSemver");function k(n,e){for(let r=0;r<3;r++){const i=n[r]??0,o=e[r]??0;if(i!==o)return i-o}return 0}a(k,"compareSemver");function w(n){const e=/github\.com\/docker\/buildx\s+(v?\d+\.\d+\.\d+)/.exec(n);return e!==null&&e[1]!==void 0?e[1]:/(\d+\.\d+\.\d+)/.exec(n)?.[1]??null}a(w,"extractBuildxVersion");async function h(n){const e=await d(n,{args:["buildx","version"],timeoutMs:b});if(e.spawnError!==void 0)return t(l(e));if(e.exitCode===null)return t(u("abort","docker buildx version was aborted",{stderrTail:e.stderrTail}));if(e.exitCode!==0){const s=await d(n,{args:["version","--format","{{.Server.Version}}"],timeoutMs:v});return s.spawnError!==void 0?t(l(s)):s.exitCode!==0?t(u("daemon_unreachable","Docker daemon is not running or not installed",{stderrTail:s.stderrTail})):t(u("buildx_unavailable",`Docker Buildx plugin is not installed (need >= ${c})`,{stderrTail:e.stderrTail}))}const r=w(e.stdout);if(r===null)return t(u("buildx_unavailable","Could not parse docker buildx version output",{stdout:e.stdout}));const i=m(r),o=m(c);return i===null||o===null?t(u("buildx_unavailable",`Could not parse buildx version "${r}"`,{stdout:e.stdout})):k(i,o)<0?t(u("buildx_unavailable",`Docker Buildx ${r} is below the required ${c} floor`,{observed:r,floor:c})):(n.logger.debug(T,"buildx available",{version:r}),f({version:r}))}a(h,"_assertBuildxAvailable");async function I(n){const e=await d(n,{args:["version","--format","json"],timeoutMs:v});if(e.spawnError!==void 0)return t(l(e));if(e.exitCode===null)return t(u("abort","docker version was aborted",{stderrTail:e.stderrTail}));if(e.exitCode!==0)return t(u("daemon_unreachable","Docker daemon is not running or not installed",{stderrTail:e.stderrTail}));let r;try{r=JSON.parse(e.stdout)}catch(x){const p=g(x instanceof Error?x.message:String(x));return t(u("daemon_unreachable",`docker version output is not valid JSON: ${p}`,{stdout:e.stdout}))}if(r===null||typeof r!="object")return t(u("daemon_unreachable","docker version did not return a JSON object"));const i=r,o=i.Server?.Name??"Docker",s=i.Server?.Version??"unknown";return o==="Podman Engine"?t(u("buildx_unavailable","Podman is not supported; install Docker Engine 23+ with the buildx plugin")):f({serverName:o,serverVersion:s})}a(I,"_detectDaemon");async function M(n,e){const r=await d(n,{args:["buildx","inspect",e],timeoutMs:b});if(r.spawnError!==void 0)return t(l(r));if(r.exitCode===0){if(/Driver:\s*docker-container/.test(r.stdout))return f({created:!1});n.logger.warn(E,"Replacing buildx builder with wrong driver",{name:e});const o=await d(n,{args:["buildx","rm",e],timeoutMs:b});if(o.spawnError!==void 0)return t(l(o));if(o.exitCode!==0)return t(u("buildx_unavailable",`Failed to remove existing buildx builder ${e}`,{stderrTail:o.stderrTail}))}const i=await d(n,{args:["buildx","create","--name",e,"--driver","docker-container","--use"],timeoutMs:b});return i.spawnError!==void 0?t(l(i)):i.exitCode!==0?t(u("buildx_unavailable",`Failed to create buildx builder ${e}`,{stderrTail:i.stderrTail})):f({created:!0})}a(M,"_ensureBuilder");export{h as _assertBuildxAvailable,I as _detectDaemon,M as _ensureBuilder};
1
+ var E=Object.defineProperty;var d=(r,e)=>E(r,"name",{value:e,configurable:!0});import{maskSensitiveOutput as w}from"../securityHelpers.js";import{BUILDX_VERSION_FLOOR as m,DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS as l,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as s,DOCKER_CLI_BUILDX_LOG_CATEGORY as T,DOCKER_CLI_LOG_CATEGORY as O}from"./dockerCliConstants.js";import{abortOrTimeoutError as a,makeLoggedError as v,spawnFailureToError as c,wasAborted as f}from"./dockerCliDiagnostics.js";import{failure as i,makeError as b,runDocker as x,success as p}from"./DockerCli.js";function g(r){const e=r.startsWith("v")?r.slice(1):r,n=/^(\d+)\.(\d+)\.(\d+)/.exec(e);return n===null?null:[parseInt(n[1]??"0",10),parseInt(n[2]??"0",10),parseInt(n[3]??"0",10)]}d(g,"parseSemver");function C(r,e){for(let n=0;n<3;n++){const o=r[n]??0,t=e[n]??0;if(o!==t)return o-t}return 0}d(C,"compareSemver");function D(r){const e=/github\.com\/docker\/buildx\s+(v?\d+\.\d+\.\d+)/.exec(r);return e!==null&&e[1]!==void 0?e[1]:/(\d+\.\d+\.\d+)/.exec(r)?.[1]??null}d(D,"extractBuildxVersion");async function M(r){const e=await x(r,{args:["buildx","version"],timeoutMs:s});if(e.spawnError!==void 0)return i(c(r,e));if(f(e))return i(a(r,e,"docker buildx version",s));if(e.exitCode!==0){const u=await x(r,{args:["version","--format","{{.Server.Version}}"],timeoutMs:s});return u.spawnError!==void 0?i(c(r,u)):f(u)?i(a(r,u,"docker version",s)):u.exitCode!==0?i(v(r,"daemon_unreachable","Docker daemon is not running or not installed",{stderrTail:u.stderrTail})):i(v(r,"buildx_unavailable",`Docker Buildx plugin is not installed (need >= ${m})`,{stderrTail:e.stderrTail}))}const n=D(e.stdout);if(n===null)return i(b("buildx_unavailable","Could not parse docker buildx version output",{stdout:e.stdout}));const o=g(n),t=g(m);return o===null||t===null?i(b("buildx_unavailable",`Could not parse buildx version "${n}"`,{stdout:e.stdout})):C(o,t)<0?i(b("buildx_unavailable",`Docker Buildx ${n} is below the required ${m} floor`,{observed:n,floor:m})):(r.logger.debug(T,"buildx available",{version:n}),p({version:n}))}d(M,"_assertBuildxAvailable");async function $(r){const e=await x(r,{args:["version","--format","json"],timeoutMs:s});if(e.spawnError!==void 0)return i(c(r,e));if(f(e))return i(a(r,e,"docker version",s));if(e.exitCode!==0)return i(v(r,"daemon_unreachable","Docker daemon is not running or not installed",{stderrTail:e.stderrTail}));let n;try{n=JSON.parse(e.stdout)}catch(_){const k=w(_ instanceof Error?_.message:String(_));return i(b("daemon_unreachable",`docker version output is not valid JSON: ${k}`,{stdout:e.stdout}))}if(n===null||typeof n!="object")return i(b("daemon_unreachable","docker version did not return a JSON object"));const o=n,t=o.Server?.Name??"Docker",u=o.Server?.Version??"unknown";return t==="Podman Engine"?i(b("buildx_unavailable","Podman is not supported; install Docker Engine 23+ with the buildx plugin")):p({serverName:t,serverVersion:u})}d($,"_detectDaemon");async function B(r,e){const n=await x(r,{args:["buildx","inspect",e],timeoutMs:l});if(n.spawnError!==void 0)return i(c(r,n));if(f(n))return i(a(r,n,`docker buildx inspect ${e}`,l));if(n.exitCode===0){if(/Driver:\s*docker-container/.test(n.stdout))return p({created:!1});r.logger.warn(O,"Replacing buildx builder with wrong driver",{name:e});const t=await x(r,{args:["buildx","rm",e],timeoutMs:l});if(t.spawnError!==void 0)return i(c(r,t));if(f(t))return i(a(r,t,`docker buildx rm ${e}`,l));if(t.exitCode!==0)return i(v(r,"buildx_unavailable",`Failed to remove existing buildx builder ${e}`,{stderrTail:t.stderrTail}))}const o=await x(r,{args:["buildx","create","--name",e,"--driver","docker-container","--use"],timeoutMs:l});return o.spawnError!==void 0?i(c(r,o)):f(o)?i(a(r,o,`docker buildx create ${e}`,l)):o.exitCode!==0?i(v(r,"buildx_unavailable",`Failed to create buildx builder ${e}`,{stderrTail:o.stderrTail})):p({created:!0})}d(B,"_ensureBuilder");export{M as _assertBuildxAvailable,$ as _detectDaemon,B as _ensureBuilder};
@@ -1 +1 @@
1
- var x=Object.defineProperty;var l=(t,e)=>x(t,"name",{value:e,configurable:!0});import{spawn as E}from"node:child_process";import{filterDangerousEnvVars as A,maskSensitiveOutput as b}from"../securityHelpers.js";import{abortChildProcess as y}from"./abortHelpers.js";import{DEFAULT_DOCKER_BIN as D,DOCKER_CLI_LOG_CATEGORY as C,STDERR_TAIL_LINE_MAX_CHARS as S,STDERR_TAIL_LINES as v}from"./dockerCliConstants.js";import{_buildxBuild as I,_imagetoolsInspect as L,_tagByDigest as R}from"./DockerCli.build.js";import{_assertBuildxAvailable as O,_detectDaemon as P,_ensureBuilder as N}from"./DockerCli.daemon.js";import{_imageInspect as F,_loginEcr as G,_pull as K,_push as M,_tag as j}from"./DockerCli.registry.js";import{failure as _,success as H}from"./result.js";class ee{static{l(this,"DockerCli")}state;constructor(e){const r=e.dockerBin!==void 0&&e.dockerBin!==""?e.dockerBin:D,n=A(e.env??process.env);this.state={logger:e.logger,dockerBin:r,env:n,abortSignal:e.abortSignal,ecrSession:void 0}}async buildxBuild(e,r,n){return I(this.state,e,r,n)}async tag(e,r){return j(this.state,e,r)}async tagByDigest(e,r,n){return R(this.state,e,r,n)}async push(e,r){return M(this.state,e,r)}async pull(e,r,n){return K(this.state,e,r,n)}async imageInspect(e){return F(this.state,e)}async imagetoolsInspect(e){return L(this.state,e)}async loginEcr(e){return G(this.state,e)}async logoutEcr(){const e=this.state.ecrSession;this.state.ecrSession=void 0,e!==void 0&&await e.dispose()}async withEcrSession(e,r,n){const o=await this.loginEcr(e);if(!o.success)return _(n(o.error));try{return await r()}finally{await this.logoutEcr().catch(()=>{})}}async ensureBuilder(e){return N(this.state,e)}async assertBuildxAvailable(){return O(this.state)}async detectDaemon(){return P(this.state)}}function U(t){const e=b(t);return e.length>S?e.slice(0,S)+"\u2026":e}l(U,"maskAndBoundStderrTailLine");function V(t,e,r){let n,o=r;if(r!==void 0&&Array.isArray(r.stderrTail)&&r.stderrTail.every(a=>typeof a=="string")){n=r.stderrTail.map(U);const{stderrTail:a,...i}=r;o=Object.keys(i).length>0?i:void 0}return{kind:t,message:e,...n!==void 0&&{stderrTail:n},...o!==void 0&&{details:o}}}l(V,"makeError");function re(t){return V("daemon_unreachable",`Docker CLI is not available: ${t.spawnError??"unknown spawn failure"}`,{stderrTail:t.stderrTail})}l(re,"spawnFailureToError");function T(t,e){if(t==="")return[];const r=t.split(/\r?\n/);for(;r.length>0&&r[r.length-1]==="";)r.pop();return r.slice(-e)}l(T,"tailLines");function te(t){return b(t)}l(te,"maskOutput");function w(t){return t.ecrSession!==void 0?{...t.env,...t.ecrSession.env}:t.env}l(w,"spawnEnv");function ne(t,e){return new Promise(r=>{let n=!1,o=!1;const a=B(t.abortSignal,e.timeoutMs,e.abortSignal);let i;try{i=E(t.dockerBin,e.args,{shell:!1,env:w(t)})}catch(s){const d=s instanceof Error?s.message:String(s);r({exitCode:null,stdout:"",stderr:d,stderrTail:[d],aborted:!1,spawnError:d});return}let f="",c="";i.stdout?.on("data",s=>{f+=s.toString()}),i.stderr?.on("data",s=>{c+=s.toString()}),e.stdin!==void 0&&(i.stdin?.on("error",s=>{t.logger.debug(C,"stdin write error (typically EPIPE on early child exit)",{error:s.message})}),i.stdin?.write(e.stdin),i.stdin?.end());const m=l(()=>{n=!0,y(i)},"onAbort");a!==void 0&&(a.aborted?m():a.addEventListener("abort",m,{once:!0})),i.once("error",s=>{if(o)return;o=!0,a!==void 0&&a.removeEventListener("abort",m);const d=s instanceof Error?s.message:String(s);r({exitCode:null,stdout:f,stderr:d,stderrTail:[d],aborted:n,spawnError:d})}),i.once("close",s=>{o||(o=!0,a!==void 0&&a.removeEventListener("abort",m),r({exitCode:s,stdout:f,stderr:c,stderrTail:T(c,v),aborted:n}))})})}l(ne,"runDocker");function se(t,e,r,n){return new Promise(o=>{let a=!1,i=!1;const f=B(t.abortSignal,e.timeoutMs,e.abortSignal);let c;try{c=E(t.dockerBin,e.args,{shell:!1,env:w(t)})}catch(u){const g=u instanceof Error?u.message:String(u);o({exitCode:null,stderrTail:[g],aborted:!1,spawnError:g,child:null});return}let m="",s="",d="";c.stdout?.on("data",u=>{m+=u.toString();const g=m.split(/\r?\n/);m=g.pop()??"";for(const h of g)r(h)}),c.stderr?.on("data",u=>{const g=u.toString();if(s+=g,n!==void 0){d+=g;const h=d.split(/\r?\n/);d=h.pop()??"";for(const k of h)n(k)}});const p=l(()=>{a=!0,y(c)},"onAbort");f!==void 0&&(f.aborted?p():f.addEventListener("abort",p,{once:!0})),c.once("error",u=>{if(i)return;i=!0,f!==void 0&&f.removeEventListener("abort",p);const g=u instanceof Error?u.message:String(u);o({exitCode:null,stderrTail:[g],aborted:a,spawnError:g,child:c})}),c.once("close",u=>{i||(i=!0,m!==""&&r(m),n!==void 0&&d!==""&&n(d),f!==void 0&&f.removeEventListener("abort",p),o({exitCode:u,stderrTail:T(s,v),aborted:a,child:c}))})})}l(se,"streamDocker");function B(t,e,r){const n=[];if(t!==void 0&&n.push(t),r!==void 0&&n.push(r),e!==void 0&&n.push(AbortSignal.timeout(e)),n.length!==0)return n.length===1?n[0]:AbortSignal.any(n)}l(B,"composeAbortSignal");export{ee as DockerCli,_ as failure,V as makeError,te as maskOutput,ne as runDocker,re as spawnFailureToError,se as streamDocker,H as success,T as tailLines};
1
+ var D=Object.defineProperty;var f=(n,e)=>D(n,"name",{value:e,configurable:!0});import{spawn as y}from"node:child_process";import{filterDangerousEnvVars as C,maskSensitiveOutput as S}from"../securityHelpers.js";import{abortChildProcess as v}from"./abortHelpers.js";import{DEFAULT_DOCKER_BIN as I,DOCKER_CLI_LOG_CATEGORY as L,STDERR_TAIL_LINE_MAX_CHARS as _,STDERR_TAIL_LINES as B}from"./dockerCliConstants.js";import{_buildxBuild as O,_imagetoolsInspect as R,_tagByDigest as P}from"./DockerCli.build.js";import{_assertBuildxAvailable as N,_detectDaemon as G,_ensureBuilder as K}from"./DockerCli.daemon.js";import{_imageInspect as M,_loginEcr as j,_pull as F,_push as H,_tag as U}from"./DockerCli.registry.js";import{failure as T,success as V}from"./result.js";class te{static{f(this,"DockerCli")}state;constructor(e){const t=e.dockerBin!==void 0&&e.dockerBin!==""?e.dockerBin:I,r=C(e.env??process.env);this.state={logger:e.logger,dockerBin:t,env:r,abortSignal:e.abortSignal,ecrSession:void 0}}async buildxBuild(e,t,r){return O(this.state,e,t,r)}async tag(e,t){return U(this.state,e,t)}async tagByDigest(e,t,r){return P(this.state,e,t,r)}async push(e,t){return H(this.state,e,t)}async pull(e,t,r){return F(this.state,e,t,r)}async imageInspect(e){return M(this.state,e)}async imagetoolsInspect(e){return R(this.state,e)}async loginEcr(e){return j(this.state,e)}async logoutEcr(){const e=this.state.ecrSession;this.state.ecrSession=void 0,e!==void 0&&await e.dispose()}async withEcrSession(e,t,r){const i=await this.loginEcr(e);if(!i.success)return T(r(i.error));try{return await t()}finally{await this.logoutEcr().catch(()=>{})}}async ensureBuilder(e){return K(this.state,e)}async assertBuildxAvailable(){return N(this.state)}async detectDaemon(){return G(this.state)}}function X(n){const e=S(n);return e.length>_?e.slice(0,_)+"\u2026":e}f(X,"maskAndBoundStderrTailLine");function re(n,e,t){let r,i=t;if(t!==void 0&&Array.isArray(t.stderrTail)&&t.stderrTail.every(u=>typeof u=="string")){r=t.stderrTail.map(X);const{stderrTail:u,...a}=t;i=Object.keys(a).length>0?a:void 0}return{kind:n,message:e,...r!==void 0&&{stderrTail:r},...i!==void 0&&{details:i}}}f(re,"makeError");function w(n,e){if(n==="")return[];const t=n.split(/\r?\n/);for(;t.length>0&&t[t.length-1]==="";)t.pop();return t.slice(-e)}f(w,"tailLines");function ne(n){return S(n)}f(ne,"maskOutput");function A(n){return n.ecrSession!==void 0?{...n.env,...n.ecrSession.env}:n.env}f(A,"spawnEnv");function ie(n,e){return new Promise(t=>{let r=!1,i=!1,u=!1;const{signal:a,timeoutSignal:h}=k(n.abortSignal,e.timeoutMs,e.abortSignal);let o;try{o=y(n.dockerBin,e.args,{shell:!1,env:A(n)})}catch(s){const d=s instanceof Error?s.message:String(s);t({exitCode:null,stdout:"",stderr:d,stderrTail:[d],aborted:!1,timedOut:!1,spawnError:d});return}let p="",c="";o.stdout?.on("data",s=>{p+=s.toString()}),o.stderr?.on("data",s=>{c+=s.toString()}),e.stdin!==void 0&&(o.stdin?.on("error",s=>{n.logger.debug(L,"stdin write error (typically EPIPE on early child exit)",{error:s.message})}),o.stdin?.write(e.stdin),o.stdin?.end());const m=f(()=>{r=!0,i=h?.aborted===!0,v(o)},"onAbort");a!==void 0&&(a.aborted?m():a.addEventListener("abort",m,{once:!0})),o.once("error",s=>{if(u)return;u=!0,a!==void 0&&a.removeEventListener("abort",m);const d=s instanceof Error?s.message:String(s);t({exitCode:null,stdout:p,stderr:d,stderrTail:[d],aborted:r,timedOut:i,spawnError:d})}),o.once("close",s=>{u||(u=!0,a!==void 0&&a.removeEventListener("abort",m),t({exitCode:s,stdout:p,stderr:c,stderrTail:w(c,B),aborted:r,timedOut:i}))})})}f(ie,"runDocker");function se(n,e,t,r){return new Promise(i=>{let u=!1,a=!1,h=!1;const{signal:o,timeoutSignal:p}=k(n.abortSignal,e.timeoutMs,e.abortSignal);let c;try{c=y(n.dockerBin,e.args,{shell:!1,env:A(n)})}catch(l){const g=l instanceof Error?l.message:String(l);i({exitCode:null,stderrTail:[g],aborted:!1,timedOut:!1,spawnError:g,child:null});return}let m="",s="",d="";c.stdout?.on("data",l=>{m+=l.toString();const g=m.split(/\r?\n/);m=g.pop()??"";for(const b of g)t(b)}),c.stderr?.on("data",l=>{const g=l.toString();if(s+=g,r!==void 0){d+=g;const b=d.split(/\r?\n/);d=b.pop()??"";for(const x of b)r(x)}});const E=f(()=>{u=!0,a=p?.aborted===!0,v(c)},"onAbort");o!==void 0&&(o.aborted?E():o.addEventListener("abort",E,{once:!0})),c.once("error",l=>{if(h)return;h=!0,o!==void 0&&o.removeEventListener("abort",E);const g=l instanceof Error?l.message:String(l);i({exitCode:null,stderrTail:[g],aborted:u,timedOut:a,spawnError:g,child:c})}),c.once("close",l=>{h||(h=!0,m!==""&&t(m),r!==void 0&&d!==""&&r(d),o!==void 0&&o.removeEventListener("abort",E),i({exitCode:l,stderrTail:w(s,B),aborted:u,timedOut:a,child:c}))})})}f(se,"streamDocker");function k(n,e,t){const r=[];n!==void 0&&r.push(n),t!==void 0&&r.push(t);const i=e!==void 0?AbortSignal.timeout(e):void 0;return i!==void 0&&r.push(i),r.length===0?{signal:void 0,timeoutSignal:i}:r.length===1?{signal:r[0],timeoutSignal:i}:{signal:AbortSignal.any(r),timeoutSignal:i}}f(k,"composeAbortSignal");export{te as DockerCli,T as failure,re as makeError,ne as maskOutput,ie as runDocker,se as streamDocker,V as success,w as tailLines};
@@ -1,3 +1,3 @@
1
- var E=Object.defineProperty;var u=(i,e)=>E(i,"name",{value:e,configurable:!0});import{maskSensitiveOutput as x}from"../securityHelpers.js";import{DEFAULT_INSPECT_TIMEOUT_MS as g,DEFAULT_PULL_TIMEOUT_MS as w,DEFAULT_PUSH_TIMEOUT_MS as $,DOCKER_CLI_LOG_CATEGORY as C}from"./dockerCliConstants.js";import{createEcrAuthSession as k}from"./ecrCredentialStore.js";import{failure as s,makeError as a,maskOutput as _,runDocker as h,spawnFailureToError as p,streamDocker as m,success as l}from"./DockerCli.js";async function D(i,e,r){const t=await h(i,{args:["tag",e,r],timeoutMs:g});return t.spawnError!==void 0?s(p(t)):t.exitCode===null?s(a("abort",`docker tag ${e} ${r} was aborted`,{stderrTail:t.stderrTail})):t.exitCode!==0?s(a("tag_failed",`docker tag ${e} ${r} failed (exit ${t.exitCode})`,{stderrTail:t.stderrTail})):l(void 0)}u(D,"_tag");async function O(i,e,r){let t;const n=await m(i,{args:["push",e],timeoutMs:$},u(f=>{if(f==="")return;let d;try{d=JSON.parse(f)}catch{i.logger.debug(C,"Skipping malformed push progress line",{line:x(f)});return}if(d===null||typeof d!="object")return;const o=d;o.aux?.digest!==void 0&&(t=o.aux.digest),r!==void 0&&o.id!==void 0&&o.status!==void 0&&r({id:o.id,status:_(o.status),...o.progressDetail?.current!==void 0&&{current:o.progressDetail.current},...o.progressDetail?.total!==void 0&&{total:o.progressDetail.total}})},"onLine"));return n.spawnError!==void 0?s(p(n)):n.aborted||n.exitCode===null?s(a("abort",`docker push ${e} was aborted`,{stderrTail:n.stderrTail})):n.exitCode!==0?n.stderrTail.join(`
2
- `).toLowerCase().includes("unauthorized")?s(a("auth_failed",`docker push ${e} unauthorized`,{stderrTail:n.stderrTail})):s(a("push_failed",`docker push ${e} failed (exit ${n.exitCode})`,{stderrTail:n.stderrTail})):t===void 0?s(a("push_failed",`docker push ${e} succeeded but no digest was reported`,{stderrTail:n.stderrTail})):l({digest:t})}u(O,"_push");async function I(i,e,r,t){const c=["pull"];r!==void 0&&r!==""&&c.push("--platform",r),c.push(e);let n="";const d=await m(i,{args:c,timeoutMs:w},u(T=>{n+=`${T}
3
- `,!(t===void 0||T==="")&&t({id:e,status:_(T)})},"onLine"));if(d.spawnError!==void 0)return s(p(d));if(d.aborted||d.exitCode===null)return s(a("abort",`docker pull ${e} was aborted`,{stderrTail:d.stderrTail}));if(d.exitCode!==0)return s(a("pull_failed",`docker pull ${e} failed (exit ${d.exitCode})`,{stderrTail:d.stderrTail}));const o=/sha256:[0-9a-f]{64}/.exec(n);return l({imageId:o?.[0]??e})}u(I,"_pull");async function U(i,e){const r=await h(i,{args:["image","inspect","--format","{{json .}}",e],timeoutMs:g});if(r.spawnError!==void 0)return s(p(r));if(r.exitCode===null)return s(a("abort",`docker image inspect ${e} was aborted`,{stderrTail:r.stderrTail}));if(r.exitCode!==0)return r.stderr.includes("No such image:")?l({exists:!1}):s(a("inspect_failed",`docker image inspect ${e} failed (exit ${r.exitCode})`,{stderrTail:r.stderrTail}));const t=/"Id":"(sha256:[0-9a-f]{64})"/.exec(r.stdout);return l({exists:!0,...t?.[1]!==void 0&&{digest:t[1]}})}u(U,"_imageInspect");async function A(i,e){try{const r=await k({registry:e.registry,username:e.username,password:e.password,baseEnv:i.env,logger:i.logger}),t=i.ecrSession;return i.ecrSession=r,t!==void 0&&await t.dispose(),l(void 0)}catch(r){return s(a("auth_failed",`Failed to configure ECR credentials for ${e.registry}: ${x(r instanceof Error?r.message:String(r))}`))}}u(A,"_loginEcr");export{U as _imageInspect,A as _loginEcr,I as _pull,O as _push,D as _tag};
1
+ var C=Object.defineProperty;var u=(e,r)=>C(e,"name",{value:r,configurable:!0});import{maskSensitiveOutput as h}from"../securityHelpers.js";import{DEFAULT_INSPECT_TIMEOUT_MS as p,DEFAULT_PULL_TIMEOUT_MS as x,DEFAULT_PUSH_TIMEOUT_MS as E,DOCKER_CLI_LOG_CATEGORY as L}from"./dockerCliConstants.js";import{abortOrTimeoutError as g,makeLoggedError as a,spawnFailureToError as T,wasAborted as m}from"./dockerCliDiagnostics.js";import{createEcrAuthSession as M}from"./ecrCredentialStore.js";import{failure as n,makeError as S,maskOutput as $,runDocker as k,streamDocker as w,success as f}from"./DockerCli.js";async function A(e,r,i){const t=await k(e,{args:["tag",r,i],timeoutMs:p});return t.spawnError!==void 0?n(T(e,t)):m(t)?n(g(e,t,`docker tag ${r} ${i}`,p)):t.exitCode!==0?n(a(e,"tag_failed",`docker tag ${r} ${i} failed (exit ${t.exitCode})`,{stderrTail:t.stderrTail})):f(void 0)}u(A,"_tag");async function F(e,r,i){let t;const s=await w(e,{args:["push",r],timeoutMs:E},u(l=>{if(l==="")return;let d;try{d=JSON.parse(l)}catch{e.logger.debug(L,"Skipping malformed push progress line",{line:h(l)});return}if(d===null||typeof d!="object")return;const o=d;o.aux?.digest!==void 0&&(t=o.aux.digest),i!==void 0&&o.id!==void 0&&o.status!==void 0&&i({id:o.id,status:$(o.status),...o.progressDetail?.current!==void 0&&{current:o.progressDetail.current},...o.progressDetail?.total!==void 0&&{total:o.progressDetail.total}})},"onLine"));return s.spawnError!==void 0?n(T(e,s)):m(s)?n(g(e,s,`docker push ${r}`,E)):s.exitCode!==0?s.stderrTail.join(`
2
+ `).toLowerCase().includes("unauthorized")?n(a(e,"auth_failed",`docker push ${r} unauthorized`,{stderrTail:s.stderrTail})):n(a(e,"push_failed",`docker push ${r} failed (exit ${s.exitCode})`,{stderrTail:s.stderrTail})):t===void 0?n(a(e,"push_failed",`docker push ${r} succeeded but no digest was reported`,{stderrTail:s.stderrTail})):f({digest:t})}u(F,"_push");async function j(e,r,i,t){const c=["pull"];i!==void 0&&i!==""&&c.push("--platform",i),c.push(r);let s="";const d=await w(e,{args:c,timeoutMs:x},u(_=>{s+=`${_}
3
+ `,!(t===void 0||_==="")&&t({id:r,status:$(_)})},"onLine"));if(d.spawnError!==void 0)return n(T(e,d));if(m(d))return n(g(e,d,`docker pull ${r}`,x));if(d.exitCode!==0)return n(a(e,"pull_failed",`docker pull ${r} failed (exit ${d.exitCode})`,{stderrTail:d.stderrTail}));const o=/sha256:[0-9a-f]{64}/.exec(s);return f({imageId:o?.[0]??r})}u(j,"_pull");async function v(e,r){const i=await k(e,{args:["image","inspect","--format","{{json .}}",r],timeoutMs:p});if(i.spawnError!==void 0)return n(T(e,i));if(m(i))return n(g(e,i,`docker image inspect ${r}`,p));if(i.exitCode!==0)return i.stderr.includes("No such image:")?f({exists:!1}):n(a(e,"inspect_failed",`docker image inspect ${r} failed (exit ${i.exitCode})`,{stderrTail:i.stderrTail}));const t=/"Id":"(sha256:[0-9a-f]{64})"/.exec(i.stdout);return f({exists:!0,...t?.[1]!==void 0&&{digest:t[1]}})}u(v,"_imageInspect");async function J(e,r){try{const i=await M({registry:r.registry,username:r.username,password:r.password,baseEnv:e.env,logger:e.logger}),t=e.ecrSession;return e.ecrSession=i,t!==void 0&&await t.dispose(),f(void 0)}catch(i){return n(S("auth_failed",`Failed to configure ECR credentials for ${r.registry}: ${h(i instanceof Error?i.message:String(i))}`))}}u(J,"_loginEcr");export{v as _imageInspect,J as _loginEcr,j as _pull,F as _push,A as _tag};
@@ -1 +1 @@
1
- var s=Object.defineProperty;var e=(t,r)=>s(t,"name",{value:r,configurable:!0});import{spawnSync as i}from"node:child_process";import{SIGTERM_GRACE_MS as n}from"./dockerCliConstants.js";function u(t,r=process.platform){if(t.pid===void 0)return;if(t.stdout&&t.stdout.destroy(),t.stderr&&t.stderr.destroy(),r==="win32"){i("taskkill",["/T","/F","/PID",String(t.pid)],{shell:!1});return}t.kill("SIGTERM");const o=setTimeout(()=>{t.killed||t.kill("SIGKILL")},n);t.once("exit",()=>clearTimeout(o))}e(u,"abortChildProcess");export{u as abortChildProcess};
1
+ var i=Object.defineProperty;var o=(t,e)=>i(t,"name",{value:e,configurable:!0});import{spawnSync as f}from"node:child_process";import{SIGTERM_GRACE_MS as n}from"./dockerCliConstants.js";function p(t,e=process.platform){if(t.pid===void 0)return;if(t.stdout&&t.stdout.destroy(),t.stderr&&t.stderr.destroy(),e==="win32"){f("taskkill",["/T","/F","/PID",String(t.pid)],{shell:!1});return}t.kill("SIGTERM");let r=!1;const s=setTimeout(()=>{r||t.kill("SIGKILL")},n);t.once("exit",()=>{r=!0,clearTimeout(s)})}o(p,"abortChildProcess");export{p as abortChildProcess};
@@ -12,5 +12,7 @@ export declare const DEFAULT_BUILD_TIMEOUT_MS = 1800000;
12
12
  export declare const DEFAULT_PUSH_STALL_TIMEOUT_MS = 300000;
13
13
  export declare const DEFAULT_PUSH_TIMEOUT_MS = 300000;
14
14
  export declare const DEFAULT_PULL_TIMEOUT_MS = 300000;
15
+ export declare const DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS = 120000;
15
16
  export declare const DEFAULT_INSPECT_TIMEOUT_MS = 30000;
17
+ export declare const DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS = 30000;
16
18
  export declare const DEFAULT_DAEMON_PROBE_TIMEOUT_MS = 10000;
@@ -1 +1 @@
1
- const _="DockerCli",o="DockerCli.buildx",E="0.13.0",t="23.0.0",T=64,L="fjall",e="docker",I=5e3,r=50,O=2e3,U=18e5,D=3e5,c=3e5,s=3e5,x=3e4,A=1e4;export{E as BUILDX_VERSION_FLOOR,L as DEFAULT_BUILDER_NAME,U as DEFAULT_BUILD_TIMEOUT_MS,A as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,e as DEFAULT_DOCKER_BIN,x as DEFAULT_INSPECT_TIMEOUT_MS,s as DEFAULT_PULL_TIMEOUT_MS,D as DEFAULT_PUSH_STALL_TIMEOUT_MS,c as DEFAULT_PUSH_TIMEOUT_MS,o as DOCKER_CLI_BUILDX_LOG_CATEGORY,_ as DOCKER_CLI_LOG_CATEGORY,t as ENGINE_VERSION_FLOOR,T as PrerequisiteMissingExitCode,I as SIGTERM_GRACE_MS,r as STDERR_TAIL_LINES,O as STDERR_TAIL_LINE_MAX_CHARS};
1
+ const _="DockerCli",E="DockerCli.buildx",o="0.13.0",t="23.0.0",T=64,L="fjall",I="docker",U=5e3,e=50,r=2e3,D=18e5,M=3e5,O=3e5,S=3e5,c=12e4,s=3e4,A=3e4,R=1e4;export{o as BUILDX_VERSION_FLOOR,A as DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS,L as DEFAULT_BUILDER_NAME,D as DEFAULT_BUILD_TIMEOUT_MS,R as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,I as DEFAULT_DOCKER_BIN,s as DEFAULT_INSPECT_TIMEOUT_MS,S as DEFAULT_PULL_TIMEOUT_MS,M as DEFAULT_PUSH_STALL_TIMEOUT_MS,O as DEFAULT_PUSH_TIMEOUT_MS,c as DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS,E as DOCKER_CLI_BUILDX_LOG_CATEGORY,_ as DOCKER_CLI_LOG_CATEGORY,t as ENGINE_VERSION_FLOOR,T as PrerequisiteMissingExitCode,U as SIGTERM_GRACE_MS,e as STDERR_TAIL_LINES,r as STDERR_TAIL_LINE_MAX_CHARS};
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Failure construction for the `DockerCli` implementation files.
3
+ *
4
+ * Two things every docker failure needs and none of them used to get:
5
+ *
6
+ * 1. The stderr tail reaching a sink. Every failure branch already builds one
7
+ * and, until these helpers existed, every consumer read only `.message` —
8
+ * docker's own words were constructed, masked, and dropped.
9
+ * `makeLoggedError` mirrors the tail to the debug log at the point of
10
+ * failure so `~/.fjall/logs/` carries it into a support bundle.
11
+ * 2. An honest abort verdict. Docker traps SIGTERM and exits 130 well inside
12
+ * the SIGKILL grace, so an `exitCode === null` test alone misses the
13
+ * ordinary abort and the run falls through to "failed (exit 130)".
14
+ *
15
+ * Masking: `makeError` masks each tail line and only then bounds it
16
+ * (mask-before-truncate), so the tail is never re-masked here. It does NOT
17
+ * mask `message`, and `spawnFailureToError` folds an error body into one — so
18
+ * the debug payload masks it at that boundary. The consumer path is a separate
19
+ * output path with its own single mask in `describeDockerFailure`.
20
+ */
21
+ import type { DockerCliError, DockerCliErrorKind } from "./dockerCliSchemas.js";
22
+ import { type DockerCliState } from "./DockerCli.js";
23
+ /** The slice of a `runDocker`/`streamDocker` result these helpers read. */
24
+ export interface DockerRunOutcome {
25
+ readonly exitCode: number | null;
26
+ readonly aborted: boolean;
27
+ readonly timedOut: boolean;
28
+ readonly stderrTail: readonly string[];
29
+ }
30
+ export declare function makeLoggedError(state: DockerCliState, kind: DockerCliErrorKind, message: string, details?: Record<string, unknown>): DockerCliError;
31
+ export declare function spawnFailureToError(state: DockerCliState, result: {
32
+ spawnError?: string;
33
+ stderrTail: readonly string[];
34
+ }): DockerCliError;
35
+ /**
36
+ * A run ended via the composed abort signal. `exitCode === null` means the
37
+ * child died to a signal it did not trap (a bare SIGTERM, or the SIGKILL
38
+ * escalation after the grace); the ordinary path is docker trapping SIGTERM
39
+ * and exiting 130 on its own, which is why `aborted` has to be consulted.
40
+ */
41
+ export declare function wasAborted(result: {
42
+ aborted: boolean;
43
+ exitCode: number | null;
44
+ }): boolean;
45
+ /**
46
+ * A per-call budget expiry and a caller cancel fire the same composed signal,
47
+ * so `timedOut` is the only discriminator — without it a real timeout reports
48
+ * as `kind: "abort"`, which reads as "the user pressed ctrl-c".
49
+ */
50
+ export declare function abortOrTimeoutError(state: DockerCliState, result: DockerRunOutcome, operation: string, timeoutMs: number): DockerCliError;
@@ -0,0 +1 @@
1
+ var u=Object.defineProperty;var a=(r,e)=>u(r,"name",{value:e,configurable:!0});import{maskSensitiveOutput as m}from"../securityHelpers.js";import{DOCKER_CLI_LOG_CATEGORY as l}from"./dockerCliConstants.js";import{makeError as s}from"./DockerCli.js";function d(r,e,o,n){const t=s(e,o,n),i=t.stderrTail;return i!==void 0&&i.length>0&&r.logger.debug(l,"docker command failed",{kind:e,message:m(o),stderrTail:i}),t}a(d,"makeLoggedError");function b(r,e){return d(r,"daemon_unreachable",`Docker CLI is not available: ${e.spawnError??"unknown spawn failure"}`,{stderrTail:e.stderrTail})}a(b,"spawnFailureToError");function E(r){return r.aborted||r.exitCode===null}a(E,"wasAborted");function g(r,e,o,n){const t={stderrTail:e.stderrTail};return e.timedOut?d(r,"timeout",`${o} timed out after ${n}ms`,t):d(r,"abort",`${o} was aborted`,t)}a(g,"abortOrTimeoutError");export{g as abortOrTimeoutError,d as makeLoggedError,b as spawnFailureToError,E as wasAborted};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Consumer-side rendering of a `DockerCliError` for a human-readable sink
3
+ * (progress callback, deploy log, thrown `Error` message).
4
+ *
5
+ * The `DockerProvider` seam in deploy-core is typed over a plain `Error`, so
6
+ * the stderr tail cannot survive past the CLI/worker provider as structured
7
+ * data — folding it into the message here is the only way docker's own words
8
+ * reach the deploy log.
9
+ *
10
+ * Masking boundary: `error.message` is masked here, once. The tail is NOT —
11
+ * `makeError` masks each line and only then bounds it (mask-before-truncate),
12
+ * so a second pass would violate one-mask-per-output-path.
13
+ */
14
+ import type { DockerCliError } from "./dockerCliSchemas.js";
15
+ export declare const STDERR_TAIL_HEADER = "--- docker stderr tail ---";
16
+ export declare const STDERR_TAIL_FOOTER = "--- end ---";
17
+ export declare function describeDockerFailure(error: DockerCliError): string;
@@ -0,0 +1,5 @@
1
+ var s=Object.defineProperty;var r=(e,t)=>s(e,"name",{value:t,configurable:!0});import{maskSensitiveOutput as i}from"../securityHelpers.js";const o="--- docker stderr tail ---",a="--- end ---";function u(e){const t=i(e.message),n=e.stderrTail;return n===void 0||n.length===0?t:`${t}
2
+ ${o}
3
+ ${n.join(`
4
+ `)}
5
+ ${a}`}r(u,"describeDockerFailure");export{a as STDERR_TAIL_FOOTER,o as STDERR_TAIL_HEADER,u as describeDockerFailure};
@@ -1,5 +1,6 @@
1
1
  export { type Result, isSuccess, isFailure, success, failure } from "./result.js";
2
- export { DOCKER_CLI_LOG_CATEGORY, DOCKER_CLI_BUILDX_LOG_CATEGORY, BUILDX_VERSION_FLOOR, ENGINE_VERSION_FLOOR, PrerequisiteMissingExitCode, DEFAULT_BUILDER_NAME, DEFAULT_DOCKER_BIN, SIGTERM_GRACE_MS, STDERR_TAIL_LINES, DEFAULT_BUILD_TIMEOUT_MS, DEFAULT_PUSH_STALL_TIMEOUT_MS, DEFAULT_PUSH_TIMEOUT_MS, DEFAULT_PULL_TIMEOUT_MS, DEFAULT_INSPECT_TIMEOUT_MS, DEFAULT_DAEMON_PROBE_TIMEOUT_MS } from "./dockerCliConstants.js";
2
+ export { DOCKER_CLI_LOG_CATEGORY, DOCKER_CLI_BUILDX_LOG_CATEGORY, BUILDX_VERSION_FLOOR, ENGINE_VERSION_FLOOR, PrerequisiteMissingExitCode, DEFAULT_BUILDER_NAME, DEFAULT_DOCKER_BIN, SIGTERM_GRACE_MS, STDERR_TAIL_LINES, DEFAULT_BUILD_TIMEOUT_MS, DEFAULT_PUSH_STALL_TIMEOUT_MS, DEFAULT_PUSH_TIMEOUT_MS, DEFAULT_PULL_TIMEOUT_MS, DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS, DEFAULT_INSPECT_TIMEOUT_MS, DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS, DEFAULT_DAEMON_PROBE_TIMEOUT_MS } from "./dockerCliConstants.js";
3
+ export { STDERR_TAIL_HEADER, STDERR_TAIL_FOOTER, describeDockerFailure } from "./dockerErrorFormat.js";
3
4
  export { BuildxBuildArgsSchema, BuildxBuildResultSchema, DockerCliErrorKindSchema, DockerCliErrorSchema, isDockerCliErrorKind, type BuildxBuildArgs, type BuildxBuildResult, type DockerCliError, type DockerCliErrorKind } from "./dockerCliSchemas.js";
4
5
  export { buildxArgvBuilder } from "./buildxArgvBuilder.js";
5
6
  export { dockerPlatformForArchitecture, ECS_TASK_ARCHITECTURE } from "./architecture.js";
@@ -1 +1 @@
1
- import{isSuccess as _,isFailure as o,success as E,failure as i}from"./result.js";import{DOCKER_CLI_LOG_CATEGORY as t,DOCKER_CLI_BUILDX_LOG_CATEGORY as T,BUILDX_VERSION_FLOOR as s,ENGINE_VERSION_FLOOR as u,PrerequisiteMissingExitCode as a,DEFAULT_BUILDER_NAME as A,DEFAULT_DOCKER_BIN as L,SIGTERM_GRACE_MS as U,STDERR_TAIL_LINES as R,DEFAULT_BUILD_TIMEOUT_MS as d,DEFAULT_PUSH_STALL_TIMEOUT_MS as S,DEFAULT_PUSH_TIMEOUT_MS as D,DEFAULT_PULL_TIMEOUT_MS as I,DEFAULT_INSPECT_TIMEOUT_MS as c,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as B}from"./dockerCliConstants.js";import{BuildxBuildArgsSchema as O,BuildxBuildResultSchema as M,DockerCliErrorKindSchema as m,DockerCliErrorSchema as x,isDockerCliErrorKind as p}from"./dockerCliSchemas.js";import{buildxArgvBuilder as N}from"./buildxArgvBuilder.js";import{dockerPlatformForArchitecture as g,ECS_TASK_ARCHITECTURE as n}from"./architecture.js";import{BUILD_TIMEOUT_ENV_VAR as h,PUSH_STALL_TIMEOUT_ENV_VAR as G,BuildxBuildMonitor as V,buildPhaseTimeoutMessage as K,publishStallTimeoutMessage as b,resolveBuildxBudgets as k}from"./buildxBuildMonitor.js";import{evaluateBakeGuard as H,evaluateResolvedBuildArgValues as y,acknowledgedBuildArgKeys as X,createBakeWarningDeduper as j,BAKE_GUARD_EXPOSURE_CLAUSE as w}from"./bakeGuard.js";import{PUBLIC_BUILD_ARG_PREFIXES as q,isPublicBuildVarName as W,inferPublicBuildArgKeys as z}from"./buildArgInference.js";import{parseRawjsonLine as Q}from"./rawjsonParser.js";import{rawjsonToVertexEvent as $}from"./rawjsonToVertexEvent.js";import{parseMetadataFile as re}from"./metadataFileParser.js";import{projectBuildxResult as oe}from"./projectBuildxResult.js";import{abortChildProcess as ie}from"./abortHelpers.js";import{DockerCli as te}from"./DockerCli.js";import{createEcrAuthSession as se}from"./ecrCredentialStore.js";import{buildCacheRepositoryName as ae,buildRegistryCacheRefs as Ae,resolveBuildCacheMode as Le,untaggedLifecyclePolicyText as Ue,BUILD_CACHE_MODE_ENV_VAR as Re,BUILD_CACHE_MODES as de,CACHE_REPO_UNTAGGED_RETENTION_DAYS as Se}from"./cacheRepository.js";export{w as BAKE_GUARD_EXPOSURE_CLAUSE,s as BUILDX_VERSION_FLOOR,de as BUILD_CACHE_MODES,Re as BUILD_CACHE_MODE_ENV_VAR,h as BUILD_TIMEOUT_ENV_VAR,O as BuildxBuildArgsSchema,V as BuildxBuildMonitor,M as BuildxBuildResultSchema,Se as CACHE_REPO_UNTAGGED_RETENTION_DAYS,A as DEFAULT_BUILDER_NAME,d as DEFAULT_BUILD_TIMEOUT_MS,B as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,L as DEFAULT_DOCKER_BIN,c as DEFAULT_INSPECT_TIMEOUT_MS,I as DEFAULT_PULL_TIMEOUT_MS,S as DEFAULT_PUSH_STALL_TIMEOUT_MS,D as DEFAULT_PUSH_TIMEOUT_MS,T as DOCKER_CLI_BUILDX_LOG_CATEGORY,t as DOCKER_CLI_LOG_CATEGORY,te as DockerCli,m as DockerCliErrorKindSchema,x as DockerCliErrorSchema,n as ECS_TASK_ARCHITECTURE,u as ENGINE_VERSION_FLOOR,q as PUBLIC_BUILD_ARG_PREFIXES,G as PUSH_STALL_TIMEOUT_ENV_VAR,a as PrerequisiteMissingExitCode,U as SIGTERM_GRACE_MS,R as STDERR_TAIL_LINES,ie as abortChildProcess,X as acknowledgedBuildArgKeys,ae as buildCacheRepositoryName,K as buildPhaseTimeoutMessage,Ae as buildRegistryCacheRefs,N as buildxArgvBuilder,j as createBakeWarningDeduper,se as createEcrAuthSession,g as dockerPlatformForArchitecture,H as evaluateBakeGuard,y as evaluateResolvedBuildArgValues,i as failure,z as inferPublicBuildArgKeys,p as isDockerCliErrorKind,o as isFailure,W as isPublicBuildVarName,_ as isSuccess,re as parseMetadataFile,Q as parseRawjsonLine,oe as projectBuildxResult,b as publishStallTimeoutMessage,$ as rawjsonToVertexEvent,Le as resolveBuildCacheMode,k as resolveBuildxBudgets,E as success,Ue as untaggedLifecyclePolicyText};
1
+ import{isSuccess as _,isFailure as E,success as o,failure as T}from"./result.js";import{DOCKER_CLI_LOG_CATEGORY as l,DOCKER_CLI_BUILDX_LOG_CATEGORY as t,BUILDX_VERSION_FLOOR as A,ENGINE_VERSION_FLOOR as R,PrerequisiteMissingExitCode as L,DEFAULT_BUILDER_NAME as s,DEFAULT_DOCKER_BIN as u,SIGTERM_GRACE_MS as a,STDERR_TAIL_LINES as U,DEFAULT_BUILD_TIMEOUT_MS as D,DEFAULT_PUSH_STALL_TIMEOUT_MS as I,DEFAULT_PUSH_TIMEOUT_MS as S,DEFAULT_PULL_TIMEOUT_MS as d,DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS as O,DEFAULT_INSPECT_TIMEOUT_MS as c,DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS as C,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as M}from"./dockerCliConstants.js";import{STDERR_TAIL_HEADER as m,STDERR_TAIL_FOOTER as x,describeDockerFailure as p}from"./dockerErrorFormat.js";import{BuildxBuildArgsSchema as F,BuildxBuildResultSchema as N,DockerCliErrorKindSchema as P,DockerCliErrorSchema as g,isDockerCliErrorKind as n}from"./dockerCliSchemas.js";import{buildxArgvBuilder as h}from"./buildxArgvBuilder.js";import{dockerPlatformForArchitecture as b,ECS_TASK_ARCHITECTURE as k}from"./architecture.js";import{BUILD_TIMEOUT_ENV_VAR as H,PUSH_STALL_TIMEOUT_ENV_VAR as v,BuildxBuildMonitor as y,buildPhaseTimeoutMessage as Y,publishStallTimeoutMessage as X,resolveBuildxBudgets as j}from"./buildxBuildMonitor.js";import{evaluateBakeGuard as q,evaluateResolvedBuildArgValues as W,acknowledgedBuildArgKeys as z,createBakeWarningDeduper as J,BAKE_GUARD_EXPOSURE_CLAUSE as Q}from"./bakeGuard.js";import{PUBLIC_BUILD_ARG_PREFIXES as $,isPublicBuildVarName as ee,inferPublicBuildArgKeys as re}from"./buildArgInference.js";import{parseRawjsonLine as Ee}from"./rawjsonParser.js";import{rawjsonToVertexEvent as Te}from"./rawjsonToVertexEvent.js";import{parseMetadataFile as le}from"./metadataFileParser.js";import{projectBuildxResult as Ae}from"./projectBuildxResult.js";import{abortChildProcess as Le}from"./abortHelpers.js";import{DockerCli as ue}from"./DockerCli.js";import{createEcrAuthSession as Ue}from"./ecrCredentialStore.js";import{buildCacheRepositoryName as Ie,buildRegistryCacheRefs as Se,resolveBuildCacheMode as de,untaggedLifecyclePolicyText as Oe,BUILD_CACHE_MODE_ENV_VAR as ce,BUILD_CACHE_MODES as Ce,CACHE_REPO_UNTAGGED_RETENTION_DAYS as Me}from"./cacheRepository.js";export{Q as BAKE_GUARD_EXPOSURE_CLAUSE,A as BUILDX_VERSION_FLOOR,Ce as BUILD_CACHE_MODES,ce as BUILD_CACHE_MODE_ENV_VAR,H as BUILD_TIMEOUT_ENV_VAR,F as BuildxBuildArgsSchema,y as BuildxBuildMonitor,N as BuildxBuildResultSchema,Me as CACHE_REPO_UNTAGGED_RETENTION_DAYS,C as DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS,s as DEFAULT_BUILDER_NAME,D as DEFAULT_BUILD_TIMEOUT_MS,M as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,u as DEFAULT_DOCKER_BIN,c as DEFAULT_INSPECT_TIMEOUT_MS,d as DEFAULT_PULL_TIMEOUT_MS,I as DEFAULT_PUSH_STALL_TIMEOUT_MS,S as DEFAULT_PUSH_TIMEOUT_MS,O as DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS,t as DOCKER_CLI_BUILDX_LOG_CATEGORY,l as DOCKER_CLI_LOG_CATEGORY,ue as DockerCli,P as DockerCliErrorKindSchema,g as DockerCliErrorSchema,k as ECS_TASK_ARCHITECTURE,R as ENGINE_VERSION_FLOOR,$ as PUBLIC_BUILD_ARG_PREFIXES,v as PUSH_STALL_TIMEOUT_ENV_VAR,L as PrerequisiteMissingExitCode,a as SIGTERM_GRACE_MS,x as STDERR_TAIL_FOOTER,m as STDERR_TAIL_HEADER,U as STDERR_TAIL_LINES,Le as abortChildProcess,z as acknowledgedBuildArgKeys,Ie as buildCacheRepositoryName,Y as buildPhaseTimeoutMessage,Se as buildRegistryCacheRefs,h as buildxArgvBuilder,J as createBakeWarningDeduper,Ue as createEcrAuthSession,p as describeDockerFailure,b as dockerPlatformForArchitecture,q as evaluateBakeGuard,W as evaluateResolvedBuildArgValues,T as failure,re as inferPublicBuildArgKeys,n as isDockerCliErrorKind,E as isFailure,ee as isPublicBuildVarName,_ as isSuccess,le as parseMetadataFile,Ee as parseRawjsonLine,Ae as projectBuildxResult,X as publishStallTimeoutMessage,Te as rawjsonToVertexEvent,de as resolveBuildCacheMode,j as resolveBuildxBudgets,o as success,Oe as untaggedLifecyclePolicyText};
@@ -75,6 +75,20 @@ export declare const ACCOUNT_ROLES: {
75
75
  };
76
76
  /** Type guard: checks whether a string is a valid account tier. */
77
77
  export declare function isAccountTier(value: string): value is AccountTier;
78
+ /**
79
+ * Tiers whose deploys are governance operations (design
80
+ * 2026-07-23-ci-plugin-complete-surface.md §4.1): machine (CI deploy-token)
81
+ * principals must carry the `deploy:governance` grant to record a deployment
82
+ * for — or mint OIDC credentials naming — an account of these tiers.
83
+ *
84
+ * Derived by EXCLUDING the workload tier rather than enumerating governance
85
+ * tiers — a deliberate inversion of the enumerate-don't-subtract rule: this
86
+ * feeds deny gates, so a future tier added to [[ACCOUNT_TIERS]] defaults to
87
+ * "governance grant required" (fail closed) instead of silently un-gated.
88
+ */
89
+ export declare const GOVERNANCE_ACCOUNT_TIERS: readonly AccountTier[];
90
+ /** Whether an account tier's deploys are governance operations. */
91
+ export declare function isGovernanceTier(tier: string): boolean;
78
92
  /**
79
93
  * Decode an inbound wire `environment` to its structural TIER. The wire stays
80
94
  * superset-tolerant: out-of-version CLIs and the post-`fjall create org`
@@ -1 +1 @@
1
- var i=Object.defineProperty;var o=(t,T)=>i(t,"name",{value:T,configurable:!0});import{z as u}from"zod";const e=["production","staging","development","platform","compliance"],O={production:"Production",staging:"Staging",development:"Development",platform:"Platform",compliance:"Compliance"},a=new Set(e);function c(t){return a.has(t)}o(c,"isAccountStage");const r={ROOT:"root",PLATFORM:"platform"},R=[...e,r.ROOT];function E(t){return c(t)?O[t]:t.charAt(0).toUpperCase()+t.slice(1)}o(E,"getEnvironmentLabel");const p=["organisation","platform","account"],f=u.enum(p),n={ORGANISATION:"organisation",PLATFORM:"platform",ACCOUNT:"account"},A={[n.ORGANISATION]:!0,[n.PLATFORM]:!0,[n.ACCOUNT]:!0},C=new Set(Object.keys(A));function N(t){return C.has(t)}o(N,"isAccountTier");function s(t){return t===r.ROOT?"organisation":t===r.PLATFORM?"platform":"account"}o(s,"environmentToTier");function m(t){return t==null||t===""||t===r.ROOT?null:c(t)?t:null}o(m,"stageFromWireEnvironment");function _(t){return t.tier??s(t.environment)}o(_,"accountTier");export{n as ACCOUNT_ROLES,e as ACCOUNT_STAGES,R as ACCOUNT_STAGES_WITH_ROOT,O as ACCOUNT_STAGE_LABELS,p as ACCOUNT_TIERS,f as AccountTierSchema,r as STRUCTURAL_ENVIRONMENTS,_ as accountTier,s as environmentToTier,E as getEnvironmentLabel,c as isAccountStage,N as isAccountTier,m as stageFromWireEnvironment};
1
+ var O=Object.defineProperty;var o=(t,i)=>O(t,"name",{value:i,configurable:!0});import{z as u}from"zod";const e=["production","staging","development","platform","compliance"],a={production:"Production",staging:"Staging",development:"Development",platform:"Platform",compliance:"Compliance"},p=new Set(e);function T(t){return p.has(t)}o(T,"isAccountStage");const r={ROOT:"root",PLATFORM:"platform"},l=[...e,r.ROOT];function f(t){return T(t)?a[t]:t.charAt(0).toUpperCase()+t.slice(1)}o(f,"getEnvironmentLabel");const c=["organisation","platform","account"],_=u.enum(c),n={ORGANISATION:"organisation",PLATFORM:"platform",ACCOUNT:"account"},A={[n.ORGANISATION]:!0,[n.PLATFORM]:!0,[n.ACCOUNT]:!0},C=new Set(Object.keys(A));function m(t){return C.has(t)}o(m,"isAccountTier");const s=c.filter(t=>t!==n.ACCOUNT),E=new Set(s);function x(t){return E.has(t)}o(x,"isGovernanceTier");function N(t){return t===r.ROOT?"organisation":t===r.PLATFORM?"platform":"account"}o(N,"environmentToTier");function U(t){return t==null||t===""||t===r.ROOT?null:T(t)?t:null}o(U,"stageFromWireEnvironment");function g(t){return t.tier??N(t.environment)}o(g,"accountTier");export{n as ACCOUNT_ROLES,e as ACCOUNT_STAGES,l as ACCOUNT_STAGES_WITH_ROOT,a as ACCOUNT_STAGE_LABELS,c as ACCOUNT_TIERS,_ as AccountTierSchema,s as GOVERNANCE_ACCOUNT_TIERS,r as STRUCTURAL_ENVIRONMENTS,g as accountTier,N as environmentToTier,f as getEnvironmentLabel,T as isAccountStage,m as isAccountTier,x as isGovernanceTier,U as stageFromWireEnvironment};
package/dist/index.d.ts CHANGED
@@ -10,11 +10,11 @@ export { singleton } from "./async/singleton.js";
10
10
  export { DANGEROUS_ENV_VARS, filterDangerousEnvVars, maskSensitiveOutput, parseShellArgs, SCOPED_TOKEN_REGEX } from "./securityHelpers.js";
11
11
  export { sleep } from "./async/sleep.js";
12
12
  export { mapSettledWithConcurrency } from "./async/concurrency.js";
13
- export { ACCOUNT_STAGES_WITH_ROOT, STRUCTURAL_ENVIRONMENTS, ACCOUNT_STAGES, ACCOUNT_STAGE_LABELS, isAccountStage, ACCOUNT_TIERS, type AccountTier, AccountTierSchema, isAccountTier, environmentToTier, stageFromWireEnvironment, accountTier, type AccountStageWithRoot, type AccountStage, getEnvironmentLabel, ACCOUNT_ROLES } from "./environments.js";
13
+ export { ACCOUNT_STAGES_WITH_ROOT, STRUCTURAL_ENVIRONMENTS, ACCOUNT_STAGES, ACCOUNT_STAGE_LABELS, isAccountStage, ACCOUNT_TIERS, type AccountTier, AccountTierSchema, isAccountTier, GOVERNANCE_ACCOUNT_TIERS, isGovernanceTier, environmentToTier, stageFromWireEnvironment, accountTier, type AccountStageWithRoot, type AccountStage, getEnvironmentLabel, ACCOUNT_ROLES } from "./environments.js";
14
14
  export { RESOURCE_CATEGORIES, type ResourceCategory, categoriseResource, getExpectedDuration, getFriendlyResourceType } from "./resourceCategorisation.js";
15
15
  export { parseGitRemoteUrl, type GitProvider, type ParsedGitRemote } from "./repo/gitRemoteParser.js";
16
16
  export { abbreviateRegion, AWS_REGIONS_METADATA, DEFAULT_REGION, getRegionInfo, MAX_SECONDARY_REGIONS, OPT_IN_REGION_CODES, optInRegionWarning, regions, suggestRegionForTimezone, type RegionCode, type RegionInfo } from "./infra/regions.js";
17
- export { SCOPE_VALUES, MACHINE_ONLY_SCOPES, USER_GRANTABLE_SCOPES, type TokenScope } from "./infra/tokenScopes.js";
17
+ export { SCOPE_VALUES, MACHINE_ONLY_SCOPES, USER_GRANTABLE_SCOPES, GOVERNANCE_SCOPE_REQUIRED_CODE, type TokenScope } from "./infra/tokenScopes.js";
18
18
  export { SECRET_NAME_PATTERN, SECRET_NAME_ERROR, SSM_COMPONENT_PATTERN, SSM_COMPONENT_ERROR, SSM_STANDARD_MAX_VALUE_BYTES, SecretNamespaceSchema, type SecretNamespace, buildNamespaceParts, buildParameterPath, parseParameterPath, isManageablePath, parseDotEnv, escapeDotEnvValue } from "./secrets.js";
19
19
  export { ConnectionWireSchema, type ConnectionWire, ConnectionsListResponseSchema, type ConnectionsListResponse } from "./infra/connectionsWire.js";
20
20
  export { deriveRegionsFromOrgConfig, deriveTargets, deriveAllTargets, environmentOrTier, findTarget, generateTargetName, type OrgConfigRegions, type TargetAccount, type DerivedTarget } from "./targets.js";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{DNS_APEX as E,getDomainExportNames as o,getDomainStackName as t,getDomainUsEast1CertificatesStackName as a,isManagedDomainBinding as _,DOMAIN_DEPLOY_DEFAULT_PHASE as S}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as T}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as n,TOKEN_STDERR_PREFIX as i}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as m}from"./infra/imageTags.js";import{toPascalCase as O,toKebab as P,toValidDatabaseName as C,toScreamingSnake as p,capitalise as f,getSafeZoneName as c,accountConstructKey as g,hasAsciiStableConstructKey as I}from"./naming/caseConversion.js";import{findAccountNameCollision as M}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as u,suffixedAccountName as d,REGION_SHORT_CODES as U,findTrailingRegionShortCode as L,regionSuffixRejectionMessage as l}from"./naming/connectedAccountName.js";import{normaliseError as F,getErrorMessage as h,hasErrorCode as G,getErrorCode as v,getErrorStack as H,formatErrorString as b}from"./errorUtils.js";import{singleton as Y}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as B,filterDangerousEnvVars as K,maskSensitiveOutput as W,parseShellArgs as k,SCOPED_TOKEN_REGEX as j}from"./securityHelpers.js";import{sleep as Z}from"./async/sleep.js";import{mapSettledWithConcurrency as w}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as Q,STRUCTURAL_ENVIRONMENTS as $,ACCOUNT_STAGES as ee,ACCOUNT_STAGE_LABELS as re,isAccountStage as Ee,ACCOUNT_TIERS as oe,AccountTierSchema as te,isAccountTier as ae,environmentToTier as _e,stageFromWireEnvironment as Se,accountTier as Ae,getEnvironmentLabel as Te,ACCOUNT_ROLES as Re}from"./environments.js";import{RESOURCE_CATEGORIES as ie,categoriseResource as Ne,getExpectedDuration as me,getFriendlyResourceType as se}from"./resourceCategorisation.js";import{parseGitRemoteUrl as Pe}from"./repo/gitRemoteParser.js";import{abbreviateRegion as pe,AWS_REGIONS_METADATA as fe,DEFAULT_REGION as ce,getRegionInfo as ge,MAX_SECONDARY_REGIONS as Ie,OPT_IN_REGION_CODES as De,optInRegionWarning as Me,regions as xe,suggestRegionForTimezone as ue}from"./infra/regions.js";import{SCOPE_VALUES as Ue,MACHINE_ONLY_SCOPES as Le,USER_GRANTABLE_SCOPES as le}from"./infra/tokenScopes.js";import{SECRET_NAME_PATTERN as Fe,SECRET_NAME_ERROR as he,SSM_COMPONENT_PATTERN as Ge,SSM_COMPONENT_ERROR as ve,SSM_STANDARD_MAX_VALUE_BYTES as He,SecretNamespaceSchema as be,buildNamespaceParts as Xe,buildParameterPath as Ye,parseParameterPath as ye,isManageablePath as Be,parseDotEnv as Ke,escapeDotEnvValue as We}from"./secrets.js";import{ConnectionWireSchema as je,ConnectionsListResponseSchema as ze}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as qe,deriveTargets as we,deriveAllTargets as Je,environmentOrTier as Qe,findTarget as $e,generateTargetName as er}from"./targets.js";import{buildAppConfigPath as Er}from"./repo/appPath.js";import{findInfrastructurePaths as tr,findBoundaryPath as ar,isInfrastructureFile as _r}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as Ar}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as Rr,RESERVED_APP_NAME_MESSAGE as nr,isReservedAppName as ir,RESERVED_APP_NAME_SUFFIX as Nr,RESERVED_APP_NAME_SUFFIX_MESSAGE as mr,hasReservedAppNameSuffix as sr}from"./naming/reservedAppNames.js";import{deriveContentHashTag as Pr,CONTENT_HASH_TAG_PATTERN as Cr}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as fr,DeployModeSchema as cr,IMAGE_TAG_PATTERN as gr,ImageTagSchema as Ir,ServiceArtefactSchema as Dr,ServiceArtefactsSchema as Mr,ARTEFACT_OUTPUT_FIELDS as xr,artefactOutputKey as ur}from"./infra/deployArtefacts.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as Ur,EXPECTED_SCHEMA_VERSION_ENV as Lr,EXPECTED_SCHEMA_VERSION_TOOL_ENV as lr,EXPECTED_CH_SCHEMA_VERSION_ENV as Vr,SCHEMA_ADMIN_USER_ENV as Fr,SCHEMA_ADMIN_PASSWORD_ENV as hr,PRISMA_MIGRATION_DIR_RE as Gr,CLICKHOUSE_MIGRATION_SKIP_RE as vr}from"./migration/constants.js";import{PHYSICAL_NAME_FALLBACK_PROPERTIES as br}from"./cfn/physicalNameProperties.js";import{PATTERN_TYPE_VALUES as Yr,PATTERN_TYPES as yr,isPatternType as Br,PATTERN_REGISTRY as Kr,DEPLOYABLE_PATTERN_TYPES as Wr,OPENNEXT_PATTERN_TYPES as kr,isOpenNextPatternType as jr,STATIC_SITE_ROUTING_VALUES as zr}from"./patterns/patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as qr,defaultFormsFromAddress as wr,defaultFormsCorsOrigin as Jr,isAddressAtDomain as Qr}from"./patterns/staticSiteForms.js";import{DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION as eE,DEV_SUBSTRATE_PARAMS_FILENAME as rE,DevSubstrateSynthPropsSchema as EE,DevSubstrateParamsSchema as oE}from"./devSubstrate/params.js";import{DEV_SUBSTRATE_ENTRYPOINT_SOURCE as aE}from"./devSubstrate/entrypoint.js";export{Re as ACCOUNT_ROLES,ee as ACCOUNT_STAGES,Q as ACCOUNT_STAGES_WITH_ROOT,re as ACCOUNT_STAGE_LABELS,oe as ACCOUNT_TIERS,n as APPROVAL_TOKEN_OUTPUT_PREFIX,xr as ARTEFACT_OUTPUT_FIELDS,fe as AWS_REGIONS_METADATA,te as AccountTierSchema,T as BACKUP_VAULT_NAME,vr as CLICKHOUSE_MIGRATION_SKIP_RE,Cr as CONTENT_HASH_TAG_PATTERN,je as ConnectionWireSchema,ze as ConnectionsListResponseSchema,B as DANGEROUS_ENV_VARS,qr as DEFAULT_FORMS_FROM_LOCAL_PART,ce as DEFAULT_REGION,Wr as DEPLOYABLE_PATTERN_TYPES,fr as DEPLOY_MODES,aE as DEV_SUBSTRATE_ENTRYPOINT_SOURCE,rE as DEV_SUBSTRATE_PARAMS_FILENAME,eE as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,E as DNS_APEX,S as DOMAIN_DEPLOY_DEFAULT_PHASE,cr as DeployModeSchema,oE as DevSubstrateParamsSchema,EE as DevSubstrateSynthPropsSchema,Vr as EXPECTED_CH_SCHEMA_VERSION_ENV,Lr as EXPECTED_SCHEMA_VERSION_ENV,lr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,gr as IMAGE_TAG_PATTERN,Ir as ImageTagSchema,Le as MACHINE_ONLY_SCOPES,Ie as MAX_SECONDARY_REGIONS,Ur as MIGRATION_SNAPSHOT_NAME_PREFIX,kr as OPENNEXT_PATTERN_TYPES,De as OPT_IN_REGION_CODES,Kr as PATTERN_REGISTRY,yr as PATTERN_TYPES,Yr as PATTERN_TYPE_VALUES,br as PHYSICAL_NAME_FALLBACK_PROPERTIES,Gr as PRISMA_MIGRATION_DIR_RE,U as REGION_SHORT_CODES,Rr as RESERVED_APP_NAMES,nr as RESERVED_APP_NAME_MESSAGE,Nr as RESERVED_APP_NAME_SUFFIX,mr as RESERVED_APP_NAME_SUFFIX_MESSAGE,ie as RESOURCE_CATEGORIES,hr as SCHEMA_ADMIN_PASSWORD_ENV,Fr as SCHEMA_ADMIN_USER_ENV,j as SCOPED_TOKEN_REGEX,Ue as SCOPE_VALUES,he as SECRET_NAME_ERROR,Fe as SECRET_NAME_PATTERN,ve as SSM_COMPONENT_ERROR,Ge as SSM_COMPONENT_PATTERN,He as SSM_STANDARD_MAX_VALUE_BYTES,zr as STATIC_SITE_ROUTING_VALUES,$ as STRUCTURAL_ENVIRONMENTS,be as SecretNamespaceSchema,Dr as ServiceArtefactSchema,Mr as ServiceArtefactsSchema,i as TOKEN_STDERR_PREFIX,le as USER_GRANTABLE_SCOPES,pe as abbreviateRegion,g as accountConstructKey,Ae as accountTier,ur as artefactOutputKey,Er as buildAppConfigPath,Xe as buildNamespaceParts,Ye as buildParameterPath,f as capitalise,Ne as categoriseResource,u as defaultConnectedAccountName,Jr as defaultFormsCorsOrigin,wr as defaultFormsFromAddress,Je as deriveAllTargets,Pr as deriveContentHashTag,qe as deriveRegionsFromOrgConfig,we as deriveTargets,Qe as environmentOrTier,_e as environmentToTier,We as escapeDotEnvValue,K as filterDangerousEnvVars,M as findAccountNameCollision,ar as findBoundaryPath,tr as findInfrastructurePaths,$e as findTarget,L as findTrailingRegionShortCode,b as formatErrorString,er as generateTargetName,o as getDomainExportNames,t as getDomainStackName,a as getDomainUsEast1CertificatesStackName,Te as getEnvironmentLabel,v as getErrorCode,h as getErrorMessage,H as getErrorStack,me as getExpectedDuration,se as getFriendlyResourceType,ge as getRegionInfo,c as getSafeZoneName,I as hasAsciiStableConstructKey,G as hasErrorCode,sr as hasReservedAppNameSuffix,m as imageTagParameterName,Ar as inferContainerFromCandidates,Ee as isAccountStage,ae as isAccountTier,Qr as isAddressAtDomain,_r as isInfrastructureFile,Be as isManageablePath,_ as isManagedDomainBinding,jr as isOpenNextPatternType,Br as isPatternType,ir as isReservedAppName,w as mapSettledWithConcurrency,W as maskSensitiveOutput,F as normaliseError,Me as optInRegionWarning,Ke as parseDotEnv,Pe as parseGitRemoteUrl,ye as parseParameterPath,k as parseShellArgs,l as regionSuffixRejectionMessage,xe as regions,Y as singleton,Z as sleep,Se as stageFromWireEnvironment,d as suffixedAccountName,ue as suggestRegionForTimezone,P as toKebab,O as toPascalCase,p as toScreamingSnake,C as toValidDatabaseName};
1
+ import{DNS_APEX as E,getDomainExportNames as o,getDomainStackName as t,getDomainUsEast1CertificatesStackName as _,isManagedDomainBinding as a,DOMAIN_DEPLOY_DEFAULT_PHASE as S}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as T}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as n,TOKEN_STDERR_PREFIX as i}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as m}from"./infra/imageTags.js";import{toPascalCase as O,toKebab as P,toValidDatabaseName as C,toScreamingSnake as p,capitalise as f,getSafeZoneName as c,accountConstructKey as g,hasAsciiStableConstructKey as I}from"./naming/caseConversion.js";import{findAccountNameCollision as M}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as u,suffixedAccountName as d,REGION_SHORT_CODES as U,findTrailingRegionShortCode as L,regionSuffixRejectionMessage as V}from"./naming/connectedAccountName.js";import{normaliseError as G,getErrorMessage as F,hasErrorCode as h,getErrorCode as v,getErrorStack as H,formatErrorString as b}from"./errorUtils.js";import{singleton as Y}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as B,filterDangerousEnvVars as K,maskSensitiveOutput as W,parseShellArgs as k,SCOPED_TOKEN_REGEX as j}from"./securityHelpers.js";import{sleep as Q}from"./async/sleep.js";import{mapSettledWithConcurrency as q}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as J,STRUCTURAL_ENVIRONMENTS as $,ACCOUNT_STAGES as ee,ACCOUNT_STAGE_LABELS as re,isAccountStage as Ee,ACCOUNT_TIERS as oe,AccountTierSchema as te,isAccountTier as _e,GOVERNANCE_ACCOUNT_TIERS as ae,isGovernanceTier as Se,environmentToTier as Ae,stageFromWireEnvironment as Te,accountTier as Re,getEnvironmentLabel as ne,ACCOUNT_ROLES as ie}from"./environments.js";import{RESOURCE_CATEGORIES as me,categoriseResource as se,getExpectedDuration as Oe,getFriendlyResourceType as Pe}from"./resourceCategorisation.js";import{parseGitRemoteUrl as pe}from"./repo/gitRemoteParser.js";import{abbreviateRegion as ce,AWS_REGIONS_METADATA as ge,DEFAULT_REGION as Ie,getRegionInfo as De,MAX_SECONDARY_REGIONS as Me,OPT_IN_REGION_CODES as xe,optInRegionWarning as ue,regions as de,suggestRegionForTimezone as Ue}from"./infra/regions.js";import{SCOPE_VALUES as Ve,MACHINE_ONLY_SCOPES as le,USER_GRANTABLE_SCOPES as Ge,GOVERNANCE_SCOPE_REQUIRED_CODE as Fe}from"./infra/tokenScopes.js";import{SECRET_NAME_PATTERN as ve,SECRET_NAME_ERROR as He,SSM_COMPONENT_PATTERN as be,SSM_COMPONENT_ERROR as Xe,SSM_STANDARD_MAX_VALUE_BYTES as Ye,SecretNamespaceSchema as ye,buildNamespaceParts as Be,buildParameterPath as Ke,parseParameterPath as We,isManageablePath as ke,parseDotEnv as je,escapeDotEnvValue as ze}from"./secrets.js";import{ConnectionWireSchema as Ze,ConnectionsListResponseSchema as qe}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as Je,deriveTargets as $e,deriveAllTargets as er,environmentOrTier as rr,findTarget as Er,generateTargetName as or}from"./targets.js";import{buildAppConfigPath as _r}from"./repo/appPath.js";import{findInfrastructurePaths as Sr,findBoundaryPath as Ar,isInfrastructureFile as Tr}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as nr}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as Nr,RESERVED_APP_NAME_MESSAGE as mr,isReservedAppName as sr,RESERVED_APP_NAME_SUFFIX as Or,RESERVED_APP_NAME_SUFFIX_MESSAGE as Pr,hasReservedAppNameSuffix as Cr}from"./naming/reservedAppNames.js";import{deriveContentHashTag as fr,CONTENT_HASH_TAG_PATTERN as cr}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as Ir,DeployModeSchema as Dr,IMAGE_TAG_PATTERN as Mr,ImageTagSchema as xr,ServiceArtefactSchema as ur,ServiceArtefactsSchema as dr,ARTEFACT_OUTPUT_FIELDS as Ur,artefactOutputKey as Lr}from"./infra/deployArtefacts.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as lr,EXPECTED_SCHEMA_VERSION_ENV as Gr,EXPECTED_SCHEMA_VERSION_TOOL_ENV as Fr,EXPECTED_CH_SCHEMA_VERSION_ENV as hr,SCHEMA_ADMIN_USER_ENV as vr,SCHEMA_ADMIN_PASSWORD_ENV as Hr,PRISMA_MIGRATION_DIR_RE as br,CLICKHOUSE_MIGRATION_SKIP_RE as Xr}from"./migration/constants.js";import{PHYSICAL_NAME_FALLBACK_PROPERTIES as yr}from"./cfn/physicalNameProperties.js";import{PATTERN_TYPE_VALUES as Kr,PATTERN_TYPES as Wr,isPatternType as kr,PATTERN_REGISTRY as jr,DEPLOYABLE_PATTERN_TYPES as zr,OPENNEXT_PATTERN_TYPES as Qr,isOpenNextPatternType as Zr,STATIC_SITE_ROUTING_VALUES as qr}from"./patterns/patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as Jr,defaultFormsFromAddress as $r,defaultFormsCorsOrigin as eE,isAddressAtDomain as rE}from"./patterns/staticSiteForms.js";import{DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION as oE,DEV_SUBSTRATE_PARAMS_FILENAME as tE,DevSubstrateSynthPropsSchema as _E,DevSubstrateParamsSchema as aE}from"./devSubstrate/params.js";import{DEV_SUBSTRATE_ENTRYPOINT_SOURCE as AE}from"./devSubstrate/entrypoint.js";export{ie as ACCOUNT_ROLES,ee as ACCOUNT_STAGES,J as ACCOUNT_STAGES_WITH_ROOT,re as ACCOUNT_STAGE_LABELS,oe as ACCOUNT_TIERS,n as APPROVAL_TOKEN_OUTPUT_PREFIX,Ur as ARTEFACT_OUTPUT_FIELDS,ge as AWS_REGIONS_METADATA,te as AccountTierSchema,T as BACKUP_VAULT_NAME,Xr as CLICKHOUSE_MIGRATION_SKIP_RE,cr as CONTENT_HASH_TAG_PATTERN,Ze as ConnectionWireSchema,qe as ConnectionsListResponseSchema,B as DANGEROUS_ENV_VARS,Jr as DEFAULT_FORMS_FROM_LOCAL_PART,Ie as DEFAULT_REGION,zr as DEPLOYABLE_PATTERN_TYPES,Ir as DEPLOY_MODES,AE as DEV_SUBSTRATE_ENTRYPOINT_SOURCE,tE as DEV_SUBSTRATE_PARAMS_FILENAME,oE as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,E as DNS_APEX,S as DOMAIN_DEPLOY_DEFAULT_PHASE,Dr as DeployModeSchema,aE as DevSubstrateParamsSchema,_E as DevSubstrateSynthPropsSchema,hr as EXPECTED_CH_SCHEMA_VERSION_ENV,Gr as EXPECTED_SCHEMA_VERSION_ENV,Fr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,ae as GOVERNANCE_ACCOUNT_TIERS,Fe as GOVERNANCE_SCOPE_REQUIRED_CODE,Mr as IMAGE_TAG_PATTERN,xr as ImageTagSchema,le as MACHINE_ONLY_SCOPES,Me as MAX_SECONDARY_REGIONS,lr as MIGRATION_SNAPSHOT_NAME_PREFIX,Qr as OPENNEXT_PATTERN_TYPES,xe as OPT_IN_REGION_CODES,jr as PATTERN_REGISTRY,Wr as PATTERN_TYPES,Kr as PATTERN_TYPE_VALUES,yr as PHYSICAL_NAME_FALLBACK_PROPERTIES,br as PRISMA_MIGRATION_DIR_RE,U as REGION_SHORT_CODES,Nr as RESERVED_APP_NAMES,mr as RESERVED_APP_NAME_MESSAGE,Or as RESERVED_APP_NAME_SUFFIX,Pr as RESERVED_APP_NAME_SUFFIX_MESSAGE,me as RESOURCE_CATEGORIES,Hr as SCHEMA_ADMIN_PASSWORD_ENV,vr as SCHEMA_ADMIN_USER_ENV,j as SCOPED_TOKEN_REGEX,Ve as SCOPE_VALUES,He as SECRET_NAME_ERROR,ve as SECRET_NAME_PATTERN,Xe as SSM_COMPONENT_ERROR,be as SSM_COMPONENT_PATTERN,Ye as SSM_STANDARD_MAX_VALUE_BYTES,qr as STATIC_SITE_ROUTING_VALUES,$ as STRUCTURAL_ENVIRONMENTS,ye as SecretNamespaceSchema,ur as ServiceArtefactSchema,dr as ServiceArtefactsSchema,i as TOKEN_STDERR_PREFIX,Ge as USER_GRANTABLE_SCOPES,ce as abbreviateRegion,g as accountConstructKey,Re as accountTier,Lr as artefactOutputKey,_r as buildAppConfigPath,Be as buildNamespaceParts,Ke as buildParameterPath,f as capitalise,se as categoriseResource,u as defaultConnectedAccountName,eE as defaultFormsCorsOrigin,$r as defaultFormsFromAddress,er as deriveAllTargets,fr as deriveContentHashTag,Je as deriveRegionsFromOrgConfig,$e as deriveTargets,rr as environmentOrTier,Ae as environmentToTier,ze as escapeDotEnvValue,K as filterDangerousEnvVars,M as findAccountNameCollision,Ar as findBoundaryPath,Sr as findInfrastructurePaths,Er as findTarget,L as findTrailingRegionShortCode,b as formatErrorString,or as generateTargetName,o as getDomainExportNames,t as getDomainStackName,_ as getDomainUsEast1CertificatesStackName,ne as getEnvironmentLabel,v as getErrorCode,F as getErrorMessage,H as getErrorStack,Oe as getExpectedDuration,Pe as getFriendlyResourceType,De as getRegionInfo,c as getSafeZoneName,I as hasAsciiStableConstructKey,h as hasErrorCode,Cr as hasReservedAppNameSuffix,m as imageTagParameterName,nr as inferContainerFromCandidates,Ee as isAccountStage,_e as isAccountTier,rE as isAddressAtDomain,Se as isGovernanceTier,Tr as isInfrastructureFile,ke as isManageablePath,a as isManagedDomainBinding,Zr as isOpenNextPatternType,kr as isPatternType,sr as isReservedAppName,q as mapSettledWithConcurrency,W as maskSensitiveOutput,G as normaliseError,ue as optInRegionWarning,je as parseDotEnv,pe as parseGitRemoteUrl,We as parseParameterPath,k as parseShellArgs,V as regionSuffixRejectionMessage,de as regions,Y as singleton,Q as sleep,Te as stageFromWireEnvironment,d as suffixedAccountName,Ue as suggestRegionForTimezone,P as toKebab,O as toPascalCase,p as toScreamingSnake,C as toValidDatabaseName};
@@ -7,9 +7,19 @@
7
7
  * a scope the CLI did not know how to request, or vice versa. Both
8
8
  * consumers now import `SCOPE_VALUES` / `TokenScope` from here.
9
9
  */
10
- export declare const SCOPE_VALUES: readonly ["read", "write", "deploy", "secrets:read", "secrets:write", "destroy", "admin", "applications:read", "applications:deploy", "deploy:oidc:mint"];
10
+ export declare const SCOPE_VALUES: readonly ["read", "write", "deploy", "secrets:read", "secrets:write", "destroy", "admin", "applications:read", "applications:deploy", "deploy:oidc:mint", "deploy:governance"];
11
11
  export type TokenScope = (typeof SCOPE_VALUES)[number];
12
12
  /** Scopes only a machine principal may hold — never a user-minted token. */
13
13
  export declare const MACHINE_ONLY_SCOPES: readonly TokenScope[];
14
+ /**
15
+ * Machine-readable `code` on the webapp's 403 when a machine (dk) principal
16
+ * attempts a governance-tier operation without the `deploy:governance` scope
17
+ * — set by BOTH the deployment-record API and the OIDC mint endpoint.
18
+ * Coupled value across repos: the CLI's `DeploymentTracker.recordStart` maps
19
+ * a 403 carrying THIS code to a blocking outcome (exempt from
20
+ * `--force-untracked`); a 403 without it stays a best-effort untracked
21
+ * fallthrough. Shared here so the producer and consumer cannot drift.
22
+ */
23
+ export declare const GOVERNANCE_SCOPE_REQUIRED_CODE = "governance_scope_required";
14
24
  /** Scopes a user `*` grant expands to (excludes `admin` + machine-only). */
15
25
  export declare const USER_GRANTABLE_SCOPES: readonly TokenScope[];
@@ -1 +1 @@
1
- const r=["read","write","deploy","secrets:read","secrets:write","destroy","admin","applications:read","applications:deploy","deploy:oidc:mint"],s={read:"user",write:"user",deploy:"user","secrets:read":"user","secrets:write":"user",destroy:"user",admin:"admin","applications:read":"user","applications:deploy":"user","deploy:oidc:mint":"machine"},t=r.filter(e=>s[e]==="machine"),i=r.filter(e=>s[e]==="user");export{t as MACHINE_ONLY_SCOPES,r as SCOPE_VALUES,i as USER_GRANTABLE_SCOPES};
1
+ const r=["read","write","deploy","secrets:read","secrets:write","destroy","admin","applications:read","applications:deploy","deploy:oidc:mint","deploy:governance"],o={read:"user",write:"user",deploy:"user","secrets:read":"user","secrets:write":"user",destroy:"user",admin:"admin","applications:read":"user","applications:deploy":"user","deploy:oidc:mint":"machine","deploy:governance":"machine"},s=r.filter(e=>o[e]==="machine"),t="governance_scope_required",i=r.filter(e=>o[e]==="user");export{t as GOVERNANCE_SCOPE_REQUIRED_CODE,s as MACHINE_ONLY_SCOPES,r as SCOPE_VALUES,i as USER_GRANTABLE_SCOPES};
@@ -21,6 +21,15 @@
21
21
  * org-tier deploy — bypassing the app-scoped session policy and injecting org
22
22
  * identity into synth.
23
23
  *
24
+ * `"domain"` is reserved because BOTH verb-first surfaces reject the word
25
+ * permanently: `fjall deploy domain` and `fjall destroy domain` are teaching
26
+ * errors routing to the noun-verb domain commands, and domain infrastructure
27
+ * lives under the `fjall/domains/<zone>/` marker tree. An application named
28
+ * "domain" would therefore be undeployable and undestroyable from the moment
29
+ * it was created, so create time is the honest place to fail closed. Residue:
30
+ * an application created under an older CLI can still carry the name on disk;
31
+ * this list closes the door, it does not rename what is already there.
32
+ *
24
33
  * **Boundary with `isReservedSlug`** — `webapp/app/.server/utils/reservedSlugs.ts`
25
34
  * guards organisation slugs (URL-scoped, webapp-only). This list is
26
35
  * application-scoped and ships from `@fjall/util` for cross-system reuse
@@ -30,7 +39,7 @@
30
39
  * before testing, so `"Fjall"`, `"FJALL"`, and `"fjALL"` all reject
31
40
  * identically.
32
41
  */
33
- export declare const RESERVED_APP_NAMES: readonly ["fjall", "organisation", "platform", "account"];
42
+ export declare const RESERVED_APP_NAMES: readonly ["fjall", "organisation", "platform", "account", "domain"];
34
43
  export type ReservedAppName = (typeof RESERVED_APP_NAMES)[number];
35
44
  /**
36
45
  * Canonical user-facing rejection message for a reserved application name.
@@ -1 +1 @@
1
- var t=Object.defineProperty;var r=(e,o)=>t(e,"name",{value:o,configurable:!0});import{toKebab as n}from"./caseConversion.js";const a=["fjall","organisation","platform","account"],c="This application name is reserved for Fjall's organisation-tier infrastructure.";function p(e){return a.includes(e.toLowerCase())}r(p,"isReservedAppName");const i="-cache",A="Application names ending in '-cache' are reserved for Fjall build-cache repositories.";function S(e){return n(e).endsWith(i)}r(S,"hasReservedAppNameSuffix");export{a as RESERVED_APP_NAMES,c as RESERVED_APP_NAME_MESSAGE,i as RESERVED_APP_NAME_SUFFIX,A as RESERVED_APP_NAME_SUFFIX_MESSAGE,S as hasReservedAppNameSuffix,p as isReservedAppName};
1
+ var t=Object.defineProperty;var r=(e,o)=>t(e,"name",{value:o,configurable:!0});import{toKebab as n}from"./caseConversion.js";const a=["fjall","organisation","platform","account","domain"],c="This application name is reserved for Fjall's organisation-tier infrastructure.";function p(e){return a.includes(e.toLowerCase())}r(p,"isReservedAppName");const i="-cache",A="Application names ending in '-cache' are reserved for Fjall build-cache repositories.";function S(e){return n(e).endsWith(i)}r(S,"hasReservedAppNameSuffix");export{a as RESERVED_APP_NAMES,c as RESERVED_APP_NAME_MESSAGE,i as RESERVED_APP_NAME_SUFFIX,A as RESERVED_APP_NAME_SUFFIX_MESSAGE,S as hasReservedAppNameSuffix,p as isReservedAppName};
@@ -1 +1 @@
1
- var C=Object.defineProperty;var o=(t,r)=>C(t,"name",{value:r,configurable:!0});import{readdir as D,readFile as _,stat as j}from"fs/promises";import{join as a}from"path";import{ROOT_CONFIG_FILENAME as E}from"../config.js";import{getErrorMessage as T}from"../errorUtils.js";import{logger as y}from"../logger.js";import{maskSensitiveOutput as g}from"../securityHelpers.js";import{INFRASTRUCTURE_FILE as x,MARKER_DIRECTORY as s}from"./findInfrastructurePaths.js";import{EXCLUDED_DIRECTORY_NAMES as I}from"./scanLocalRepository.js";const O=3,S=2e3;async function q(t,r={}){const e=r.maxDepth??O,n=r.maxDirectories??S,i=[];let c=[t],l=0;for(let m=0;m<e&&c.length>0;m++){const d=[];for(const A of c){const R=await f(A);for(const w of R){if(l>=n)return i;l++;const h=await v(w);h?i.push(h):d.push(w)}}c=d}return i}o(q,"findProjectsBelow");async function G(t){return(await u(a(t,s))).length>0}o(G,"hasFjallAppLayout");async function v(t){const r=a(t,s,E);if(await p(r))return F(t,r,a(t,s));const e=a(t,E);return await p(e)?F(t,e,t):null}o(v,"inspectForProject");async function F(t,r,e){return{projectDir:t,configPath:r,activeTarget:await b(r),apps:await L(t,e)}}o(F,"buildProject");async function L(t,r){const e=new Set(await u(r));for(const n of await f(t))if(n!==r)for(const i of await u(a(n,s)))e.add(i);return[...e].sort()}o(L,"listProjectApps");async function b(t){try{const r=await _(t,"utf8"),e=JSON.parse(r);if(e!==null&&typeof e=="object"&&"activeTarget"in e){const n=e.activeTarget;if(typeof n=="string"&&n!=="")return n}}catch(r){y.debug("FindProjectsBelow",`Could not read activeTarget from ${t}`,{error:g(T(r))})}}o(b,"readActiveTarget");async function u(t){const r=[];for(const e of await f(t))await p(a(e,x))&&r.push(e.slice(t.length+1));return r}o(u,"listApps");async function f(t){let r;try{r=await D(t,{withFileTypes:!0})}catch(e){return y.debug("FindProjectsBelow",`Could not read directory ${t}`,{error:g(T(e))}),[]}return r.filter(e=>!e.isSymbolicLink()&&e.isDirectory()&&!e.name.startsWith(".")&&!I.has(e.name)).map(e=>e.name).sort().map(e=>a(t,e))}o(f,"listSubdirectories");function p(t){return j(t).then(r=>r.isFile(),()=>!1)}o(p,"fileExists");export{q as findProjectsBelow,G as hasFjallAppLayout};
1
+ var _=Object.defineProperty;var o=(t,r)=>_(t,"name",{value:r,configurable:!0});import{readdir as R,readFile as I,stat as L}from"fs/promises";import{dirname as O,join as i}from"path";import{LOCAL_CONFIG_DIRNAME as D,LOCAL_CONFIG_FILENAME as j,ROOT_CONFIG_FILENAME as E}from"../config.js";import{getErrorMessage as A}from"../errorUtils.js";import{logger as F}from"../logger.js";import{maskSensitiveOutput as T}from"../securityHelpers.js";import{INFRASTRUCTURE_FILE as N,MARKER_DIRECTORY as s}from"./findInfrastructurePaths.js";import{EXCLUDED_DIRECTORY_NAMES as x}from"./scanLocalRepository.js";const M=3,S=2e3;async function J(t,r={}){const e=r.maxDepth??M,n=r.maxDirectories??S,a=[];let c=[t],l=0;for(let m=0;m<e&&c.length>0;m++){const d=[];for(const y of c){const C=await f(y);for(const h of C){if(l>=n)return a;l++;const w=await v(h);w?a.push(w):d.push(h)}}c=d}return a}o(J,"findProjectsBelow");async function K(t){return(await u(i(t,s))).length>0}o(K,"hasFjallAppLayout");async function v(t){const r=i(t,s,E);if(await p(r))return g(t,r,i(t,s));const e=i(t,E);return await p(e)?g(t,e,t):null}o(v,"inspectForProject");async function g(t,r,e){return{projectDir:t,configPath:r,activeTarget:await b(r),apps:await P(t,e)}}o(g,"buildProject");async function P(t,r){const e=new Set(await u(r));for(const n of await f(t))if(n!==r)for(const a of await u(i(n,s)))e.add(a);return[...e].sort()}o(P,"listProjectApps");async function b(t){const r=i(O(t),D,j);try{const e=await I(r,"utf8"),n=JSON.parse(e);if(n!==null&&typeof n=="object"&&"activeTarget"in n){const a=n.activeTarget;if(typeof a=="string"&&a!=="")return a}}catch(e){F.debug("FindProjectsBelow",`Could not read activeTarget from ${r}`,{error:T(A(e))})}}o(b,"readActiveTarget");async function u(t){const r=[];for(const e of await f(t))await p(i(e,N))&&r.push(e.slice(t.length+1));return r}o(u,"listApps");async function f(t){let r;try{r=await R(t,{withFileTypes:!0})}catch(e){return F.debug("FindProjectsBelow",`Could not read directory ${t}`,{error:T(A(e))}),[]}return r.filter(e=>!e.isSymbolicLink()&&e.isDirectory()&&!e.name.startsWith(".")&&!x.has(e.name)).map(e=>e.name).sort().map(e=>i(t,e))}o(f,"listSubdirectories");function p(t){return L(t).then(r=>r.isFile(),()=>!1)}o(p,"fileExists");export{J as findProjectsBelow,K as hasFjallAppLayout};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/util",
3
- "version": "3.11.0",
3
+ "version": "4.1.0",
4
4
  "description": "Common utility methods",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -134,5 +134,5 @@
134
134
  "engines": {
135
135
  "node": ">=22.0.0"
136
136
  },
137
- "gitHead": "a15125ea026d52428ff5433d098699899695ca3f"
137
+ "gitHead": "09dee795753839b82aedbb7d0cef1c5ef283df55"
138
138
  }