@fjall/util 2.13.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 61 files minified at 2026-06-10T21:04:19.408Z
1
+ 64 files minified at 2026-06-12T02:44:39.673Z
package/dist/Config.d.ts CHANGED
@@ -14,6 +14,20 @@ export type VaultLockMode = (typeof VAULT_LOCK_MODES)[number];
14
14
  */
15
15
  export declare const S3_BPA_MODES: readonly ["enforced", "off"];
16
16
  export type S3BpaMode = (typeof S3_BPA_MODES)[number];
17
+ /**
18
+ * Org-level centralised root-access management mode (ADR
19
+ * 2026-06-10-centralised-root-access-default-on). Absent ⇒ "centralised"
20
+ * (default-on); "off" maps to OrgSetupConfig.skipRootAccessManagement at the
21
+ * adapter boundaries (CLI buildOrgSetupConfig, webapp setup route). The
22
+ * webapp carries a twin of this pair at
23
+ * webapp/app/.server/constants/rootAccess.ts.
24
+ * TODO 2026-06-11, owner: paul — tracked in
25
+ * aiDocs/plans/tasks/2026-06-11-root-access-p2-t11-org-config-field-fjall.md:
26
+ * once a published @fjall/util ships this tuple, convert the webapp twin into
27
+ * a re-export from "@fjall/util/config" (TRAIL_LIFECYCLE_STATES precedent).
28
+ */
29
+ export declare const ROOT_ACCESS_MANAGEMENT_MODES: readonly ["centralised", "off"];
30
+ export type RootAccessManagementMode = (typeof ROOT_ACCESS_MANAGEMENT_MODES)[number];
17
31
  /**
18
32
  * Per-account management-events-trail lifecycle for the organisation-trail
19
33
  * migration. Absent ⇒ legacy per-account trail, auto-eligible for migration
package/dist/Config.js CHANGED
@@ -1 +1 @@
1
- import*as i from"fs";import*as f from"path";import{z as c}from"zod";import{failure as d,success as h}from"./docker/result.js";import{getErrorMessage as g}from"./errorUtils.js";import{logger as u}from"./logger.js";import{maskSensitiveOutput as l}from"./securityHelpers.js";const y=10,O=["compliance","governance","none"],$=["enforced","off"],_=["account","draining","org"],x="managementEvents",F="organisationManagementEvents",K=["active","draining","removed"],b="FjallTrailBucketName",k="FjallTrailKeyArn",L="OrganisationTrailBucketName",w=c.object({name:c.string(),type:c.enum(["apex","delegated"]),parentDomain:c.string().optional(),account:c.string().optional()}).strict(),m=c.object({activeTarget:c.string().optional(),domains:c.array(w).optional()}).strict(),T=m.keyof().options;function E(C,e,t){const n=e[t];n!==void 0&&(C[t]=n)}class s{rootConfig;configPath=null;loadFailed=!1;clearedKeys=new Set;constructor(e,t){this.rootConfig=e??{},this.configPath=t??null}static findConfigDirectory(e){let t=e!==void 0&&e!==""?e:process.cwd();for(let n=0;n<y;n++){const o=f.join(t,"fjall"),a=f.join(o,"fjall-config.json");if(i.existsSync(a))return o;const r=f.join(t,"fjall-config.json");if(i.existsSync(r))return t;const p=f.dirname(t);if(p===t)break;t=p}return null}static loadConfigFile(e){try{return i.accessSync(e,i.constants.R_OK),i.readFileSync(e,{encoding:"utf8"})}catch(t){return u.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:l(g(t))}),null}}static loadConfig(e){const t=s.findConfigDirectory(e);if(!t)return new s;const n=f.join(t,"fjall-config.json"),o=s.loadConfigFile(n);if(o===null){const r=new s(void 0,n);return r.loadFailed=!0,r}let a;if(o!=="")try{a=m.parse(JSON.parse(o))}catch(r){throw s.formatZodError(r,"fjall-config.json")}return new s(a,n)}static formatZodError(e,t){if(e instanceof c.ZodError&&e.issues.length>0){const a=e.issues.map(r=>`${r.path.join(".")}: ${r.message}`).join("; ");return new Error(`Failed to parse ${t}: ${a}`)}const o=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${o}`)}saveConfig(){let e=this.configPath;if(!e){const r=s.findConfigDirectory()||f.join(process.cwd(),"fjall");e=f.join(r,"fjall-config.json")}if(this.loadFailed)return d(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 t=f.dirname(e);try{i.mkdirSync(t,{recursive:!0})}catch(r){return d(new Error(`Cannot create config directory ${t}: ${l(g(r))}`))}const n=s.assertWritable(e,t);if(!n.success)return n;const o=JSON.stringify(this.mergeWithDisk(e),null,2),a=`${e}.tmp-${process.pid}`;try{i.writeFileSync(a,o,{mode:384}),i.renameSync(a,e)}catch(r){return d(new Error(`Failed to save ${e}: ${l(g(r))}`))}return h(void 0)}static assertWritable(e,t){if(i.existsSync(e))try{i.accessSync(e,i.constants.W_OK)}catch{return d(new Error(`Cannot save ${e}: the file is read-only. Make it writable (e.g. chmod u+w ${e}) and retry.`))}try{i.accessSync(t,i.constants.W_OK)}catch{return d(new Error(`Cannot save ${e}: the directory ${t} is not writable. Make it writable (e.g. chmod u+w ${t}) and retry.`))}return h(void 0)}mergeWithDisk(e){const t=s.readDiskConfigForMerge(e);if(t===void 0)return this.rootConfig;const n={...t};for(const o of T)E(n,this.rootConfig,o);for(const o of this.clearedKeys)delete n[o];return n}static readDiskConfigForMerge(e){if(!i.existsSync(e))return;let t;try{t=JSON.parse(i.readFileSync(e,{encoding:"utf8"}))}catch(o){u.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:l(g(o))});return}const n=m.safeParse(t);if(!n.success){u.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:l(n.error.message)});return}return n.data}static getConfigDirectory(e){return s.findConfigDirectory(e)}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(e){this.rootConfig.activeTarget=e,this.clearedKeys.delete("activeTarget")}clearActiveTarget(){this.rootConfig.activeTarget=void 0,this.clearedKeys.add("activeTarget")}getDomains(){return this.rootConfig.domains??[]}setDomains(e){this.rootConfig.domains=e}addDomain(e){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(e)}getDomain(e){return this.rootConfig.domains?.find(t=>t.name.toLowerCase()===e.toLowerCase())}removeDomain(e){if(!this.rootConfig.domains)return!1;const t=this.rootConfig.domains.findIndex(n=>n.name.toLowerCase()===e.toLowerCase());return t===-1?!1:(this.rootConfig.domains.splice(t,1),!0)}}export{x as ACCOUNT_TRAIL_NAME,K as ACCOUNT_TRAIL_STATES,s as Config,F as ORGANISATION_TRAIL_NAME,L as ORG_TRAIL_BUCKET_OUTPUT_KEY,m as RootConfigSchema,$ as S3_BPA_MODES,b as TRAIL_BUCKET_OUTPUT_KEY,k as TRAIL_KEY_ARN_OUTPUT_KEY,_ as TRAIL_LIFECYCLE_STATES,O as VAULT_LOCK_MODES};
1
+ import*as i from"fs";import*as f from"path";import{z as c}from"zod";import{failure as d,success as C}from"./docker/result.js";import{getErrorMessage as g}from"./errorUtils.js";import{logger as u}from"./logger.js";import{maskSensitiveOutput as l}from"./securityHelpers.js";const y=10,D=["compliance","governance","none"],_=["enforced","off"],$=["centralised","off"],x=["account","draining","org"],F="managementEvents",K="organisationManagementEvents",M=["active","draining","removed"],R="FjallTrailBucketName",b="FjallTrailKeyArn",k="OrganisationTrailBucketName",T=c.object({name:c.string(),type:c.enum(["apex","delegated"]),parentDomain:c.string().optional(),account:c.string().optional()}).strict(),m=c.object({activeTarget:c.string().optional(),domains:c.array(T).optional()}).strict(),w=m.keyof().options;function E(h,e,t){const n=e[t];n!==void 0&&(h[t]=n)}class s{rootConfig;configPath=null;loadFailed=!1;clearedKeys=new Set;constructor(e,t){this.rootConfig=e??{},this.configPath=t??null}static findConfigDirectory(e){let t=e!==void 0&&e!==""?e:process.cwd();for(let n=0;n<y;n++){const o=f.join(t,"fjall"),a=f.join(o,"fjall-config.json");if(i.existsSync(a))return o;const r=f.join(t,"fjall-config.json");if(i.existsSync(r))return t;const p=f.dirname(t);if(p===t)break;t=p}return null}static loadConfigFile(e){try{return i.accessSync(e,i.constants.R_OK),i.readFileSync(e,{encoding:"utf8"})}catch(t){return u.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:l(g(t))}),null}}static loadConfig(e){const t=s.findConfigDirectory(e);if(!t)return new s;const n=f.join(t,"fjall-config.json"),o=s.loadConfigFile(n);if(o===null){const r=new s(void 0,n);return r.loadFailed=!0,r}let a;if(o!=="")try{a=m.parse(JSON.parse(o))}catch(r){throw s.formatZodError(r,"fjall-config.json")}return new s(a,n)}static formatZodError(e,t){if(e instanceof c.ZodError&&e.issues.length>0){const a=e.issues.map(r=>`${r.path.join(".")}: ${r.message}`).join("; ");return new Error(`Failed to parse ${t}: ${a}`)}const o=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${o}`)}saveConfig(){let e=this.configPath;if(!e){const r=s.findConfigDirectory()||f.join(process.cwd(),"fjall");e=f.join(r,"fjall-config.json")}if(this.loadFailed)return d(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 t=f.dirname(e);try{i.mkdirSync(t,{recursive:!0})}catch(r){return d(new Error(`Cannot create config directory ${t}: ${l(g(r))}`))}const n=s.assertWritable(e,t);if(!n.success)return n;const o=JSON.stringify(this.mergeWithDisk(e),null,2),a=`${e}.tmp-${process.pid}`;try{i.writeFileSync(a,o,{mode:384}),i.renameSync(a,e)}catch(r){return d(new Error(`Failed to save ${e}: ${l(g(r))}`))}return C(void 0)}static assertWritable(e,t){if(i.existsSync(e))try{i.accessSync(e,i.constants.W_OK)}catch{return d(new Error(`Cannot save ${e}: the file is read-only. Make it writable (e.g. chmod u+w ${e}) and retry.`))}try{i.accessSync(t,i.constants.W_OK)}catch{return d(new Error(`Cannot save ${e}: the directory ${t} is not writable. Make it writable (e.g. chmod u+w ${t}) and retry.`))}return C(void 0)}mergeWithDisk(e){const t=s.readDiskConfigForMerge(e);if(t===void 0)return this.rootConfig;const n={...t};for(const o of w)E(n,this.rootConfig,o);for(const o of this.clearedKeys)delete n[o];return n}static readDiskConfigForMerge(e){if(!i.existsSync(e))return;let t;try{t=JSON.parse(i.readFileSync(e,{encoding:"utf8"}))}catch(o){u.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:l(g(o))});return}const n=m.safeParse(t);if(!n.success){u.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:l(n.error.message)});return}return n.data}static getConfigDirectory(e){return s.findConfigDirectory(e)}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(e){this.rootConfig.activeTarget=e,this.clearedKeys.delete("activeTarget")}clearActiveTarget(){this.rootConfig.activeTarget=void 0,this.clearedKeys.add("activeTarget")}getDomains(){return this.rootConfig.domains??[]}setDomains(e){this.rootConfig.domains=e}addDomain(e){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(e)}getDomain(e){return this.rootConfig.domains?.find(t=>t.name.toLowerCase()===e.toLowerCase())}removeDomain(e){if(!this.rootConfig.domains)return!1;const t=this.rootConfig.domains.findIndex(n=>n.name.toLowerCase()===e.toLowerCase());return t===-1?!1:(this.rootConfig.domains.splice(t,1),!0)}}export{F as ACCOUNT_TRAIL_NAME,M as ACCOUNT_TRAIL_STATES,s as Config,K as ORGANISATION_TRAIL_NAME,k as ORG_TRAIL_BUCKET_OUTPUT_KEY,$ as ROOT_ACCESS_MANAGEMENT_MODES,m as RootConfigSchema,_ as S3_BPA_MODES,R as TRAIL_BUCKET_OUTPUT_KEY,b as TRAIL_KEY_ARN_OUTPUT_KEY,x as TRAIL_LIFECYCLE_STATES,D as VAULT_LOCK_MODES};
@@ -1,3 +1,6 @@
1
1
  export { AWSError, NoRolesFoundError, InvalidCredentialsError, SSOTokenExpiredError, MissingRegionError, ProfileNotFoundError, CommandError, isAWSError, isNoRolesFoundError, isSSOUnauthorizedError } from "./errors.js";
2
2
  export { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH, type ResourceEvent, isResourceEvent } from "./cloudformationTypes.js";
3
3
  export { CloudFormationFailureAnalyser, type RootCause, type FailureAnalysis } from "./CloudFormationFailureAnalyser.js";
4
+ export { IPAM_OPERATIONS_POOL_TAG_KEY, formatIpamPairTagValue } from "./ipamTags.js";
5
+ export { SDK_PRE_EMPTY_TAG_KEY } from "./infraTags.js";
6
+ export { ACCOUNT_MONITORING_ROLE_NAME } from "./monitoringRole.js";
package/dist/aws/index.js CHANGED
@@ -1 +1 @@
1
- import{AWSError as e,NoRolesFoundError as i,InvalidCredentialsError as n,SSOTokenExpiredError as E,MissingRegionError as s,ProfileNotFoundError as d,CommandError as S,isAWSError as l,isNoRolesFoundError as t,isSSOUnauthorizedError as a}from"./errors.js";import{STACK_NOT_FOUND_PATTERN as A,CDK_NO_STACKS_MATCH as C,isResourceEvent as N}from"./cloudformationTypes.js";import{CloudFormationFailureAnalyser as m}from"./CloudFormationFailureAnalyser.js";export{e as AWSError,C as CDK_NO_STACKS_MATCH,m as CloudFormationFailureAnalyser,S as CommandError,n as InvalidCredentialsError,s as MissingRegionError,i as NoRolesFoundError,d as ProfileNotFoundError,E as SSOTokenExpiredError,A as STACK_NOT_FOUND_PATTERN,l as isAWSError,t as isNoRolesFoundError,N as isResourceEvent,a as isSSOUnauthorizedError};
1
+ import{AWSError as e,NoRolesFoundError as E,InvalidCredentialsError as _,SSOTokenExpiredError as i,MissingRegionError as T,ProfileNotFoundError as n,CommandError as A,isAWSError as O,isNoRolesFoundError as a,isSSOUnauthorizedError as t}from"./errors.js";import{STACK_NOT_FOUND_PATTERN as m,CDK_NO_STACKS_MATCH as s,isResourceEvent as S}from"./cloudformationTypes.js";import{CloudFormationFailureAnalyser as l}from"./CloudFormationFailureAnalyser.js";import{IPAM_OPERATIONS_POOL_TAG_KEY as R,formatIpamPairTagValue as f}from"./ipamTags.js";import{SDK_PRE_EMPTY_TAG_KEY as u}from"./infraTags.js";import{ACCOUNT_MONITORING_ROLE_NAME as x}from"./monitoringRole.js";export{x as ACCOUNT_MONITORING_ROLE_NAME,e as AWSError,s as CDK_NO_STACKS_MATCH,l as CloudFormationFailureAnalyser,A as CommandError,R as IPAM_OPERATIONS_POOL_TAG_KEY,_ as InvalidCredentialsError,T as MissingRegionError,E as NoRolesFoundError,n as ProfileNotFoundError,u as SDK_PRE_EMPTY_TAG_KEY,i as SSOTokenExpiredError,m as STACK_NOT_FOUND_PATTERN,f as formatIpamPairTagValue,O as isAWSError,a as isNoRolesFoundError,S as isResourceEvent,t as isSSOUnauthorizedError};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The SDK pre-empty marker tag, shared by producer and consumer:
3
+ *
4
+ * - Producer (`@fjall/components-infrastructure`): the `S3Bucket` wrapper
5
+ * (`storage/s3.ts`) tags every DESTROY-policy bucket so destroy paths
6
+ * know it is safe to SDK-empty before CloudFormation delete (Phase 3
7
+ * autoDeleteObjects retirement).
8
+ * - Consumer (`@fjall/deploy-core`): `stackCleanup/bucketOps.ts` includes
9
+ * the key in `PRE_EMPTY_TAG_KEYS` — presence with value "true" opts a
10
+ * stack bucket into the pre-empty pass; a bucket carrying no pre-empty
11
+ * tag (Retain) is never touched.
12
+ *
13
+ * The literal is deployed infrastructure state — already-tagged buckets
14
+ * would silently lose pre-empty coverage (and so re-expose the quarantine
15
+ * window) on any rename. The pin test in `__tests__/infraTags.test.ts`
16
+ * guards against casual renames.
17
+ */
18
+ export declare const SDK_PRE_EMPTY_TAG_KEY = "fjall:sdk-pre-empty";
@@ -0,0 +1 @@
1
+ const _="fjall:sdk-pre-empty";export{_ as SDK_PRE_EMPTY_TAG_KEY};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The IPAM operations-pool pair tag, shared by producer and consumer:
3
+ *
4
+ * - Producer (`@fjall/components-infrastructure`): `ipamPool.ts` tags each
5
+ * account-scoped IPAM pool, and `standardTagsAspect.ts` tags each
6
+ * IPAM-allocated VPC, with `formatIpamPairTagValue(accountId, region)`.
7
+ * - Consumer (`@fjall/deploy-core`): `targetReadiness.ts` matches the
8
+ * owner-side pool for a deploy target by the same key + value.
9
+ *
10
+ * The literal shapes are deployed infrastructure state — changing either
11
+ * orphans every already-tagged pool and VPC. The pin test in
12
+ * `__tests__/ipamTags.test.ts` guards against casual renames.
13
+ */
14
+ export declare const IPAM_OPERATIONS_POOL_TAG_KEY = "fjall:operations:pool";
15
+ export declare function formatIpamPairTagValue(accountId: string, region: string): string;
@@ -0,0 +1 @@
1
+ const r="fjall:operations:pool";function t(o,a){return`${o}-${a}`}export{r as IPAM_OPERATIONS_POOL_TAG_KEY,t as formatIpamPairTagValue};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The fixed-name cross-account monitoring role, shared by producer and
3
+ * consumer:
4
+ *
5
+ * - Producer (`@fjall/components-infrastructure`): `AccountMonitoringRole`
6
+ * (`config/aws/accountMonitoringRole.ts`) creates the role under this
7
+ * name — the only resource the account-globals branch creates
8
+ * unconditionally (`patterns/aws/account.ts`).
9
+ * - Consumer (`@fjall/deploy-core`): `accountGlobals.ts` probes the role by
10
+ * name (IAM GetRole) — its presence IS "account globals deployed", the
11
+ * prerequisite for a non-primary-region deploy.
12
+ *
13
+ * The literal is deployed infrastructure state — the platform assumes this
14
+ * exact name in every connected account, so a rename strands existing
15
+ * estates. The pin test in `__tests__/monitoringRole.test.ts` guards
16
+ * against casual renames.
17
+ */
18
+ export declare const ACCOUNT_MONITORING_ROLE_NAME = "FjallMonitoring";
@@ -0,0 +1 @@
1
+ const o="FjallMonitoring";export{o as ACCOUNT_MONITORING_ROLE_NAME};
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { mapSettledWithConcurrency } from "./concurrency.js";
10
10
  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";
