@fjall/util 3.10.0 → 4.0.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
- 89 files minified at 2026-07-22T07:34:05.207Z
1
+ 92 files minified at 2026-07-24T06:41:48.349Z
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};
@@ -22,21 +22,35 @@ import { type BuildxBuildArgs, type BuildxBuildResult, type DockerCliError } fro
22
22
  import { type BuildxProgressEvent, type DockerCliState, type ImagetoolsInspect, type Result } from "./DockerCli.js";
23
23
  export declare function _buildxBuild(state: DockerCliState, args: BuildxBuildArgs, onProgress: (event: BuildxProgressEvent) => void, abortSignal?: AbortSignal): Promise<Result<BuildxBuildResult, DockerCliError>>;
24
24
  /**
25
- * Create one or more manifest-list tags pointing at an existing image
26
- * digest without re-uploading layers.
25
+ * Create one or more tags pointing at an existing image digest without
26
+ * re-uploading layers.
27
27
  *
28
- * Implementation: `docker buildx imagetools create --tag <tag1> --tag
29
- * <tag2> ... <sourceImage>@<digest>`. The source `image@digest` reference
30
- * pins the immutable content; each `--tag` argument creates (or moves) a
31
- * mutable name pointing at that digest. The registry stores no new blobs,
32
- * only a new manifest reference per tag.
28
+ * Implementation: `docker buildx imagetools create --prefer-index=false
29
+ * --tag <tag1> --tag <tag2> ... <sourceImage>@<digest>`. The source
30
+ * `image@digest` reference pins the immutable content; each `--tag`
31
+ * argument creates (or moves) a mutable name pointing at that digest. The
32
+ * registry stores no new blobs, only a new manifest reference per tag.
33
+ *
34
+ * `--prefer-index=false` is required: `imagetools create` defaults to
35
+ * wrapping even a SINGLE source manifest in a new image index (a "fat
36
+ * manifest") rather than carbon-copying it, on the assumption that its
37
+ * caller might be assembling a multi-arch tag from several
38
+ * platform-specific sources. fjall never does that — every call here
39
+ * re-tags exactly one already-built (single-platform) digest — so the
40
+ * index wrapper is pure overhead, and worse: AWS Lambda's
41
+ * CreateFunction/UpdateFunctionCode rejects an image index outright, so a
42
+ * Lambda pulling by this tag would fail even though the digest-addressed
43
+ * push (buildAndPush) produced a flat manifest. `--prefer-index=false`
44
+ * makes `imagetools create` copy the source manifest byte-for-byte under
45
+ * the new tag instead.
33
46
  *
34
47
  * Discipline:
35
48
  * - argv-only spawn (`shell: false` via `runDocker`)
36
49
  * - tag values are validated to be non-empty strings before they enter argv
37
50
  * - stderr is masked and bounded by `makeError(... stderrTail)`
38
- * - timeout: re-uses `DEFAULT_INSPECT_TIMEOUT_MS`; imagetools create is a
39
- * 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.
40
54
  */
41
55
  export declare function _tagByDigest(state: DockerCliState, sourceImage: string, digest: string, tags: readonly string[]): Promise<Result<void, DockerCliError>>;
