@fjall/util 3.11.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/config.d.ts +22 -6
- package/dist/config.js +2 -1
- package/dist/devSubstrate/params.d.ts +2 -0
- package/dist/devSubstrate/params.js +1 -1
- package/dist/docker/DockerCli.build.d.ts +3 -2
- package/dist/docker/DockerCli.build.js +1 -1
- package/dist/docker/DockerCli.d.ts +15 -11
- package/dist/docker/DockerCli.daemon.js +1 -1
- package/dist/docker/DockerCli.js +1 -1
- package/dist/docker/DockerCli.registry.js +3 -3
- package/dist/docker/abortHelpers.js +1 -1
- package/dist/docker/dockerCliConstants.d.ts +2 -0
- package/dist/docker/dockerCliConstants.js +1 -1
- package/dist/docker/dockerCliDiagnostics.d.ts +50 -0
- package/dist/docker/dockerCliDiagnostics.js +1 -0
- package/dist/docker/dockerErrorFormat.d.ts +17 -0
- package/dist/docker/dockerErrorFormat.js +5 -0
- package/dist/docker/index.d.ts +2 -1
- package/dist/docker/index.js +1 -1
- package/dist/environments.d.ts +14 -0
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/infra/tokenScopes.d.ts +11 -1
- package/dist/infra/tokenScopes.js +1 -1
- package/dist/naming/reservedAppNames.d.ts +10 -1
- package/dist/naming/reservedAppNames.js +1 -1
- package/dist/repo/findProjectsBelow.js +1 -1
- package/package.json +2 -2
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
92 files minified at 2026-07-24T23:00:42.861Z
|
package/dist/config.d.ts
CHANGED
|
@@ -186,6 +186,17 @@ export declare const RootConfigReadSchema: z.ZodObject<{
|
|
|
186
186
|
}, z.core.$strict>>>;
|
|
187
187
|
}, z.core.$strip>;
|
|
188
188
|
export type RootConfigRead = z.infer<typeof RootConfigReadSchema>;
|
|
189
|
+
/**
|
|
190
|
+
* Local, NON-COMMITTED per-checkout state (`.fjall/local.json`, gitignored),
|
|
191
|
+
* co-located with fjall-config.json. Holds the developer's active deployment
|
|
192
|
+
* target: it lives here rather than in the committed fjall-config.json so a
|
|
193
|
+
* committed value can never silently re-aim someone else's deploy or a CI
|
|
194
|
+
* pipeline (the ci-deploy misdeploy fix). A legacy `activeTarget` in
|
|
195
|
+
* fjall-config.json is read only to warn-and-migrate, never to resolve a
|
|
196
|
+
* target.
|
|
197
|
+
*/
|
|
198
|
+
export declare const LOCAL_CONFIG_DIRNAME = ".fjall";
|
|
199
|
+
export declare const LOCAL_CONFIG_FILENAME = "local.json";
|
|
189
200
|
/**
|
|
190
201
|
* Canonical serialiser for the root fjall-config.json - the single source of
|
|
191
202
|
* truth for the file's on-disk shape. Both `Config.saveConfig` and the webapp
|
|
@@ -221,12 +232,6 @@ export declare class Config {
|
|
|
221
232
|
* vanished from memory.
|
|
222
233
|
*/
|
|
223
234
|
private parseDegraded;
|
|
224
|
-
/**
|
|
225
|
-
* Top-level keys explicitly cleared this session (clearActiveTarget).
|
|
226
|
-
* The disk-preserving merge in saveConfig would otherwise resurrect them
|
|
227
|
-
* from the on-disk copy.
|
|
228
|
-
*/
|
|
229
|
-
private readonly clearedKeys;
|
|
230
235
|
/**
|
|
231
236
|
* Deep copy of the state this instance LOADED from disk (empty for a
|
|
232
237
|
* programmatically-constructed Config, whose entire state is session
|
|
@@ -306,6 +311,17 @@ export declare class Config {
|
|
|
306
311
|
* does not exist.
|
|
307
312
|
*/
|
|
308
313
|
isContentUnavailable(): boolean;
|
|
314
|
+
private static warnedLegacyActiveTarget;
|
|
315
|
+
/**
|
|
316
|
+
* In-memory view of `.fjall/local.json`, lazily loaded once per instance.
|
|
317
|
+
* Reads serve from here so `set` then `get` is coherent within a process
|
|
318
|
+
* and hot-path resolution never re-hits disk; writes flush through it.
|
|
319
|
+
*/
|
|
320
|
+
private localConfigCache;
|
|
321
|
+
private localConfigFile;
|
|
322
|
+
private loadLocalConfig;
|
|
323
|
+
private writeLocalConfig;
|
|
324
|
+
private static warnLegacyActiveTargetOnce;
|
|
309
325
|
getActiveTarget(): string | undefined;
|
|
310
326
|
setActiveTarget(name: string): void;
|
|
311
327
|
clearActiveTarget(): void;
|
package/dist/config.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var x=Object.defineProperty;var h=(u,e)=>x(u,"name",{value:e,configurable:!0});import*as a from"fs";import*as g from"path";import{z as c}from"zod";import{failure as y,success as O}from"./docker/result.js";import{getErrorMessage as S}from"./errorUtils.js";import{logger as p}from"./logger.js";import{maskSensitiveOutput as w}from"./securityHelpers.js";const N=10,v="fjall-config.json",G=["compliance","governance","none"],B=["enforced","off"],W=["centralised","off"],Y=["account","draining","org"],Z="managementEvents",H="organisationManagementEvents",q=["active","draining","removed"],z="FjallTrailBucketName",V="FjallTrailKeyArn",X="OrganisationTrailBucketName",j=c.object({name:c.string(),type:c.enum(["apex","delegated"]),parentDomain:c.string().optional(),account:c.string().optional(),region:c.string().optional()}).strict(),A=c.object({activeTarget:c.string().optional(),domains:c.array(j).optional()}).strict(),L=c.object({activeTarget:c.string().optional(),domains:c.array(j).optional()}),_=".fjall",k="local.json",R=c.object({activeTarget:c.string().optional()});function $(u={}){return JSON.stringify(u,null,2)}h($,"serialiseRootConfig");const D=A.keyof().options;function I(u,e,n){const t=e[n];t!==void 0&&(u[n]=t)}h(I,"copyDefinedKey");function F(u,e){if(e===void 0)return!1;const n=new Set([...Object.keys(u),...Object.keys(e)]);for(const t of n)if(u[t]!==e[t])return!1;return!0}h(F,"domainEntriesEqual");class f{static{h(this,"Config")}rootConfig;configPath=null;loadFailed=!1;parseDegraded=!1;loadedSnapshot={};constructor(e,n){this.rootConfig=e??{},this.configPath=n??null}static findConfigDirectory(e){let n=e!==void 0&&e!==""?e:process.cwd();for(let t=0;t<N;t++){const i=g.join(n,"fjall"),o=g.join(i,v);if(a.existsSync(o))return i;const s=g.join(n,v);if(a.existsSync(s))return n;const l=g.dirname(n);if(l===n)break;n=l}return null}static loadConfigFile(e){try{return a.accessSync(e,a.constants.R_OK),a.readFileSync(e,{encoding:"utf8"})}catch(n){return p.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:w(S(n))}),null}}static loadConfig(e){const n=f.findConfigDirectory(e);if(!n)return new f;const t=g.join(n,v),i=f.loadConfigFile(t);if(i===null){const d=new f(void 0,t);return d.loadFailed=!0,d}let o,s=!1;if(i!==""){let d;try{d=JSON.parse(i)}catch(r){throw f.formatZodError(r,v)}const C=A.safeParse(d);if(C.success)o=C.data;else{const r=L.safeParse(d);o=r.success?r.data:{},s=!r.success,r.success?p.warn("Config","fjall-config.json contains keys this version does not recognise; they were ignored (only activeTarget and domains are read). If this is an old config, regenerate it with `fjall create ...` or re-run `fjall connect`.",{file:t}):p.warn("Config","fjall-config.json has a malformed activeTarget or domains value, so its contents were ignored for this run. Fix the file (or regenerate it with `fjall create ...` / `fjall connect`).",{file:t})}}const l=new f(o,t);return l.parseDegraded=s,l.loadedSnapshot=structuredClone(l.rootConfig),l}static formatZodError(e,n){if(e instanceof c.ZodError&&e.issues.length>0){const o=e.issues.map(s=>`${s.path.join(".")}: ${s.message}`).join("; ");return new Error(`Failed to parse ${n}: ${o}`)}const i=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${n}: ${i}`)}saveConfig(){let e=this.configPath;if(!e){const s=f.findConfigDirectory()||g.join(process.cwd(),"fjall");e=g.join(s,v)}if(this.loadFailed)return y(new Error(`Refusing to save ${e}: the file exists but could not be read when this config loaded, so saving would replace its contents with state that never included them. Fix the file permissions (e.g. chmod u+rw ${e}) and retry.`));const n=g.dirname(e);try{a.mkdirSync(n,{recursive:!0})}catch(s){return y(new Error(`Cannot create config directory ${n}: ${w(S(s))}`))}const t=f.assertWritable(e,n);if(!t.success)return t;const i=$(this.mergeWithDisk(e)),o=`${e}.tmp-${process.pid}`;try{a.writeFileSync(o,i,{mode:384}),a.renameSync(o,e)}catch(s){return y(new Error(`Failed to save ${e}: ${w(S(s))}`))}return O(void 0)}static assertWritable(e,n){if(a.existsSync(e))try{a.accessSync(e,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the file is read-only. Make it writable (e.g. chmod u+w ${e}) and retry.`))}try{a.accessSync(n,a.constants.W_OK)}catch{return y(new Error(`Cannot save ${e}: the directory ${n} is not writable. Make it writable (e.g. chmod u+w ${n}) and retry.`))}return O(void 0)}mergeWithDisk(e){const n=f.readDiskConfigForMerge(e);if(n===void 0)return this.rootConfig;const t={...n.foreign,...n.known};for(const o of D)o!=="domains"&&this.sessionChangedKey(o)&&(this.rootConfig[o]===void 0?delete t[o]:I(t,this.rootConfig,o));const i=this.mergeDomains(n.known.domains);return i!==void 0?t.domains=i:delete t.domains,t}sessionChangedKey(e){return JSON.stringify(this.rootConfig[e])!==JSON.stringify(this.loadedSnapshot[e])}mergeDomains(e){const n=this.rootConfig.domains,t=this.loadedSnapshot.domains??[];if(n===void 0&&t.length===0)return e;const i=h(r=>r.toLowerCase(),"norm"),o=n??[],s=new Map(t.map(r=>[i(r.name),r])),l=new Map(o.map(r=>[i(r.name),r])),d=[],C=new Set;for(const r of e??[]){const m=i(r.name);C.add(m);const T=l.get(m),E=s.get(m);if(T!==void 0&&!F(T,E)){d.push(T);continue}T===void 0&&E!==void 0||d.push(r)}for(const r of o){const m=i(r.name);C.has(m)||F(r,s.get(m))||d.push(r)}return d}static readDiskConfigForMerge(e){if(!a.existsSync(e))return;let n;try{n=JSON.parse(a.readFileSync(e,{encoding:"utf8"}))}catch(o){p.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:w(S(o))});return}const t=L.safeParse(n);if(!t.success){p.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:w(t.error.message)});return}const i={};if(typeof n=="object"&&n!==null){const o=D;for(const[s,l]of Object.entries(n))o.includes(s)||(i[s]=l)}return{known:t.data,foreign:i}}static getConfigDirectory(e){return f.findConfigDirectory(e)}getConfigPath(){return this.configPath}isContentUnavailable(){return this.loadFailed||this.parseDegraded}static warnedLegacyActiveTarget=!1;localConfigCache;localConfigFile(){const e=this.configPath!==null?g.dirname(this.configPath):process.cwd();return g.join(e,_,k)}loadLocalConfig(){if(this.localConfigCache===void 0)try{const e=a.readFileSync(this.localConfigFile(),{encoding:"utf8"}),n=R.safeParse(JSON.parse(e));this.localConfigCache=n.success?n.data:{}}catch{this.localConfigCache={}}return this.localConfigCache}writeLocalConfig(e){const n=this.localConfigFile();a.mkdirSync(g.dirname(n),{recursive:!0}),a.writeFileSync(n,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,{encoding:"utf8"}),this.localConfigCache=e}static warnLegacyActiveTargetOnce(){f.warnedLegacyActiveTarget||(f.warnedLegacyActiveTarget=!0,p.warn("Config","`activeTarget` in fjall-config.json is deprecated and ignored: a committed target must never re-aim a deploy or CI pipeline. Run `fjall target set <name>` to store it locally in .fjall/local.json, then remove `activeTarget` from the committed fjall-config.json."))}getActiveTarget(){const e=this.loadLocalConfig().activeTarget;if(e!==void 0)return e;this.rootConfig.activeTarget!==void 0&&f.warnLegacyActiveTargetOnce()}setActiveTarget(e){const n=this.loadLocalConfig();n.activeTarget=e,this.writeLocalConfig(n)}clearActiveTarget(){const e=this.loadLocalConfig();delete e.activeTarget,this.writeLocalConfig(e)}getDomains(){return this.rootConfig.domains??[]}setDomains(e){this.rootConfig.domains=e}addDomain(e){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(e)}getDomain(e){return this.rootConfig.domains?.find(n=>n.name.toLowerCase()===e.toLowerCase())}removeDomain(e){if(!this.rootConfig.domains)return!1;const n=this.rootConfig.domains.findIndex(t=>t.name.toLowerCase()===e.toLowerCase());return n===-1?!1:(this.rootConfig.domains.splice(n,1),!0)}}export{Z as ACCOUNT_TRAIL_NAME,q as ACCOUNT_TRAIL_STATES,f as Config,j as DomainConfigSchema,_ as LOCAL_CONFIG_DIRNAME,k as LOCAL_CONFIG_FILENAME,H as ORGANISATION_TRAIL_NAME,X as ORG_TRAIL_BUCKET_OUTPUT_KEY,W as ROOT_ACCESS_MANAGEMENT_MODES,v as ROOT_CONFIG_FILENAME,L as RootConfigReadSchema,A as RootConfigSchema,B as S3_BPA_MODES,z as TRAIL_BUCKET_OUTPUT_KEY,V as TRAIL_KEY_ARN_OUTPUT_KEY,Y as TRAIL_LIFECYCLE_STATES,G as VAULT_LOCK_MODES,$ as serialiseRootConfig};
|
|
@@ -30,6 +30,7 @@ export declare const DevSubstrateSynthPropsSchema: z.ZodObject<{
|
|
|
30
30
|
appKebab: z.ZodString;
|
|
31
31
|
engineVersion: z.ZodOptional<z.ZodString>;
|
|
32
32
|
adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
|
|
33
|
+
adoptCacheEcr: z.ZodOptional<z.ZodBoolean>;
|
|
33
34
|
phase: z.ZodOptional<z.ZodEnum<{
|
|
34
35
|
zone: "zone";
|
|
35
36
|
full: "full";
|
|
@@ -58,6 +59,7 @@ export declare const DevSubstrateParamsSchema: z.ZodObject<{
|
|
|
58
59
|
appKebab: z.ZodString;
|
|
59
60
|
engineVersion: z.ZodOptional<z.ZodString>;
|
|
60
61
|
adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
|
|
62
|
+
adoptCacheEcr: z.ZodOptional<z.ZodBoolean>;
|
|
61
63
|
phase: z.ZodOptional<z.ZodEnum<{
|
|
62
64
|
zone: "zone";
|
|
63
65
|
full: "full";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as e}from"zod";const o=2,r="dev-substrate-params.json",i=e.object({devDomain:e.string().min(1),hostedZoneId:e.string().min(1).optional(),parentDomain:e.string().min(1).optional(),parentDelegationRoleArn:e.string().min(1).optional()}).strict().superRefine((t,n)=>{if(t.hostedZoneId!==void 0){t.parentDomain!==void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDomain \u2014 the adopted zone was already delegated; omit parentDomain"}),t.parentDelegationRoleArn!==void 0&&n.addIssue({code:"custom",path:["parentDelegationRoleArn"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDelegationRoleArn \u2014 the adopted zone was already delegated; omit parentDelegationRoleArn"});return}t.parentDelegationRoleArn!==void 0&&t.parentDomain===void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDelegationRoleArn requires parentDomain \u2014 set parentDomain to the parent zone the DelegationRole belongs to"}),t.parentDomain!==void 0&&!t.devDomain.endsWith(`.${t.parentDomain}`)&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDomain must be a proper DNS suffix of devDomain \u2014 devDomain must end with `.<parentDomain>`"})}),a={appId:e.string().min(1),appKebab:e.string().min(1),engineVersion:e.string().min(1).optional(),adoptSlotEcr:e.boolean().optional(),phase:e.enum(["zone","full"]).optional(),domain:i.optional()},p=e.object(a).strict(),d=e.object({...a,version:e.literal(o),appName:e.string().min(1)}).strict().refine(t=>t.appName===`fjall-dev-${t.appKebab}`,{message:"appName must equal `fjall-dev-${appKebab}` (single-source identity)",path:["appName"]});export{r as DEV_SUBSTRATE_PARAMS_FILENAME,o as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,d as DevSubstrateParamsSchema,p as DevSubstrateSynthPropsSchema};
|
|
1
|
+
import{z as e}from"zod";const o=2,r="dev-substrate-params.json",i=e.object({devDomain:e.string().min(1),hostedZoneId:e.string().min(1).optional(),parentDomain:e.string().min(1).optional(),parentDelegationRoleArn:e.string().min(1).optional()}).strict().superRefine((t,n)=>{if(t.hostedZoneId!==void 0){t.parentDomain!==void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDomain \u2014 the adopted zone was already delegated; omit parentDomain"}),t.parentDelegationRoleArn!==void 0&&n.addIssue({code:"custom",path:["parentDelegationRoleArn"],message:"hostedZoneId (adopt mode) is mutually exclusive with parentDelegationRoleArn \u2014 the adopted zone was already delegated; omit parentDelegationRoleArn"});return}t.parentDelegationRoleArn!==void 0&&t.parentDomain===void 0&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDelegationRoleArn requires parentDomain \u2014 set parentDomain to the parent zone the DelegationRole belongs to"}),t.parentDomain!==void 0&&!t.devDomain.endsWith(`.${t.parentDomain}`)&&n.addIssue({code:"custom",path:["parentDomain"],message:"parentDomain must be a proper DNS suffix of devDomain \u2014 devDomain must end with `.<parentDomain>`"})}),a={appId:e.string().min(1),appKebab:e.string().min(1),engineVersion:e.string().min(1).optional(),adoptSlotEcr:e.boolean().optional(),adoptCacheEcr:e.boolean().optional(),phase:e.enum(["zone","full"]).optional(),domain:i.optional()},p=e.object(a).strict(),d=e.object({...a,version:e.literal(o),appName:e.string().min(1)}).strict().refine(t=>t.appName===`fjall-dev-${t.appKebab}`,{message:"appName must equal `fjall-dev-${appKebab}` (single-source identity)",path:["appName"]});export{r as DEV_SUBSTRATE_PARAMS_FILENAME,o as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,d as DevSubstrateParamsSchema,p as DevSubstrateSynthPropsSchema};
|
|
@@ -48,8 +48,9 @@ export declare function _buildxBuild(state: DockerCliState, args: BuildxBuildArg
|
|
|
48
48
|
* - argv-only spawn (`shell: false` via `runDocker`)
|
|
49
49
|
* - tag values are validated to be non-empty strings before they enter argv
|
|
50
50
|
* - stderr is masked and bounded by `makeError(... stderrTail)`
|
|
51
|
-
* - timeout:
|
|
52
|
-
* manifest
|
|
51
|
+
* - timeout: `DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS`. This is a REGISTRY
|
|
52
|
+
* WRITE — an auth exchange plus one manifest PUT per tag — so it must not
|
|
53
|
+
* borrow the local-daemon inspect budget, which it did until 2026-07-24.
|
|
53
54
|
*/
|
|
54
55
|
export declare function _tagByDigest(state: DockerCliState, sourceImage: string, digest: string, tags: readonly string[]): Promise<Result<void, DockerCliError>>;
|
|
55
56
|
export declare function _imagetoolsInspect(state: DockerCliState, image: string): Promise<Result<ImagetoolsInspect, DockerCliError>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var N=Object.defineProperty;var g=(e,n)=>N(e,"name",{value:n,configurable:!0});import{rm as U}from"node:fs/promises";import{buildxArgvBuilder as J}from"./buildxArgvBuilder.js";import{BuildxBuildArgsSchema as Y}from"./dockerCliSchemas.js";import{DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS as B,DOCKER_CLI_BUILDX_LOG_CATEGORY as S}from"./dockerCliConstants.js";import{abortOrTimeoutError as I,makeLoggedError as p,spawnFailureToError as k,wasAborted as $}from"./dockerCliDiagnostics.js";import{BuildxBuildMonitor as H,buildPhaseTimeoutMessage as K,publishStallTimeoutMessage as P,resolveBuildxBudgets as V}from"./buildxBuildMonitor.js";import{parseMetadataFile as W}from"./metadataFileParser.js";import{parseRawjsonLine as X}from"./rawjsonParser.js";import{rawjsonToVertexEvent as q}from"./rawjsonToVertexEvent.js";import{maskSensitiveOutput as y}from"../securityHelpers.js";import{failure as r,makeError as m,maskOutput as v,runDocker as L,streamDocker as z,success as D}from"./DockerCli.js";function R(e){return e instanceof Error?e.message:String(e)}g(R,"getErrorMessage");async function le(e,n,t,d){const i=Y.safeParse(n);if(!i.success){const o=y(i.error.message);return r(m("validation",`Invalid BuildxBuildArgs: ${o}`,{issues:i.error.issues}))}const s=V(e.env);if(!s.success)return r(m("validation",y(s.error.message)));const x=s.data,u=J(i.data);let f=0,M=0;const C=new Set;let E,T;try{const o=new H(x);E=o,T=i.data.metadataFile;const j=g(b=>{const A=X(b);if(A===null){b.trim()!==""&&e.logger.debug(S,"Skipping malformed rawjson line",{line:y(b)});return}const c=q(A);if(c===null)return;const h=o.observe(c);h!==void 0&&t({type:"status",message:v(h.message),...h.percentage!==void 0&&{percentage:h.percentage}});for(const a of c.vertexes)a.completed!==void 0&&!C.has(a.digest)&&(C.add(a.digest),a.cached===!0?f++:M++),t({type:"vertex",message:v(a.name),vertex:a.digest});for(const a of c.logs)t({type:"log",message:v(a.data),vertex:a.vertex});for(const a of c.statuses)t({type:"status",message:v(`${a.name} ${a.current}`),vertex:a.vertex});for(const a of c.warnings)t({type:"warning",message:v(a.short),vertex:a.vertex})},"onStdoutLine"),G=d!==void 0?AbortSignal.any([o.signal,d]):o.signal,l=await z(e,{args:u,abortSignal:G},j);if(l.spawnError!==void 0)return r(k(e,l));if($(l))return o.timedOutPhase==="build"?r(p(e,"timeout",K(x),{stderrTail:l.stderrTail})):o.timedOutPhase==="publish"?r(p(e,"timeout",P(x,o.progressSummary()),{stderrTail:l.stderrTail})):r(p(e,"abort","docker buildx build was aborted",{stderrTail:l.stderrTail}));if(l.exitCode!==0)return r(p(e,"build_failed",`docker buildx build failed (exit ${l.exitCode})`,{stderrTail:l.stderrTail}));const _=await W(i.data.metadataFile);if(!_.success)return _;const F=_.data,w=F["containerimage.digest"];if(typeof w!="string"||w==="")return r(m("metadata_missing_digest","Buildx metadata file does not contain containerimage.digest",{metadataFile:i.data.metadataFile}));const O={};for(const b of i.data.tags)O[b]=w;return D({digest:w,imageDigests:O,metadata:F,platforms:i.data.platforms,cacheHits:f,cacheMisses:M})}finally{E!==void 0&&E.dispose(),T!==void 0&&await U(T,{force:!0}).catch(o=>{e.logger.warn(S,"Failed to clean up buildx metadata file",{path:T,error:y(R(o))})})}}g(le,"_buildxBuild");async function fe(e,n,t,d){if(n.trim()==="")return r(m("validation","tagByDigest: sourceImage must be non-empty"));if(!t.startsWith("sha256:"))return r(m("validation",`tagByDigest: expected sha256:... digest, got ${t.slice(0,32)}`));if(d.length===0)return r(m("validation","tagByDigest: tags must be a non-empty list"));for(const f of d)if(typeof f!="string"||f.trim()==="")return r(m("validation","tagByDigest: every tag must be non-empty"));const i=`${n}@${t}`,s=[];for(const f of d)s.push("--tag",f);const x=["buildx","imagetools","create","--prefer-index=false",...s,i],u=await L(e,{args:x,timeoutMs:B});return u.spawnError!==void 0?r(k(e,u)):$(u)?r(I(e,u,`docker buildx imagetools create for ${i}`,B)):u.exitCode!==0?r(p(e,"tag_failed",`docker buildx imagetools create for ${i} failed (exit ${u.exitCode})`,{stderrTail:u.stderrTail})):D(void 0)}g(fe,"_tagByDigest");async function me(e,n){const t=await L(e,{args:["buildx","imagetools","inspect",n,"--raw"],timeoutMs:B});if(t.spawnError!==void 0)return r(k(e,t));if($(t))return r(I(e,t,`docker buildx imagetools inspect ${n}`,B));if(t.exitCode!==0)return r(p(e,"inspect_failed",`docker buildx imagetools inspect ${n} failed (exit ${t.exitCode})`,{stderrTail:t.stderrTail}));let d,i;try{const s=JSON.parse(t.stdout);typeof s.mediaType=="string"&&(d=s.mediaType),typeof s.digest=="string"&&(i=s.digest)}catch(s){e.logger.debug(S,"imagetools inspect output is not JSON; preserving raw",{error:y(R(s))})}return D({raw:t.stdout,...d!==void 0&&{mediaType:d},...i!==void 0&&{digest:i}})}g(me,"_imagetoolsInspect");export{le as _buildxBuild,me as _imagetoolsInspect,fe as _tagByDigest};
|
|
@@ -155,22 +155,26 @@ export interface SpawnDockerResult {
|
|
|
155
155
|
readonly stderr: string;
|
|
156
156
|
readonly stderrTail: readonly string[];
|
|
157
157
|
readonly aborted: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* True when the per-call `timeoutMs` budget is what fired the abort. A
|
|
160
|
+
* caller cancel and a budget expiry compose into ONE signal, so `aborted`
|
|
161
|
+
* alone cannot tell them apart and every timeout would report as a cancel.
|
|
162
|
+
*/
|
|
163
|
+
readonly timedOut: boolean;
|
|
164
|
+
readonly spawnError?: string;
|
|
165
|
+
}
|
|
166
|
+
export interface StreamDockerResult {
|
|
167
|
+
readonly exitCode: number | null;
|
|
168
|
+
readonly stderrTail: readonly string[];
|
|
169
|
+
readonly aborted: boolean;
|
|
170
|
+
readonly timedOut: boolean;
|
|
158
171
|
readonly spawnError?: string;
|
|
172
|
+
readonly child: ChildProcess | null;
|
|
159
173
|
}
|
|
160
174
|
export declare function makeError(kind: DockerCliErrorKind, message: string, details?: Record<string, unknown>): DockerCliError;
|
|
161
|
-
export declare function spawnFailureToError(result: {
|
|
162
|
-
spawnError?: string;
|
|
163
|
-
stderrTail: readonly string[];
|
|
164
|
-
}): DockerCliError;
|
|
165
175
|
export declare function tailLines(buffer: string, count: number): string[];
|
|
166
176
|
export declare function maskOutput(value: string): string;
|
|
167
177
|
export declare function runDocker(state: DockerCliState, opts: SpawnDockerOptions): Promise<SpawnDockerResult>;
|
|
168
|
-
export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<
|
|
169
|
-
exitCode: number | null;
|
|
170
|
-
stderrTail: readonly string[];
|
|
171
|
-
aborted: boolean;
|
|
172
|
-
spawnError?: string;
|
|
173
|
-
child: ChildProcess | null;
|
|
174
|
-
}>;
|
|
178
|
+
export declare function streamDocker(state: DockerCliState, opts: SpawnDockerOptions, onStdoutLine: (line: string) => void, onStderrLine?: (line: string) => void): Promise<StreamDockerResult>;
|
|
175
179
|
export { failure, success };
|
|
176
180
|
export type { Result };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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};
|
package/dist/docker/DockerCli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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
|
|
2
|
-
`).toLowerCase().includes("unauthorized")?
|
|
3
|
-
`,!(t===void 0||
|
|
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
|
|
1
|
+
var i=Object.defineProperty;var o=(t,e)=>i(t,"name",{value:e,configurable:!0});import{spawnSync as f}from"node:child_process";import{SIGTERM_GRACE_MS as n}from"./dockerCliConstants.js";function p(t,e=process.platform){if(t.pid===void 0)return;if(t.stdout&&t.stdout.destroy(),t.stderr&&t.stderr.destroy(),e==="win32"){f("taskkill",["/T","/F","/PID",String(t.pid)],{shell:!1});return}t.kill("SIGTERM");let r=!1;const s=setTimeout(()=>{r||t.kill("SIGKILL")},n);t.once("exit",()=>{r=!0,clearTimeout(s)})}o(p,"abortChildProcess");export{p as abortChildProcess};
|
|
@@ -12,5 +12,7 @@ export declare const DEFAULT_BUILD_TIMEOUT_MS = 1800000;
|
|
|
12
12
|
export declare const DEFAULT_PUSH_STALL_TIMEOUT_MS = 300000;
|
|
13
13
|
export declare const DEFAULT_PUSH_TIMEOUT_MS = 300000;
|
|
14
14
|
export declare const DEFAULT_PULL_TIMEOUT_MS = 300000;
|
|
15
|
+
export declare const DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS = 120000;
|
|
15
16
|
export declare const DEFAULT_INSPECT_TIMEOUT_MS = 30000;
|
|
17
|
+
export declare const DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS = 30000;
|
|
16
18
|
export declare const DEFAULT_DAEMON_PROBE_TIMEOUT_MS = 10000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const _="DockerCli",
|
|
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};
|
package/dist/docker/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { type Result, isSuccess, isFailure, success, failure } from "./result.js";
|
|
2
|
-
export { DOCKER_CLI_LOG_CATEGORY, DOCKER_CLI_BUILDX_LOG_CATEGORY, BUILDX_VERSION_FLOOR, ENGINE_VERSION_FLOOR, PrerequisiteMissingExitCode, DEFAULT_BUILDER_NAME, DEFAULT_DOCKER_BIN, SIGTERM_GRACE_MS, STDERR_TAIL_LINES, DEFAULT_BUILD_TIMEOUT_MS, DEFAULT_PUSH_STALL_TIMEOUT_MS, DEFAULT_PUSH_TIMEOUT_MS, DEFAULT_PULL_TIMEOUT_MS, DEFAULT_INSPECT_TIMEOUT_MS, DEFAULT_DAEMON_PROBE_TIMEOUT_MS } from "./dockerCliConstants.js";
|
|
2
|
+
export { DOCKER_CLI_LOG_CATEGORY, DOCKER_CLI_BUILDX_LOG_CATEGORY, BUILDX_VERSION_FLOOR, ENGINE_VERSION_FLOOR, PrerequisiteMissingExitCode, DEFAULT_BUILDER_NAME, DEFAULT_DOCKER_BIN, SIGTERM_GRACE_MS, STDERR_TAIL_LINES, DEFAULT_BUILD_TIMEOUT_MS, DEFAULT_PUSH_STALL_TIMEOUT_MS, DEFAULT_PUSH_TIMEOUT_MS, DEFAULT_PULL_TIMEOUT_MS, DEFAULT_REGISTRY_MANIFEST_TIMEOUT_MS, DEFAULT_INSPECT_TIMEOUT_MS, DEFAULT_BUILDER_LIFECYCLE_TIMEOUT_MS, DEFAULT_DAEMON_PROBE_TIMEOUT_MS } from "./dockerCliConstants.js";
|
|
3
|
+
export { STDERR_TAIL_HEADER, STDERR_TAIL_FOOTER, describeDockerFailure } from "./dockerErrorFormat.js";
|
|
3
4
|
export { BuildxBuildArgsSchema, BuildxBuildResultSchema, DockerCliErrorKindSchema, DockerCliErrorSchema, isDockerCliErrorKind, type BuildxBuildArgs, type BuildxBuildResult, type DockerCliError, type DockerCliErrorKind } from "./dockerCliSchemas.js";
|
|
4
5
|
export { buildxArgvBuilder } from "./buildxArgvBuilder.js";
|
|
5
6
|
export { dockerPlatformForArchitecture, ECS_TASK_ARCHITECTURE } from "./architecture.js";
|
package/dist/docker/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isSuccess as _,isFailure as
|
|
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};
|
package/dist/environments.d.ts
CHANGED
|
@@ -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`
|
package/dist/environments.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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
|
|
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"],
|
|
1
|
+
const r=["read","write","deploy","secrets:read","secrets:write","destroy","admin","applications:read","applications:deploy","deploy:oidc:mint","deploy:governance"],o={read:"user",write:"user",deploy:"user","secrets:read":"user","secrets:write":"user",destroy:"user",admin:"admin","applications:read":"user","applications:deploy":"user","deploy:oidc:mint":"machine","deploy:governance":"machine"},s=r.filter(e=>o[e]==="machine"),t="governance_scope_required",i=r.filter(e=>o[e]==="user");export{t as GOVERNANCE_SCOPE_REQUIRED_CODE,s as MACHINE_ONLY_SCOPES,r as SCOPE_VALUES,i as USER_GRANTABLE_SCOPES};
|
|
@@ -21,6 +21,15 @@
|
|
|
21
21
|
* org-tier deploy — bypassing the app-scoped session policy and injecting org
|
|
22
22
|
* identity into synth.
|
|
23
23
|
*
|
|
24
|
+
* `"domain"` is reserved because BOTH verb-first surfaces reject the word
|
|
25
|
+
* permanently: `fjall deploy domain` and `fjall destroy domain` are teaching
|
|
26
|
+
* errors routing to the noun-verb domain commands, and domain infrastructure
|
|
27
|
+
* lives under the `fjall/domains/<zone>/` marker tree. An application named
|
|
28
|
+
* "domain" would therefore be undeployable and undestroyable from the moment
|
|
29
|
+
* it was created, so create time is the honest place to fail closed. Residue:
|
|
30
|
+
* an application created under an older CLI can still carry the name on disk;
|
|
31
|
+
* this list closes the door, it does not rename what is already there.
|
|
32
|
+
*
|
|
24
33
|
* **Boundary with `isReservedSlug`** — `webapp/app/.server/utils/reservedSlugs.ts`
|
|
25
34
|
* guards organisation slugs (URL-scoped, webapp-only). This list is
|
|
26
35
|
* application-scoped and ships from `@fjall/util` for cross-system reuse
|
|
@@ -30,7 +39,7 @@
|
|
|
30
39
|
* before testing, so `"Fjall"`, `"FJALL"`, and `"fjALL"` all reject
|
|
31
40
|
* identically.
|
|
32
41
|
*/
|
|
33
|
-
export declare const RESERVED_APP_NAMES: readonly ["fjall", "organisation", "platform", "account"];
|
|
42
|
+
export declare const RESERVED_APP_NAMES: readonly ["fjall", "organisation", "platform", "account", "domain"];
|
|
34
43
|
export type ReservedAppName = (typeof RESERVED_APP_NAMES)[number];
|
|
35
44
|
/**
|
|
36
45
|
* Canonical user-facing rejection message for a reserved application name.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var t=Object.defineProperty;var r=(e,o)=>t(e,"name",{value:o,configurable:!0});import{toKebab as n}from"./caseConversion.js";const a=["fjall","organisation","platform","account"],c="This application name is reserved for Fjall's organisation-tier infrastructure.";function p(e){return a.includes(e.toLowerCase())}r(p,"isReservedAppName");const i="-cache",A="Application names ending in '-cache' are reserved for Fjall build-cache repositories.";function S(e){return n(e).endsWith(i)}r(S,"hasReservedAppNameSuffix");export{a as RESERVED_APP_NAMES,c as RESERVED_APP_NAME_MESSAGE,i as RESERVED_APP_NAME_SUFFIX,A as RESERVED_APP_NAME_SUFFIX_MESSAGE,S as hasReservedAppNameSuffix,p as isReservedAppName};
|
|
1
|
+
var t=Object.defineProperty;var r=(e,o)=>t(e,"name",{value:o,configurable:!0});import{toKebab as n}from"./caseConversion.js";const a=["fjall","organisation","platform","account","domain"],c="This application name is reserved for Fjall's organisation-tier infrastructure.";function p(e){return a.includes(e.toLowerCase())}r(p,"isReservedAppName");const i="-cache",A="Application names ending in '-cache' are reserved for Fjall build-cache repositories.";function S(e){return n(e).endsWith(i)}r(S,"hasReservedAppNameSuffix");export{a as RESERVED_APP_NAMES,c as RESERVED_APP_NAME_MESSAGE,i as RESERVED_APP_NAME_SUFFIX,A as RESERVED_APP_NAME_SUFFIX_MESSAGE,S as hasReservedAppNameSuffix,p as isReservedAppName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
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
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Common utility methods",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -134,5 +134,5 @@
|
|
|
134
134
|
"engines": {
|
|
135
135
|
"node": ">=22.0.0"
|
|
136
136
|
},
|
|
137
|
-
"gitHead": "
|
|
137
|
+
"gitHead": "09dee795753839b82aedbb7d0cef1c5ef283df55"
|
|
138
138
|
}
|