11
11
  export { RESOURCE_CATEGORIES, type ResourceCategory, categoriseResource, getExpectedDuration, getFriendlyResourceType } from "./resourceCategorisation.js";
12
12
  export { parseGitRemoteUrl, type GitProvider, type ParsedGitRemote } from "./gitRemoteParser.js";
13
- export { abbreviateRegion } from "./regions.js";
13
+ export { abbreviateRegion, AWS_REGIONS_METADATA, DEFAULT_REGION, getRegionInfo, MAX_SECONDARY_REGIONS, regions, suggestRegionForTimezone, type RegionCode, type RegionInfo } from "./regions.js";
14
14
  export { SCOPE_VALUES, type TokenScope } from "./tokenScopes.js";
15
15
  export { ConnectionWireSchema, type ConnectionWire, ConnectionsListResponseSchema, type ConnectionsListResponse } from "./connectionsWire.js";
16
16
  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 o,getDomainExportNames as t}from"./domainExports.js";import{BACKUP_VAULT_NAME as n}from"./backupVault.js";import{toPascalCase as i,toKebab as S,toValidDatabaseName as m,toScreamingSnake as s,capitalise as _,getSafeZoneName as A,accountConstructKey as C,hasAsciiStableConstructKey as p}from"./caseConversion.js";import{findAccountNameCollision as f}from"./accountNameCollision.js";import{normaliseError as R,getErrorMessage as c,hasErrorCode as g,getErrorCode as O,getErrorStack as x,formatErrorString as I}from"./errorUtils.js";import{singleton as l}from"./singleton.js";import{DANGEROUS_ENV_VARS as P,filterDangerousEnvVars as M,maskSensitiveOutput as V,parseShellArgs as U}from"./securityHelpers.js";import{sleep as v}from"./sleep.js";import{mapSettledWithConcurrency as H}from"./concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as G,STRUCTURAL_ENVIRONMENTS as b,ACCOUNT_STAGES as y,ACCOUNT_STAGE_LABELS as F,isAccountStage as K,ACCOUNT_TIERS as W,AccountTierSchema as X,isAccountTier as k,environmentToTier as B,stageFromWireEnvironment as Z,accountTier as j,getEnvironmentLabel as q,ACCOUNT_ROLES as w}from"./environments.js";import{RESOURCE_CATEGORIES as J,categoriseResource as Q,getExpectedDuration as Y,getFriendlyResourceType as $}from"./resourceCategorisation.js";import{parseGitRemoteUrl as re}from"./gitRemoteParser.js";import{abbreviateRegion as te}from"./regions.js";import{SCOPE_VALUES as ne}from"./tokenScopes.js";import{ConnectionWireSchema as ie,ConnectionsListResponseSchema as Se}from"./connectionsWire.js";import{deriveRegionsFromOrgConfig as se,deriveTargets as _e,deriveAllTargets as Ae,environmentOrTier as Ce,findTarget as pe,generateTargetName as Te}from"./targets.js";import{buildAppConfigPath as Ne}from"./appPath.js";import{findInfrastructurePaths as ce,findBoundaryPath as ge,isInfrastructureFile as Oe}from"./findInfrastructurePaths.js";import{inferContainerFromCandidates as Ie}from"./inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as le,isReservedAppName as de}from"./reservedAppNames.js";import{deriveContentHashTag as Me}from"./deriveContentHashTag.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as Ue,EXPECTED_SCHEMA_VERSION_ENV as De,EXPECTED_SCHEMA_VERSION_TOOL_ENV as ve,EXPECTED_CH_SCHEMA_VERSION_ENV as he,SCHEMA_ADMIN_USER_ENV as He,SCHEMA_ADMIN_PASSWORD_ENV as Le,PRISMA_MIGRATION_DIR_RE as Ge,CLICKHOUSE_MIGRATION_SKIP_RE as be}from"./migration/constants.js";export{w as ACCOUNT_ROLES,y as ACCOUNT_STAGES,G as ACCOUNT_STAGES_WITH_ROOT,F as ACCOUNT_STAGE_LABELS,W as ACCOUNT_TIERS,X as AccountTierSchema,n as BACKUP_VAULT_NAME,be as CLICKHOUSE_MIGRATION_SKIP_RE,ie as ConnectionWireSchema,Se as ConnectionsListResponseSchema,P as DANGEROUS_ENV_VARS,o as DNS_APEX,he as EXPECTED_CH_SCHEMA_VERSION_ENV,De as EXPECTED_SCHEMA_VERSION_ENV,ve as EXPECTED_SCHEMA_VERSION_TOOL_ENV,Ue as MIGRATION_SNAPSHOT_NAME_PREFIX,Ge as PRISMA_MIGRATION_DIR_RE,le as RESERVED_APP_NAMES,J as RESOURCE_CATEGORIES,Le as SCHEMA_ADMIN_PASSWORD_ENV,He as SCHEMA_ADMIN_USER_ENV,ne as SCOPE_VALUES,b as STRUCTURAL_ENVIRONMENTS,te as abbreviateRegion,C as accountConstructKey,j as accountTier,Ne as buildAppConfigPath,_ as capitalise,Q as categoriseResource,Ae as deriveAllTargets,Me as deriveContentHashTag,se as deriveRegionsFromOrgConfig,_e as deriveTargets,Ce as environmentOrTier,B as environmentToTier,M as filterDangerousEnvVars,f as findAccountNameCollision,ge as findBoundaryPath,ce as findInfrastructurePaths,pe as findTarget,I as formatErrorString,Te as generateTargetName,t as getDomainExportNames,q as getEnvironmentLabel,O as getErrorCode,c as getErrorMessage,x as getErrorStack,Y as getExpectedDuration,$ as getFriendlyResourceType,A as getSafeZoneName,p as hasAsciiStableConstructKey,g as hasErrorCode,Ie as inferContainerFromCandidates,K as isAccountStage,k as isAccountTier,Oe as isInfrastructureFile,de as isReservedAppName,H as mapSettledWithConcurrency,V as maskSensitiveOutput,R as normaliseError,re as parseGitRemoteUrl,U as parseShellArgs,l as singleton,v as sleep,Z as stageFromWireEnvironment,S as toKebab,i as toPascalCase,s as toScreamingSnake,m as toValidDatabaseName};