42
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",...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};
@@ -97,9 +97,10 @@ export declare class DockerCli {
97
97
  buildxBuild(args: BuildxBuildArgs, onProgress: (event: BuildxProgressEvent) => void, abortSignal?: AbortSignal): Promise<Result<BuildxBuildResult, DockerCliError>>;
98
98
  tag(source: string, target: string): Promise<Result<void, DockerCliError>>;
99
99
  /**
100
- * Create one or more manifest-list tags pointing at a previously pushed
101
- * digest without re-uploading layers. Uses `docker buildx imagetools
102
- * create` against `<sourceImage>@<digest>`.
100
+ * Create one or more tags pointing at a previously pushed digest without
101
+ * re-uploading layers. Uses `docker buildx imagetools create
102
+ * --prefer-index=false` (a flat carbon-copy, not an image index — see
103
+ * `_tagByDigest`) against `<sourceImage>@<digest>`.
103
104
  */
104
105
  tagByDigest(sourceImage: string, digest: string, tags: readonly string[]): Promise<Result<void, DockerCliError>>;
105
106
  push(image: string, onProgress?: (event: PushProgressEvent) => void): Promise<Result<PushResult, DockerCliError>>;
@@ -154,22 +155,26 @@ export interface SpawnDockerResult {
154
155
  readonly stderr: string;
155
156
  readonly stderrTail: readonly string[];
156
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;
157
171
  readonly spawnError?: string;
172
+ readonly child: ChildProcess | null;
158
173
  }
159
174
  export declare function makeError(kind: DockerCliErrorKind, message: string, details?: Record<string, unknown>): DockerCliError;
160
- export declare function spawnFailureToError(result: {
161
- spawnError?: string;
162
- stderrTail: readonly string[];
163
- }): DockerCliError;
164
175
  export declare function tailLines(buffer: string, count: number): string[];
165
176
  export declare function maskOutput(value: string): string;
166
177
  export declare function runDocker(state: DockerCliState, opts: SpawnDockerOptions): Promise<SpawnDockerResult>;
167
- export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<{
168
- exitCode: number | null;
169
- stderrTail: readonly string[];
170
- aborted: boolean;
171
- spawnError?: string;
172
- child: ChildProcess | null;
173
- }>;
178
+ export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<StreamDockerResult>;
174
179
  export { failure, success };
175
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};
@@ -0,0 +1,12 @@
1
+ import type { LambdaArchitecture } from "../manifest/schemas.js";
2
+ export declare function dockerPlatformForArchitecture(architecture: LambdaArchitecture): string;
3
+ /**
4
+ * The CPU architecture every fjall ECS task runs on. Coupled to the construct
5
+ * pin `runtimePlatform.cpuArchitecture: CpuArchitecture.ARM64` in
6
+ * `components/infrastructure/lib/resources/aws/compute/ecsTaskDefinition.ts`
7
+ * (both task-definition call sites) — ECS manifest entries carry no
8
+ * `architecture` field, so build orchestrators consume this constant to
9
+ * enumerate the implicit constraint when an ECS service shares a build group
10
+ * with an architecture-declaring Lambda.
11
+ */
12
+ export declare const ECS_TASK_ARCHITECTURE: LambdaArchitecture;
@@ -0,0 +1 @@
1
+ var n=Object.defineProperty;var t=(r,o)=>n(r,"name",{value:o,configurable:!0});const A={arm64:"linux/arm64",x86_64:"linux/amd64"};function _(r){return A[r]}t(_,"dockerPlatformForArchitecture");const c="arm64";export{c as ECS_TASK_ARCHITECTURE,_ as dockerPlatformForArchitecture};
@@ -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,7 +1,9 @@
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";
6
+ export { dockerPlatformForArchitecture, ECS_TASK_ARCHITECTURE } from "./architecture.js";
5
7
  export { BUILD_TIMEOUT_ENV_VAR, PUSH_STALL_TIMEOUT_ENV_VAR, BuildxBuildMonitor, buildPhaseTimeoutMessage, publishStallTimeoutMessage, resolveBuildxBudgets, type BuildxBudgets, type BuildxTimeoutPhase, type SyntheticBuildxProgress } from "./buildxBuildMonitor.js";
6
8
  export { evaluateBakeGuard, evaluateResolvedBuildArgValues, acknowledgedBuildArgKeys, createBakeWarningDeduper, BAKE_GUARD_EXPOSURE_CLAUSE, type BakeGuardFinding, type BakeGuardFindingReason, type BakeGuardWarning, type BakeGuardResult } from "./bakeGuard.js";
7
9
  export { PUBLIC_BUILD_ARG_PREFIXES, isPublicBuildVarName, inferPublicBuildArgKeys, type InferPublicBuildArgKeysInput } from "./buildArgInference.js";
