@fjall/util 2.9.1 → 2.11.1

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
- 59 files minified at 2026-06-02T21:42:36.870Z
1
+ 59 files minified at 2026-06-07T01:01:57.688Z
package/dist/Config.d.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * Backup Vault Lock modes for an account's DisasterRecovery vault.
4
+ * See decisions/2026-06-03-backup-vault-lock-mode-by-intent.md.
5
+ */
6
+ export declare const VAULT_LOCK_MODES: readonly ["compliance", "governance", "none"];
7
+ export type VaultLockMode = (typeof VAULT_LOCK_MODES)[number];
2
8
  export type ProviderAccount = {
3
9
  id: string;
4
10
  name: string;
@@ -11,6 +17,15 @@ export type ProviderAccount = {
11
17
  * lookup (accountId+region), so it must be stamped before cascade deploy.
12
18
  */
13
19
  region?: string;
20
+ /**
21
+ * Backup Vault Lock mode for this account's DisasterRecovery vault. Defaults
22
+ * to "governance" (lock-protected but removable by privileged IAM).
23
+ * "compliance" is permanently immutable after a 3-day cooling-off and
24
+ * requires acknowledgeImmutableVaultLock. "none" disables the lock.
25
+ */
26
+ vaultLock?: VaultLockMode;
27
+ /** Explicit acknowledgement that vaultLock: "compliance" is irreversible. */
28
+ acknowledgeImmutableVaultLock?: boolean;
14
29
  };
15
30
  export type Profile = {
16
31
  type: "sso" | "oidc";
package/dist/Config.js CHANGED
@@ -1 +1 @@
1
- import*as r from"fs";import*as s from"path";import{z as e}from"zod";import{logger as g}from"./logger.js";const l=10,m=e.object({name:e.string(),type:e.enum(["apex","delegated"]),parentDomain:e.string().optional(),account:e.string().optional()}).strict(),d=e.object({activeTarget:e.string().optional(),domains:e.array(m).optional()}).strict();class a{rootConfig;configPath=null;constructor(o,t){this.rootConfig=o??{},this.configPath=t??null}static findConfigDirectory(){let o=process.cwd();for(let t=0;t<l;t++){const n=s.join(o,"fjall"),i=s.join(n,"fjall-config.json");if(r.existsSync(i))return n;const c=s.join(o,"fjall-config.json");if(r.existsSync(c))return o;const f=s.dirname(o);if(f===o)break;o=f}return null}static loadConfigFile(o){try{return r.accessSync(o,r.constants.R_OK|r.constants.W_OK),r.readFileSync(o,{encoding:"utf8"})}catch(t){return g.debug("Config","Config file not accessible",{file:o,error:t instanceof Error?t.message:String(t)}),null}}static loadConfig(){const o=a.findConfigDirectory();if(!o)return new a;const t=s.join(o,"fjall-config.json"),n=a.loadConfigFile(t);let i;if(n)try{i=d.parse(JSON.parse(n))}catch(c){throw a.formatZodError(c,"fjall-config.json")}return new a(i,t)}static formatZodError(o,t){if(o instanceof e.ZodError&&o.issues.length>0){const c=o.issues.map(f=>`${f.path.join(".")}: ${f.message}`).join("; ");return new Error(`Failed to parse ${t}: ${c}`)}const i=(o instanceof Error?o.message:String(o)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${i}`)}saveConfig(){let o=this.configPath;if(!o){const c=a.findConfigDirectory()||s.join(process.cwd(),"fjall");o=s.join(c,"fjall-config.json")}const t=s.dirname(o);r.mkdirSync(t,{recursive:!0});const n=JSON.stringify(this.rootConfig,null,2),i=`${o}.tmp`;r.writeFileSync(i,n,{mode:384}),r.renameSync(i,o)}static getConfigDirectory(){return a.findConfigDirectory()}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(o){this.rootConfig.activeTarget=o}clearActiveTarget(){this.rootConfig.activeTarget=void 0}getDomains(){return this.rootConfig.domains??[]}setDomains(o){this.rootConfig.domains=o}addDomain(o){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(o)}getDomain(o){return this.rootConfig.domains?.find(t=>t.name.toLowerCase()===o.toLowerCase())}removeDomain(o){if(!this.rootConfig.domains)return!1;const t=this.rootConfig.domains.findIndex(n=>n.name.toLowerCase()===o.toLowerCase());return t===-1?!1:(this.rootConfig.domains.splice(t,1),!0)}}export{a as Config,d as RootConfigSchema};
1
+ import*as r from"fs";import*as s from"path";import{z as e}from"zod";import{logger as g}from"./logger.js";const l=10,h=["compliance","governance","none"],m=e.object({name:e.string(),type:e.enum(["apex","delegated"]),parentDomain:e.string().optional(),account:e.string().optional()}).strict(),d=e.object({activeTarget:e.string().optional(),domains:e.array(m).optional()}).strict();class a{rootConfig;configPath=null;constructor(o,t){this.rootConfig=o??{},this.configPath=t??null}static findConfigDirectory(){let o=process.cwd();for(let t=0;t<l;t++){const n=s.join(o,"fjall"),i=s.join(n,"fjall-config.json");if(r.existsSync(i))return n;const c=s.join(o,"fjall-config.json");if(r.existsSync(c))return o;const f=s.dirname(o);if(f===o)break;o=f}return null}static loadConfigFile(o){try{return r.accessSync(o,r.constants.R_OK|r.constants.W_OK),r.readFileSync(o,{encoding:"utf8"})}catch(t){return g.debug("Config","Config file not accessible",{file:o,error:t instanceof Error?t.message:String(t)}),null}}static loadConfig(){const o=a.findConfigDirectory();if(!o)return new a;const t=s.join(o,"fjall-config.json"),n=a.loadConfigFile(t);let i;if(n)try{i=d.parse(JSON.parse(n))}catch(c){throw a.formatZodError(c,"fjall-config.json")}return new a(i,t)}static formatZodError(o,t){if(o instanceof e.ZodError&&o.issues.length>0){const c=o.issues.map(f=>`${f.path.join(".")}: ${f.message}`).join("; ");return new Error(`Failed to parse ${t}: ${c}`)}const i=(o instanceof Error?o.message:String(o)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${i}`)}saveConfig(){let o=this.configPath;if(!o){const c=a.findConfigDirectory()||s.join(process.cwd(),"fjall");o=s.join(c,"fjall-config.json")}const t=s.dirname(o);r.mkdirSync(t,{recursive:!0});const n=JSON.stringify(this.rootConfig,null,2),i=`${o}.tmp`;r.writeFileSync(i,n,{mode:384}),r.renameSync(i,o)}static getConfigDirectory(){return a.findConfigDirectory()}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(o){this.rootConfig.activeTarget=o}clearActiveTarget(){this.rootConfig.activeTarget=void 0}getDomains(){return this.rootConfig.domains??[]}setDomains(o){this.rootConfig.domains=o}addDomain(o){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(o)}getDomain(o){return this.rootConfig.domains?.find(t=>t.name.toLowerCase()===o.toLowerCase())}removeDomain(o){if(!this.rootConfig.domains)return!1;const t=this.rootConfig.domains.findIndex(n=>n.name.toLowerCase()===o.toLowerCase());return t===-1?!1:(this.rootConfig.domains.splice(t,1),!0)}}export{a as Config,d as RootConfigSchema,h as VAULT_LOCK_MODES};
@@ -76,6 +76,7 @@ declare const ManifestServiceSchema: z.ZodObject<{
76
76
  containerPort: z.ZodOptional<z.ZodNumber>;
77
77
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
78
78
  ssmSecretsPath: z.ZodOptional<z.ZodString>;
79
+ importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
79
80
  }, z.core.$strict>;
80
81
  export type ManifestService = z.infer<typeof ManifestServiceSchema>;
81
82
  declare const ManifestPatternSchema: z.ZodObject<{
@@ -94,6 +95,7 @@ declare const ManifestLambdaSchema: z.ZodObject<{
94
95
  name: z.ZodString;
95
96
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
96
97
  ssmSecretsPath: z.ZodOptional<z.ZodString>;
98
+ importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
97
99
  }, z.core.$strict>;
98
100
  export type ManifestLambda = z.infer<typeof ManifestLambdaSchema>;
99
101
  declare const ManifestStackHashSchema: z.ZodObject<{
@@ -133,11 +135,13 @@ export declare const FjallManifestSchema: z.ZodObject<{
133
135
  containerPort: z.ZodOptional<z.ZodNumber>;
134
136
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
135
137
  ssmSecretsPath: z.ZodOptional<z.ZodString>;
138
+ importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
136
139
  }, z.core.$strict>>;
137
140
  lambdas: z.ZodArray<z.ZodObject<{
138
141
  name: z.ZodString;
139
142
  secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
140
143
  ssmSecretsPath: z.ZodOptional<z.ZodString>;
144
+ importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
141
145
  }, z.core.$strict>>;
142
146
  pattern: z.ZodOptional<z.ZodObject<{
143
147
  type: z.ZodEnum<{
@@ -1 +1 @@
1
- import{z as t}from"zod";const h="fjall-manifest.json",u=1,s=t.object({path:t.string(),context:t.string().min(1,"context cannot be empty").optional(),target:t.string().min(1,"target cannot be empty").optional(),buildArgs:t.record(t.string(),t.string()).optional()}).strict(),b=s.partial();function S(e,n){if(n===void 0)return e;const r=n.context??e.context,a=n.target??e.target,o=n.buildArgs??e.buildArgs;return{path:n.path??e.path,...r!==void 0&&{context:r},...a!==void 0&&{target:a},...o!==void 0&&{buildArgs:o}}}const c=t.object({name:t.string(),clusterName:t.string().optional(),docker:s.optional(),containerPort:t.number().optional(),secrets:t.array(t.string()).optional(),ssmSecretsPath:t.string().optional()}).strict(),i=t.object({type:t.enum(["payload"]),name:t.string(),source:t.string()}).strict(),p=t.object({repositoryName:t.string()}).strict(),g=t.object({name:t.string(),secrets:t.array(t.string()).optional(),ssmSecretsPath:t.string().optional()}).strict(),m=t.object({templateHash:t.string(),synthTimestamp:t.string()}).strict(),l=t.object({constructPath:t.string().max(512),group:t.string().max(128),resourceType:t.string().max(256)}).strict(),x=t.object({version:t.literal(u),generatedAt:t.string(),appName:t.string(),services:t.array(c),lambdas:t.array(g),pattern:i.optional(),ecr:p.optional(),stacks:t.record(t.string(),m),resourceMap:t.record(t.string(),l).optional()}).strict();export{b as DockerBuildPartialSchema,s as DockerBuildSchema,h as FJALL_MANIFEST_FILENAME,x as FjallManifestSchema,u as MANIFEST_SCHEMA_VERSION,p as ManifestEcrSchema,g as ManifestLambdaSchema,i as ManifestPatternSchema,c as ManifestServiceSchema,m as ManifestStackHashSchema,l as ResourceMapEntrySchema,S as mergeDockerBuild};
1
+ import{z as t}from"zod";const h="fjall-manifest.json",d=1,s=t.object({path:t.string(),context:t.string().min(1,"context cannot be empty").optional(),target:t.string().min(1,"target cannot be empty").optional(),buildArgs:t.record(t.string(),t.string()).optional()}).strict(),S=s.partial();function b(e,r){if(r===void 0)return e;const n=r.context??e.context,a=r.target??e.target,o=r.buildArgs??e.buildArgs;return{path:r.path??e.path,...n!==void 0&&{context:n},...a!==void 0&&{target:a},...o!==void 0&&{buildArgs:o}}}const c=t.object({name:t.string(),clusterName:t.string().optional(),docker:s.optional(),containerPort:t.number().optional(),secrets:t.array(t.string()).optional(),ssmSecretsPath:t.string().optional(),importedSecretNames:t.array(t.string()).optional()}).strict(),i=t.object({type:t.enum(["payload"]),name:t.string(),source:t.string()}).strict(),p=t.object({repositoryName:t.string()}).strict(),g=t.object({name:t.string(),secrets:t.array(t.string()).optional(),ssmSecretsPath:t.string().optional(),importedSecretNames:t.array(t.string()).optional()}).strict(),m=t.object({templateHash:t.string(),synthTimestamp:t.string()}).strict(),l=t.object({constructPath:t.string().max(512),group:t.string().max(128),resourceType:t.string().max(256)}).strict(),x=t.object({version:t.literal(d),generatedAt:t.string(),appName:t.string(),services:t.array(c),lambdas:t.array(g),pattern:i.optional(),ecr:p.optional(),stacks:t.record(t.string(),m),resourceMap:t.record(t.string(),l).optional()}).strict();export{S as DockerBuildPartialSchema,s as DockerBuildSchema,h as FJALL_MANIFEST_FILENAME,x as FjallManifestSchema,d as MANIFEST_SCHEMA_VERSION,p as ManifestEcrSchema,g as ManifestLambdaSchema,i as ManifestPatternSchema,c as ManifestServiceSchema,m as ManifestStackHashSchema,l as ResourceMapEntrySchema,b as mergeDockerBuild};
@@ -189,11 +189,11 @@ export declare const ResolvedInputsSchema: z.ZodObject<{
189
189
  }>>;
190
190
  patternDomain: z.ZodOptional<z.ZodString>;
191
191
  network: z.ZodOptional<z.ZodEnum<{
192
+ none: "none";
192
193
  lightweight: "lightweight";
193
194
  standard: "standard";
194
195
  resilient: "resilient";
195
196
  enterprise: "enterprise";
196
- none: "none";
197
197
  }>>;
198
198
  template: z.ZodOptional<z.ZodString>;
199
199
  docker: z.ZodOptional<z.ZodObject<{
@@ -225,11 +225,11 @@ export declare const ScaffoldPlanSchema: z.ZodObject<{
225
225
  }>>;
226
226
  patternDomain: z.ZodOptional<z.ZodString>;
227
227
  network: z.ZodOptional<z.ZodEnum<{
228
+ none: "none";
228
229
  lightweight: "lightweight";
229
230
  standard: "standard";
230
231
  resilient: "resilient";
231
232
  enterprise: "enterprise";
232
- none: "none";
233
233
  }>>;
234
234
  template: z.ZodOptional<z.ZodString>;
235
235
  docker: z.ZodOptional<z.ZodObject<{
@@ -1 +1 @@
1
- const n=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function i(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!n.has(t.toUpperCase())))}const c=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/,l=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/g;function o(e){return e.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----(?:[^"\\]|\\[\\nrt"])*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,"$1***$2").replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,"$1$2***").replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,"$1***").replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,"***").replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,"$1***").replace(/(?<=AWS_SECRET_ACCESS_KEY=|SecretAccessKey[=:]\s*|"secretAccessKey":\s*")[A-Za-z0-9/+=]{40,}/g,"***").replace(/(arn:aws[^:]*:[^:]*:[^:]*:)(\d{12})(:[^\s]*)/g,"$1***$3").replace(/(?<="(aws)?[Ss]essionToken":\s*")[^"]+/g,"***").replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,"***").replace(l,"fjall_ak_***")}function D(e){const t=[];let E="",r=!1,A=!1,_=!1,s=!1;for(const a of e){if(_){E+=a,_=!1;continue}if(a==="\\"&&!r){_=!0;continue}if(a==="'"&&!A){r=!r,s=!0;continue}if(a==='"'&&!r){A=!A,s=!0;continue}if(a===" "&&!r&&!A){(E||s)&&(t.push(E),E="",s=!1);continue}E+=a}if(r||A)throw new Error(`Unbalanced ${r?"single":"double"} quote in command: ${e.slice(0,80)}`);if(_)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(E||s)&&t.push(E),t}export{n as DANGEROUS_ENV_VARS,c as SCOPED_TOKEN_REGEX,i as filterDangerousEnvVars,o as maskSensitiveOutput,D as parseShellArgs};
1
+ const n=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function c(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!n.has(t.toUpperCase())))}const i=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/,l=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/g;function o(e){return e.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----(?:[^"\\]|\\[\\nrt"])*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,"$1***$2").replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,"$1$2***").replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,"$1***").replace(/\b(sk|rk)_(live|test)_[A-Za-z0-9]{8,}/g,"$1_$2_***").replace(/\bwhsec_[A-Za-z0-9]{8,}/g,"whsec_***").replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,"***").replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,"$1***").replace(/(?<=AWS_SECRET_ACCESS_KEY=|SecretAccessKey[=:]\s*|"secretAccessKey":\s*")[A-Za-z0-9/+=]{40,}/g,"***").replace(/(arn:aws[^:]*:[^:]*:[^:]*:)(\d{12})(:[^\s]*)/g,"$1***$3").replace(/(?<="(aws)?[Ss]essionToken":\s*")[^"]+/g,"***").replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,"***").replace(l,"fjall_ak_***")}function D(e){const t=[];let E="",r=!1,A=!1,_=!1,s=!1;for(const a of e){if(_){E+=a,_=!1;continue}if(a==="\\"&&!r){_=!0;continue}if(a==="'"&&!A){r=!r,s=!0;continue}if(a==='"'&&!r){A=!A,s=!0;continue}if(a===" "&&!r&&!A){(E||s)&&(t.push(E),E="",s=!1);continue}E+=a}if(r||A)throw new Error(`Unbalanced ${r?"single":"double"} quote in command: ${e.slice(0,80)}`);if(_)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(E||s)&&t.push(E),t}export{n as DANGEROUS_ENV_VARS,i as SCOPED_TOKEN_REGEX,c as filterDangerousEnvVars,o as maskSensitiveOutput,D as parseShellArgs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/util",
3
- "version": "2.9.1",
3
+ "version": "2.11.1",
4
4
  "description": "Common utility methods",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -117,5 +117,5 @@
117
117
  "engines": {
118
118
  "node": ">=22.0.0"
119
119
  },
120
- "gitHead": "a97423cf3df727994364a0907fa2b5c544a86b0d"
120
+ "gitHead": "69823c3d7f2eacba419657464381119c5b5b5fd6"
121
121
  }