1
+ import{DNS_APEX as o,getDomainExportNames as t}from"./domainExports.js";import{BACKUP_VAULT_NAME as n}from"./backupVault.js";import{toPascalCase as i,toKebab as S,toValidDatabaseName as A,toScreamingSnake as _,capitalise as s,getSafeZoneName as m,accountConstructKey as C,hasAsciiStableConstructKey as T}from"./caseConversion.js";import{findAccountNameCollision as N}from"./accountNameCollision.js";import{normaliseError as f,getErrorMessage as g,hasErrorCode as c,getErrorCode as O,getErrorStack as I,formatErrorString as x}from"./errorUtils.js";import{singleton as l}from"./singleton.js";import{DANGEROUS_ENV_VARS as M,filterDangerousEnvVars as D,maskSensitiveOutput as P,parseShellArgs as U}from"./securityHelpers.js";import{sleep as v}from"./sleep.js";import{mapSettledWithConcurrency as h}from"./concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as H,STRUCTURAL_ENVIRONMENTS as b,ACCOUNT_STAGES as F,ACCOUNT_STAGE_LABELS as y,isAccountStage as K,ACCOUNT_TIERS as W,AccountTierSchema as X,isAccountTier as k,environmentToTier as B,stageFromWireEnvironment as z,accountTier as Y,getEnvironmentLabel as Z,ACCOUNT_ROLES as j}from"./environments.js";import{RESOURCE_CATEGORIES as w,categoriseResource as J,getExpectedDuration as Q,getFriendlyResourceType as $}from"./resourceCategorisation.js";import{parseGitRemoteUrl as re}from"./gitRemoteParser.js";import{abbreviateRegion as te,AWS_REGIONS_METADATA as Ee,DEFAULT_REGION as ne,getRegionInfo as ae,MAX_SECONDARY_REGIONS as ie,regions as Se,suggestRegionForTimezone as Ae}from"./regions.js";import{SCOPE_VALUES as se}from"./tokenScopes.js";import{ConnectionWireSchema as Ce,ConnectionsListResponseSchema as Te}from"./connectionsWire.js";import{deriveRegionsFromOrgConfig as Ne,deriveTargets as pe,deriveAllTargets as fe,environmentOrTier as ge,findTarget as ce,generateTargetName as Oe}from"./targets.js";import{buildAppConfigPath as xe}from"./appPath.js";import{findInfrastructurePaths as le,findBoundaryPath as de,isInfrastructureFile as Me}from"./findInfrastructurePaths.js";import{inferContainerFromCandidates as Pe}from"./inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as Ve,isReservedAppName as ve}from"./reservedAppNames.js";import{deriveContentHashTag as he}from"./deriveContentHashTag.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as He,EXPECTED_SCHEMA_VERSION_ENV as be,EXPECTED_SCHEMA_VERSION_TOOL_ENV as Fe,EXPECTED_CH_SCHEMA_VERSION_ENV as ye,SCHEMA_ADMIN_USER_ENV as Ke,SCHEMA_ADMIN_PASSWORD_ENV as We,PRISMA_MIGRATION_DIR_RE as Xe,CLICKHOUSE_MIGRATION_SKIP_RE as ke}from"./migration/constants.js";export{j as ACCOUNT_ROLES,F as ACCOUNT_STAGES,H as ACCOUNT_STAGES_WITH_ROOT,y as ACCOUNT_STAGE_LABELS,W as ACCOUNT_TIERS,Ee as AWS_REGIONS_METADATA,X as AccountTierSchema,n as BACKUP_VAULT_NAME,ke as CLICKHOUSE_MIGRATION_SKIP_RE,Ce as ConnectionWireSchema,Te as ConnectionsListResponseSchema,M as DANGEROUS_ENV_VARS,ne as DEFAULT_REGION,o as DNS_APEX,ye as EXPECTED_CH_SCHEMA_VERSION_ENV,be as EXPECTED_SCHEMA_VERSION_ENV,Fe as EXPECTED_SCHEMA_VERSION_TOOL_ENV,ie as MAX_SECONDARY_REGIONS,He as MIGRATION_SNAPSHOT_NAME_PREFIX,Xe as PRISMA_MIGRATION_DIR_RE,Ve as RESERVED_APP_NAMES,w as RESOURCE_CATEGORIES,We as SCHEMA_ADMIN_PASSWORD_ENV,Ke as SCHEMA_ADMIN_USER_ENV,se as SCOPE_VALUES,b as STRUCTURAL_ENVIRONMENTS,te as abbreviateRegion,C as accountConstructKey,Y as accountTier,xe as buildAppConfigPath,s as capitalise,J as categoriseResource,fe as deriveAllTargets,he as deriveContentHashTag,Ne as deriveRegionsFromOrgConfig,pe as deriveTargets,ge as environmentOrTier,B as environmentToTier,D as filterDangerousEnvVars,N as findAccountNameCollision,de as findBoundaryPath,le as findInfrastructurePaths,ce as findTarget,x as formatErrorString,Oe as generateTargetName,t as getDomainExportNames,Z as getEnvironmentLabel,O as getErrorCode,g as getErrorMessage,I as getErrorStack,Q as getExpectedDuration,$ as getFriendlyResourceType,ae as getRegionInfo,m as getSafeZoneName,T as hasAsciiStableConstructKey,c as hasErrorCode,Pe as inferContainerFromCandidates,K as isAccountStage,k as isAccountTier,Me as isInfrastructureFile,ve as isReservedAppName,h as mapSettledWithConcurrency,P as maskSensitiveOutput,f as normaliseError,re as parseGitRemoteUrl,U as parseShellArgs,Se as regions,l as singleton,v as sleep,z as stageFromWireEnvironment,Ae as suggestRegionForTimezone,S as toKebab,i as toPascalCase,_ as toScreamingSnake,A as toValidDatabaseName};
package/dist/regions.d.ts CHANGED
@@ -1,8 +1,169 @@
1
1
  /**
2
- * Region abbreviation utilities shared across CLI, webapp, and deploy-core.
2
+ * Region utilities shared across CLI, webapp, generator, and deploy-core.
3
+ *
4
+ * Single source of truth for AWS region metadata (ADR
5
+ * decisions/2026-06-11-region-semantics-and-provisioning.md D8). The
6
+ * generator and CLI re-export from here; the webapp imports directly.
3
7
  */