@@ -1 +1 @@
1
- import{isSuccess as _,isFailure as E,success as o,failure as i}from"./result.js";import{DOCKER_CLI_LOG_CATEGORY as s,DOCKER_CLI_BUILDX_LOG_CATEGORY as t,BUILDX_VERSION_FLOOR as T,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 d,DEFAULT_BUILD_TIMEOUT_MS as D,DEFAULT_PUSH_STALL_TIMEOUT_MS as R,DEFAULT_PUSH_TIMEOUT_MS as S,DEFAULT_PULL_TIMEOUT_MS as B,DEFAULT_INSPECT_TIMEOUT_MS as I,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as O}from"./dockerCliConstants.js";import{BuildxBuildArgsSchema as C,BuildxBuildResultSchema as M,DockerCliErrorKindSchema as x,DockerCliErrorSchema as m,isDockerCliErrorKind as p}from"./dockerCliSchemas.js";import{buildxArgvBuilder as N}from"./buildxArgvBuilder.js";import{BUILD_TIMEOUT_ENV_VAR as g,PUSH_STALL_TIMEOUT_ENV_VAR as n,BuildxBuildMonitor as F,buildPhaseTimeoutMessage as G,publishStallTimeoutMessage as h,resolveBuildxBudgets as V}from"./buildxBuildMonitor.js";import{evaluateBakeGuard as K,evaluateResolvedBuildArgValues as k,acknowledgedBuildArgKeys as v,createBakeWarningDeduper as y,BAKE_GUARD_EXPOSURE_CLAUSE as H}from"./bakeGuard.js";import{PUBLIC_BUILD_ARG_PREFIXES as j,isPublicBuildVarName as w,inferPublicBuildArgKeys as Y}from"./buildArgInference.js";import{parseRawjsonLine as W}from"./rawjsonParser.js";import{rawjsonToVertexEvent as J}from"./rawjsonToVertexEvent.js";import{parseMetadataFile as Z}from"./metadataFileParser.js";import{projectBuildxResult as ee}from"./projectBuildxResult.js";import{abortChildProcess as _e}from"./abortHelpers.js";import{DockerCli as oe}from"./DockerCli.js";import{createEcrAuthSession as le}from"./ecrCredentialStore.js";import{buildCacheRepositoryName as te,buildRegistryCacheRefs as Te,resolveBuildCacheMode as ue,untaggedLifecyclePolicyText as ae,BUILD_CACHE_MODE_ENV_VAR as Ae,BUILD_CACHE_MODES as Le,CACHE_REPO_UNTAGGED_RETENTION_DAYS as Ue}from"./cacheRepository.js";export{H as BAKE_GUARD_EXPOSURE_CLAUSE,T as BUILDX_VERSION_FLOOR,Le as BUILD_CACHE_MODES,Ae as BUILD_CACHE_MODE_ENV_VAR,g as BUILD_TIMEOUT_ENV_VAR,C as BuildxBuildArgsSchema,F as BuildxBuildMonitor,M as BuildxBuildResultSchema,Ue as CACHE_REPO_UNTAGGED_RETENTION_DAYS,A as DEFAULT_BUILDER_NAME,D as DEFAULT_BUILD_TIMEOUT_MS,O as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,L as DEFAULT_DOCKER_BIN,I as DEFAULT_INSPECT_TIMEOUT_MS,B as DEFAULT_PULL_TIMEOUT_MS,R as DEFAULT_PUSH_STALL_TIMEOUT_MS,S as DEFAULT_PUSH_TIMEOUT_MS,t as DOCKER_CLI_BUILDX_LOG_CATEGORY,s as DOCKER_CLI_LOG_CATEGORY,oe as DockerCli,x as DockerCliErrorKindSchema,m as DockerCliErrorSchema,u as ENGINE_VERSION_FLOOR,j as PUBLIC_BUILD_ARG_PREFIXES,n as PUSH_STALL_TIMEOUT_ENV_VAR,a as PrerequisiteMissingExitCode,U as SIGTERM_GRACE_MS,d as STDERR_TAIL_LINES,_e as abortChildProcess,v as acknowledgedBuildArgKeys,te as buildCacheRepositoryName,G as buildPhaseTimeoutMessage,Te as buildRegistryCacheRefs,N as buildxArgvBuilder,y as createBakeWarningDeduper,le as createEcrAuthSession,K as evaluateBakeGuard,k as evaluateResolvedBuildArgValues,i as failure,Y as inferPublicBuildArgKeys,p as isDockerCliErrorKind,E as isFailure,w as isPublicBuildVarName,_ as isSuccess,Z as parseMetadataFile,W as parseRawjsonLine,ee as projectBuildxResult,h as publishStallTimeoutMessage,J as rawjsonToVertexEvent,ue as resolveBuildCacheMode,V as resolveBuildxBudgets,o as success,ae 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};
@@ -6,7 +6,7 @@
6
6
  * that need the docker-shape types without I/O (e.g. the generator) MUST
7
7
  * import from `./schemas` directly.
8
8
  */
9
- import { type DockerBuild, type FjallManifest, type ResourceMapEntry } from "./schemas.js";
9
+ import { type DockerBuild, type FjallManifest, type LambdaArchitecture, type ResourceMapEntry } from "./schemas.js";
10
10
  /**
11
11
  * Get the manifest file path for a cdk.out directory.
12
12
  */
