@fjall/util 3.5.2 → 3.6.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
- 88 files minified at 2026-07-19T20:58:59.626Z
1
+ 88 files minified at 2026-07-19T21:13:09.946Z
package/dist/config.d.ts CHANGED
@@ -130,7 +130,13 @@ export type SSOSession = {
130
130
  ssoRegion: string;
131
131
  ssoStartUrl: string;
132
132
  };
133
- declare const DomainConfigSchema: z.ZodObject<{
133
+ /**
134
+ * STRICT wire contract for one `domains[]` entry in fjall-config.json —
135
+ * unknown keys are rejected, never silently carried. This is the write-path
136
+ * schema shared with the webapp's domain-topology resolver; a scaffold or
137
+ * saver emitting a key this schema rejects is exactly the drift it prevents.
138
+ */
139
+ export declare const DomainConfigSchema: z.ZodObject<{
134
140
  name: z.ZodString;
135
141
  type: z.ZodEnum<{
136
142
  apex: "apex";
@@ -153,6 +159,30 @@ export declare const RootConfigSchema: z.ZodObject<{
153
159
  }, z.core.$strict>>>;
154
160
  }, z.core.$strict>;
155
161
  export type RootConfig = z.infer<typeof RootConfigSchema>;
162
+ /**
163
+ * Tolerant READ schema for the deploy-time load path. Intentionally NOT
164
+ * `.strict()`: a fjall-config.json written by a different fjall version, or a
165
+ * legacy pre-config-split file, may carry top-level keys this version does not
166
+ * know (`version`, `services`, `providerAccounts`, `generatorVersion`, …).
167
+ * Stripping them and reading only the recognised keys keeps `fjall deploy` from
168
+ * hard-failing on an otherwise-harmless older file. The strict RootConfigSchema
169
+ * remains the contract for the WRITE/save path — never use this schema to
170
+ * validate what you are about to write. Domain entries stay nested-strict
171
+ * (DomainConfigSchema): tolerance applies to unknown TOP-LEVEL keys only.
172
+ */
173
+ export declare const RootConfigReadSchema: z.ZodObject<{
174
+ activeTarget: z.ZodOptional<z.ZodString>;
175
+ domains: z.ZodOptional<z.ZodArray<z.ZodObject<{
176
+ name: z.ZodString;
177
+ type: z.ZodEnum<{
178
+ apex: "apex";
179
+ delegated: "delegated";
180
+ }>;
181
+ parentDomain: z.ZodOptional<z.ZodString>;
182
+ account: z.ZodOptional<z.ZodString>;
183
+ }, z.core.$strict>>>;
184
+ }, z.core.$strip>;
185
+ export type RootConfigRead = z.infer<typeof RootConfigReadSchema>;
156
186
  /**
157
187
  * Canonical serialiser for the root fjall-config.json — the single source of
158
188
  * truth for the file's on-disk shape. Both `Config.saveConfig` and the webapp
@@ -225,4 +255,3 @@ export declare class Config {
225
255
  getDomain(name: string): DomainConfig | undefined;
226
256
  removeDomain(name: string): boolean;
227
257
  }
228
- export {};
package/dist/config.js CHANGED
@@ -1 +1 @@
1
- var S=Object.defineProperty;var h=(d,e)=>S(d,"name",{value:e,configurable:!0});import*as i from"fs";import*as f from"path";import{z as s}from"zod";import{failure as g,success as w}from"./docker/result.js";import{getErrorMessage as C}from"./errorUtils.js";import{logger as y}from"./logger.js";import{maskSensitiveOutput as u}from"./securityHelpers.js";const v=10,m="fjall-config.json",M=["compliance","governance","none"],N=["enforced","off"],b=["centralised","off"],k=["account","draining","org"],I="managementEvents",L="organisationManagementEvents",U=["active","draining","removed"],J="FjallTrailBucketName",G="FjallTrailKeyArn",W="OrganisationTrailBucketName",E=s.object({name:s.string(),type:s.enum(["apex","delegated"]),parentDomain:s.string().optional(),account:s.string().optional()}).strict(),T=s.object({activeTarget:s.string().optional(),domains:s.array(E).optional()}).strict(),O=s.object({activeTarget:s.string().optional(),domains:s.array(E).optional()});function A(d={}){return JSON.stringify(d,null,2)}h(A,"serialiseRootConfig");const _=T.keyof().options;function D(d,e,t){const n=e[t];n!==void 0&&(d[t]=n)}h(D,"copyDefinedKey");class a{static{h(this,"Config")}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<v;n++){const r=f.join(t,"fjall"),c=f.join(r,m);if(i.existsSync(c))return r;const o=f.join(t,m);if(i.existsSync(o))return t;const l=f.dirname(t);if(l===t)break;t=l}return null}static loadConfigFile(e){try{return i.accessSync(e,i.constants.R_OK),i.readFileSync(e,{encoding:"utf8"})}catch(t){return y.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:u(C(t))}),null}}static loadConfig(e){const t=a.findConfigDirectory(e);if(!t)return new a;const n=f.join(t,m),r=a.loadConfigFile(n);if(r===null){const o=new a(void 0,n);return o.loadFailed=!0,o}let c;if(r!==""){let o;try{o=JSON.parse(r)}catch(p){throw a.formatZodError(p,m)}const l=T.safeParse(o);if(l.success)c=l.data;else{const p=O.safeParse(o);c=p.success?p.data:{},y.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:n})}}return new a(c,n)}static formatZodError(e,t){if(e instanceof s.ZodError&&e.issues.length>0){const c=e.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join("; ");return new Error(`Failed to parse ${t}: ${c}`)}const r=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${r}`)}saveConfig(){let e=this.configPath;if(!e){const o=a.findConfigDirectory()||f.join(process.cwd(),"fjall");e=f.join(o,m)}if(this.loadFailed)return g(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(o){return g(new Error(`Cannot create config directory ${t}: ${u(C(o))}`))}const n=a.assertWritable(e,t);if(!n.success)return n;const r=A(this.mergeWithDisk(e)),c=`${e}.tmp-${process.pid}`;try{i.writeFileSync(c,r,{mode:384}),i.renameSync(c,e)}catch(o){return g(new Error(`Failed to save ${e}: ${u(C(o))}`))}return w(void 0)}static assertWritable(e,t){if(i.existsSync(e))try{i.accessSync(e,i.constants.W_OK)}catch{return g(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 g(new Error(`Cannot save ${e}: the directory ${t} is not writable. Make it writable (e.g. chmod u+w ${t}) and retry.`))}return w(void 0)}mergeWithDisk(e){const t=a.readDiskConfigForMerge(e);if(t===void 0)return this.rootConfig;const n={...t};for(const r of _)D(n,this.rootConfig,r);for(const r of this.clearedKeys)delete n[r];return n}static readDiskConfigForMerge(e){if(!i.existsSync(e))return;let t;try{t=JSON.parse(i.readFileSync(e,{encoding:"utf8"}))}catch(r){y.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:u(C(r))});return}const n=T.safeParse(t);if(!n.success){y.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:u(n.error.message)});return}return n.data}static getConfigDirectory(e){return a.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{I as ACCOUNT_TRAIL_NAME,U as ACCOUNT_TRAIL_STATES,a as Config,L as ORGANISATION_TRAIL_NAME,W as ORG_TRAIL_BUCKET_OUTPUT_KEY,b as ROOT_ACCESS_MANAGEMENT_MODES,m as ROOT_CONFIG_FILENAME,T as RootConfigSchema,N as S3_BPA_MODES,J as TRAIL_BUCKET_OUTPUT_KEY,G as TRAIL_KEY_ARN_OUTPUT_KEY,k as TRAIL_LIFECYCLE_STATES,M as VAULT_LOCK_MODES,A as serialiseRootConfig};
1
+ var S=Object.defineProperty;var h=(d,e)=>S(d,"name",{value:e,configurable:!0});import*as i from"fs";import*as f from"path";import{z as s}from"zod";import{failure as g,success as w}from"./docker/result.js";import{getErrorMessage as C}from"./errorUtils.js";import{logger as y}from"./logger.js";import{maskSensitiveOutput as u}from"./securityHelpers.js";const v=10,m="fjall-config.json",M=["compliance","governance","none"],N=["enforced","off"],b=["centralised","off"],k=["account","draining","org"],I="managementEvents",L="organisationManagementEvents",U=["active","draining","removed"],J="FjallTrailBucketName",G="FjallTrailKeyArn",W="OrganisationTrailBucketName",E=s.object({name:s.string(),type:s.enum(["apex","delegated"]),parentDomain:s.string().optional(),account:s.string().optional()}).strict(),T=s.object({activeTarget:s.string().optional(),domains:s.array(E).optional()}).strict(),O=s.object({activeTarget:s.string().optional(),domains:s.array(E).optional()});function A(d={}){return JSON.stringify(d,null,2)}h(A,"serialiseRootConfig");const _=T.keyof().options;function x(d,e,t){const n=e[t];n!==void 0&&(d[t]=n)}h(x,"copyDefinedKey");class a{static{h(this,"Config")}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<v;n++){const r=f.join(t,"fjall"),c=f.join(r,m);if(i.existsSync(c))return r;const o=f.join(t,m);if(i.existsSync(o))return t;const l=f.dirname(t);if(l===t)break;t=l}return null}static loadConfigFile(e){try{return i.accessSync(e,i.constants.R_OK),i.readFileSync(e,{encoding:"utf8"})}catch(t){return y.warn("Config",`Config file at ${e} could not be read; using defaults`,{file:e,error:u(C(t))}),null}}static loadConfig(e){const t=a.findConfigDirectory(e);if(!t)return new a;const n=f.join(t,m),r=a.loadConfigFile(n);if(r===null){const o=new a(void 0,n);return o.loadFailed=!0,o}let c;if(r!==""){let o;try{o=JSON.parse(r)}catch(p){throw a.formatZodError(p,m)}const l=T.safeParse(o);if(l.success)c=l.data;else{const p=O.safeParse(o);c=p.success?p.data:{},y.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:n})}}return new a(c,n)}static formatZodError(e,t){if(e instanceof s.ZodError&&e.issues.length>0){const c=e.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join("; ");return new Error(`Failed to parse ${t}: ${c}`)}const r=(e instanceof Error?e.message:String(e)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${r}`)}saveConfig(){let e=this.configPath;if(!e){const o=a.findConfigDirectory()||f.join(process.cwd(),"fjall");e=f.join(o,m)}if(this.loadFailed)return g(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(o){return g(new Error(`Cannot create config directory ${t}: ${u(C(o))}`))}const n=a.assertWritable(e,t);if(!n.success)return n;const r=A(this.mergeWithDisk(e)),c=`${e}.tmp-${process.pid}`;try{i.writeFileSync(c,r,{mode:384}),i.renameSync(c,e)}catch(o){return g(new Error(`Failed to save ${e}: ${u(C(o))}`))}return w(void 0)}static assertWritable(e,t){if(i.existsSync(e))try{i.accessSync(e,i.constants.W_OK)}catch{return g(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 g(new Error(`Cannot save ${e}: the directory ${t} is not writable. Make it writable (e.g. chmod u+w ${t}) and retry.`))}return w(void 0)}mergeWithDisk(e){const t=a.readDiskConfigForMerge(e);if(t===void 0)return this.rootConfig;const n={...t};for(const r of _)x(n,this.rootConfig,r);for(const r of this.clearedKeys)delete n[r];return n}static readDiskConfigForMerge(e){if(!i.existsSync(e))return;let t;try{t=JSON.parse(i.readFileSync(e,{encoding:"utf8"}))}catch(r){y.warn("Config",`Could not re-read ${e} before saving; writing in-memory state without merging`,{file:e,error:u(C(r))});return}const n=T.safeParse(t);if(!n.success){y.warn("Config",`On-disk ${e} failed validation before saving; writing in-memory state without merging`,{file:e,error:u(n.error.message)});return}return n.data}static getConfigDirectory(e){return a.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{I as ACCOUNT_TRAIL_NAME,U as ACCOUNT_TRAIL_STATES,a as Config,E as DomainConfigSchema,L as ORGANISATION_TRAIL_NAME,W as ORG_TRAIL_BUCKET_OUTPUT_KEY,b as ROOT_ACCESS_MANAGEMENT_MODES,m as ROOT_CONFIG_FILENAME,O as RootConfigReadSchema,T as RootConfigSchema,N as S3_BPA_MODES,J as TRAIL_BUCKET_OUTPUT_KEY,G as TRAIL_KEY_ARN_OUTPUT_KEY,k as TRAIL_LIFECYCLE_STATES,M as VAULT_LOCK_MODES,A as serialiseRootConfig};
@@ -31,8 +31,8 @@ export declare const DevSubstrateSynthPropsSchema: z.ZodObject<{
31
31
  engineVersion: z.ZodOptional<z.ZodString>;
32
32
  adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
33
33
  phase: z.ZodOptional<z.ZodEnum<{
34
- full: "full";
35
34
  zone: "zone";
35
+ full: "full";
36
36
  }>>;
37
37
  domain: z.ZodOptional<z.ZodObject<{
38
38
  devDomain: z.ZodString;
@@ -59,8 +59,8 @@ export declare const DevSubstrateParamsSchema: z.ZodObject<{
59
59
  engineVersion: z.ZodOptional<z.ZodString>;
60
60
  adoptSlotEcr: z.ZodOptional<z.ZodBoolean>;
61
61
  phase: z.ZodOptional<z.ZodEnum<{
62
- full: "full";
63
62
  zone: "zone";
63
+ full: "full";
64
64
  }>>;
65
65
  domain: z.ZodOptional<z.ZodObject<{
66
66
  devDomain: z.ZodString;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { DNS_APEX, getDomainExportNames, isManagedDomainBinding, type ManagedDomainBinding, type ManagedDomainExports } from "./infra/domainExports.js";
1
+ export { DNS_APEX, getDomainExportNames, getDomainStackName, getDomainUsEast1CertificatesStackName, isManagedDomainBinding, DOMAIN_DEPLOY_DEFAULT_PHASE, type DomainDeployPhase, type ManagedDomainBinding, type ManagedDomainExports } from "./infra/domainExports.js";
2
2
  export { BACKUP_VAULT_NAME } from "./infra/backupVault.js";
3
3
  export { APPROVAL_TOKEN_OUTPUT_PREFIX, TOKEN_STDERR_PREFIX } from "./deploy/approvalTokenOutput.js";
4
4
  export { imageTagParameterName } from "./infra/imageTags.js";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{DNS_APEX as E,getDomainExportNames as o,isManagedDomainBinding as t}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as S}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as A,TOKEN_STDERR_PREFIX as T}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as n}from"./infra/imageTags.js";import{toPascalCase as N,toKebab as m,toValidDatabaseName as s,toScreamingSnake as O,capitalise as P,getSafeZoneName as C,accountConstructKey as p,hasAsciiStableConstructKey as f}from"./naming/caseConversion.js";import{findAccountNameCollision as g}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as M,suffixedAccountName as D,REGION_SHORT_CODES as x,findTrailingRegionShortCode as u,regionSuffixRejectionMessage as d}from"./naming/connectedAccountName.js";import{normaliseError as l,getErrorMessage as L,hasErrorCode as V,getErrorCode as h,getErrorStack as F,formatErrorString as G}from"./errorUtils.js";import{singleton as H}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as X,filterDangerousEnvVars as Y,maskSensitiveOutput as y,parseShellArgs as B,SCOPED_TOKEN_REGEX as K}from"./securityHelpers.js";import{sleep as k}from"./async/sleep.js";import{mapSettledWithConcurrency as z}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as q,STRUCTURAL_ENVIRONMENTS as w,ACCOUNT_STAGES as J,ACCOUNT_STAGE_LABELS as Q,isAccountStage as $,ACCOUNT_TIERS as ee,AccountTierSchema as re,isAccountTier as Ee,environmentToTier as oe,stageFromWireEnvironment as te,accountTier as _e,getEnvironmentLabel as Se,ACCOUNT_ROLES as ae}from"./environments.js";import{RESOURCE_CATEGORIES as Te,categoriseResource as Re,getExpectedDuration as ne,getFriendlyResourceType as ie}from"./resourceCategorisation.js";import{parseGitRemoteUrl as me}from"./repo/gitRemoteParser.js";import{abbreviateRegion as Oe,AWS_REGIONS_METADATA as Pe,DEFAULT_REGION as Ce,getRegionInfo as pe,MAX_SECONDARY_REGIONS as fe,OPT_IN_REGION_CODES as ce,optInRegionWarning as ge,regions as Ie,suggestRegionForTimezone as Me}from"./infra/regions.js";import{SCOPE_VALUES as xe,MACHINE_ONLY_SCOPES as ue,USER_GRANTABLE_SCOPES as de}from"./infra/tokenScopes.js";import{SECRET_NAME_PATTERN as le,SECRET_NAME_ERROR as Le,SSM_COMPONENT_PATTERN as Ve,SSM_COMPONENT_ERROR as he,SSM_STANDARD_MAX_VALUE_BYTES as Fe,SecretNamespaceSchema as Ge,buildNamespaceParts as ve,buildParameterPath as He,parseParameterPath as be,isManageablePath as Xe,parseDotEnv as Ye,escapeDotEnvValue as ye}from"./secrets.js";import{ConnectionWireSchema as Ke,ConnectionsListResponseSchema as We}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as je,deriveTargets as ze,deriveAllTargets as Ze,environmentOrTier as qe,findTarget as we,generateTargetName as Je}from"./targets.js";import{buildAppConfigPath as $e}from"./repo/appPath.js";import{findInfrastructurePaths as rr,findBoundaryPath as Er,isInfrastructureFile as or}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as _r}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as ar,RESERVED_APP_NAME_MESSAGE as Ar,isReservedAppName as Tr,RESERVED_APP_NAME_SUFFIX as Rr,RESERVED_APP_NAME_SUFFIX_MESSAGE as nr,hasReservedAppNameSuffix as ir}from"./naming/reservedAppNames.js";import{deriveContentHashTag as mr,CONTENT_HASH_TAG_PATTERN as sr}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as Pr,DeployModeSchema as Cr,IMAGE_TAG_PATTERN as pr,ImageTagSchema as fr,ServiceArtefactSchema as cr,ServiceArtefactsSchema as gr,ARTEFACT_OUTPUT_FIELDS as Ir,artefactOutputKey as Mr}from"./infra/deployArtefacts.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as xr,EXPECTED_SCHEMA_VERSION_ENV as ur,EXPECTED_SCHEMA_VERSION_TOOL_ENV as dr,EXPECTED_CH_SCHEMA_VERSION_ENV as Ur,SCHEMA_ADMIN_USER_ENV as lr,SCHEMA_ADMIN_PASSWORD_ENV as Lr,PRISMA_MIGRATION_DIR_RE as Vr,CLICKHOUSE_MIGRATION_SKIP_RE as hr}from"./migration/constants.js";import{PHYSICAL_NAME_FALLBACK_PROPERTIES as Gr}from"./cfn/physicalNameProperties.js";import{PATTERN_TYPE_VALUES as Hr,PATTERN_TYPES as br,isPatternType as Xr,PATTERN_REGISTRY as Yr,DEPLOYABLE_PATTERN_TYPES as yr,OPENNEXT_PATTERN_TYPES as Br,isOpenNextPatternType as Kr,STATIC_SITE_ROUTING_VALUES as Wr}from"./patterns/patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as jr,defaultFormsFromAddress as zr,defaultFormsCorsOrigin as Zr,isAddressAtDomain as qr}from"./patterns/staticSiteForms.js";import{DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION as Jr,DEV_SUBSTRATE_PARAMS_FILENAME as Qr,DevSubstrateSynthPropsSchema as $r,DevSubstrateParamsSchema as eE}from"./devSubstrate/params.js";import{DEV_SUBSTRATE_ENTRYPOINT_SOURCE as EE}from"./devSubstrate/entrypoint.js";export{ae as ACCOUNT_ROLES,J as ACCOUNT_STAGES,q as ACCOUNT_STAGES_WITH_ROOT,Q as ACCOUNT_STAGE_LABELS,ee as ACCOUNT_TIERS,A as APPROVAL_TOKEN_OUTPUT_PREFIX,Ir as ARTEFACT_OUTPUT_FIELDS,Pe as AWS_REGIONS_METADATA,re as AccountTierSchema,S as BACKUP_VAULT_NAME,hr as CLICKHOUSE_MIGRATION_SKIP_RE,sr as CONTENT_HASH_TAG_PATTERN,Ke as ConnectionWireSchema,We as ConnectionsListResponseSchema,X as DANGEROUS_ENV_VARS,jr as DEFAULT_FORMS_FROM_LOCAL_PART,Ce as DEFAULT_REGION,yr as DEPLOYABLE_PATTERN_TYPES,Pr as DEPLOY_MODES,EE as DEV_SUBSTRATE_ENTRYPOINT_SOURCE,Qr as DEV_SUBSTRATE_PARAMS_FILENAME,Jr as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,E as DNS_APEX,Cr as DeployModeSchema,eE as DevSubstrateParamsSchema,$r as DevSubstrateSynthPropsSchema,Ur as EXPECTED_CH_SCHEMA_VERSION_ENV,ur as EXPECTED_SCHEMA_VERSION_ENV,dr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,pr as IMAGE_TAG_PATTERN,fr as ImageTagSchema,ue as MACHINE_ONLY_SCOPES,fe as MAX_SECONDARY_REGIONS,xr as MIGRATION_SNAPSHOT_NAME_PREFIX,Br as OPENNEXT_PATTERN_TYPES,ce as OPT_IN_REGION_CODES,Yr as PATTERN_REGISTRY,br as PATTERN_TYPES,Hr as PATTERN_TYPE_VALUES,Gr as PHYSICAL_NAME_FALLBACK_PROPERTIES,Vr as PRISMA_MIGRATION_DIR_RE,x as REGION_SHORT_CODES,ar as RESERVED_APP_NAMES,Ar as RESERVED_APP_NAME_MESSAGE,Rr as RESERVED_APP_NAME_SUFFIX,nr as RESERVED_APP_NAME_SUFFIX_MESSAGE,Te as RESOURCE_CATEGORIES,Lr as SCHEMA_ADMIN_PASSWORD_ENV,lr as SCHEMA_ADMIN_USER_ENV,K as SCOPED_TOKEN_REGEX,xe as SCOPE_VALUES,Le as SECRET_NAME_ERROR,le as SECRET_NAME_PATTERN,he as SSM_COMPONENT_ERROR,Ve as SSM_COMPONENT_PATTERN,Fe as SSM_STANDARD_MAX_VALUE_BYTES,Wr as STATIC_SITE_ROUTING_VALUES,w as STRUCTURAL_ENVIRONMENTS,Ge as SecretNamespaceSchema,cr as ServiceArtefactSchema,gr as ServiceArtefactsSchema,T as TOKEN_STDERR_PREFIX,de as USER_GRANTABLE_SCOPES,Oe as abbreviateRegion,p as accountConstructKey,_e as accountTier,Mr as artefactOutputKey,$e as buildAppConfigPath,ve as buildNamespaceParts,He as buildParameterPath,P as capitalise,Re as categoriseResource,M as defaultConnectedAccountName,Zr as defaultFormsCorsOrigin,zr as defaultFormsFromAddress,Ze as deriveAllTargets,mr as deriveContentHashTag,je as deriveRegionsFromOrgConfig,ze as deriveTargets,qe as environmentOrTier,oe as environmentToTier,ye as escapeDotEnvValue,Y as filterDangerousEnvVars,g as findAccountNameCollision,Er as findBoundaryPath,rr as findInfrastructurePaths,we as findTarget,u as findTrailingRegionShortCode,G as formatErrorString,Je as generateTargetName,o as getDomainExportNames,Se as getEnvironmentLabel,h as getErrorCode,L as getErrorMessage,F as getErrorStack,ne as getExpectedDuration,ie as getFriendlyResourceType,pe as getRegionInfo,C as getSafeZoneName,f as hasAsciiStableConstructKey,V as hasErrorCode,ir as hasReservedAppNameSuffix,n as imageTagParameterName,_r as inferContainerFromCandidates,$ as isAccountStage,Ee as isAccountTier,qr as isAddressAtDomain,or as isInfrastructureFile,Xe as isManageablePath,t as isManagedDomainBinding,Kr as isOpenNextPatternType,Xr as isPatternType,Tr as isReservedAppName,z as mapSettledWithConcurrency,y as maskSensitiveOutput,l as normaliseError,ge as optInRegionWarning,Ye as parseDotEnv,me as parseGitRemoteUrl,be as parseParameterPath,B as parseShellArgs,d as regionSuffixRejectionMessage,Ie as regions,H as singleton,k as sleep,te as stageFromWireEnvironment,D as suffixedAccountName,Me as suggestRegionForTimezone,m as toKebab,N as toPascalCase,O as toScreamingSnake,s as toValidDatabaseName};
1
+ import{DNS_APEX as E,getDomainExportNames as o,getDomainStackName as t,getDomainUsEast1CertificatesStackName as a,isManagedDomainBinding as _,DOMAIN_DEPLOY_DEFAULT_PHASE as S}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as T}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as n,TOKEN_STDERR_PREFIX as i}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as m}from"./infra/imageTags.js";import{toPascalCase as O,toKebab as P,toValidDatabaseName as C,toScreamingSnake as p,capitalise as f,getSafeZoneName as c,accountConstructKey as g,hasAsciiStableConstructKey as I}from"./naming/caseConversion.js";import{findAccountNameCollision as M}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as u,suffixedAccountName as d,REGION_SHORT_CODES as U,findTrailingRegionShortCode as L,regionSuffixRejectionMessage as l}from"./naming/connectedAccountName.js";import{normaliseError as F,getErrorMessage as h,hasErrorCode as G,getErrorCode as v,getErrorStack as H,formatErrorString as b}from"./errorUtils.js";import{singleton as Y}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as B,filterDangerousEnvVars as K,maskSensitiveOutput as W,parseShellArgs as k,SCOPED_TOKEN_REGEX as j}from"./securityHelpers.js";import{sleep as Z}from"./async/sleep.js";import{mapSettledWithConcurrency as w}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as Q,STRUCTURAL_ENVIRONMENTS as $,ACCOUNT_STAGES as ee,ACCOUNT_STAGE_LABELS as re,isAccountStage as Ee,ACCOUNT_TIERS as oe,AccountTierSchema as te,isAccountTier as ae,environmentToTier as _e,stageFromWireEnvironment as Se,accountTier as Ae,getEnvironmentLabel as Te,ACCOUNT_ROLES as Re}from"./environments.js";import{RESOURCE_CATEGORIES as ie,categoriseResource as Ne,getExpectedDuration as me,getFriendlyResourceType as se}from"./resourceCategorisation.js";import{parseGitRemoteUrl as Pe}from"./repo/gitRemoteParser.js";import{abbreviateRegion as pe,AWS_REGIONS_METADATA as fe,DEFAULT_REGION as ce,getRegionInfo as ge,MAX_SECONDARY_REGIONS as Ie,OPT_IN_REGION_CODES as De,optInRegionWarning as Me,regions as xe,suggestRegionForTimezone as ue}from"./infra/regions.js";import{SCOPE_VALUES as Ue,MACHINE_ONLY_SCOPES as Le,USER_GRANTABLE_SCOPES as le}from"./infra/tokenScopes.js";import{SECRET_NAME_PATTERN as Fe,SECRET_NAME_ERROR as he,SSM_COMPONENT_PATTERN as Ge,SSM_COMPONENT_ERROR as ve,SSM_STANDARD_MAX_VALUE_BYTES as He,SecretNamespaceSchema as be,buildNamespaceParts as Xe,buildParameterPath as Ye,parseParameterPath as ye,isManageablePath as Be,parseDotEnv as Ke,escapeDotEnvValue as We}from"./secrets.js";import{ConnectionWireSchema as je,ConnectionsListResponseSchema as ze}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as qe,deriveTargets as we,deriveAllTargets as Je,environmentOrTier as Qe,findTarget as $e,generateTargetName as er}from"./targets.js";import{buildAppConfigPath as Er}from"./repo/appPath.js";import{findInfrastructurePaths as tr,findBoundaryPath as ar,isInfrastructureFile as _r}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as Ar}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as Rr,RESERVED_APP_NAME_MESSAGE as nr,isReservedAppName as ir,RESERVED_APP_NAME_SUFFIX as Nr,RESERVED_APP_NAME_SUFFIX_MESSAGE as mr,hasReservedAppNameSuffix as sr}from"./naming/reservedAppNames.js";import{deriveContentHashTag as Pr,CONTENT_HASH_TAG_PATTERN as Cr}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as fr,DeployModeSchema as cr,IMAGE_TAG_PATTERN as gr,ImageTagSchema as Ir,ServiceArtefactSchema as Dr,ServiceArtefactsSchema as Mr,ARTEFACT_OUTPUT_FIELDS as xr,artefactOutputKey as ur}from"./infra/deployArtefacts.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as Ur,EXPECTED_SCHEMA_VERSION_ENV as Lr,EXPECTED_SCHEMA_VERSION_TOOL_ENV as lr,EXPECTED_CH_SCHEMA_VERSION_ENV as Vr,SCHEMA_ADMIN_USER_ENV as Fr,SCHEMA_ADMIN_PASSWORD_ENV as hr,PRISMA_MIGRATION_DIR_RE as Gr,CLICKHOUSE_MIGRATION_SKIP_RE as vr}from"./migration/constants.js";import{PHYSICAL_NAME_FALLBACK_PROPERTIES as br}from"./cfn/physicalNameProperties.js";import{PATTERN_TYPE_VALUES as Yr,PATTERN_TYPES as yr,isPatternType as Br,PATTERN_REGISTRY as Kr,DEPLOYABLE_PATTERN_TYPES as Wr,OPENNEXT_PATTERN_TYPES as kr,isOpenNextPatternType as jr,STATIC_SITE_ROUTING_VALUES as zr}from"./patterns/patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as qr,defaultFormsFromAddress as wr,defaultFormsCorsOrigin as Jr,isAddressAtDomain as Qr}from"./patterns/staticSiteForms.js";import{DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION as eE,DEV_SUBSTRATE_PARAMS_FILENAME as rE,DevSubstrateSynthPropsSchema as EE,DevSubstrateParamsSchema as oE}from"./devSubstrate/params.js";import{DEV_SUBSTRATE_ENTRYPOINT_SOURCE as aE}from"./devSubstrate/entrypoint.js";export{Re as ACCOUNT_ROLES,ee as ACCOUNT_STAGES,Q as ACCOUNT_STAGES_WITH_ROOT,re as ACCOUNT_STAGE_LABELS,oe as ACCOUNT_TIERS,n as APPROVAL_TOKEN_OUTPUT_PREFIX,xr as ARTEFACT_OUTPUT_FIELDS,fe as AWS_REGIONS_METADATA,te as AccountTierSchema,T as BACKUP_VAULT_NAME,vr as CLICKHOUSE_MIGRATION_SKIP_RE,Cr as CONTENT_HASH_TAG_PATTERN,je as ConnectionWireSchema,ze as ConnectionsListResponseSchema,B as DANGEROUS_ENV_VARS,qr as DEFAULT_FORMS_FROM_LOCAL_PART,ce as DEFAULT_REGION,Wr as DEPLOYABLE_PATTERN_TYPES,fr as DEPLOY_MODES,aE as DEV_SUBSTRATE_ENTRYPOINT_SOURCE,rE as DEV_SUBSTRATE_PARAMS_FILENAME,eE as DEV_SUBSTRATE_PARAMS_SCHEMA_VERSION,E as DNS_APEX,S as DOMAIN_DEPLOY_DEFAULT_PHASE,cr as DeployModeSchema,oE as DevSubstrateParamsSchema,EE as DevSubstrateSynthPropsSchema,Vr as EXPECTED_CH_SCHEMA_VERSION_ENV,Lr as EXPECTED_SCHEMA_VERSION_ENV,lr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,gr as IMAGE_TAG_PATTERN,Ir as ImageTagSchema,Le as MACHINE_ONLY_SCOPES,Ie as MAX_SECONDARY_REGIONS,Ur as MIGRATION_SNAPSHOT_NAME_PREFIX,kr as OPENNEXT_PATTERN_TYPES,De as OPT_IN_REGION_CODES,Kr as PATTERN_REGISTRY,yr as PATTERN_TYPES,Yr as PATTERN_TYPE_VALUES,br as PHYSICAL_NAME_FALLBACK_PROPERTIES,Gr as PRISMA_MIGRATION_DIR_RE,U as REGION_SHORT_CODES,Rr as RESERVED_APP_NAMES,nr as RESERVED_APP_NAME_MESSAGE,Nr as RESERVED_APP_NAME_SUFFIX,mr as RESERVED_APP_NAME_SUFFIX_MESSAGE,ie as RESOURCE_CATEGORIES,hr as SCHEMA_ADMIN_PASSWORD_ENV,Fr as SCHEMA_ADMIN_USER_ENV,j as SCOPED_TOKEN_REGEX,Ue as SCOPE_VALUES,he as SECRET_NAME_ERROR,Fe as SECRET_NAME_PATTERN,ve as SSM_COMPONENT_ERROR,Ge as SSM_COMPONENT_PATTERN,He as SSM_STANDARD_MAX_VALUE_BYTES,zr as STATIC_SITE_ROUTING_VALUES,$ as STRUCTURAL_ENVIRONMENTS,be as SecretNamespaceSchema,Dr as ServiceArtefactSchema,Mr as ServiceArtefactsSchema,i as TOKEN_STDERR_PREFIX,le as USER_GRANTABLE_SCOPES,pe as abbreviateRegion,g as accountConstructKey,Ae as accountTier,ur as artefactOutputKey,Er as buildAppConfigPath,Xe as buildNamespaceParts,Ye as buildParameterPath,f as capitalise,Ne as categoriseResource,u as defaultConnectedAccountName,Jr as defaultFormsCorsOrigin,wr as defaultFormsFromAddress,Je as deriveAllTargets,Pr as deriveContentHashTag,qe as deriveRegionsFromOrgConfig,we as deriveTargets,Qe as environmentOrTier,_e as environmentToTier,We as escapeDotEnvValue,K as filterDangerousEnvVars,M as findAccountNameCollision,ar as findBoundaryPath,tr as findInfrastructurePaths,$e as findTarget,L as findTrailingRegionShortCode,b as formatErrorString,er as generateTargetName,o as getDomainExportNames,t as getDomainStackName,a as getDomainUsEast1CertificatesStackName,Te as getEnvironmentLabel,v as getErrorCode,h as getErrorMessage,H as getErrorStack,me as getExpectedDuration,se as getFriendlyResourceType,ge as getRegionInfo,c as getSafeZoneName,I as hasAsciiStableConstructKey,G as hasErrorCode,sr as hasReservedAppNameSuffix,m as imageTagParameterName,Ar as inferContainerFromCandidates,Ee as isAccountStage,ae as isAccountTier,Qr as isAddressAtDomain,_r as isInfrastructureFile,Be as isManageablePath,_ as isManagedDomainBinding,jr as isOpenNextPatternType,Br as isPatternType,ir as isReservedAppName,w as mapSettledWithConcurrency,W as maskSensitiveOutput,F as normaliseError,Me as optInRegionWarning,Ke as parseDotEnv,Pe as parseGitRemoteUrl,ye as parseParameterPath,k as parseShellArgs,l as regionSuffixRejectionMessage,xe as regions,Y as singleton,Z as sleep,Se as stageFromWireEnvironment,d as suffixedAccountName,ue as suggestRegionForTimezone,P as toKebab,O as toPascalCase,p as toScreamingSnake,C as toValidDatabaseName};
@@ -1,4 +1,48 @@
1
1
  export declare const DNS_APEX: "@";
2
+ /**
3
+ * CloudFormation stack name for a domain component (D2 wiring contract).
4
+ *
5
+ * Mirrors the domain generator's app naming (`cli/generators/domain/
6
+ * generator.ts`: `toPascalCase(domainName.split(".").join(""))` + `Domain`,
7
+ * the name passed to both `App.getApp(...)` and `app.getStack(...)` in the
8
+ * emitted `infrastructure.ts` — the stack key IS the deployed stack name),
9
+ * e.g. `example.com` → `ExamplecomDomain`. Both the deploy-state record
10
+ * (`domainDeployOperation`) and the binding resolution
11
+ * (`DomainService.resolveDomainForApp` → DescribeStacks) MUST derive the name
12
+ * through this helper so the stack the deploy recorded is the stack the
13
+ * resolution describes.
14
+ */
15
+ export declare function getDomainStackName(domainName: string): string;
16
+ /**
17
+ * Name of a domain stack's paired us-east-1 certificate stack (D3 contract).
18
+ *
19
+ * When a domain declares a `cloudFront: true` certificate and the domain
20
+ * stack itself does NOT deploy to us-east-1, the Domain construct mints the
21
+ * certificate (and the `<zone>-us-east-1-certificate-arn` export) in this
22
+ * paired stack instead, us-east-1 being the only region CloudFront accepts
23
+ * viewer certificates from. Both the construct side
24
+ * (`composeDomainCertificates`) and the read side
25
+ * (`DomainService.resolveDomainForApp`, which describes the paired stack
26
+ * with a us-east-1 CloudFormation client) MUST derive the name here so the
27
+ * stack the synth mints is the stack the resolution reads.
28
+ */
29
+ export declare function getDomainUsEast1CertificatesStackName(domainStackName: string): string;
30
+ /**
31
+ * Two-step deploy phase for constructs whose certificates must not be issued
32
+ * before their zone's NS delegation has propagated (design R2 cert-hang
33
+ * guard). `"zone"` synthesises the hosted zone (+ delegation record) only;
34
+ * `"full"` additionally issues certificates. Shared by the delegated `Domain`
35
+ * topology, `DevSubstrate`, and the deploy-core two-phase orchestration —
36
+ * homed here (the lowest layer) so the construct and deploy sides read one
37
+ * contract.
38
+ */
39
+ export type DomainDeployPhase = "zone" | "full";
40
+ /**
41
+ * Default deploy phase when `phase` is omitted — the complete build. The
42
+ * two-step guard (R2) sets `"zone"` explicitly for step 1, so the safe
43
+ * default for a single-shot deploy is everything.
44
+ */
45
+ export declare const DOMAIN_DEPLOY_DEFAULT_PHASE: DomainDeployPhase;
2
46
  /**
3
47
  * Compute predictable CloudFormation export names for domain stack outputs.
4
48
  * Used by both infrastructure constructs (to set export names) and CLI services
@@ -1 +1 @@
1
- var r=Object.defineProperty;var n=(t,e)=>r(t,"name",{value:e,configurable:!0});const a="@";function i(t){const e=t.replace(/\./g,"-");return{hostedZoneId:`${e}-hosted-zone-id`,certificateArn:`${e}-certificate-arn`,usEast1CertificateArn:`${e}-us-east-1-certificate-arn`,delegationRoleArn:`${e}-delegation-role-arn`,nameservers:`${e}-nameservers`}}n(i,"getDomainExportNames");function s(t){return typeof t.hostedZoneId=="string"}n(s,"isManagedDomainBinding");export{a as DNS_APEX,i as getDomainExportNames,s as isManagedDomainBinding};
1
+ var r=Object.defineProperty;var n=(e,t)=>r(e,"name",{value:t,configurable:!0});import{toPascalCase as a}from"../naming/caseConversion.js";const s="@";function c(e){return`${a(e.split(".").join(""))}Domain`}n(c,"getDomainStackName");function f(e){return`${e}UsEast1Certificates`}n(f,"getDomainUsEast1CertificatesStackName");const m="full";function p(e){const t=e.replace(/\./g,"-");return{hostedZoneId:`${t}-hosted-zone-id`,certificateArn:`${t}-certificate-arn`,usEast1CertificateArn:`${t}-us-east-1-certificate-arn`,delegationRoleArn:`${t}-delegation-role-arn`,nameservers:`${t}-nameservers`}}n(p,"getDomainExportNames");function u(e){return typeof e.hostedZoneId=="string"}n(u,"isManagedDomainBinding");export{s as DNS_APEX,m as DOMAIN_DEPLOY_DEFAULT_PHASE,p as getDomainExportNames,c as getDomainStackName,f as getDomainUsEast1CertificatesStackName,u as isManagedDomainBinding};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/util",
3
- "version": "3.5.2",
3
+ "version": "3.6.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": "f59755647b2fd81ff692a3545e8e199d93c3bdf8"
137
+ "gitHead": "10366db602d7e80669e22825a52071a47e05c3b7"
138
138
  }