8
+ export interface RegionInfo {
9
+ code: string;
10
+ name: string;
11
+ city: string;
12
+ country: string;
13
+ }
14
+ export declare const DEFAULT_REGION = "us-east-2";
15
+ /** Cap on Organisation.secondaryRegions — API contract shared by every write surface. */
16
+ export declare const MAX_SECONDARY_REGIONS = 10;
17
+ export declare const AWS_REGIONS_METADATA: readonly [{
18
+ readonly code: "us-east-2";
19
+ readonly name: "US East";
20
+ readonly city: "Ohio";
21
+ readonly country: "USA";
22
+ }, {
23
+ readonly code: "us-west-2";
24
+ readonly name: "US West";
25
+ readonly city: "Oregon";
26
+ readonly country: "USA";
27
+ }, {
28
+ readonly code: "us-east-1";
29
+ readonly name: "US East";
30
+ readonly city: "N. Virginia";
31
+ readonly country: "USA";
32
+ }, {
33
+ readonly code: "eu-west-1";
34
+ readonly name: "EU West";
35
+ readonly city: "Ireland";
36
+ readonly country: "Ireland";
37
+ }, {
38
+ readonly code: "eu-central-1";
39
+ readonly name: "EU Central";
40
+ readonly city: "Frankfurt";
41
+ readonly country: "Germany";
42
+ }, {
43
+ readonly code: "ap-southeast-1";
44
+ readonly name: "Asia Pacific";
45
+ readonly city: "Singapore";
46
+ readonly country: "Singapore";
47
+ }, {
48
+ readonly code: "ap-northeast-1";
49
+ readonly name: "Asia Pacific";
50
+ readonly city: "Tokyo";
51
+ readonly country: "Japan";
52
+ }, {
53
+ readonly code: "ap-southeast-2";
54
+ readonly name: "Asia Pacific";
55
+ readonly city: "Sydney";
56
+ readonly country: "Australia";
57
+ }, {
58
+ readonly code: "us-west-1";
59
+ readonly name: "US West";
60
+ readonly city: "N. California";
61
+ readonly country: "USA";
62
+ }, {
63
+ readonly code: "ca-central-1";
64
+ readonly name: "Canada";
65
+ readonly city: "Central";
66
+ readonly country: "Canada";
67
+ }, {
68
+ readonly code: "af-south-1";
69
+ readonly name: "Africa";
70
+ readonly city: "Cape Town";
71
+ readonly country: "South Africa";
72
+ }, {
73
+ readonly code: "ap-east-1";
74
+ readonly name: "Asia Pacific";
75
+ readonly city: "Hong Kong";
76
+ readonly country: "China";
77
+ }, {
78
+ readonly code: "ap-northeast-2";
79
+ readonly name: "Asia Pacific";
80
+ readonly city: "Seoul";
81
+ readonly country: "South Korea";
82
+ }, {
83
+ readonly code: "ap-northeast-3";
84
+ readonly name: "Asia Pacific";
85
+ readonly city: "Osaka";
86
+ readonly country: "Japan";
87
+ }, {
88
+ readonly code: "ap-south-1";
89
+ readonly name: "Asia Pacific";
90
+ readonly city: "Mumbai";
91
+ readonly country: "India";
92
+ }, {
93
+ readonly code: "ap-south-2";
94
+ readonly name: "Asia Pacific";
95
+ readonly city: "Hyderabad";
96
+ readonly country: "India";
97
+ }, {
98
+ readonly code: "ap-southeast-3";
99
+ readonly name: "Asia Pacific";
100
+ readonly city: "Jakarta";
101
+ readonly country: "Indonesia";
102
+ }, {
103
+ readonly code: "ap-southeast-4";
104
+ readonly name: "Asia Pacific";
105
+ readonly city: "Melbourne";
106
+ readonly country: "Australia";
107
+ }, {
108
+ readonly code: "eu-central-2";
109
+ readonly name: "EU Central";
110
+ readonly city: "Zurich";
111
+ readonly country: "Switzerland";
112
+ }, {
113
+ readonly code: "eu-north-1";
114
+ readonly name: "EU North";
115
+ readonly city: "Stockholm";
116
+ readonly country: "Sweden";
117
+ }, {
118
+ readonly code: "eu-south-1";
119
+ readonly name: "EU South";
120
+ readonly city: "Milan";
121
+ readonly country: "Italy";
122
+ }, {
123
+ readonly code: "eu-south-2";
124
+ readonly name: "EU South";
125
+ readonly city: "Spain";
126
+ readonly country: "Spain";
127
+ }, {
128
+ readonly code: "eu-west-2";
129
+ readonly name: "EU West";
130
+ readonly city: "London";
131
+ readonly country: "UK";
132
+ }, {
133
+ readonly code: "eu-west-3";
134
+ readonly name: "EU West";
135
+ readonly city: "Paris";
136
+ readonly country: "France";
137
+ }, {
138
+ readonly code: "me-central-1";
139
+ readonly name: "Middle East";
140
+ readonly city: "UAE";
141
+ readonly country: "UAE";
142
+ }, {
143
+ readonly code: "me-south-1";
144
+ readonly name: "Middle East";
145
+ readonly city: "Bahrain";
146
+ readonly country: "Bahrain";
147
+ }, {
148
+ readonly code: "sa-east-1";
149
+ readonly name: "South America";
150
+ readonly city: "São Paulo";
151
+ readonly country: "Brazil";
152
+ }];
153
+ /** Union of supported region codes, derived from the metadata (SSoT). */
154
+ export type RegionCode = (typeof AWS_REGIONS_METADATA)[number]["code"];
155
+ export declare const regions: readonly string[];
156
+ export declare function getRegionInfo(code: string): RegionInfo | undefined;
4
157
  /**
5
158
  * Abbreviate an AWS region name to a compact form.
6
159
  * e.g. "us-east-1" → "use1", "eu-west-1" → "euw1"
7
160
  */