@@ -49,6 +49,14 @@ export interface ManifestDockerService {
49
49
  * (`@fjall/deploy-core` dockerInterface.ts).
50
50
  */
51
51
  readonly isLambda?: boolean;
52
+ /**
53
+ * The Lambda's CPU architecture (absent for ECS services and for
54
+ * bring-your-own-image Lambdas). Lets the Docker build pipeline pick a
55
+ * `buildx --platform` matching the function's configured `Architectures`
56
+ * instead of always building the default arm64 — see
57
+ * `dockerPlatformForArchitecture` in `@fjall/util/docker`.
58
+ */
59
+ readonly architecture?: LambdaArchitecture;
52
60
  }
53
61
  /**
54
62
  * Extract services with a Docker build configuration from the Fjall manifest.
@@ -84,13 +92,14 @@ export declare function parseDockerServicesFromManifest(cdkOutPath: string): Man
84
92
  * contains no Lambda Docker entries. Lambdas without a `docker` block
85
93
  * (bring-your-own-image) are skipped, not an error.
86
94
  *
87
- * Throws deliberately (not a `Result`) on divergent shared-`imageKey` docker
88
- * configs even though `Result`-returning callers sit above it: the throw is a
89
- * config error surfaced before any build starts, and every production
90
- * boundary converts it — the CLI's `catch` in `runApplicationDeployment`
95
+ * Throws deliberately (not a `Result`) on entries sharing an `imageKey` with
96
+ * divergent docker configs or `architecture` even though `Result`-returning
97
+ * callers sit above it: the throw is a config error surfaced before any build
98
+ * starts, and every production boundary converts it — the CLI's `catch` in
99
+ * `deployApplication`
91
100
  * (`cli/src/services/deployment/applicationDeployment.ts`), the webapp
92
101
  * worker's job-level `catch` in `deploymentJobHandler`, and the `fjall build`
93
- * orchestrator's call-site `catch` in `resolveLambdaBuildIdentityKeys`
102
+ * orchestrator's call-site `catch` in `resolveLambdaBuildIdentities`
94
103
  * (`cli/src/services/container/EcrBuildOrchestrator.ts`). Sanctioned per the
95
104
  * 2026-07-16 docker-lambda review; do not re-litigate as a Pitfall-4 escape.
96
105
  */
@@ -102,6 +111,7 @@ export interface ManifestLambdaDockerEntry {
102
111
  /** Shared build/tag key — `imageKey` if set, else `name`. */
103
112
  readonly imageKey: string;
104
113
  readonly docker: DockerBuild;
114
+ readonly architecture?: LambdaArchitecture;
105
115
  }
