@fjall/util 2.29.0 → 2.30.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/docker/bakeGuard.d.ts +11 -0
- package/dist/docker/bakeGuard.js +1 -1
- package/dist/docker/index.d.ts +1 -1
- package/dist/docker/index.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/infra/deployArtefacts.d.ts +10 -0
- package/dist/infra/deployArtefacts.js +1 -1
- package/dist/manifest/index.d.ts +1 -1
- package/dist/manifest/index.js +1 -1
- package/dist/manifest/io.d.ts +36 -0
- package/dist/manifest/io.js +1 -1
- package/dist/manifest/schemas.d.ts +56 -0
- package/dist/manifest/schemas.js +1 -1
- package/dist/migration/compareSchemaVersion.d.ts +50 -0
- package/dist/migration/compareSchemaVersion.js +1 -0
- package/dist/migration/compareSchemaVersion.test.d.ts +1 -0
- package/dist/migration/compareSchemaVersion.test.js +1 -0
- package/dist/migration/constants.d.ts +9 -0
- package/dist/migration/constants.js +1 -1
- package/dist/migration/index.d.ts +1 -0
- package/dist/migration/index.js +1 -1
- package/dist/migration/verifyExpectedSchemaVersion.d.ts +5 -0
- package/dist/migration/verifyExpectedSchemaVersion.js +1 -1
- package/dist/migration/verifyExpectedSchemaVersion.test.js +1 -1
- package/dist/patterns/index.d.ts +11 -0
- package/dist/patterns/index.js +1 -0
- package/dist/patterns/patternTypes.d.ts +91 -0
- package/dist/patterns/patternTypes.js +1 -0
- package/dist/patterns/staticSiteForms.d.ts +24 -0
- package/dist/patterns/staticSiteForms.js +1 -0
- package/package.json +7 -2
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
84 files minified at 2026-07-16T01:01:28.523Z
|
|
@@ -29,6 +29,17 @@ import type { DockerBuildArgValue } from "../manifest/schemas.js";
|
|
|
29
29
|
* finding/warning message states the same blast radius (design R4).
|
|
30
30
|
*/
|
|
31
31
|
export declare const BAKE_GUARD_EXPOSURE_CLAUSE: string;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a per-deploy de-duplicator for bake-exposure warnings.
|
|
34
|
+
*
|
|
35
|
+
* A buildArg baked into more than one service image of the same app (an app +
|
|
36
|
+
* its workers, built as separate groups) produces byte-identical exposure
|
|
37
|
+
* warnings — the exposure is a property of the KEY, not of each image, so the
|
|
38
|
+
* operator only needs to see each warning once per deploy. Returns a predicate
|
|
39
|
+
* that is `true` the first time it sees a message and `false` thereafter; share
|
|
40
|
+
* one instance across a deploy's build groups.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createBakeWarningDeduper(): (message: string) => boolean;
|
|
32
43
|
export type BakeGuardFindingReason = "sourced-ref" | "credential-shape";
|
|
33
44
|
export interface BakeGuardFinding {
|
|
34
45
|
/** The offending `buildArgs` key. */
|
package/dist/docker/bakeGuard.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{maskSensitiveOutput as
|
|
1
|
+
import{maskSensitiveOutput as o}from"../securityHelpers.js";import{PUBLIC_BUILD_ARG_PREFIXES as l,isPublicBuildVarName as s}from"./buildArgInference.js";const u="baked values are world-readable in `docker history`, image provenance/SBOM, and the `<app>-cache` mode=max ECR cache repo";function p(){const e=new Set;return n=>e.has(n)?!1:(e.add(n),!0)}function g(e,n){const a=[],i=[];if(e===void 0)return{findings:a,warnings:i};for(const[t,r]of Object.entries(e)){if(typeof r=="object"){if(r.acknowledgePublic===!0)continue;if(r.ssm!==void 0||r.secretsManager!==void 0){a.push({key:t,reason:"sourced-ref",message:`buildArg "${t}" (app "${n}") is sourced from a secret store (SSM/Secrets Manager) but baked via --build-arg \u2014 ${u}. Move it to docker.buildSecrets (BuildKit --secret mount, never baked), or, if the value is genuinely public-but-sensitive and must ship in the client bundle, set acknowledgePublic: true to bake it deliberately.`});continue}s(t)||i.push({key:t,message:d(t)});continue}if(o(r)!==r){a.push({key:t,reason:"credential-shape",message:c(t,n)});continue}s(t)||i.push({key:t,message:d(t)})}return{findings:a,warnings:i}}function d(e){return`buildArg "${e}" does not start with a public prefix (${l.join(", ")}) and is frozen into the image at build time \u2014 ${u}. If this value should vary per deploy, use a runtime env var (ECS task environment) instead of baking it.`}function c(e,n){return`buildArg "${e}" (app "${n}") has a value that looks like a credential but would be baked via --build-arg \u2014 ${u}. Move it to docker.buildSecrets (BuildKit --secret mount, never baked). If it is a genuinely public-but-sensitive value, use the object form { ssm/secretsManager/env, acknowledgePublic: true } to bake it deliberately.`}function k(e){const n=new Set;if(e===void 0)return n;for(const[a,i]of Object.entries(e))typeof i=="object"&&i.acknowledgePublic===!0&&n.add(a);return n}function v(e,n,a){const i=[];for(const[t,r]of Object.entries(e))a.has(t)||o(r)!==r&&i.push({key:t,reason:"credential-shape",message:c(t,n)});return i}export{u as BAKE_GUARD_EXPOSURE_CLAUSE,k as acknowledgedBuildArgKeys,p as createBakeWarningDeduper,g as evaluateBakeGuard,v as evaluateResolvedBuildArgValues};
|
package/dist/docker/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { type Result, isSuccess, isFailure, success, failure } from "./result.js
|
|
|
2
2
|
export { DOCKER_CLI_LOG_CATEGORY, DOCKER_CLI_BUILDX_LOG_CATEGORY, BUILDX_VERSION_FLOOR, ENGINE_VERSION_FLOOR, PrerequisiteMissingExitCode, DEFAULT_BUILDER_NAME, DEFAULT_DOCKER_BIN, SIGTERM_GRACE_MS, STDERR_TAIL_LINES, DEFAULT_BUILD_TIMEOUT_MS, DEFAULT_PUSH_TIMEOUT_MS, DEFAULT_PULL_TIMEOUT_MS, DEFAULT_INSPECT_TIMEOUT_MS, DEFAULT_DAEMON_PROBE_TIMEOUT_MS } from "./dockerCliConstants.js";
|
|
3
3
|
export { BuildxBuildArgsSchema, BuildxBuildResultSchema, DockerCliErrorKindSchema, DockerCliErrorSchema, isDockerCliErrorKind, type BuildxBuildArgs, type BuildxBuildResult, type DockerCliError, type DockerCliErrorKind } from "./dockerCliSchemas.js";
|
|
4
4
|
export { buildxArgvBuilder } from "./buildxArgvBuilder.js";
|
|
5
|
-
export { evaluateBakeGuard, evaluateResolvedBuildArgValues, acknowledgedBuildArgKeys, BAKE_GUARD_EXPOSURE_CLAUSE, type BakeGuardFinding, type BakeGuardFindingReason, type BakeGuardWarning, type BakeGuardResult } from "./bakeGuard.js";
|
|
5
|
+
export { evaluateBakeGuard, evaluateResolvedBuildArgValues, acknowledgedBuildArgKeys, createBakeWarningDeduper, BAKE_GUARD_EXPOSURE_CLAUSE, type BakeGuardFinding, type BakeGuardFindingReason, type BakeGuardWarning, type BakeGuardResult } from "./bakeGuard.js";
|
|
6
6
|
export { PUBLIC_BUILD_ARG_PREFIXES, isPublicBuildVarName, inferPublicBuildArgKeys, type InferPublicBuildArgKeysInput } from "./buildArgInference.js";
|
|
7
7
|
export { parseRawjsonLine, type RawjsonEnvelope, type RawjsonVertex, type RawjsonStatus, type RawjsonLog, type RawjsonWarning } from "./rawjsonParser.js";
|
|
8
8
|
export { rawjsonToVertexEvent, type RawjsonVertexEvent, type NormalisedVertex, type NormalisedStatus, type NormalisedLog, type NormalisedWarning } from "./rawjsonToVertexEvent.js";
|
package/dist/docker/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isSuccess as o,isFailure as E,success as _,failure as i}from"./result.js";import{DOCKER_CLI_LOG_CATEGORY as
|
|
1
|
+
import{isSuccess as o,isFailure as E,success as _,failure as i}from"./result.js";import{DOCKER_CLI_LOG_CATEGORY as a,DOCKER_CLI_BUILDX_LOG_CATEGORY as l,BUILDX_VERSION_FLOOR as s,ENGINE_VERSION_FLOOR as u,PrerequisiteMissingExitCode as T,DEFAULT_BUILDER_NAME as A,DEFAULT_DOCKER_BIN as L,SIGTERM_GRACE_MS as R,STDERR_TAIL_LINES as D,DEFAULT_BUILD_TIMEOUT_MS as c,DEFAULT_PUSH_TIMEOUT_MS as U,DEFAULT_PULL_TIMEOUT_MS as d,DEFAULT_INSPECT_TIMEOUT_MS as I,DEFAULT_DAEMON_PROBE_TIMEOUT_MS as O}from"./dockerCliConstants.js";import{BuildxBuildArgsSchema as x,BuildxBuildResultSchema as B,DockerCliErrorKindSchema as m,DockerCliErrorSchema as C,isDockerCliErrorKind as p}from"./dockerCliSchemas.js";import{buildxArgvBuilder as M}from"./buildxArgvBuilder.js";import{evaluateBakeGuard as n,evaluateResolvedBuildArgValues as P,acknowledgedBuildArgKeys as F,createBakeWarningDeduper as G,BAKE_GUARD_EXPOSURE_CLAUSE as g}from"./bakeGuard.js";import{PUBLIC_BUILD_ARG_PREFIXES as h,isPublicBuildVarName as k,inferPublicBuildArgKeys as b}from"./buildArgInference.js";import{parseRawjsonLine as y}from"./rawjsonParser.js";import{rawjsonToVertexEvent as X}from"./rawjsonToVertexEvent.js";import{parseMetadataFile as w}from"./metadataFileParser.js";import{projectBuildxResult as H}from"./projectBuildxResult.js";import{abortChildProcess as W}from"./abortHelpers.js";import{DockerCli as J}from"./DockerCli.js";import{createEcrAuthSession as Z}from"./ecrCredentialStore.js";import{buildCacheRepositoryName as ee,untaggedLifecyclePolicyText as re,CACHE_REPO_UNTAGGED_RETENTION_DAYS as oe}from"./cacheRepository.js";export{g as BAKE_GUARD_EXPOSURE_CLAUSE,s as BUILDX_VERSION_FLOOR,x as BuildxBuildArgsSchema,B as BuildxBuildResultSchema,oe as CACHE_REPO_UNTAGGED_RETENTION_DAYS,A as DEFAULT_BUILDER_NAME,c as DEFAULT_BUILD_TIMEOUT_MS,O as DEFAULT_DAEMON_PROBE_TIMEOUT_MS,L as DEFAULT_DOCKER_BIN,I as DEFAULT_INSPECT_TIMEOUT_MS,d as DEFAULT_PULL_TIMEOUT_MS,U as DEFAULT_PUSH_TIMEOUT_MS,l as DOCKER_CLI_BUILDX_LOG_CATEGORY,a as DOCKER_CLI_LOG_CATEGORY,J as DockerCli,m as DockerCliErrorKindSchema,C as DockerCliErrorSchema,u as ENGINE_VERSION_FLOOR,h as PUBLIC_BUILD_ARG_PREFIXES,T as PrerequisiteMissingExitCode,R as SIGTERM_GRACE_MS,D as STDERR_TAIL_LINES,W as abortChildProcess,F as acknowledgedBuildArgKeys,ee as buildCacheRepositoryName,M as buildxArgvBuilder,G as createBakeWarningDeduper,Z as createEcrAuthSession,n as evaluateBakeGuard,P as evaluateResolvedBuildArgValues,i as failure,b as inferPublicBuildArgKeys,p as isDockerCliErrorKind,E as isFailure,k as isPublicBuildVarName,o as isSuccess,w as parseMetadataFile,y as parseRawjsonLine,H as projectBuildxResult,X as rawjsonToVertexEvent,_ as success,re as untaggedLifecyclePolicyText};
|
package/dist/index.d.ts
CHANGED
|
@@ -27,3 +27,5 @@ export { deriveContentHashTag, CONTENT_HASH_TAG_PATTERN } from "./infra/deriveCo
|
|
|
27
27
|
export { DEPLOY_MODES, DeployModeSchema, type DeployMode, IMAGE_TAG_PATTERN, ImageTagSchema, type ImageTag, ServiceArtefactSchema, type ServiceArtefact, ServiceArtefactsSchema, type ServiceArtefacts, ARTEFACT_OUTPUT_FIELDS, type ArtefactOutputField, artefactOutputKey } from "./infra/deployArtefacts.js";
|
|
28
28
|
export { MIGRATION_SNAPSHOT_NAME_PREFIX, EXPECTED_SCHEMA_VERSION_ENV, EXPECTED_SCHEMA_VERSION_TOOL_ENV, EXPECTED_CH_SCHEMA_VERSION_ENV, SCHEMA_ADMIN_USER_ENV, SCHEMA_ADMIN_PASSWORD_ENV, PRISMA_MIGRATION_DIR_RE, CLICKHOUSE_MIGRATION_SKIP_RE } from "./migration/constants.js";
|
|
29
29
|
export { PHYSICAL_NAME_FALLBACK_PROPERTIES } from "./cfn/physicalNameProperties.js";
|
|
30
|
+
export { PATTERN_TYPE_VALUES, type PatternType, PATTERN_TYPES, isPatternType, type PatternArtefact, type PatternStackPlacement, type PatternDescriptor, PATTERN_REGISTRY, type OpenNextPatternType, OPENNEXT_PATTERN_TYPES, isOpenNextPatternType, STATIC_SITE_ROUTING_VALUES, type StaticSiteRouting } from "./patterns/patternTypes.js";
|
|
31
|
+
export { DEFAULT_FORMS_FROM_LOCAL_PART, defaultFormsFromAddress, defaultFormsCorsOrigin, isAddressAtDomain } from "./patterns/staticSiteForms.js";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DNS_APEX as
|
|
1
|
+
import{DNS_APEX as E,getDomainExportNames as o}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as _}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as S,TOKEN_STDERR_PREFIX as A}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as n}from"./infra/imageTags.js";import{toPascalCase as i,toKebab as N,toValidDatabaseName as m,toScreamingSnake as s,capitalise as O,getSafeZoneName as P,accountConstructKey as C,hasAsciiStableConstructKey as p}from"./naming/caseConversion.js";import{findAccountNameCollision as c}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as I,suffixedAccountName as M,REGION_SHORT_CODES as x,findTrailingRegionShortCode as u,regionSuffixRejectionMessage as d}from"./naming/connectedAccountName.js";import{normaliseError as l,getErrorMessage as U,hasErrorCode as L,getErrorCode as V,getErrorStack as F,formatErrorString as G}from"./errorUtils.js";import{singleton as v}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as X,filterDangerousEnvVars as b,maskSensitiveOutput as K,parseShellArgs as y,SCOPED_TOKEN_REGEX as Y}from"./securityHelpers.js";import{sleep as B}from"./async/sleep.js";import{mapSettledWithConcurrency as j}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as Z,STRUCTURAL_ENVIRONMENTS as q,ACCOUNT_STAGES as w,ACCOUNT_STAGE_LABELS as J,isAccountStage as Q,ACCOUNT_TIERS as $,AccountTierSchema as ee,isAccountTier as re,environmentToTier as Ee,stageFromWireEnvironment as oe,accountTier as te,getEnvironmentLabel as _e,ACCOUNT_ROLES as ae}from"./environments.js";import{RESOURCE_CATEGORIES as Ae,categoriseResource as Te,getExpectedDuration as ne,getFriendlyResourceType as Re}from"./resourceCategorisation.js";import{parseGitRemoteUrl as Ne}from"./repo/gitRemoteParser.js";import{abbreviateRegion as se,AWS_REGIONS_METADATA as Oe,DEFAULT_REGION as Pe,getRegionInfo as Ce,MAX_SECONDARY_REGIONS as pe,OPT_IN_REGION_CODES as fe,optInRegionWarning as ce,regions as ge,suggestRegionForTimezone as Ie}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 Ue,SSM_COMPONENT_PATTERN as Le,SSM_COMPONENT_ERROR as Ve,SSM_STANDARD_MAX_VALUE_BYTES as Fe,SecretNamespaceSchema as Ge,buildNamespaceParts as he,buildParameterPath as ve,parseParameterPath as He,isManageablePath as Xe,parseDotEnv as be,escapeDotEnvValue as Ke}from"./secrets.js";import{ConnectionWireSchema as Ye,ConnectionsListResponseSchema as We}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as ke,deriveTargets as je,deriveAllTargets as ze,environmentOrTier as Ze,findTarget as qe,generateTargetName as we}from"./targets.js";import{buildAppConfigPath as Qe}from"./repo/appPath.js";import{findInfrastructurePaths as er,findBoundaryPath as rr,isInfrastructureFile as Er}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as tr}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as ar,RESERVED_APP_NAME_MESSAGE as Sr,isReservedAppName as Ar,RESERVED_APP_NAME_SUFFIX as Tr,RESERVED_APP_NAME_SUFFIX_MESSAGE as nr,hasReservedAppNameSuffix as Rr}from"./naming/reservedAppNames.js";import{deriveContentHashTag as Nr,CONTENT_HASH_TAG_PATTERN as mr}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as Or,DeployModeSchema as Pr,IMAGE_TAG_PATTERN as Cr,ImageTagSchema as pr,ServiceArtefactSchema as fr,ServiceArtefactsSchema as cr,ARTEFACT_OUTPUT_FIELDS as gr,artefactOutputKey as Ir}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 Dr,SCHEMA_ADMIN_USER_ENV as lr,SCHEMA_ADMIN_PASSWORD_ENV as Ur,PRISMA_MIGRATION_DIR_RE as Lr,CLICKHOUSE_MIGRATION_SKIP_RE as Vr}from"./migration/constants.js";import{PHYSICAL_NAME_FALLBACK_PROPERTIES as Gr}from"./cfn/physicalNameProperties.js";import{PATTERN_TYPE_VALUES as vr,PATTERN_TYPES as Hr,isPatternType as Xr,PATTERN_REGISTRY as br,OPENNEXT_PATTERN_TYPES as Kr,isOpenNextPatternType as yr,STATIC_SITE_ROUTING_VALUES as Yr}from"./patterns/patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as Br,defaultFormsFromAddress as kr,defaultFormsCorsOrigin as jr,isAddressAtDomain as zr}from"./patterns/staticSiteForms.js";export{ae as ACCOUNT_ROLES,w as ACCOUNT_STAGES,Z as ACCOUNT_STAGES_WITH_ROOT,J as ACCOUNT_STAGE_LABELS,$ as ACCOUNT_TIERS,S as APPROVAL_TOKEN_OUTPUT_PREFIX,gr as ARTEFACT_OUTPUT_FIELDS,Oe as AWS_REGIONS_METADATA,ee as AccountTierSchema,_ as BACKUP_VAULT_NAME,Vr as CLICKHOUSE_MIGRATION_SKIP_RE,mr as CONTENT_HASH_TAG_PATTERN,Ye as ConnectionWireSchema,We as ConnectionsListResponseSchema,X as DANGEROUS_ENV_VARS,Br as DEFAULT_FORMS_FROM_LOCAL_PART,Pe as DEFAULT_REGION,Or as DEPLOY_MODES,E as DNS_APEX,Pr as DeployModeSchema,Dr as EXPECTED_CH_SCHEMA_VERSION_ENV,ur as EXPECTED_SCHEMA_VERSION_ENV,dr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,Cr as IMAGE_TAG_PATTERN,pr as ImageTagSchema,ue as MACHINE_ONLY_SCOPES,pe as MAX_SECONDARY_REGIONS,xr as MIGRATION_SNAPSHOT_NAME_PREFIX,Kr as OPENNEXT_PATTERN_TYPES,fe as OPT_IN_REGION_CODES,br as PATTERN_REGISTRY,Hr as PATTERN_TYPES,vr as PATTERN_TYPE_VALUES,Gr as PHYSICAL_NAME_FALLBACK_PROPERTIES,Lr as PRISMA_MIGRATION_DIR_RE,x as REGION_SHORT_CODES,ar as RESERVED_APP_NAMES,Sr as RESERVED_APP_NAME_MESSAGE,Tr as RESERVED_APP_NAME_SUFFIX,nr as RESERVED_APP_NAME_SUFFIX_MESSAGE,Ae as RESOURCE_CATEGORIES,Ur as SCHEMA_ADMIN_PASSWORD_ENV,lr as SCHEMA_ADMIN_USER_ENV,Y as SCOPED_TOKEN_REGEX,xe as SCOPE_VALUES,Ue as SECRET_NAME_ERROR,le as SECRET_NAME_PATTERN,Ve as SSM_COMPONENT_ERROR,Le as SSM_COMPONENT_PATTERN,Fe as SSM_STANDARD_MAX_VALUE_BYTES,Yr as STATIC_SITE_ROUTING_VALUES,q as STRUCTURAL_ENVIRONMENTS,Ge as SecretNamespaceSchema,fr as ServiceArtefactSchema,cr as ServiceArtefactsSchema,A as TOKEN_STDERR_PREFIX,de as USER_GRANTABLE_SCOPES,se as abbreviateRegion,C as accountConstructKey,te as accountTier,Ir as artefactOutputKey,Qe as buildAppConfigPath,he as buildNamespaceParts,ve as buildParameterPath,O as capitalise,Te as categoriseResource,I as defaultConnectedAccountName,jr as defaultFormsCorsOrigin,kr as defaultFormsFromAddress,ze as deriveAllTargets,Nr as deriveContentHashTag,ke as deriveRegionsFromOrgConfig,je as deriveTargets,Ze as environmentOrTier,Ee as environmentToTier,Ke as escapeDotEnvValue,b as filterDangerousEnvVars,c as findAccountNameCollision,rr as findBoundaryPath,er as findInfrastructurePaths,qe as findTarget,u as findTrailingRegionShortCode,G as formatErrorString,we as generateTargetName,o as getDomainExportNames,_e as getEnvironmentLabel,V as getErrorCode,U as getErrorMessage,F as getErrorStack,ne as getExpectedDuration,Re as getFriendlyResourceType,Ce as getRegionInfo,P as getSafeZoneName,p as hasAsciiStableConstructKey,L as hasErrorCode,Rr as hasReservedAppNameSuffix,n as imageTagParameterName,tr as inferContainerFromCandidates,Q as isAccountStage,re as isAccountTier,zr as isAddressAtDomain,Er as isInfrastructureFile,Xe as isManageablePath,yr as isOpenNextPatternType,Xr as isPatternType,Ar as isReservedAppName,j as mapSettledWithConcurrency,K as maskSensitiveOutput,l as normaliseError,ce as optInRegionWarning,be as parseDotEnv,Ne as parseGitRemoteUrl,He as parseParameterPath,y as parseShellArgs,d as regionSuffixRejectionMessage,ge as regions,v as singleton,B as sleep,oe as stageFromWireEnvironment,M as suffixedAccountName,Ie as suggestRegionForTimezone,N as toKebab,i as toPascalCase,s as toScreamingSnake,m as toValidDatabaseName};
|
|
@@ -42,6 +42,12 @@ export type ImageTag = z.infer<typeof ImageTagSchema>;
|
|
|
42
42
|
* neither may invent placeholder values. `previousTaskDefinitionArn` exists
|
|
43
43
|
* only where an explicit RegisterTaskDefinition rollout captured it.
|
|
44
44
|
* `ecrRepositoryArn` is absent for non-ECR registries.
|
|
45
|
+
*
|
|
46
|
+
* `functionArn`/`publishedVersion` are the Lambda analogue of
|
|
47
|
+
* `taskDefinitionArn`: present only for a container-image Lambda function
|
|
48
|
+
* whose image was rebuilt this deploy AND whose post-deploy
|
|
49
|
+
* `lambda:PublishVersion` call succeeded (best-effort — a publish failure
|
|
50
|
+
* degrades the artefact, it never fails an already-succeeded deploy).
|
|
45
51
|
*/
|
|
46
52
|
export declare const ServiceArtefactSchema: z.ZodObject<{
|
|
47
53
|
serviceName: z.ZodString;
|
|
@@ -51,6 +57,8 @@ export declare const ServiceArtefactSchema: z.ZodObject<{
|
|
|
51
57
|
ecrRepositoryArn: z.ZodOptional<z.ZodString>;
|
|
52
58
|
taskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
53
59
|
previousTaskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
60
|
+
functionArn: z.ZodOptional<z.ZodString>;
|
|
61
|
+
publishedVersion: z.ZodOptional<z.ZodString>;
|
|
54
62
|
}, z.core.$strict>;
|
|
55
63
|
export type ServiceArtefact = z.infer<typeof ServiceArtefactSchema>;
|
|
56
64
|
export declare const ServiceArtefactsSchema: z.ZodArray<z.ZodObject<{
|
|
@@ -61,6 +69,8 @@ export declare const ServiceArtefactsSchema: z.ZodArray<z.ZodObject<{
|
|
|
61
69
|
ecrRepositoryArn: z.ZodOptional<z.ZodString>;
|
|
62
70
|
taskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
63
71
|
previousTaskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
72
|
+
functionArn: z.ZodOptional<z.ZodString>;
|
|
73
|
+
publishedVersion: z.ZodOptional<z.ZodString>;
|
|
64
74
|
}, z.core.$strict>>;
|
|
65
75
|
export type ServiceArtefacts = z.infer<typeof ServiceArtefactsSchema>;
|
|
66
76
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as
|
|
1
|
+
import{z as i}from"zod";const n=["full","code-only","rollback","restart"],c=i.enum(n),o=/^[a-zA-Z0-9._-]+$/,r=i.string().min(1).max(128).regex(o),a=i.object({serviceName:i.string().min(1),imageTag:r,imageDigest:i.string().min(1).optional(),imageUri:i.string().min(1),ecrRepositoryArn:i.string().min(1).optional(),taskDefinitionArn:i.string().min(1).optional(),previousTaskDefinitionArn:i.string().min(1).optional(),functionArn:i.string().min(1).optional(),publishedVersion:i.string().min(1).optional()}).strict(),m=i.array(a),g=["TaskDefinition","PreviousTaskDefinition","ImageTag","ImageUri","EcrRepositoryArn","ImageDigest"];function p(t,e){return`${t}${e}`}export{g as ARTEFACT_OUTPUT_FIELDS,n as DEPLOY_MODES,c as DeployModeSchema,o as IMAGE_TAG_PATTERN,r as ImageTagSchema,a as ServiceArtefactSchema,m as ServiceArtefactsSchema,p as artefactOutputKey};
|
package/dist/manifest/index.d.ts
CHANGED
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
* the barrel — the package.json `exports` map exposes both subpaths.
|
|
8
8
|
*/
|
|
9
9
|
export { DockerBuildSchema, DockerBuildArgValueSchema, DockerBuildSecretRefSchema, DockerBuildPartialSchema, mergeDockerBuild, ManifestServiceSchema, ManifestPatternSchema, ManifestEcrSchema, ManifestLambdaSchema, ManifestStackHashSchema, ResourceMapEntrySchema, FjallManifestSchema, FJALL_MANIFEST_FILENAME, MANIFEST_SCHEMA_VERSION, BUILDKIT_SECRET_ID_PATTERN, type DockerBuild, type DockerBuildArgValue, type DockerBuildSecretRef, type DockerBuildPartial, type ManifestService, type ManifestPattern, type ManifestEcr, type ManifestLambda, type ManifestStackHash, type ResourceMapEntry, type FjallManifest } from "./schemas.js";
|
|
10
|
-
export { getManifestFilePath, readManifestFile, writeManifestFile, createEmptyManifest, readConstructMap, parseDockerServicesFromManifest, type ManifestDockerService } from "./io.js";
|
|
10
|
+
export { getManifestFilePath, readManifestFile, writeManifestFile, createEmptyManifest, readConstructMap, parseDockerServicesFromManifest, parseLambdaDockerServicesFromManifest, parseLambdaDockerEntriesFromManifest, type ManifestDockerService, type ManifestLambdaDockerEntry } from "./io.js";
|
package/dist/manifest/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DockerBuildSchema as r,DockerBuildArgValueSchema as t,DockerBuildSecretRefSchema as c,DockerBuildPartialSchema as i,mergeDockerBuild as
|
|
1
|
+
import{DockerBuildSchema as r,DockerBuildArgValueSchema as t,DockerBuildSecretRefSchema as c,DockerBuildPartialSchema as i,mergeDockerBuild as s,ManifestServiceSchema as m,ManifestPatternSchema as S,ManifestEcrSchema as M,ManifestLambdaSchema as n,ManifestStackHashSchema as o,ResourceMapEntrySchema as f,FjallManifestSchema as h,FJALL_MANIFEST_FILENAME as E,MANIFEST_SCHEMA_VERSION as l,BUILDKIT_SECRET_ID_PATTERN as F}from"./schemas.js";import{getManifestFilePath as D,readManifestFile as k,writeManifestFile as p,createEmptyManifest as u,readConstructMap as A,parseDockerServicesFromManifest as I,parseLambdaDockerServicesFromManifest as L,parseLambdaDockerEntriesFromManifest as _}from"./io.js";export{F as BUILDKIT_SECRET_ID_PATTERN,t as DockerBuildArgValueSchema,i as DockerBuildPartialSchema,r as DockerBuildSchema,c as DockerBuildSecretRefSchema,E as FJALL_MANIFEST_FILENAME,h as FjallManifestSchema,l as MANIFEST_SCHEMA_VERSION,M as ManifestEcrSchema,n as ManifestLambdaSchema,S as ManifestPatternSchema,m as ManifestServiceSchema,o as ManifestStackHashSchema,f as ResourceMapEntrySchema,u as createEmptyManifest,D as getManifestFilePath,s as mergeDockerBuild,I as parseDockerServicesFromManifest,_ as parseLambdaDockerEntriesFromManifest,L as parseLambdaDockerServicesFromManifest,A as readConstructMap,k as readManifestFile,p as writeManifestFile};
|
package/dist/manifest/io.d.ts
CHANGED
|
@@ -58,3 +58,39 @@ export interface ManifestDockerService {
|
|
|
58
58
|
* the deploy phase.
|
|
59
59
|
*/
|
|
60
60
|
export declare function parseDockerServicesFromManifest(cdkOutPath: string): ManifestDockerService[];
|
|
61
|
+
/**
|
|
62
|
+
* Extract Lambda container-image entries with a Docker build configuration
|
|
63
|
+
* from the Fjall manifest, projected into the same `{name, docker}` shape
|
|
64
|
+
* `parseDockerServicesFromManifest` returns for ECS services — this lets both
|
|
65
|
+
* lists feed the same downstream build/grouping/tagging pipeline unchanged.
|
|
66
|
+
*
|
|
67
|
+
* The projected `name` is the Lambda's `imageKey` (falling back to its own
|
|
68
|
+
* manifest `name` when absent): several Lambda functions may deliberately
|
|
69
|
+
* share one built image, so the build/tag identity is keyed by the shared
|
|
70
|
+
* image, not by each function's own name. Entries sharing one `imageKey` are
|
|
71
|
+
* deduped to a single build; a divergent `docker` config across entries
|
|
72
|
+
* sharing an `imageKey` is a real config mistake (ambiguous which build
|
|
73
|
+
* should win) and throws rather than silently picking one.
|
|
74
|
+
*
|
|
75
|
+
* Returns an empty array if the manifest does not exist, cannot be parsed, or
|
|
76
|
+
* contains no Lambda Docker entries. Lambdas without a `docker` block
|
|
77
|
+
* (bring-your-own-image) are skipped, not an error.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parseLambdaDockerServicesFromManifest(cdkOutPath: string): ManifestDockerService[];
|
|
80
|
+
/** A single container-Lambda manifest entry that declared a `docker` block. */
|
|
81
|
+
export interface ManifestLambdaDockerEntry {
|
|
82
|
+
/** The Lambda's own manifest/function name (NOT the shared image key). */
|
|
83
|
+
readonly name: string;
|
|
84
|
+
/** Shared build/tag key — `imageKey` if set, else `name`. */
|
|
85
|
+
readonly imageKey: string;
|
|
86
|
+
readonly docker: DockerBuild;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract every container-Lambda manifest entry that declared a `docker`
|
|
90
|
+
* block, WITHOUT deduping by `imageKey` — unlike
|
|
91
|
+
* `parseLambdaDockerServicesFromManifest`, this preserves each function's own
|
|
92
|
+
* `name`, needed to map a built image's shared `imageKey` back to the actual
|
|
93
|
+
* Lambda function name(s) that need a fresh published version after deploy
|
|
94
|
+
* (`lambda:PublishVersion` takes a function name, not an image key).
|
|
95
|
+
*/
|
|
96
|
+
export declare function parseLambdaDockerEntriesFromManifest(cdkOutPath: string): ManifestLambdaDockerEntry[];
|
package/dist/manifest/io.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFile as
|
|
1
|
+
import{readFile as g,writeFile as y,unlink as h,rename as M,mkdir as b}from"fs/promises";import{readFileSync as S}from"fs";import{dirname as k,join as f}from"path";import{logger as s}from"../logger.js";import{fileExists as A}from"../fsHelpers.js";import{getErrorMessage as d}from"../errorUtils.js";import{recordToConstructMap as F}from"../constructMap.js";import{DockerBuildSchema as w,FjallManifestSchema as x,FJALL_MANIFEST_FILENAME as u,MANIFEST_SCHEMA_VERSION as E}from"./schemas.js";function m(e){return f(e,u)}async function j(e){const n=m(e);if(!await A(n))return null;try{const r=await g(n,"utf-8"),t=JSON.parse(r),i=x.safeParse(t);return i.success||s.debug("FjallManifest","Manifest validation failed",{path:n,errors:i.error.issues.map(a=>`${a.path.join(".")}: ${a.message}`)}),i.success?i.data:null}catch(r){return s.debug("FjallManifest","Failed to read manifest file",{path:n,error:d(r)}),null}}async function C(e,n){const r=m(e),t=`${r}.${Date.now()}.tmp`;await b(k(r),{recursive:!0});try{await y(t,JSON.stringify(n,null,2),"utf-8"),await M(t,r)}catch(i){try{await h(t)}catch(a){s.debug("FjallManifest","Temp file cleanup failed (non-fatal)",{path:t,error:d(a)})}throw i}}function T(e){return{version:E,generatedAt:new Date().toISOString(),appName:e,services:[],lambdas:[],stacks:{}}}async function _(e){const n=await j(e);return n?.resourceMap?F(n.resourceMap):new Map}function o(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function p(e){if(!o(e)||typeof e.path!="string"||e.path.length===0)return;const n=typeof e.context=="string"&&e.context.length>0?e.context:void 0,r=typeof e.target=="string"&&e.target.length>0?e.target:void 0,t=o(e.buildArgs)&&Object.keys(e.buildArgs).length>0?e.buildArgs:void 0,i=Array.isArray(e.buildSecrets)&&e.buildSecrets.length>0?e.buildSecrets:void 0,a={path:e.path,...n!==void 0&&{context:n},...r!==void 0&&{target:r},...t!==void 0&&{buildArgs:t},...i!==void 0&&{buildSecrets:i}},c=w.safeParse(a);return c.success?c.data:void 0}function l(e,n){const r=f(e,u);let t;try{t=S(r,"utf-8")}catch{s.debug("FjallManifest",`Manifest file not readable \u2014 no ${n} extracted`,{path:r});return}let i;try{i=JSON.parse(t)}catch{s.debug("FjallManifest",`Manifest is not valid JSON \u2014 no ${n} extracted`,{path:r});return}return o(i)?i:void 0}function R(e){const n=l(e,"Docker services");if(n===void 0||!Array.isArray(n.services))return[];const r=[];for(const t of n.services){if(!o(t)||typeof t.name!="string")continue;const i=p(t.docker);i!==void 0&&r.push({name:t.name,docker:i})}return r}function B(e){const n=new Map;for(const{name:r,imageKey:t,docker:i}of D(e)){const a=n.get(t);if(a===void 0){n.set(t,{name:t,docker:i});continue}if(JSON.stringify(a.docker)!==JSON.stringify(i))throw new Error(`Lambda functions sharing image key "${t}" declare different \`docker\` configs \u2014 every Lambda function sharing an \`image\` must build from the identical Dockerfile/context/target/buildArgs (offending function: "${r}").`)}return Array.from(n.values())}function D(e){const n=l(e,"Lambda Docker entries");if(n===void 0||!Array.isArray(n.lambdas))return[];const r=[];for(const t of n.lambdas){if(!o(t)||typeof t.name!="string")continue;const i=p(t.docker);if(i===void 0)continue;const a=typeof t.imageKey=="string"&&t.imageKey.length>0?t.imageKey:t.name;r.push({name:t.name,imageKey:a,docker:i})}return r}export{T as createEmptyManifest,m as getManifestFilePath,R as parseDockerServicesFromManifest,D as parseLambdaDockerEntriesFromManifest,B as parseLambdaDockerServicesFromManifest,_ as readConstructMap,j as readManifestFile,C as writeManifestFile};
|
|
@@ -213,6 +213,8 @@ export type ManifestService = z.infer<typeof ManifestServiceSchema>;
|
|
|
213
213
|
declare const ManifestPatternSchema: z.ZodObject<{
|
|
214
214
|
type: z.ZodEnum<{
|
|
215
215
|
payload: "payload";
|
|
216
|
+
nextjs: "nextjs";
|
|
217
|
+
staticsite: "staticsite";
|
|
216
218
|
}>;
|
|
217
219
|
name: z.ZodString;
|
|
218
220
|
source: z.ZodString;
|
|
@@ -227,6 +229,32 @@ declare const ManifestLambdaSchema: z.ZodObject<{
|
|
|
227
229
|
secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
228
230
|
ssmSecretsPath: z.ZodOptional<z.ZodString>;
|
|
229
231
|
importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
232
|
+
docker: z.ZodOptional<z.ZodObject<{
|
|
233
|
+
path: z.ZodString;
|
|
234
|
+
context: z.ZodOptional<z.ZodString>;
|
|
235
|
+
target: z.ZodOptional<z.ZodString>;
|
|
236
|
+
buildArgs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
237
|
+
ssm: z.ZodOptional<z.ZodString>;
|
|
238
|
+
secretsManager: z.ZodOptional<z.ZodObject<{
|
|
239
|
+
name: z.ZodOptional<z.ZodString>;
|
|
240
|
+
arn: z.ZodOptional<z.ZodString>;
|
|
241
|
+
field: z.ZodOptional<z.ZodString>;
|
|
242
|
+
}, z.core.$strict>>;
|
|
243
|
+
env: z.ZodOptional<z.ZodString>;
|
|
244
|
+
acknowledgePublic: z.ZodOptional<z.ZodBoolean>;
|
|
245
|
+
}, z.core.$strict>]>>>;
|
|
246
|
+
buildSecrets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
247
|
+
id: z.ZodString;
|
|
248
|
+
ssm: z.ZodOptional<z.ZodString>;
|
|
249
|
+
secretsManager: z.ZodOptional<z.ZodObject<{
|
|
250
|
+
name: z.ZodOptional<z.ZodString>;
|
|
251
|
+
arn: z.ZodOptional<z.ZodString>;
|
|
252
|
+
field: z.ZodOptional<z.ZodString>;
|
|
253
|
+
}, z.core.$strict>>;
|
|
254
|
+
env: z.ZodOptional<z.ZodString>;
|
|
255
|
+
}, z.core.$strict>>>;
|
|
256
|
+
}, z.core.$strict>>;
|
|
257
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
230
258
|
}, z.core.$strict>;
|
|
231
259
|
export type ManifestLambda = z.infer<typeof ManifestLambdaSchema>;
|
|
232
260
|
declare const ManifestStackHashSchema: z.ZodObject<{
|
|
@@ -292,10 +320,38 @@ export declare const FjallManifestSchema: z.ZodObject<{
|
|
|
292
320
|
secrets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
293
321
|
ssmSecretsPath: z.ZodOptional<z.ZodString>;
|
|
294
322
|
importedSecretNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
323
|
+
docker: z.ZodOptional<z.ZodObject<{
|
|
324
|
+
path: z.ZodString;
|
|
325
|
+
context: z.ZodOptional<z.ZodString>;
|
|
326
|
+
target: z.ZodOptional<z.ZodString>;
|
|
327
|
+
buildArgs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
328
|
+
ssm: z.ZodOptional<z.ZodString>;
|
|
329
|
+
secretsManager: z.ZodOptional<z.ZodObject<{
|
|
330
|
+
name: z.ZodOptional<z.ZodString>;
|
|
331
|
+
arn: z.ZodOptional<z.ZodString>;
|
|
332
|
+
field: z.ZodOptional<z.ZodString>;
|
|
333
|
+
}, z.core.$strict>>;
|
|
334
|
+
env: z.ZodOptional<z.ZodString>;
|
|
335
|
+
acknowledgePublic: z.ZodOptional<z.ZodBoolean>;
|
|
336
|
+
}, z.core.$strict>]>>>;
|
|
337
|
+
buildSecrets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
338
|
+
id: z.ZodString;
|
|
339
|
+
ssm: z.ZodOptional<z.ZodString>;
|
|
340
|
+
secretsManager: z.ZodOptional<z.ZodObject<{
|
|
341
|
+
name: z.ZodOptional<z.ZodString>;
|
|
342
|
+
arn: z.ZodOptional<z.ZodString>;
|
|
343
|
+
field: z.ZodOptional<z.ZodString>;
|
|
344
|
+
}, z.core.$strict>>;
|
|
345
|
+
env: z.ZodOptional<z.ZodString>;
|
|
346
|
+
}, z.core.$strict>>>;
|
|
347
|
+
}, z.core.$strict>>;
|
|
348
|
+
imageKey: z.ZodOptional<z.ZodString>;
|
|
295
349
|
}, z.core.$strict>>;
|
|
296
350
|
pattern: z.ZodOptional<z.ZodObject<{
|
|
297
351
|
type: z.ZodEnum<{
|
|
298
352
|
payload: "payload";
|
|
353
|
+
nextjs: "nextjs";
|
|
354
|
+
staticsite: "staticsite";
|
|
299
355
|
}>;
|
|
300
356
|
name: z.ZodString;
|
|
301
357
|
source: z.ZodString;
|
package/dist/manifest/schemas.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as e}from"zod";const
|
|
1
|
+
import{z as e}from"zod";import{PATTERN_TYPE_VALUES as u}from"../patterns/patternTypes.js";const M="fjall-manifest.json",b=1,f=/^[A-Za-z0-9_.-]+$/,y=e.object({id:e.string().min(1,"buildSecret id cannot be empty").regex(f,"buildSecret id may contain only letters, digits, '_', '.', and '-' (no comma, '=', or whitespace, which would break the buildx --secret argv)"),ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildSecret requires exactly one source (ssm, secretsManager, or env)"})}),S=e.union([e.string(),e.object({ssm:e.string().min(1,"ssm parameter name cannot be empty").optional(),secretsManager:e.object({name:e.string().min(1,"secret name cannot be empty").optional(),arn:e.string().min(1,"secret arn cannot be empty").optional(),field:e.string().min(1,"secret field cannot be empty").optional()}).strict().refine(t=>t.name===void 0!=(t.arn===void 0),"secretsManager requires exactly one of name or arn").optional(),env:e.string().min(1,"env variable name cannot be empty").optional(),acknowledgePublic:e.boolean().optional()}).strict().superRefine((t,n)=>{[t.ssm,t.secretsManager,t.env].filter(r=>r!==void 0).length!==1&&n.addIssue({code:e.ZodIssueCode.custom,message:"buildArg value requires exactly one source (ssm, secretsManager, or env)"})})]),o=e.object({path:e.string(),context:e.string().min(1,"context cannot be empty").optional(),target:e.string().min(1,"target cannot be empty").optional(),buildArgs:e.record(e.string(),S).optional(),buildSecrets:e.array(y).optional()}).strict(),A=o.partial();function j(t,n){if(n===void 0)return t;const a=n.context??t.context,r=n.target??t.target,s=n.buildArgs??t.buildArgs,i=n.buildSecrets??t.buildSecrets;return{path:n.path??t.path,...a!==void 0&&{context:a},...r!==void 0&&{target:r},...s!==void 0&&{buildArgs:s},...i!==void 0&&{buildSecrets:i}}}const c=e.object({name:e.string(),clusterName:e.string().optional(),docker:o.optional(),containerPort:e.number().optional(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional()}).strict(),m=e.object({type:e.enum(u),name:e.string(),source:e.string()}).strict(),l=e.object({repositoryName:e.string()}).strict(),p=e.object({name:e.string(),secrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),importedSecretNames:e.array(e.string()).optional(),docker:o.optional(),imageKey:e.string().optional()}).strict(),d=e.object({templateHash:e.string(),synthTimestamp:e.string()}).strict(),g=e.object({constructPath:e.string().max(512),group:e.string().max(128),resourceType:e.string().max(256)}).strict(),E=e.object({version:e.literal(b),generatedAt:e.string(),appName:e.string(),services:e.array(c),lambdas:e.array(p),pattern:m.optional(),ecr:l.optional(),stacks:e.record(e.string(),d),resourceMap:e.record(e.string(),g).optional()}).strict();export{f as BUILDKIT_SECRET_ID_PATTERN,S as DockerBuildArgValueSchema,A as DockerBuildPartialSchema,o as DockerBuildSchema,y as DockerBuildSecretRefSchema,M as FJALL_MANIFEST_FILENAME,E as FjallManifestSchema,b as MANIFEST_SCHEMA_VERSION,l as ManifestEcrSchema,p as ManifestLambdaSchema,m as ManifestPatternSchema,c as ManifestServiceSchema,d as ManifestStackHashSchema,g as ResourceMapEntrySchema,j as mergeDockerBuild};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot-gate version comparison with expand-only rollback tolerance.
|
|
3
|
+
*
|
|
4
|
+
* The schema-version gates (`verifyExpectedSchemaVersion` for Postgres,
|
|
5
|
+
* `verifyExpectedClickHouseSchemaVersion` for ClickHouse) both decide whether a
|
|
6
|
+
* booting image's EXPECTED schema version is SATISFIED by what the database has
|
|
7
|
+
* APPLIED. Both route their `matches` verdict through here so the two gates can
|
|
8
|
+
* never drift (Code Quality § "Coupled values: shared source at 2 occurrences").
|
|
9
|
+
*
|
|
10
|
+
* The tolerance exists because expand/contract migrations make an old image safe
|
|
11
|
+
* against a newer schema: rolling the image back without rolling the DB back is
|
|
12
|
+
* the intended expand-only rollback, and a strict-equality gate defeated it by
|
|
13
|
+
* refusing to boot. See `aiDocs/patterns/migration-safety-pattern.md`.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* True when `version` is a monotonically-ordered migration identifier whose
|
|
17
|
+
* lexicographic order equals its chronological/sequence order — a Prisma
|
|
18
|
+
* migration name (`20260706120000_add_users`, fixed-width timestamp) or a
|
|
19
|
+
* ClickHouse migration filename (`011-issue-brief-citations.sql`, zero-padded
|
|
20
|
+
* numeric prefix). Both are selected as "latest" by `.sort()` at synth time
|
|
21
|
+
* (`pickLatest*Migration`) and apply time (the runner), so lexicographic
|
|
22
|
+
* comparison here mirrors that selection exactly.
|
|
23
|
+
*
|
|
24
|
+
* Returns false for the non-orderable audit fields — `_schema_migrations.version`
|
|
25
|
+
* (sha256 content hash) and `prisma_version` (the constant marker `"applied"`) —
|
|
26
|
+
* so the caller fails closed on exact identity for those.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isOrderableSchemaVersion(version: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the `expected` schema version baked into the image is satisfied by the
|
|
31
|
+
* `actual` version applied to the database.
|
|
32
|
+
*
|
|
33
|
+
* When BOTH are orderable migration identifiers, the DB may sit AT or AHEAD of
|
|
34
|
+
* the image's expectation — an old image booting against a newer expand-only
|
|
35
|
+
* schema is safe, so `actual >= expected` passes. The forward direction
|
|
36
|
+
* (`actual < expected`, new code against an older schema) still fails: the
|
|
37
|
+
* columns/tables the image needs may not exist yet.
|
|
38
|
+
*
|
|
39
|
+
* When EITHER is non-orderable (content hash, constant marker), there is no
|
|
40
|
+
* defined newer/older, so it falls back to strict identity — fail-closed,
|
|
41
|
+
* byte-identical to the pre-tolerance behaviour.
|
|
42
|
+
*
|
|
43
|
+
* The `>=` is lexicographic, which equals sequence order only when both
|
|
44
|
+
* versions share an equal-width numeric prefix (Prisma's fixed 14-digit
|
|
45
|
+
* timestamp always does; a ClickHouse `NNN-…` filename only if authored
|
|
46
|
+
* zero-padded). If the widths differ — or a `.sql` name carries no numeric
|
|
47
|
+
* prefix at all — lexicographic order cannot be trusted, so it also falls back
|
|
48
|
+
* to strict identity (fail-closed) rather than return a possibly-inverted `>=`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isSchemaVersionSatisfied(expected: string, actual: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{PRISMA_MIGRATION_DIR_RE as u,CLICKHOUSE_MIGRATION_FILE_RE as s}from"./constants.js";function e(n){return u.test(n)||s.test(n)}function o(n){const r=/^(\d+)/.exec(n);return r===null?null:r[1].length}function I(n,r){if(e(n)&&e(r)){const t=o(n),i=o(r);return t!==null&&i!==null&&t===i?r>=n:r===n}return r===n}export{e as isOrderableSchemaVersion,I as isSchemaVersionSatisfied};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{describe as i,expect as e,it as s}from"vitest";import{isOrderableSchemaVersion as t,isSchemaVersionSatisfied as a}from"./compareSchemaVersion.js";i("isOrderableSchemaVersion",()=>{s("recognises a Prisma migration name",()=>{e(t("20260706120000_add_log_events")).toBe(!0)}),s("recognises a ClickHouse migration filename",()=>{e(t("011-issue-brief-citations.sql")).toBe(!0)}),s("rejects a sha256 content hash (the audit `version` field)",()=>{e(t("3f2b1c9d8e7a6f5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b")).toBe(!1)}),s("rejects the constant `prisma_version` marker",()=>{e(t("applied")).toBe(!1)}),s("rejects a digit-leading string with no migration shape",()=>{e(t("19bc2fdeadbeef")).toBe(!1)})}),i("isSchemaVersionSatisfied",()=>{i("orderable versions (expand-only tolerance)",()=>{s("passes when actual equals expected (Prisma)",()=>{e(a("20260520000000_add_widgets","20260520000000_add_widgets")).toBe(!0)}),s("passes when actual is AHEAD of expected (Prisma rollback)",()=>{e(a("20260520000000_add_widgets","20260521000000_more")).toBe(!0)}),s("fails when actual TRAILS expected (Prisma \u2014 new code, old schema)",()=>{e(a("20260520000000_add_widgets","20260519000000_old")).toBe(!1)}),s("passes when actual is AHEAD of expected (ClickHouse rollback)",()=>{e(a("010-issue-briefs.sql","011-citations.sql")).toBe(!0)}),s("fails when actual TRAILS expected (ClickHouse)",()=>{e(a("010-issue-briefs.sql","009-old.sql")).toBe(!1)}),s("respects zero-padded numeric ordering, not naive digit count",()=>{e(a("010-a.sql","002-b.sql")).toBe(!1),e(a("002-b.sql","010-a.sql")).toBe(!0)})}),i("mismatched-width prefixes (fail-closed, no lexicographic trust)",()=>{s("refuses `>=` when CH numeric-prefix widths differ",()=>{e(a("010-x.sql","9-hotfix.sql")).toBe(!1),e(a("9-hotfix.sql","010-x.sql")).toBe(!1)}),s("still passes on exact identity even at a mismatched-width name",()=>{e(a("9-hotfix.sql","9-hotfix.sql")).toBe(!0)}),s("fails closed for a `.sql` name with no numeric prefix",()=>{e(a("010-x.sql","hotfix.sql")).toBe(!1),e(a("hotfix.sql","hotfix.sql")).toBe(!0)})}),i("non-orderable versions (fail-closed strict identity)",()=>{s("passes only on exact hash identity",()=>{const o="3f2b1c9d8e7a6f5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b";e(a(o,o)).toBe(!0)}),s("fails for a different hash even when it sorts later",()=>{e(a("00000000","ffffffff")).toBe(!1)}),s("fails when only one side is orderable",()=>{e(a("20260520000000_add_widgets","applied")).toBe(!1),e(a("applied","20260521000000_more")).toBe(!1)})})});
|
|
@@ -59,3 +59,12 @@ export declare const PRISMA_MIGRATION_DIR_RE: RegExp;
|
|
|
59
59
|
* record, hard-failing every boot.
|
|
60
60
|
*/
|
|
61
61
|
export declare const CLICKHOUSE_MIGRATION_SKIP_RE: RegExp;
|
|
62
|
+
/**
|
|
63
|
+
* A ClickHouse migration filename — the `.sql` suffix by which
|
|
64
|
+
* `pickLatestClickHouseMigration` / `runSqlMigrations` select the "latest"
|
|
65
|
+
* migration (both `.endsWith(".sql")` then `.sort()`). The boot gate's
|
|
66
|
+
* order-tolerant comparison uses it to tell a monotonically-ordered CH version
|
|
67
|
+
* (`ch_version` = the latest `.sql` filename) from the non-orderable audit
|
|
68
|
+
* fields (`version` = sha256 hash, `prisma_version` = the constant `"applied"`).
|
|
69
|
+
*/
|
|
70
|
+
export declare const CLICKHOUSE_MIGRATION_FILE_RE: RegExp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const E="fjall-premigrate",_="EXPECTED_SCHEMA_VERSION",S="EXPECTED_SCHEMA_VERSION_TOOL",
|
|
1
|
+
const E="fjall-premigrate",_="EXPECTED_SCHEMA_VERSION",S="EXPECTED_SCHEMA_VERSION_TOOL",I="EXPECTED_CH_SCHEMA_VERSION",A="SCHEMA_ADMIN_USER",C="SCHEMA_ADMIN_PASSWORD",N=/^\d{14}_/,M=/\.dev\.sql$/,R=/\.sql$/;export{R as CLICKHOUSE_MIGRATION_FILE_RE,M as CLICKHOUSE_MIGRATION_SKIP_RE,I as EXPECTED_CH_SCHEMA_VERSION_ENV,_ as EXPECTED_SCHEMA_VERSION_ENV,S as EXPECTED_SCHEMA_VERSION_TOOL_ENV,E as MIGRATION_SNAPSHOT_NAME_PREFIX,N as PRISMA_MIGRATION_DIR_RE,C as SCHEMA_ADMIN_PASSWORD_ENV,A as SCHEMA_ADMIN_USER_ENV};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { MIGRATION_SNAPSHOT_NAME_PREFIX, EXPECTED_SCHEMA_VERSION_ENV, EXPECTED_SCHEMA_VERSION_TOOL_ENV, EXPECTED_CH_SCHEMA_VERSION_ENV, SCHEMA_ADMIN_USER_ENV, SCHEMA_ADMIN_PASSWORD_ENV, PRISMA_MIGRATION_DIR_RE, CLICKHOUSE_MIGRATION_SKIP_RE } from "./constants.js";
|
|
2
2
|
export { pickLatestPrismaMigration } from "./pickLatestPrismaMigration.js";
|
|
3
3
|
export { pickLatestClickHouseMigration } from "./pickLatestClickHouseMigration.js";
|
|
4
|
+
export { isOrderableSchemaVersion, isSchemaVersionSatisfied } from "./compareSchemaVersion.js";
|
|
4
5
|
export { type MigrationsSqlClient, type VerifyExpectedSchemaVersionOpts, type VerifyExpectedSchemaVersionResult, verifyExpectedSchemaVersion } from "./verifyExpectedSchemaVersion.js";
|
|
5
6
|
export { CLICKHOUSE_MANAGED_USERS_ENV, MANAGED_USER_NAME_PATTERN, userPasswordEnvName, ManagedUserNameSchema, ManagedUserNamesSchema, type ManagedUserName, type ManagedUserNames } from "./clickhouseSqlUsers.js";
|
package/dist/migration/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MIGRATION_SNAPSHOT_NAME_PREFIX as e,EXPECTED_SCHEMA_VERSION_ENV as
|
|
1
|
+
import{MIGRATION_SNAPSHOT_NAME_PREFIX as e,EXPECTED_SCHEMA_VERSION_ENV as S,EXPECTED_SCHEMA_VERSION_TOOL_ENV as r,EXPECTED_CH_SCHEMA_VERSION_ENV as N,SCHEMA_ADMIN_USER_ENV as a,SCHEMA_ADMIN_PASSWORD_ENV as A,PRISMA_MIGRATION_DIR_RE as o,CLICKHOUSE_MIGRATION_SKIP_RE as M}from"./constants.js";import{pickLatestPrismaMigration as i}from"./pickLatestPrismaMigration.js";import{pickLatestClickHouseMigration as R}from"./pickLatestClickHouseMigration.js";import{isOrderableSchemaVersion as t,isSchemaVersionSatisfied as C}from"./compareSchemaVersion.js";import{verifyExpectedSchemaVersion as V}from"./verifyExpectedSchemaVersion.js";import{CLICKHOUSE_MANAGED_USERS_ENV as H,MANAGED_USER_NAME_PATTERN as T,userPasswordEnvName as c,ManagedUserNameSchema as p,ManagedUserNamesSchema as D}from"./clickhouseSqlUsers.js";export{H as CLICKHOUSE_MANAGED_USERS_ENV,M as CLICKHOUSE_MIGRATION_SKIP_RE,N as EXPECTED_CH_SCHEMA_VERSION_ENV,S as EXPECTED_SCHEMA_VERSION_ENV,r as EXPECTED_SCHEMA_VERSION_TOOL_ENV,T as MANAGED_USER_NAME_PATTERN,e as MIGRATION_SNAPSHOT_NAME_PREFIX,p as ManagedUserNameSchema,D as ManagedUserNamesSchema,o as PRISMA_MIGRATION_DIR_RE,A as SCHEMA_ADMIN_PASSWORD_ENV,a as SCHEMA_ADMIN_USER_ENV,t as isOrderableSchemaVersion,C as isSchemaVersionSatisfied,R as pickLatestClickHouseMigration,i as pickLatestPrismaMigration,c as userPasswordEnvName,V as verifyExpectedSchemaVersion};
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
* Returns `{ matches, expected, actual }` rather than throwing on mismatch:
|
|
21
21
|
* the boot gate's caller owns the exit-code / log shape so the helper can
|
|
22
22
|
* be reused by integration tests, dashboards, and CLI checks alike.
|
|
23
|
+
*
|
|
24
|
+
* `matches` tolerates expand-only rollback: an old image booting against a
|
|
25
|
+
* NEWER applied schema passes (`actual >= expected`), while the forward
|
|
26
|
+
* direction (new code against an older schema) still fails. The comparison is
|
|
27
|
+
* delegated to `isSchemaVersionSatisfied` — see it for the orderability rules.
|
|
23
28
|
*/
|
|
24
29
|
/**
|
|
25
30
|
* Minimal SQL-driver shim. Compatible with `pg.Client.query`, `mysql2.query`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
import{isSchemaVersionSatisfied as c}from"./compareSchemaVersion.js";const s="SELECT migration_name FROM _prisma_migrations WHERE finished_at IS NOT NULL ORDER BY finished_at DESC LIMIT 1",a="migration_name";async function m(e){if(e.signal?.aborted)throw new Error("verifyExpectedSchemaVersion: aborted by signal before query");let r,n;if(e.tool==="prisma")r=s,n=a;else if(e.tool==="custom"){if(e.customQuery===void 0)throw new Error('verifyExpectedSchemaVersion: tool="custom" requires customQuery');r=e.customQuery.sql,n=e.customQuery.column}else throw new Error(`verifyExpectedSchemaVersion: unknown tool ${String(e.tool)}`);const i=(await e.client.query(r)).rows[0],o=i!==void 0?i[n]:void 0,t=typeof o=="string"?o:null;return{matches:t!==null&&c(e.expected,t),expected:e.expected,actual:t}}export{m as verifyExpectedSchemaVersion};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{describe as
|
|
1
|
+
import{describe as c,expect as a,it as s,vi as l}from"vitest";import{verifyExpectedSchemaVersion as o}from"./verifyExpectedSchemaVersion.js";function n(t){const e=[];return{client:{query:l.fn(async i=>(e.push(i),{rows:t}))},queries:e}}c("verifyExpectedSchemaVersion",()=>{c('tool: "prisma"',()=>{s("returns matches=true when actual === expected",async()=>{const{client:t,queries:e}=n([{migration_name:"20260520000000_add_widgets"}]),r=await o({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(r).toEqual({matches:!0,expected:"20260520000000_add_widgets",actual:"20260520000000_add_widgets"}),a(e).toHaveLength(1),a(e[0]).toContain("_prisma_migrations"),a(e[0]).toContain("finished_at IS NOT NULL"),a(e[0]).toContain("ORDER BY finished_at DESC LIMIT 1")}),s("returns matches=false with actual when DB trails image",async()=>{const{client:t}=n([{migration_name:"20260519000000_old"}]),e=await o({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(e).toEqual({matches:!1,expected:"20260520000000_add_widgets",actual:"20260519000000_old"})}),s("returns matches=true when DB is AHEAD (expand-only rollback)",async()=>{const{client:t}=n([{migration_name:"20260521000000_add_more_widgets"}]),e=await o({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(e).toEqual({matches:!0,expected:"20260520000000_add_widgets",actual:"20260521000000_add_more_widgets"})}),s("returns actual=null on empty result set",async()=>{const{client:t}=n([]),e=await o({tool:"prisma",expected:"20260520000000_add_widgets",client:t});a(e).toEqual({matches:!1,expected:"20260520000000_add_widgets",actual:null})}),s("returns actual=null when row's migration_name is not a string",async()=>{const{client:t}=n([{migration_name:12345}]),e=await o({tool:"prisma",expected:"x",client:t});a(e.actual).toBeNull(),a(e.matches).toBe(!1)})}),c('tool: "custom"',()=>{s("uses customQuery sql and column",async()=>{const{client:t,queries:e}=n([{hash:"deadbeef"}]),r=await o({tool:"custom",expected:"deadbeef",client:t,customQuery:{sql:"SELECT hash FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 1",column:"hash"}});a(r.matches).toBe(!0),a(r.actual).toBe("deadbeef"),a(e[0]).toBe("SELECT hash FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 1")}),s("stays strict for non-orderable versions even when actual sorts later",async()=>{const{client:t}=n([{hash:"ffffffff"}]),e=await o({tool:"custom",expected:"00000000",client:t,customQuery:{sql:"SELECT hash FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 1",column:"hash"}});a(e.matches).toBe(!1),a(e.actual).toBe("ffffffff")}),s("throws when customQuery is omitted",async()=>{const{client:t}=n([]);await a(o({tool:"custom",expected:"x",client:t})).rejects.toThrow(/tool="custom" requires customQuery/)})}),c("abort signal",()=>{s("short-circuits before query when signal is already aborted",async()=>{const{client:t,queries:e}=n([{migration_name:"irrelevant"}]),r=new AbortController;r.abort(),await a(o({tool:"prisma",expected:"x",client:t,signal:r.signal})).rejects.toThrow(/aborted by signal before query/),a(e).toEqual([])})})});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pattern vocabulary, its dispatch registry and the static-site forms
|
|
3
|
+
* address rules.
|
|
4
|
+
*
|
|
5
|
+
* Available as the `@fjall/util/patterns` subpath so consumers that must stay
|
|
6
|
+
* free of `node:*` imports (deploy-core's browser-safe `types/` barrel, which
|
|
7
|
+
* the webapp bundles) can take the vocabulary without depending on the root
|
|
8
|
+
* barrel's contents staying pure.
|
|
9
|
+
*/
|
|
10
|
+
export { PATTERN_TYPE_VALUES, type PatternType, PATTERN_TYPES, isPatternType, type PatternArtefact, type PatternStackPlacement, type PatternDescriptor, PATTERN_REGISTRY, type OpenNextPatternType, OPENNEXT_PATTERN_TYPES, isOpenNextPatternType, STATIC_SITE_ROUTING_VALUES, type StaticSiteRouting } from "./patternTypes.js";
|
|
11
|
+
export { DEFAULT_FORMS_FROM_LOCAL_PART, defaultFormsFromAddress, defaultFormsCorsOrigin, isAddressAtDomain } from "./staticSiteForms.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{PATTERN_TYPE_VALUES as r,PATTERN_TYPES as A,isPatternType as _,PATTERN_REGISTRY as e,OPENNEXT_PATTERN_TYPES as P,isOpenNextPatternType as s,STATIC_SITE_ROUTING_VALUES as t}from"./patternTypes.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as o,defaultFormsFromAddress as N,defaultFormsCorsOrigin as S,isAddressAtDomain as O}from"./staticSiteForms.js";export{o as DEFAULT_FORMS_FROM_LOCAL_PART,P as OPENNEXT_PATTERN_TYPES,e as PATTERN_REGISTRY,A as PATTERN_TYPES,r as PATTERN_TYPE_VALUES,t as STATIC_SITE_ROUTING_VALUES,S as defaultFormsCorsOrigin,N as defaultFormsFromAddress,O as isAddressAtDomain,s as isOpenNextPatternType,_ as isPatternType};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pattern vocabulary — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* A "pattern" is a whole-application shape (Payload CMS, a Next.js app, a
|
|
5
|
+
* pre-built static site) that the generator emits, the CDK synthesises,
|
|
6
|
+
* deploy-core builds and the CLI offers. Before this module the vocabulary was
|
|
7
|
+
* declared four times — generator schemas, CDK interfaces, the deploy manifest
|
|
8
|
+
* and the CLI create flow — each with a different membership, so adding a
|
|
9
|
+
* pattern to one left the others silently wrong.
|
|
10
|
+
*
|
|
11
|
+
* `PATTERN_REGISTRY` is the compiler's checklist. It is a `Record<PatternType,
|
|
12
|
+
* PatternDescriptor>`, so a new member of `PATTERN_TYPE_VALUES` fails to
|
|
13
|
+
* compile until every dispatch decision — which stack, which builder, which
|
|
14
|
+
* construct id — is stated. Dispatch sites read the descriptor instead of
|
|
15
|
+
* re-deriving the answer from a ternary chain whose `else` branch would
|
|
16
|
+
* otherwise absorb the new pattern.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Every pattern the toolchain knows about.
|
|
20
|
+
*
|
|
21
|
+
* - `payload` — Payload CMS on OpenNext (always has a database + migrations).
|
|
22
|
+
* - `nextjs` — a plain Next.js app on OpenNext (optional database). No CDK
|
|
23
|
+
* construct exists yet: `IPatternProps` (components/infrastructure) omits it,
|
|
24
|
+
* so a generated `nextjs` app fails to synthesise. Add the props + a
|
|
25
|
+
* `PatternFactory.build` case to close the gap.
|
|
26
|
+
* - `staticsite` — pre-built static assets on private S3 + CloudFront/OAC.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PATTERN_TYPE_VALUES: readonly ["payload", "nextjs", "staticsite"];
|
|
29
|
+
export type PatternType = (typeof PATTERN_TYPE_VALUES)[number];
|
|
30
|
+
export declare const PATTERN_TYPES: ReadonlySet<string>;
|
|
31
|
+
export declare function isPatternType(value: unknown): value is PatternType;
|
|
32
|
+
/**
|
|
33
|
+
* What a pattern's build step produces. Selects the deploy-core builder:
|
|
34
|
+
* `opennext-lambda` runs the OpenNext build and uploads a Lambda bundle;
|
|
35
|
+
* `static-assets` runs the site's own build command and syncs a directory to S3.
|
|
36
|
+
*/
|
|
37
|
+
export type PatternArtefact = "opennext-lambda" | "static-assets";
|
|
38
|
+
/**
|
|
39
|
+
* Which stack the pattern's constructs are placed into. A static site has no
|
|
40
|
+
* VPC or compute, so it co-locates in the CDN stack; OpenNext patterns carry a
|
|
41
|
+
* Lambda (and usually a database) and belong in the compute stack.
|
|
42
|
+
*/
|
|
43
|
+
export type PatternStackPlacement = "compute" | "cdn";
|
|
44
|
+
export interface PatternDescriptor {
|
|
45
|
+
/** Human-facing name, used by the CLI picker and progress output. */
|
|
46
|
+
readonly label: string;
|
|
47
|
+
/** Suffix the generator appends to the PascalCase app name for the construct id. */
|
|
48
|
+
readonly constructIdSuffix: string;
|
|
49
|
+
/** What the build produces — selects the deploy-core framework builder. */
|
|
50
|
+
readonly artefact: PatternArtefact;
|
|
51
|
+
/** Stack the pattern's constructs are placed into. */
|
|
52
|
+
readonly stackPlacement: PatternStackPlacement;
|
|
53
|
+
}
|
|
54
|
+
export declare const PATTERN_REGISTRY: {
|
|
55
|
+
readonly payload: {
|
|
56
|
+
readonly label: "Payload CMS";
|
|
57
|
+
readonly constructIdSuffix: "Payload";
|
|
58
|
+
readonly artefact: "opennext-lambda";
|
|
59
|
+
readonly stackPlacement: "compute";
|
|
60
|
+
};
|
|
61
|
+
readonly nextjs: {
|
|
62
|
+
readonly label: "Next.js";
|
|
63
|
+
readonly constructIdSuffix: "Nextjs";
|
|
64
|
+
readonly artefact: "opennext-lambda";
|
|
65
|
+
readonly stackPlacement: "compute";
|
|
66
|
+
};
|
|
67
|
+
readonly staticsite: {
|
|
68
|
+
readonly label: "Static site";
|
|
69
|
+
readonly constructIdSuffix: "StaticSite";
|
|
70
|
+
readonly artefact: "static-assets";
|
|
71
|
+
readonly stackPlacement: "cdn";
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The OpenNext-shaped patterns, derived from the registry at both the type and
|
|
76
|
+
* the value level — a new OpenNext pattern joins this accept-set by declaring
|
|
77
|
+
* its artefact, not by being remembered in a second list. (`as const satisfies`
|
|
78
|
+
* above is what keeps the literal `artefact` types the mapped type reads.)
|
|
79
|
+
*/
|
|
80
|
+
export type OpenNextPatternType = {
|
|
81
|
+
[K in PatternType]: (typeof PATTERN_REGISTRY)[K]["artefact"] extends "opennext-lambda" ? K : never;
|
|
82
|
+
}[PatternType];
|
|
83
|
+
export declare const OPENNEXT_PATTERN_TYPES: readonly OpenNextPatternType[];
|
|
84
|
+
export declare function isOpenNextPatternType(value: string | undefined | null): value is OpenNextPatternType;
|
|
85
|
+
/**
|
|
86
|
+
* Routing mode for a static site. Shared with the CDN construct and the
|
|
87
|
+
* generator's `StaticSiteRoutingSchema`, both of which derive from this tuple —
|
|
88
|
+
* a third mode must not be addable to one without the other.
|
|
89
|
+
*/
|
|
90
|
+
export declare const STATIC_SITE_ROUTING_VALUES: readonly ["multipage", "spa"];
|
|
91
|
+
export type StaticSiteRouting = (typeof STATIC_SITE_ROUTING_VALUES)[number];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=["payload","nextjs","staticsite"],a=new Set(e);function o(t){return typeof t=="string"&&a.has(t)}const n={payload:{label:"Payload CMS",constructIdSuffix:"Payload",artefact:"opennext-lambda",stackPlacement:"compute"},nextjs:{label:"Next.js",constructIdSuffix:"Nextjs",artefact:"opennext-lambda",stackPlacement:"compute"},staticsite:{label:"Static site",constructIdSuffix:"StaticSite",artefact:"static-assets",stackPlacement:"cdn"}},c=e.filter(t=>n[t].artefact==="opennext-lambda"),s=new Set(c);function T(t){return t!=null&&s.has(t)}const r=["multipage","spa"];export{c as OPENNEXT_PATTERN_TYPES,n as PATTERN_REGISTRY,a as PATTERN_TYPES,e as PATTERN_TYPE_VALUES,r as STATIC_SITE_ROUTING_VALUES,T as isOpenNextPatternType,o as isPatternType};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Address derivation for the static-site contact form.
|
|
3
|
+
*
|
|
4
|
+
* The generator validates `forms.from` against these rules and the CDK
|
|
5
|
+
* construct derives the defaults from them, so they are a coupled pair: a
|
|
6
|
+
* change to the default sender that landed on one side only would deploy an
|
|
7
|
+
* endpoint whose IAM `ses:FromAddress` condition denies its own send.
|
|
8
|
+
*/
|
|
9
|
+
/** Local part of the default envelope sender: `noreply@<domain>`. */
|
|
10
|
+
export declare const DEFAULT_FORMS_FROM_LOCAL_PART = "noreply";
|
|
11
|
+
/**
|
|
12
|
+
* The envelope sender used when `forms.from` is absent. Sits at the site's
|
|
13
|
+
* domain because SES sends only from a verified identity.
|
|
14
|
+
*/
|
|
15
|
+
export declare function defaultFormsFromAddress(domain: string): string;
|
|
16
|
+
/** The origin allowed to POST the form when `forms.corsOrigin` is absent. */
|
|
17
|
+
export declare function defaultFormsCorsOrigin(domain: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether `address` sits at exactly `domain`. Subdomains are rejected: the
|
|
20
|
+
* pattern grants `ses:SendEmail` under a `ses:FromAddress` condition pinned to
|
|
21
|
+
* the address, so an address SES would accept but the policy would not is a
|
|
22
|
+
* deploy-green/runtime-500 trap.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAddressAtDomain(address: string, domain: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const s="noreply";function f(r){return`${s}@${r}`}function i(r){return`https://${r}`}function u(r,o){const t=r.split("@");if(t.length!==2)return!1;const[e,n]=t;return e===void 0||e===""||n===void 0?!1:n.toLowerCase()===o.toLowerCase()}export{s as DEFAULT_FORMS_FROM_LOCAL_PART,i as defaultFormsCorsOrigin,f as defaultFormsFromAddress,u as isAddressAtDomain};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/util",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.30.3",
|
|
4
4
|
"description": "Common utility methods",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"types": "./dist/manifest/index.d.ts",
|
|
31
31
|
"default": "./dist/manifest/index.js"
|
|
32
32
|
},
|
|
33
|
+
"./patterns": {
|
|
34
|
+
"types": "./dist/patterns/index.d.ts",
|
|
35
|
+
"default": "./dist/patterns/index.js"
|
|
36
|
+
},
|
|
33
37
|
"./manifest/schemas": {
|
|
34
38
|
"types": "./dist/manifest/schemas.d.ts",
|
|
35
39
|
"default": "./dist/manifest/schemas.js"
|
|
@@ -101,6 +105,7 @@
|
|
|
101
105
|
"clean": "rm -rf ./dist ./sourcemaps",
|
|
102
106
|
"clean:node": "rm -rf ./node_modules",
|
|
103
107
|
"build": "npm run clean && npx tsc && node ../scripts/minify-dist.mjs dist",
|
|
108
|
+
"prepack": "node ../scripts/check-dist-freshness.mjs",
|
|
104
109
|
"watch": "npm run build && npx tsc-watch",
|
|
105
110
|
"watch:only": "npx tsc-watch",
|
|
106
111
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
|
|
@@ -129,5 +134,5 @@
|
|
|
129
134
|
"engines": {
|
|
130
135
|
"node": ">=22.0.0"
|
|
131
136
|
},
|
|
132
|
-
"gitHead": "
|
|
137
|
+
"gitHead": "d398edc0c0edf661d7ab6e5a47623529f68dfd2a"
|
|
133
138
|
}
|