8
161
  export declare function abbreviateRegion(region: string): string;
162
+ /**
163
+ * Suggest the geographically nearest supported AWS region for an IANA
164
+ * timezone (e.g. from Intl.DateTimeFormat().resolvedOptions().timeZone).
165
+ * Returns undefined when no sensible suggestion exists (UTC, Etc/*,
166
+ * unrecognised input) — callers fall back to DEFAULT_REGION without
167
+ * "closest to you" justification copy.
168
+ */
169
+ export declare function suggestRegionForTimezone(timeZone: string): RegionCode | undefined;
package/dist/regions.js CHANGED
@@ -1 +1 @@
1
- function n(e){const t=e.split("-");return t.length===3&&t[0]&&t[1]&&t[1].length>0&&t[2]?`${t[0]}${t[1][0]}${t[2]}`:e}export{n as abbreviateRegion};
1
+ const i="us-east-2",n=10,o=Object.freeze([{code:"us-east-2",name:"US East",city:"Ohio",country:"USA"},{code:"us-west-2",name:"US West",city:"Oregon",country:"USA"},{code:"us-east-1",name:"US East",city:"N. Virginia",country:"USA"},{code:"eu-west-1",name:"EU West",city:"Ireland",country:"Ireland"},{code:"eu-central-1",name:"EU Central",city:"Frankfurt",country:"Germany"},{code:"ap-southeast-1",name:"Asia Pacific",city:"Singapore",country:"Singapore"},{code:"ap-northeast-1",name:"Asia Pacific",city:"Tokyo",country:"Japan"},{code:"ap-southeast-2",name:"Asia Pacific",city:"Sydney",country:"Australia"},{code:"us-west-1",name:"US West",city:"N. California",country:"USA"},{code:"ca-central-1",name:"Canada",city:"Central",country:"Canada"},{code:"af-south-1",name:"Africa",city:"Cape Town",country:"South Africa"},{code:"ap-east-1",name:"Asia Pacific",city:"Hong Kong",country:"China"},{code:"ap-northeast-2",name:"Asia Pacific",city:"Seoul",country:"South Korea"},{code:"ap-northeast-3",name:"Asia Pacific",city:"Osaka",country:"Japan"},{code:"ap-south-1",name:"Asia Pacific",city:"Mumbai",country:"India"},{code:"ap-south-2",name:"Asia Pacific",city:"Hyderabad",country:"India"},{code:"ap-southeast-3",name:"Asia Pacific",city:"Jakarta",country:"Indonesia"},{code:"ap-southeast-4",name:"Asia Pacific",city:"Melbourne",country:"Australia"},{code:"eu-central-2",name:"EU Central",city:"Zurich",country:"Switzerland"},{code:"eu-north-1",name:"EU North",city:"Stockholm",country:"Sweden"},{code:"eu-south-1",name:"EU South",city:"Milan",country:"Italy"},{code:"eu-south-2",name:"EU South",city:"Spain",country:"Spain"},{code:"eu-west-2",name:"EU West",city:"London",country:"UK"},{code:"eu-west-3",name:"EU West",city:"Paris",country:"France"},{code:"me-central-1",name:"Middle East",city:"UAE",country:"UAE"},{code:"me-south-1",name:"Middle East",city:"Bahrain",country:"Bahrain"},{code:"sa-east-1",name:"South America",city:"S\xE3o Paulo",country:"Brazil"}]),r=Object.freeze(o.map(e=>e.code));function c(e){return o.find(a=>a.code===e)}function A(e){const a=e.split("-");return a.length===3&&a[0]&&a[1]&&a[2]?`${a[0]}${a[1][0]}${a[2]}`:e}const s=Object.freeze({"Europe/London":"eu-west-2","Europe/Jersey":"eu-west-2","Europe/Guernsey":"eu-west-2","Europe/Isle_of_Man":"eu-west-2","Europe/Dublin":"eu-west-1","Europe/Paris":"eu-west-3","Europe/Berlin":"eu-central-1","Europe/Amsterdam":"eu-central-1","Europe/Brussels":"eu-central-1","Europe/Luxembourg":"eu-central-1","Europe/Vienna":"eu-central-1","Europe/Prague":"eu-central-1","Europe/Warsaw":"eu-central-1","Europe/Budapest":"eu-central-1","Europe/Zurich":"eu-central-2","Europe/Stockholm":"eu-north-1","Europe/Oslo":"eu-north-1","Europe/Copenhagen":"eu-north-1","Europe/Helsinki":"eu-north-1","Europe/Tallinn":"eu-north-1","Europe/Riga":"eu-north-1","Europe/Vilnius":"eu-north-1","Europe/Rome":"eu-south-1","Europe/Malta":"eu-south-1","Europe/Athens":"eu-south-1","Europe/Madrid":"eu-south-2","Europe/Lisbon":"eu-south-2","America/New_York":"us-east-1","America/Detroit":"us-east-1","America/Toronto":"ca-central-1","America/Montreal":"ca-central-1","America/Halifax":"ca-central-1","America/Winnipeg":"ca-central-1","America/Chicago":"us-east-2","America/Indiana/Indianapolis":"us-east-2","America/Mexico_City":"us-east-2","America/Denver":"us-west-2","America/Phoenix":"us-west-2","America/Edmonton":"us-west-2","America/Vancouver":"us-west-2","America/Los_Angeles":"us-west-1","America/Tijuana":"us-west-1","America/Sao_Paulo":"sa-east-1","America/Argentina/Buenos_Aires":"sa-east-1","America/Santiago":"sa-east-1","America/Montevideo":"sa-east-1","America/Lima":"sa-east-1","America/Bogota":"us-east-1","Asia/Tokyo":"ap-northeast-1","Asia/Seoul":"ap-northeast-2","Asia/Hong_Kong":"ap-east-1","Asia/Shanghai":"ap-east-1","Asia/Taipei":"ap-east-1","Asia/Macau":"ap-east-1","Asia/Singapore":"ap-southeast-1","Asia/Kuala_Lumpur":"ap-southeast-1","Asia/Bangkok":"ap-southeast-1","Asia/Ho_Chi_Minh":"ap-southeast-1","Asia/Manila":"ap-southeast-1","Asia/Jakarta":"ap-southeast-3","Asia/Kolkata":"ap-south-1","Asia/Calcutta":"ap-south-1","Asia/Karachi":"ap-south-1","Asia/Dhaka":"ap-south-1","Asia/Colombo":"ap-south-1","Asia/Dubai":"me-central-1","Asia/Muscat":"me-central-1","Asia/Qatar":"me-south-1","Asia/Bahrain":"me-south-1","Asia/Riyadh":"me-south-1","Asia/Kuwait":"me-south-1","Asia/Jerusalem":"me-south-1","Australia/Sydney":"ap-southeast-2","Australia/Brisbane":"ap-southeast-2","Australia/Canberra":"ap-southeast-2","Australia/Melbourne":"ap-southeast-4","Australia/Hobart":"ap-southeast-4","Australia/Adelaide":"ap-southeast-4","Pacific/Auckland":"ap-southeast-2","Pacific/Honolulu":"us-west-1","Africa/Johannesburg":"af-south-1","Africa/Casablanca":"eu-south-2","Africa/Cairo":"me-south-1","Indian/Maldives":"ap-south-1"}),u=Object.freeze({Europe:"eu-central-1",America:"us-east-1",Asia:"ap-southeast-1",Australia:"ap-southeast-2",Pacific:"ap-southeast-2",Africa:"af-south-1",Atlantic:"eu-west-1",Indian:"ap-south-1"});function p(e){if(!e)return;const a=s[e];if(a!==void 0)return a;const t=e.split("/")[0];if(t!==void 0)return u[t]}export{o as AWS_REGIONS_METADATA,i as DEFAULT_REGION,n as MAX_SECONDARY_REGIONS,A as abbreviateRegion,c as getRegionInfo,r as regions,p as suggestRegionForTimezone};
@@ -32,8 +32,8 @@ export declare const SCOPED_TOKEN_REGEX: RegExp;
32
32
  * Mask sensitive information in output strings to prevent credential leakage.