106
116
  /**
107
117
  * Extract every container-Lambda manifest entry that declared a `docker`
@@ -1 +1 @@
1
- var y=Object.defineProperty;var i=(t,e)=>y(t,"name",{value:e,configurable:!0});import{readFile as M,writeFile as h,unlink as b,rename as F,mkdir as k}from"fs/promises";import{readFileSync as w}from"fs";import{dirname as S,join as f}from"path";import{logger as s}from"../logger.js";import{fileExists as A}from"../fsHelpers.js";import{getErrorMessage as u}from"../errorUtils.js";import{recordToConstructMap as x}from"../constructMap.js";import{FjallManifestSchema as E,FJALL_MANIFEST_FILENAME as d,MANIFEST_SCHEMA_VERSION as v,normaliseDockerBuild as m}from"./schemas.js";function l(t){return f(t,d)}i(l,"getManifestFilePath");async function D(t){const e=l(t);if(!await A(e))return null;try{const n=await M(e,"utf-8"),r=JSON.parse(n),a=E.safeParse(r);return a.success||s.debug("FjallManifest","Manifest validation failed",{path:e,errors:a.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`)}),a.success?a.data:null}catch(n){return s.debug("FjallManifest","Failed to read manifest file",{path:e,error:u(n)}),null}}i(D,"readManifestFile");async function _(t,e){const n=l(t),r=`${n}.${Date.now()}.tmp`;await k(S(n),{recursive:!0});try{await h(r,JSON.stringify(e,null,2),"utf-8"),await F(r,n)}catch(a){try{await b(r)}catch(o){s.debug("FjallManifest","Temp file cleanup failed (non-fatal)",{path:r,error:u(o)})}throw a}}i(_,"writeManifestFile");function C(t){return{version:v,generatedAt:new Date().toISOString(),appName:t,services:[],lambdas:[],stacks:{}}}i(C,"createEmptyManifest");async function R(t){const e=await D(t);return e?.resourceMap?x(e.resourceMap):new Map}i(R,"readConstructMap");function c(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(c,"isRecord");function p(t,e){const n=f(t,d);let r;try{r=w(n,"utf-8")}catch{s.debug("FjallManifest",`Manifest file not readable \u2014 no ${e} extracted`,{path:n});return}let a;try{a=JSON.parse(r)}catch{s.debug("FjallManifest",`Manifest is not valid JSON \u2014 no ${e} extracted`,{path:n});return}return c(a)?a:void 0}i(p,"readManifestRecord");function L(t){const e=p(t,"Docker services");if(e===void 0||!Array.isArray(e.services))return[];const n=[];for(const r of e.services){if(!c(r)||typeof r.name!="string")continue;const a=m(r.docker);a!==void 0&&n.push({name:r.name,docker:a,isLambda:!1})}return n}i(L,"parseDockerServicesFromManifest");function B(t){const e=new Map;for(const{name:n,imageKey:r,docker:a}of g(t)){const o=e.get(r);if(o===void 0){e.set(r,{name:r,docker:a,isLambda:!0});continue}if(JSON.stringify(o.docker)!==JSON.stringify(a))throw new Error(`Lambda functions sharing image key "${r}" declare different \`docker\` configs \u2014 every Lambda function sharing an \`image\` must build from the identical Dockerfile/context/target/buildArgs (offending function: "${n}").`)}return Array.from(e.values())}i(B,"parseLambdaDockerServicesFromManifest");function g(t){const e=p(t,"Lambda Docker entries");if(e===void 0||!Array.isArray(e.lambdas))return[];const n=[];for(const r of e.lambdas){if(!c(r)||typeof r.name!="string")continue;const a=m(r.docker);if(a===void 0)continue;const o=typeof r.imageKey=="string"&&r.imageKey.length>0?r.imageKey:r.name;n.push({name:r.name,imageKey:o,docker:a})}return n}i(g,"parseLambdaDockerEntriesFromManifest");function H(t){const e=L(t),n=g(t);return{ecsServices:e,lambdaEntries:n,declaresBuild:e.length>0||n.length>0}}i(H,"parseDockerDeclarationsFromManifest");export{C as createEmptyManifest,l as getManifestFilePath,H as parseDockerDeclarationsFromManifest,L as parseDockerServicesFromManifest,g as parseLambdaDockerEntriesFromManifest,B as parseLambdaDockerServicesFromManifest,R as readConstructMap,D as readManifestFile,_ as writeManifestFile};
1
+ var y=Object.defineProperty;var i=(t,r)=>y(t,"name",{value:r,configurable:!0});import{readFile as M,writeFile as b,unlink as F,rename as k,mkdir as S}from"fs/promises";import{readFileSync as A}from"fs";import{dirname as w,join as u}from"path";import{logger as c}from"../logger.js";import{maskSensitiveOutput as E}from"../securityHelpers.js";import{fileExists as v}from"../fsHelpers.js";import{getErrorMessage as d}from"../errorUtils.js";import{recordToConstructMap as x}from"../constructMap.js";import{FjallManifestSchema as L,FJALL_MANIFEST_FILENAME as m,MANIFEST_SCHEMA_VERSION as D,normaliseDockerBuild as l,LAMBDA_ARCHITECTURE_VALUES as j}from"./schemas.js";function p(t){return u(t,m)}i(p,"getManifestFilePath");async function N(t){const r=p(t);if(!await v(r))return null;try{const n=await M(r,"utf-8"),e=JSON.parse(n),a=L.safeParse(e);return a.success||c.debug("FjallManifest","Manifest validation failed",{path:r,errors:a.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`)}),a.success?a.data:null}catch(n){return c.debug("FjallManifest","Failed to read manifest file",{path:r,error:d(n)}),null}}i(N,"readManifestFile");async function H(t,r){const n=p(t),e=`${n}.${Date.now()}.tmp`;await S(w(n),{recursive:!0});try{await b(e,JSON.stringify(r,null,2),"utf-8"),await k(e,n)}catch(a){try{await F(e)}catch(o){c.debug("FjallManifest","Temp file cleanup failed (non-fatal)",{path:e,error:d(o)})}throw a}}i(H,"writeManifestFile");function U(t){return{version:D,generatedAt:new Date().toISOString(),appName:t,services:[],lambdas:[],stacks:{}}}i(U,"createEmptyManifest");async function V(t){const r=await N(t);return r?.resourceMap?x(r.resourceMap):new Map}i(V,"readConstructMap");function f(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(f,"isRecord");function g(t,r){const n=u(t,m);let e;try{e=A(n,"utf-8")}catch{c.debug("FjallManifest",`Manifest file not readable \u2014 no ${r} extracted`,{path:n});return}let a;try{a=JSON.parse(e)}catch{c.debug("FjallManifest",`Manifest is not valid JSON \u2014 no ${r} extracted`,{path:n});return}return f(a)?a:void 0}i(g,"readManifestRecord");function I(t){const r=g(t,"Docker services");if(r===void 0||!Array.isArray(r.services))return[];const n=[];for(const e of r.services){if(!f(e)||typeof e.name!="string")continue;const a=l(e.docker);a!==void 0&&n.push({name:e.name,docker:a,isLambda:!1})}return n}i(I,"parseDockerServicesFromManifest");function q(t){const r=new Map;for(const{name:n,imageKey:e,docker:a,architecture:o}of h(t)){const s=r.get(e);if(s===void 0){r.set(e,{name:e,docker:a,isLambda:!0,...o!==void 0&&{architecture:o}});continue}if(JSON.stringify(s.docker)!==JSON.stringify(a)||s.architecture!==o)throw new Error(`Lambda functions sharing image key "${e}" declare different \`docker\` configs or \`architecture\` \u2014 every Lambda function sharing an \`image\` must build from the identical Dockerfile/context/target/buildArgs and target the same architecture (offending function: "${n}").`)}return Array.from(r.values())}i(q,"parseLambdaDockerServicesFromManifest");function h(t){const r=g(t,"Lambda Docker entries");if(r===void 0||!Array.isArray(r.lambdas))return[];const n=[];for(const e of r.lambdas){if(!f(e)||typeof e.name!="string")continue;const a=l(e.docker);if(a===void 0)continue;const o=typeof e.imageKey=="string"&&e.imageKey.length>0?e.imageKey:e.name;let s;typeof e.architecture=="string"&&(j.includes(e.architecture)?s=e.architecture:c.debug("FjallManifest","Ignoring unrecognised Lambda architecture \u2014 build falls back to the default platform",{lambda:e.name,architecture:E(e.architecture)})),n.push({name:e.name,imageKey:o,docker:a,...s!==void 0&&{architecture:s}})}return n}i(h,"parseLambdaDockerEntriesFromManifest");function z(t){const r=I(t),n=h(t);return{ecsServices:r,lambdaEntries:n,declaresBuild:r.length>0||n.length>0}}i(z,"parseDockerDeclarationsFromManifest");export{U as createEmptyManifest,p as getManifestFilePath,z as parseDockerDeclarationsFromManifest,I as parseDockerServicesFromManifest,h as parseLambdaDockerEntriesFromManifest,q as parseLambdaDockerServicesFromManifest,V as readConstructMap,N as readManifestFile,H as writeManifestFile};
@@ -253,6 +253,15 @@ declare const ManifestEcrSchema: z.ZodObject<{
253
253
  repositoryName: z.ZodString;
254
254
  }, z.core.$strict>;
255
255
  export type ManifestEcr = z.infer<typeof ManifestEcrSchema>;
256
+ /**
257
+ * CPU architecture values a container Lambda's `architecture` prop can take,
258
+ * mirrored from CDK's `Architecture.ARM_64.name` / `Architecture.X86_64.name`
259
+ * so the manifest never depends on aws-cdk-lib. Consumed by the Docker build
260
+ * pipeline (`dockerPlatformForArchitecture`, `@fjall/util/docker`) to pick the
261
+ * `buildx --platform` that matches the Lambda's own CPU architecture.
262
+ */
263
+ export declare const LAMBDA_ARCHITECTURE_VALUES: readonly ["x86_64", "arm64"];
264
+ export type LambdaArchitecture = (typeof LAMBDA_ARCHITECTURE_VALUES)[number];
256
265
  declare const ManifestLambdaSchema: z.ZodObject<{
257
266
  name: z.ZodString;
258
267
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -284,6 +293,10 @@ declare const ManifestLambdaSchema: z.ZodObject<{
284
293
  }, z.core.$strict>>>;
285
294
  }, z.core.$strict>>;