33
33
  * Patterns: postgres://user:pass@host, password=xxx, secret=xxx, apikey=xxx,
34
34
  * GitHub tokens (ghu_/ghs_/ghp_/gho_/github_pat_), bare AWS access-key IDs
35
- * (AKIA-prefixed and ASIA-prefixed), AWS secret keys, ARN account IDs,
36
- * scoped agent tokens.
35
+ * (AKIA-prefixed and ASIA-prefixed), AWS secret keys (env, INI, and JSON key
36
+ * spellings), session tokens, ARN account IDs, scoped agent tokens.
37
37
  *
38
38
  * Single source of truth — consumer loggers (CLI, worker, webapp) MUST
39
39
  * NOT re-implement these patterns inline. See
@@ -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 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};
1
+ const A=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 l(e){return Object.fromEntries(Object.entries(e).filter(([r])=>!A.has(r.toUpperCase())))}const i=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/,c=/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\s*=\s*["']?|SecretAccessKey[=:]\s*|"(aws)?secretAccessKey":\s*"|"aws_secret_access_key":\s*")[A-Za-z0-9/+=]{40,}/gi,"***").replace(/(arn:aws[^:]*:[^:]*:[^:]*:)(\d{12})(:[^\s]*)/g,"$1***$3").replace(/(?<="(aws)?sessionToken":\s*"|"aws_session_token":\s*")[^"]+/gi,"***").replace(/(?<=aws_session_token\s*[=:]\s*["']?|SessionToken[=:]\s*["']?)[^\s"']+/gi,"***").replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,"***").replace(c,"fjall_ak_***")}function D(e){const r=[];let s="",E=!1,_=!1,n=!1,t=!1;for(const a of e){if(n){s+=a,n=!1;continue}if(a==="\\"&&!E){n=!0;continue}if(a==="'"&&!_){E=!E,t=!0;continue}if(a==='"'&&!E){_=!_,t=!0;continue}if(a===" "&&!E&&!_){(s||t)&&(r.push(s),s="",t=!1);continue}s+=a}if(E||_)throw new Error(`Unbalanced ${E?"single":"double"} quote in command: ${e.slice(0,80)}`);if(n)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(s||t)&&r.push(s),r}export{A as DANGEROUS_ENV_VARS,i as SCOPED_TOKEN_REGEX,l 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.13.0",
3
+ "version": "2.15.0",
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": "5b16c5731256628f829d4168c65cf165b3516f9a"
120
+ "gitHead": "b2223855907cb6d467e47df073b2a5b28c684ede"
121
121
  }