286
295
  imageKey: z.ZodOptional<z.ZodString>;
296
+ architecture: z.ZodOptional<z.ZodEnum<{
297
+ x86_64: "x86_64";
298
+ arm64: "arm64";
299
+ }>>;
287
300
  }, z.core.$strict>;
288
301
  export type ManifestLambda = z.infer<typeof ManifestLambdaSchema>;
289
302
  declare const ManifestStackHashSchema: z.ZodObject<{
@@ -375,6 +388,10 @@ export declare const FjallManifestSchema: z.ZodObject<{
375
388
  }, z.core.$strict>>>;
376
389
  }, z.core.$strict>>;
377
390
  imageKey: z.ZodOptional<z.ZodString>;
391
+ architecture: z.ZodOptional<z.ZodEnum<{
392
+ x86_64: "x86_64";
393
+ arm64: "arm64";
394
+ }>>;
378
395
  }, z.core.$strict>>;
379
396
  pattern: z.ZodOptional<z.ZodObject<{
380
397
  type: z.ZodEnum<{
@@ -1 +1 @@
1
- var y=Object.defineProperty;var i=(t,n)=>y(t,"name",{value:n,configurable:!0});import{z as e}from"zod";import{PATTERN_TYPE_VALUES as S}from"../patterns/patternTypes.js";const T="fjall-manifest.json",h=1,x=/^[A-Za-z0-9_.-]+$/,A=e.object({id:e.string().min(1,"buildSecret id cannot be empty").regex(x,"buildSecret id may contain only letters, digits, '_', '.', and '-' (no comma, '=', or whitespace, which would break the buildx --secret argv)"),ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildSecret requires exactly one source (ssm, secretsManager, or env)"})}),M=e.union([e.string(),e.object({ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional(),acknowledgePublic:e.boolean().optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildArg value requires exactly one source (ssm, secretsManager, or env)"})})]),c=e.object({path:e.string(),context:e.string().min(1,"context cannot be empty").optional(),target:e.string().min(1,"target cannot be empty").optional(),buildArgs:e.record(e.string(),M).optional(),buildSecrets:e.array(A).optional()}).strict(),I=c.partial();function P(t,n){if(n===void 0)return t;const s=n.context??t.context,r=n.target??t.target,o=n.buildArgs??t.buildArgs,a=n.buildSecrets??t.buildSecrets;return{path:n.path??t.path,...s!==void 0&&{context:s},...r!==void 0&&{target:r},...o!==void 0&&{buildArgs:o},...a!==void 0&&{buildSecrets:a}}}i(P,"mergeDockerBuild");function m(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(m,"isRecord");function j(t){if(!m(t)||typeof t.path!="string"||t.path.length===0)return;const n=typeof t.context=="string"&&t.context.length>0?t.context:void 0,s=typeof t.target=="string"&&t.target.length>0?t.target:void 0,r=m(t.buildArgs)&&Object.keys(t.buildArgs).length>0?t.buildArgs:void 0,o=Array.isArray(t.buildSecrets)&&t.buildSecrets.length>0?t.buildSecrets:void 0,a={path:t.path,...n!==void 0&&{context:n},...s!==void 0&&{target:s},...r!==void 0&&{buildArgs:r},...o!==void 0&&{buildSecrets:o}},d=c.safeParse(a);return d.success?d.data:void 0}i(j,"normaliseDockerBuild");function _(t){if(t===void 0)return;const n=j(t);return n===void 0?void 0:JSON.stringify(n)}i(_,"dockerBuildFingerprint");const p=e.object({name:e.string(),clusterName:e.string().optional(),docker:c.optional(),containerPort:e.number().optional(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional()}).strict(),g=e.object({type:e.enum(S),name:e.string(),source:e.string()}).strict(),l=e.object({repositoryName:e.string()}).strict(),u=e.object({name:e.string(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional(),docker:c.optional(),imageKey:e.string().optional()}).strict(),f=e.object({templateHash:e.string(),synthTimestamp:e.string()}).strict(),b=e.object({constructPath:e.string().max(512),group:e.string().max(128),resourceType:e.string().max(256)}).strict(),B=e.object({version:e.literal(h),generatedAt:e.string(),appName:e.string(),services:e.array(p),lambdas:e.array(u),pattern:g.optional(),ecr:l.optional(),stacks:e.record(e.string(),f),resourceMap:e.record(e.string(),b).optional()}).strict();export{x as BUILDKIT_SECRET_ID_PATTERN,M as DockerBuildArgValueSchema,I as DockerBuildPartialSchema,c as DockerBuildSchema,A as DockerBuildSecretRefSchema,T as FJALL_MANIFEST_FILENAME,B as FjallManifestSchema,h as MANIFEST_SCHEMA_VERSION,l as ManifestEcrSchema,u as ManifestLambdaSchema,g as ManifestPatternSchema,p as ManifestServiceSchema,f as ManifestStackHashSchema,b as ResourceMapEntrySchema,_ as dockerBuildFingerprint,P as mergeDockerBuild,j as normaliseDockerBuild};
1
+ var y=Object.defineProperty;var i=(t,n)=>y(t,"name",{value:n,configurable:!0});import{z as e}from"zod";import{PATTERN_TYPE_VALUES as S}from"../patterns/patternTypes.js";const I="fjall-manifest.json",h=1,x=/^[A-Za-z0-9_.-]+$/,A=e.object({id:e.string().min(1,"buildSecret id cannot be empty").regex(x,"buildSecret id may contain only letters, digits, '_', '.', and '-' (no comma, '=', or whitespace, which would break the buildx --secret argv)"),ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildSecret requires exactly one source (ssm, secretsManager, or env)"})}),M=e.union([e.string(),e.object({ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional(),acknowledgePublic:e.boolean().optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildArg value requires exactly one source (ssm, secretsManager, or env)"})})]),c=e.object({path:e.string(),context:e.string().min(1,"context cannot be empty").optional(),target:e.string().min(1,"target cannot be empty").optional(),buildArgs:e.record(e.string(),M).optional(),buildSecrets:e.array(A).optional()}).strict(),N=c.partial();function B(t,n){if(n===void 0)return t;const o=n.context??t.context,r=n.target??t.target,s=n.buildArgs??t.buildArgs,a=n.buildSecrets??t.buildSecrets;return{path:n.path??t.path,...o!==void 0&&{context:o},...r!==void 0&&{target:r},...s!==void 0&&{buildArgs:s},...a!==void 0&&{buildSecrets:a}}}i(B,"mergeDockerBuild");function m(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}i(m,"isRecord");function j(t){if(!m(t)||typeof t.path!="string"||t.path.length===0)return;const n=typeof t.context=="string"&&t.context.length>0?t.context:void 0,o=typeof t.target=="string"&&t.target.length>0?t.target:void 0,r=m(t.buildArgs)&&Object.keys(t.buildArgs).length>0?t.buildArgs:void 0,s=Array.isArray(t.buildSecrets)&&t.buildSecrets.length>0?t.buildSecrets:void 0,a={path:t.path,...n!==void 0&&{context:n},...o!==void 0&&{target:o},...r!==void 0&&{buildArgs:r},...s!==void 0&&{buildSecrets:s}},d=c.safeParse(a);return d.success?d.data:void 0}i(j,"normaliseDockerBuild");function P(t){if(t===void 0)return;const n=j(t);return n===void 0?void 0:JSON.stringify(n)}i(P,"dockerBuildFingerprint");const p=e.object({name:e.string(),clusterName:e.string().optional(),docker:c.optional(),containerPort:e.number().optional(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional()}).strict(),g=e.object({type:e.enum(S),name:e.string(),source:e.string()}).strict(),l=e.object({repositoryName:e.string()}).strict(),E=["x86_64","arm64"],u=e.object({name:e.string(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional(),docker:c.optional(),imageKey:e.string().optional(),architecture:e.enum(E).optional()}).strict(),f=e.object({templateHash:e.string(),synthTimestamp:e.string()}).strict(),b=e.object({constructPath:e.string().max(512),group:e.string().max(128),resourceType:e.string().max(256)}).strict(),R=e.object({version:e.literal(h),generatedAt:e.string(),appName:e.string(),services:e.array(p),lambdas:e.array(u),pattern:g.optional(),ecr:l.optional(),stacks:e.record(e.string(),f),resourceMap:e.record(e.string(),b).optional()}).strict();export{x as BUILDKIT_SECRET_ID_PATTERN,M as DockerBuildArgValueSchema,N as DockerBuildPartialSchema,c as DockerBuildSchema,A as DockerBuildSecretRefSchema,I as FJALL_MANIFEST_FILENAME,R as FjallManifestSchema,E as LAMBDA_ARCHITECTURE_VALUES,h as MANIFEST_SCHEMA_VERSION,l as ManifestEcrSchema,u as ManifestLambdaSchema,g as ManifestPatternSchema,p as ManifestServiceSchema,f as ManifestStackHashSchema,b as ResourceMapEntrySchema,P as dockerBuildFingerprint,B as mergeDockerBuild,j as normaliseDockerBuild};
@@ -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.10.0",
3
+ "version": "4.0.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": "ce4e8471194f7e3e5e7df66844bf9387503983f6"
137
+ "gitHead": "14031063ccd2685e390b70f6a3f4efe2bac7ced9"
138
138
  }