@fjall/util 37.0.0 → 39.0.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.
Files changed (43) hide show
  1. package/dist/.minified +1 -1
  2. package/dist/appTypes.d.ts +27 -0
  3. package/dist/appTypes.js +1 -0
  4. package/dist/aws/index.d.ts +2 -1
  5. package/dist/aws/index.js +1 -1
  6. package/dist/aws/infraTags.d.ts +50 -4
  7. package/dist/aws/infraTags.js +1 -1
  8. package/dist/aws/resourceTypes.d.ts +54 -0
  9. package/dist/aws/resourceTypes.js +1 -0
  10. package/dist/backupTiers.d.ts +11 -4
  11. package/dist/childEnvPolicy.d.ts +72 -0
  12. package/dist/childEnvPolicy.js +1 -0
  13. package/dist/ciSetup.d.ts +25 -0
  14. package/dist/ciSetup.js +1 -0
  15. package/dist/complianceIssue.d.ts +10 -0
  16. package/dist/complianceIssue.js +1 -1
  17. package/dist/deploy/approvalTokenOutput.d.ts +0 -9
  18. package/dist/deploy/approvalTokenOutput.js +1 -1
  19. package/dist/index.d.ts +13 -5
  20. package/dist/index.js +1 -1
  21. package/dist/infra/backupVault.d.ts +79 -2
  22. package/dist/infra/backupVault.js +1 -1
  23. package/dist/insights/vocabulary.d.ts +29 -0
  24. package/dist/insights/vocabulary.js +1 -0
  25. package/dist/manifest/io.d.ts +9 -0
  26. package/dist/manifest/io.js +1 -1
  27. package/dist/mcpProtocol/index.d.ts +47 -10
  28. package/dist/mcpProtocol/index.js +1 -1
  29. package/dist/naming/applicationStackNames.d.ts +47 -0
  30. package/dist/naming/applicationStackNames.js +1 -0
  31. package/dist/naming/stackExportNames.d.ts +78 -0
  32. package/dist/naming/stackExportNames.js +1 -0
  33. package/dist/nonEmptyPartial.d.ts +19 -0
  34. package/dist/nonEmptyPartial.js +1 -0
  35. package/dist/patterns/frameworkPatterns.d.ts +1 -8
  36. package/dist/patterns/index.d.ts +2 -2
  37. package/dist/patterns/index.js +1 -1
  38. package/dist/patterns/patternTypes.d.ts +44 -2
  39. package/dist/patterns/patternTypes.js +1 -1
  40. package/dist/posturePolicy/registry.d.ts +6 -10
  41. package/dist/posturePolicy/resourceKinds.d.ts +6 -6
  42. package/dist/posturePolicy/resourceKinds.js +1 -1
  43. package/package.json +6 -2
@@ -1,7 +1,84 @@
1
+ import { z } from "zod";
1
2
  /**
2
3
  * Fixed name of the disaster-recovery backup vault. Single source of truth for
3
4
  * the @fjall/components-infrastructure DisasterRecovery construct (which
4
- * creates the vault under this name) and deploy-core's adopt-vs-create and
5
- * survival probes (which look it up by the same name).
5
+ * creates the vault under this name) and deploy-core's ownership and survival
6
+ * probes (which look it up by the same name).
6
7
  */
7
8
  export declare const BACKUP_VAULT_NAME = "backupVault";
9
+ /**
10
+ * CDK context key the deploy engine injects the backup-vault ownership verdict
11
+ * under (`-c fjallBackupVault=<json>`), read by `readBackupVaultVerdict` in
12
+ * @fjall/components-infrastructure (`utils/cdkContext.ts`). One JSON value,
13
+ * not a boolean: the construct needs the live key ARN (a create-only vault
14
+ * property) to declare a re-owned vault without replacing it, and the live
15
+ * lock to report it and warn when the configured intent differs.
16
+ */
17
+ export declare const BACKUP_VAULT_CONTEXT_KEY = "fjallBackupVault";
18
+ /**
19
+ * The live vault lock as `DescribeBackupVault` reports it. `lockDate` is the
20
+ * instant a compliance-mode lock became (or becomes) immutable; a governance
21
+ * lock carries none. Every field is optional: a lock put through the Backup
22
+ * API may carry a maximum without a minimum, or a lock date alone, so a
23
+ * locked vault can report none of them. The lock is never declared in a
24
+ * template — the construct reads it for the `VaultLockEnabled` output and the
25
+ * intent warning only.
26
+ */
27
+ export declare const BackupVaultLockSchema: z.ZodObject<{
28
+ minRetentionDays: z.ZodOptional<z.ZodNumber>;
29
+ maxRetentionDays: z.ZodOptional<z.ZodNumber>;
30
+ lockDate: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strict>;
32
+ export type BackupVaultLock = z.infer<typeof BackupVaultLockSchema>;
33
+ /**
34
+ * Ownership verdict for the fixed-name DR vault in one account/region, probed
35
+ * by the deploy engine before synth (`probeBackupVaultOwnership` in
36
+ * deploy-core):
37
+ *
38
+ * - `absent` — no vault; the construct creates it with a stack-managed key.
39
+ * - `owned` — an `AWS::Backup::BackupVault` with physical id
40
+ * `backupVault` is a current resource of THIS account stack. `keyInStack`
41
+ * says whether its KMS key is too: if so the template is the create
42
+ * template (the vault keeps converging to intent); if not the key is a
43
+ * retained survivor and the vault is re-owned — declared against the live
44
+ * key, with no lock declared (Fjall never issues a lock call against a
45
+ * vault it did not create; the live lock stays as it is).
46
+ * - `unowned` — the vault exists but is not in the stack: the import
47
+ * candidate. The construct declares identity only and the engine deploys
48
+ * with `--import-existing-resources`, then re-probes and converges.
49
+ *
50
+ * `keyArn` is always a customer-managed key: the probe refuses a vault on an
51
+ * AWS-owned or AWS-managed key before any verdict is issued, because the
52
+ * re-owned template aliases the key and the DR plans' cross-account copies
53
+ * need a customer-managed one. A vault owned by ANOTHER stack is not
54
+ * distinguished here: the import change set fails with CloudFormation's own
55
+ * "already belongs to another stack".
56
+ */
57
+ export declare const BackupVaultVerdictSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
58
+ state: z.ZodLiteral<"absent">;
59
+ }, z.core.$strict>, z.ZodObject<{
60
+ state: z.ZodLiteral<"owned">;
61
+ keyArn: z.ZodString;
62
+ keyInStack: z.ZodBoolean;
63
+ lock: z.ZodNullable<z.ZodObject<{
64
+ minRetentionDays: z.ZodOptional<z.ZodNumber>;
65
+ maxRetentionDays: z.ZodOptional<z.ZodNumber>;
66
+ lockDate: z.ZodOptional<z.ZodString>;
67
+ }, z.core.$strict>>;
68
+ }, z.core.$strict>, z.ZodObject<{
69
+ state: z.ZodLiteral<"unowned">;
70
+ keyArn: z.ZodString;
71
+ lock: z.ZodNullable<z.ZodObject<{
72
+ minRetentionDays: z.ZodOptional<z.ZodNumber>;
73
+ maxRetentionDays: z.ZodOptional<z.ZodNumber>;
74
+ lockDate: z.ZodOptional<z.ZodString>;
75
+ }, z.core.$strict>>;
76
+ }, z.core.$strict>], "state">;
77
+ export type BackupVaultVerdict = z.infer<typeof BackupVaultVerdictSchema>;
78
+ /**
79
+ * Whether a verdict selects the re-owned template: the vault is (or is about
80
+ * to be) a stack resource whose key lives outside the stack. Shared by the
81
+ * construct (mode selection) and the engine (the import pass is the
82
+ * `unowned` half of this).
83
+ */
84
+ export declare function isReownedBackupVault(verdict: BackupVaultVerdict): boolean;
@@ -1 +1 @@
1
- const t="backupVault";export{t as BACKUP_VAULT_NAME};
1
+ var i=Object.defineProperty;var n=(e,a)=>i(e,"name",{value:a,configurable:!0});import{z as t}from"zod";const s="backupVault",r="fjallBackupVault",o=t.object({minRetentionDays:t.number().int().positive().optional(),maxRetentionDays:t.number().int().positive().optional(),lockDate:t.string().datetime({offset:!0}).optional()}).strict(),u=t.discriminatedUnion("state",[t.object({state:t.literal("absent")}).strict(),t.object({state:t.literal("owned"),keyArn:t.string().min(1),keyInStack:t.boolean(),lock:o.nullable()}).strict(),t.object({state:t.literal("unowned"),keyArn:t.string().min(1),lock:o.nullable()}).strict()]);function p(e){return e.state==="unowned"||e.state==="owned"&&!e.keyInStack}n(p,"isReownedBackupVault");export{r as BACKUP_VAULT_CONTEXT_KEY,s as BACKUP_VAULT_NAME,o as BackupVaultLockSchema,u as BackupVaultVerdictSchema,p as isReownedBackupVault};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Insight list vocabulary — the filter and ordering tokens `GET /api/insights`
3
+ * accepts, minted once so every consumer spells them the same way.
4
+ *
5
+ * Owner today: the webapp (`webapp/app/lib/insights/insights.ts` —
6
+ * `TAB_VALUES`, `INSIGHT_CATEGORIES`, `INSIGHT_SEVERITIES`, `SORT_VALUES`).
7
+ * The MCP's `list_insights` tool re-typed all four by hand and advertised
8
+ * them on its wire surface, so a token added or renamed in the webapp left
9
+ * the tool silently offering a filter the route rejects. The MCP now derives
10
+ * from here, and the route's 400-on-unknown gate is what keeps the two honest
11
+ * while the webapp still carries its own copies.
12
+ *
13
+ * Client-safe (no node built-ins): exported from the root barrel and as the
14
+ * `@fjall/util/insights/vocabulary` subpath so browser code can take it
15
+ * without the root barrel.
16
+ */
17
+ export declare const INSIGHT_TAB_VALUES: readonly ["open", "snoozed", "resolved", "all"];
18
+ /** @public — tuple companion type; ships with its tuple (knip skips @public) */
19
+ export type InsightTab = (typeof INSIGHT_TAB_VALUES)[number];
20
+ export declare const INSIGHT_CATEGORIES: readonly ["performance", "cost", "reliability", "security", "deployment"];
21
+ /** @public — tuple companion type; ships with its tuple (knip skips @public) */
22
+ export type InsightCategory = (typeof INSIGHT_CATEGORIES)[number];
23
+ /** Most severe first. */
24
+ export declare const INSIGHT_SEVERITIES: readonly ["critical", "warning", "recommendation", "info"];
25
+ /** @public — tuple companion type; ships with its tuple (knip skips @public) */
26
+ export type InsightSeverity = (typeof INSIGHT_SEVERITIES)[number];
27
+ export declare const INSIGHT_SORT_VALUES: readonly ["matters", "severity", "newest", "oldest", "savings"];
28
+ /** @public — tuple companion type; ships with its tuple (knip skips @public) */
29
+ export type InsightSort = (typeof INSIGHT_SORT_VALUES)[number];
@@ -0,0 +1 @@
1
+ const e=["open","snoozed","resolved","all"],o=["performance","cost","reliability","security","deployment"],t=["critical","warning","recommendation","info"],n=["matters","severity","newest","oldest","savings"];export{o as INSIGHT_CATEGORIES,t as INSIGHT_SEVERITIES,n as INSIGHT_SORT_VALUES,e as INSIGHT_TAB_VALUES};
@@ -75,6 +75,15 @@ export interface ManifestDockerService {
75
75
  * `dockerPlatformForArchitecture` in `@fjall/util/docker`.
76
76
  */
77
77
  readonly architecture?: LambdaArchitecture;
78
+ /**
79
+ * The ECS cluster the service is placed in — the construct id of the
80
+ * cluster, which is also its physical cluster name and the token the
81
+ * `DeployableService` export is built from. Present for every service the
82
+ * compute pattern records (constructs >= 0.87.0); absent for a manifest
83
+ * written by an older assembly, in which case the deploy engine cannot
84
+ * bind the entry to a live service by name and must say so.
85
+ */
86
+ readonly clusterName?: string;
78
87
  }
79
88
  /**
80
89
  * The engine-compatibility envelope, hand-walked from the manifest rather than
@@ -1 +1 @@
1
- var M=Object.defineProperty;var s=(n,e)=>M(n,"name",{value:e,configurable:!0});import{readFile as k,writeFile as E,unlink as N,rename as S,mkdir as w}from"fs/promises";import{readFileSync as p}from"fs";import{dirname as F,join as m}from"path";import{logger as c}from"../logger.js";import{maskSensitiveOutput as f}from"../securityHelpers.js";import{fileExists as x}from"../fsHelpers.js";import{getErrorMessage as d}from"../errorUtils.js";import{recordToConstructMap as A}from"../constructMap.js";import{FjallManifestSchema as v,FJALL_MANIFEST_FILENAME as l,MANIFEST_SCHEMA_VERSION as j,normaliseDockerBuild as y,LAMBDA_ARCHITECTURE_VALUES as C}from"./schemas.js";function h(n){return m(n,l)}s(h,"getManifestFilePath");async function O(n){const e=h(n);if(!await x(e))return{status:"absent"};try{const a=await k(e,"utf-8"),t=JSON.parse(a),r=v.safeParse(t);if(!r.success){const i=f(r.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join("; "));return c.debug("FjallManifest","Manifest validation failed",{path:e,errors:i}),{status:"unreadable",detail:i}}return{status:"ok",manifest:r.data}}catch(a){const t=f(d(a));return c.debug("FjallManifest","Failed to read manifest file",{path:e,error:t}),{status:"unreadable",detail:t}}}s(O,"readManifestFileOutcome");async function I(n){const e=await O(n);return e.status==="ok"?e.manifest:null}s(I,"readManifestFile");async function U(n,e){const a=h(n),t=`${a}.${Date.now()}.tmp`;await w(F(a),{recursive:!0});try{await E(t,JSON.stringify(e,null,2),"utf-8"),await S(t,a)}catch(r){try{await N(t)}catch(i){c.debug("FjallManifest","Temp file cleanup failed (non-fatal)",{path:t,error:d(i)})}throw r}}s(U,"writeManifestFile");function q(n){return{version:j,generatedAt:new Date().toISOString(),appName:n,services:[],lambdas:[],stacks:{}}}s(q,"createEmptyManifest");async function z(n){const e=await I(n);return e?.resourceMap?A(e.resourceMap):new Map}s(z,"readConstructMap");function u(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}s(u,"isRecord");function g(n,e){const a=m(n,l);let t;try{t=p(a,"utf-8")}catch{c.debug("FjallManifest",`Manifest file not readable \u2014 no ${e} extracted`,{path:a});return}let r;try{r=JSON.parse(t)}catch{c.debug("FjallManifest",`Manifest is not valid JSON \u2014 no ${e} extracted`,{path:a});return}return u(r)?r:void 0}s(g,"readManifestRecord");function T(n){const e=n.engineCompat;if(!u(e))return;const a=e.minimumEngineVersion;if(typeof a!="string"||a.length===0)return;const t=typeof e.synthesisedBy=="string"&&e.synthesisedBy.length>0?e.synthesisedBy:"",r=typeof e.maximumEngineMajor=="number"&&Number.isInteger(e.maximumEngineMajor)&&e.maximumEngineMajor>=0?e.maximumEngineMajor:void 0,i=e.minimumAwsCdkCli,o=i===void 0?void 0:typeof i=="string"?i:String(i);return{synthesisedBy:t,minimumEngineVersion:a,...r!==void 0&&{maximumEngineMajor:r},...o!==void 0&&{minimumAwsCdkCli:o}}}s(T,"extractEngineCompat");function Q(n){const e=g(n,"engine compatibility");if(e===void 0)return{manifestVersion:void 0,engineCompatPresent:!1,engineCompat:void 0};const a=typeof e.version=="number"?e.version:void 0,t=u(e.engineCompat),r=T(e);return{manifestVersion:a,engineCompatPresent:t,engineCompat:r}}s(Q,"readEngineCompatEnvelope");function W(n){const e=$(n);return e.status==="ok"?e.appName:void 0}s(W,"readManifestAppName");function $(n){const e=m(n,l);let a;try{a=p(e,"utf-8")}catch(r){const i=r!==null&&typeof r=="object"&&"code"in r?r.code:void 0;return i==="ENOENT"||i==="ENOTDIR"?{status:"absent"}:(c.debug("FjallManifest","Manifest file not readable \u2014 no app name",{path:e,error:f(d(r))}),{status:"unreadable"})}let t;try{t=JSON.parse(a)}catch(r){return c.debug("FjallManifest","Manifest is not valid JSON \u2014 no app name",{path:e,error:f(d(r))}),{status:"unreadable"}}return u(t)?{status:"ok",appName:typeof t.appName=="string"&&t.appName!==""?t.appName:void 0}:{status:"unreadable"}}s($,"readManifestAppNameOutcome");function D(n){const e=g(n,"Docker services");if(e===void 0||!Array.isArray(e.services))return[];const a=[];for(const t of e.services){if(!u(t)||typeof t.name!="string")continue;const r=y(t.docker);r!==void 0&&a.push({name:t.name,docker:r,isLambda:!1})}return a}s(D,"parseDockerServicesFromManifest");function X(n){const e=new Map;for(const{name:a,imageKey:t,docker:r,architecture:i}of b(n)){const o=e.get(t);if(o===void 0){e.set(t,{name:t,docker:r,isLambda:!0,...i!==void 0&&{architecture:i}});continue}if(JSON.stringify(o.docker)!==JSON.stringify(r)||o.architecture!==i)throw new Error(`Lambda functions sharing image key "${t}" declare different \`docker\` configs or \`architecture\` \u2014 every Lambda function sharing an \`image\` must build from the identical Dockerfile/context/target/buildArgs and target the same architecture (offending function: "${a}").`)}return Array.from(e.values())}s(X,"parseLambdaDockerServicesFromManifest");function b(n){const e=g(n,"Lambda Docker entries");if(e===void 0||!Array.isArray(e.lambdas))return[];const a=[];for(const t of e.lambdas){if(!u(t)||typeof t.name!="string")continue;const r=y(t.docker);if(r===void 0)continue;const i=typeof t.imageKey=="string"&&t.imageKey.length>0?t.imageKey:t.name;let o;typeof t.architecture=="string"&&(C.includes(t.architecture)?o=t.architecture:c.debug("FjallManifest","Ignoring unrecognised Lambda architecture \u2014 build falls back to the default platform",{lambda:t.name,architecture:f(t.architecture)})),a.push({name:t.name,imageKey:i,docker:r,...o!==void 0&&{architecture:o}})}return a}s(b,"parseLambdaDockerEntriesFromManifest");function Y(n){const e=D(n),a=b(n);return{ecsServices:e,lambdaEntries:a,declaresBuild:e.length>0||a.length>0}}s(Y,"parseDockerDeclarationsFromManifest");function Z(n){const e=m(n,l);let a;try{a=p(e,"utf-8")}catch(i){const o=i.code;return o==="ENOENT"?{outcome:"ok",tags:[]}:{outcome:"manifest-unreadable",reason:`manifest at ${e} exists but could not be read (${o??"unknown error"})`}}let t;try{t=JSON.parse(a)}catch{return{outcome:"manifest-unreadable",reason:`manifest at ${e} is not valid JSON`}}if(!u(t))return{outcome:"manifest-unreadable",reason:`manifest at ${e} is not a JSON object`};if(t.services===void 0)return{outcome:"ok",tags:[]};if(!Array.isArray(t.services))return{outcome:"manifest-unreadable",reason:`manifest at ${e} has a non-array \`services\` field`};const r=new Set;for(const i of t.services)if(u(i)&&i.schemaGateImageTag!==void 0){if(typeof i.schemaGateImageTag!="string"||i.schemaGateImageTag==="")return{outcome:"manifest-unreadable",reason:`manifest at ${e} declares an invalid schemaGateImageTag for service '${typeof i.name=="string"?i.name:"<unnamed>"}'`};r.add(i.schemaGateImageTag)}return{outcome:"ok",tags:[...r].sort()}}s(Z,"parseSchemaGateImageTagsFromManifest");export{q as createEmptyManifest,h as getManifestFilePath,Y as parseDockerDeclarationsFromManifest,D as parseDockerServicesFromManifest,b as parseLambdaDockerEntriesFromManifest,X as parseLambdaDockerServicesFromManifest,Z as parseSchemaGateImageTagsFromManifest,z as readConstructMap,Q as readEngineCompatEnvelope,W as readManifestAppName,$ as readManifestAppNameOutcome,I as readManifestFile,O as readManifestFileOutcome,U as writeManifestFile};
1
+ var M=Object.defineProperty;var s=(n,e)=>M(n,"name",{value:e,configurable:!0});import{readFile as k,writeFile as E,unlink as N,rename as S,mkdir as w}from"fs/promises";import{readFileSync as p}from"fs";import{dirname as F,join as m}from"path";import{logger as c}from"../logger.js";import{maskSensitiveOutput as f}from"../securityHelpers.js";import{fileExists as x}from"../fsHelpers.js";import{getErrorMessage as d}from"../errorUtils.js";import{recordToConstructMap as A}from"../constructMap.js";import{FjallManifestSchema as v,FJALL_MANIFEST_FILENAME as l,MANIFEST_SCHEMA_VERSION as j,normaliseDockerBuild as y,LAMBDA_ARCHITECTURE_VALUES as C}from"./schemas.js";function h(n){return m(n,l)}s(h,"getManifestFilePath");async function O(n){const e=h(n);if(!await x(e))return{status:"absent"};try{const r=await k(e,"utf-8"),t=JSON.parse(r),a=v.safeParse(t);if(!a.success){const i=f(a.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join("; "));return c.debug("FjallManifest","Manifest validation failed",{path:e,errors:i}),{status:"unreadable",detail:i}}return{status:"ok",manifest:a.data}}catch(r){const t=f(d(r));return c.debug("FjallManifest","Failed to read manifest file",{path:e,error:t}),{status:"unreadable",detail:t}}}s(O,"readManifestFileOutcome");async function I(n){const e=await O(n);return e.status==="ok"?e.manifest:null}s(I,"readManifestFile");async function U(n,e){const r=h(n),t=`${r}.${Date.now()}.tmp`;await w(F(r),{recursive:!0});try{await E(t,JSON.stringify(e,null,2),"utf-8"),await S(t,r)}catch(a){try{await N(t)}catch(i){c.debug("FjallManifest","Temp file cleanup failed (non-fatal)",{path:t,error:d(i)})}throw a}}s(U,"writeManifestFile");function q(n){return{version:j,generatedAt:new Date().toISOString(),appName:n,services:[],lambdas:[],stacks:{}}}s(q,"createEmptyManifest");async function z(n){const e=await I(n);return e?.resourceMap?A(e.resourceMap):new Map}s(z,"readConstructMap");function u(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}s(u,"isRecord");function g(n,e){const r=m(n,l);let t;try{t=p(r,"utf-8")}catch{c.debug("FjallManifest",`Manifest file not readable \u2014 no ${e} extracted`,{path:r});return}let a;try{a=JSON.parse(t)}catch{c.debug("FjallManifest",`Manifest is not valid JSON \u2014 no ${e} extracted`,{path:r});return}return u(a)?a:void 0}s(g,"readManifestRecord");function T(n){const e=n.engineCompat;if(!u(e))return;const r=e.minimumEngineVersion;if(typeof r!="string"||r.length===0)return;const t=typeof e.synthesisedBy=="string"&&e.synthesisedBy.length>0?e.synthesisedBy:"",a=typeof e.maximumEngineMajor=="number"&&Number.isInteger(e.maximumEngineMajor)&&e.maximumEngineMajor>=0?e.maximumEngineMajor:void 0,i=e.minimumAwsCdkCli,o=i===void 0?void 0:typeof i=="string"?i:String(i);return{synthesisedBy:t,minimumEngineVersion:r,...a!==void 0&&{maximumEngineMajor:a},...o!==void 0&&{minimumAwsCdkCli:o}}}s(T,"extractEngineCompat");function Q(n){const e=g(n,"engine compatibility");if(e===void 0)return{manifestVersion:void 0,engineCompatPresent:!1,engineCompat:void 0};const r=typeof e.version=="number"?e.version:void 0,t=u(e.engineCompat),a=T(e);return{manifestVersion:r,engineCompatPresent:t,engineCompat:a}}s(Q,"readEngineCompatEnvelope");function W(n){const e=$(n);return e.status==="ok"?e.appName:void 0}s(W,"readManifestAppName");function $(n){const e=m(n,l);let r;try{r=p(e,"utf-8")}catch(a){const i=a!==null&&typeof a=="object"&&"code"in a?a.code:void 0;return i==="ENOENT"||i==="ENOTDIR"?{status:"absent"}:(c.debug("FjallManifest","Manifest file not readable \u2014 no app name",{path:e,error:f(d(a))}),{status:"unreadable"})}let t;try{t=JSON.parse(r)}catch(a){return c.debug("FjallManifest","Manifest is not valid JSON \u2014 no app name",{path:e,error:f(d(a))}),{status:"unreadable"}}return u(t)?{status:"ok",appName:typeof t.appName=="string"&&t.appName!==""?t.appName:void 0}:{status:"unreadable"}}s($,"readManifestAppNameOutcome");function D(n){const e=g(n,"Docker services");if(e===void 0||!Array.isArray(e.services))return[];const r=[];for(const t of e.services){if(!u(t)||typeof t.name!="string")continue;const a=y(t.docker);a!==void 0&&r.push({name:t.name,docker:a,isLambda:!1,...typeof t.clusterName=="string"&&{clusterName:t.clusterName}})}return r}s(D,"parseDockerServicesFromManifest");function X(n){const e=new Map;for(const{name:r,imageKey:t,docker:a,architecture:i}of b(n)){const o=e.get(t);if(o===void 0){e.set(t,{name:t,docker:a,isLambda:!0,...i!==void 0&&{architecture:i}});continue}if(JSON.stringify(o.docker)!==JSON.stringify(a)||o.architecture!==i)throw new Error(`Lambda functions sharing image key "${t}" declare different \`docker\` configs or \`architecture\` \u2014 every Lambda function sharing an \`image\` must build from the identical Dockerfile/context/target/buildArgs and target the same architecture (offending function: "${r}").`)}return Array.from(e.values())}s(X,"parseLambdaDockerServicesFromManifest");function b(n){const e=g(n,"Lambda Docker entries");if(e===void 0||!Array.isArray(e.lambdas))return[];const r=[];for(const t of e.lambdas){if(!u(t)||typeof t.name!="string")continue;const a=y(t.docker);if(a===void 0)continue;const i=typeof t.imageKey=="string"&&t.imageKey.length>0?t.imageKey:t.name;let o;typeof t.architecture=="string"&&(C.includes(t.architecture)?o=t.architecture:c.debug("FjallManifest","Ignoring unrecognised Lambda architecture \u2014 build falls back to the default platform",{lambda:t.name,architecture:f(t.architecture)})),r.push({name:t.name,imageKey:i,docker:a,...o!==void 0&&{architecture:o}})}return r}s(b,"parseLambdaDockerEntriesFromManifest");function Y(n){const e=D(n),r=b(n);return{ecsServices:e,lambdaEntries:r,declaresBuild:e.length>0||r.length>0}}s(Y,"parseDockerDeclarationsFromManifest");function Z(n){const e=m(n,l);let r;try{r=p(e,"utf-8")}catch(i){const o=i.code;return o==="ENOENT"?{outcome:"ok",tags:[]}:{outcome:"manifest-unreadable",reason:`manifest at ${e} exists but could not be read (${o??"unknown error"})`}}let t;try{t=JSON.parse(r)}catch{return{outcome:"manifest-unreadable",reason:`manifest at ${e} is not valid JSON`}}if(!u(t))return{outcome:"manifest-unreadable",reason:`manifest at ${e} is not a JSON object`};if(t.services===void 0)return{outcome:"ok",tags:[]};if(!Array.isArray(t.services))return{outcome:"manifest-unreadable",reason:`manifest at ${e} has a non-array \`services\` field`};const a=new Set;for(const i of t.services)if(u(i)&&i.schemaGateImageTag!==void 0){if(typeof i.schemaGateImageTag!="string"||i.schemaGateImageTag==="")return{outcome:"manifest-unreadable",reason:`manifest at ${e} declares an invalid schemaGateImageTag for service '${typeof i.name=="string"?i.name:"<unnamed>"}'`};a.add(i.schemaGateImageTag)}return{outcome:"ok",tags:[...a].sort()}}s(Z,"parseSchemaGateImageTagsFromManifest");export{q as createEmptyManifest,h as getManifestFilePath,Y as parseDockerDeclarationsFromManifest,D as parseDockerServicesFromManifest,b as parseLambdaDockerEntriesFromManifest,X as parseLambdaDockerServicesFromManifest,Z as parseSchemaGateImageTagsFromManifest,z as readConstructMap,Q as readEngineCompatEnvelope,W as readManifestAppName,$ as readManifestAppNameOutcome,I as readManifestFile,O as readManifestFileOutcome,U as writeManifestFile};
@@ -176,23 +176,32 @@ export declare const TargetAccountSchema: z.ZodObject<{
176
176
  source: z.ZodString;
177
177
  }, z.core.$strict>;
178
178
  export type TargetAccount = z.infer<typeof TargetAccountSchema>;
179
+ /**
180
+ * Every input `plan_app_scaffold` accepts, as the plan records it and
181
+ * `apply_app_scaffold` replays it (`buildApplyCommand`). Plan keys ⊆ these
182
+ * keys is pinned by the MCP's resolvedInputsParity test: a key the plan
183
+ * accepts but this schema drops was silently absent from the apply argv
184
+ * (`type`, `database`, `containerPort` were, until Train 4).
185
+ */
179
186
  export declare const ResolvedInputsSchema: z.ZodObject<{
180
187
  name: z.ZodString;
181
- pattern: z.ZodOptional<z.ZodString>;
188
+ pattern: z.ZodOptional<z.ZodEnum<{
189
+ payload: "payload";
190
+ staticsite: "staticsite";
191
+ }>>;
182
192
  patternTier: z.ZodOptional<z.ZodEnum<{
193
+ lightweight: "lightweight";
183
194
  standard: "standard";
184
195
  resilient: "resilient";
185
- enterprise: "enterprise";
186
- tinkerer: "tinkerer";
187
- lightweight: "lightweight";
196
+ custom: "custom";
188
197
  }>>;
189
198
  patternDomain: z.ZodOptional<z.ZodString>;
190
199
  network: z.ZodOptional<z.ZodEnum<{
200
+ lightweight: "lightweight";
191
201
  standard: "standard";
192
202
  resilient: "resilient";
193
203
  enterprise: "enterprise";
194
204
  none: "none";
195
- lightweight: "lightweight";
196
205
  }>>;
197
206
  template: z.ZodOptional<z.ZodString>;
198
207
  docker: z.ZodOptional<z.ZodObject<{
@@ -200,6 +209,19 @@ export declare const ResolvedInputsSchema: z.ZodObject<{
200
209
  context: z.ZodOptional<z.ZodString>;
201
210
  target: z.ZodOptional<z.ZodString>;
202
211
  }, z.core.$strict>>;
212
+ type: z.ZodOptional<z.ZodEnum<{
213
+ tinkerer: "tinkerer";
214
+ lightweight: "lightweight";
215
+ standard: "standard";
216
+ resilient: "resilient";
217
+ enterprise: "enterprise";
218
+ custom: "custom";
219
+ }>>;
220
+ database: z.ZodOptional<z.ZodBoolean>;
221
+ containerPort: z.ZodOptional<z.ZodNumber>;
222
+ source: z.ZodOptional<z.ZodString>;
223
+ buildCommand: z.ZodOptional<z.ZodString>;
224
+ outputDir: z.ZodOptional<z.ZodString>;
203
225
  git: z.ZodOptional<z.ZodBoolean>;
204
226
  github: z.ZodOptional<z.ZodBoolean>;
205
227
  repoVisibility: z.ZodOptional<z.ZodEnum<{
@@ -214,21 +236,23 @@ export declare const ScaffoldPlanSchema: z.ZodObject<{
214
236
  normalisedPath: z.ZodString;
215
237
  resolvedInputs: z.ZodObject<{
216
238
  name: z.ZodString;
217
- pattern: z.ZodOptional<z.ZodString>;
239
+ pattern: z.ZodOptional<z.ZodEnum<{
240
+ payload: "payload";
241
+ staticsite: "staticsite";
242
+ }>>;
218
243
  patternTier: z.ZodOptional<z.ZodEnum<{
244
+ lightweight: "lightweight";
219
245
  standard: "standard";
220
246
  resilient: "resilient";
221
- enterprise: "enterprise";
222
- tinkerer: "tinkerer";
223
- lightweight: "lightweight";
247
+ custom: "custom";
224
248
  }>>;
225
249
  patternDomain: z.ZodOptional<z.ZodString>;
226
250
  network: z.ZodOptional<z.ZodEnum<{
251
+ lightweight: "lightweight";
227
252
  standard: "standard";
228
253
  resilient: "resilient";
229
254
  enterprise: "enterprise";
230
255
  none: "none";
231
- lightweight: "lightweight";
232
256
  }>>;
233
257
  template: z.ZodOptional<z.ZodString>;
234
258
  docker: z.ZodOptional<z.ZodObject<{
@@ -236,6 +260,19 @@ export declare const ScaffoldPlanSchema: z.ZodObject<{
236
260
  context: z.ZodOptional<z.ZodString>;
237
261
  target: z.ZodOptional<z.ZodString>;
238
262
  }, z.core.$strict>>;
263
+ type: z.ZodOptional<z.ZodEnum<{
264
+ tinkerer: "tinkerer";
265
+ lightweight: "lightweight";
266
+ standard: "standard";
267
+ resilient: "resilient";
268
+ enterprise: "enterprise";
269
+ custom: "custom";
270
+ }>>;
271
+ database: z.ZodOptional<z.ZodBoolean>;
272
+ containerPort: z.ZodOptional<z.ZodNumber>;
273
+ source: z.ZodOptional<z.ZodString>;
274
+ buildCommand: z.ZodOptional<z.ZodString>;
275
+ outputDir: z.ZodOptional<z.ZodString>;
239
276
  git: z.ZodOptional<z.ZodBoolean>;
240
277
  github: z.ZodOptional<z.ZodBoolean>;
241
278
  repoVisibility: z.ZodOptional<z.ZodEnum<{
@@ -1 +1 @@
1
- var p=Object.defineProperty;var a=(n,r)=>p(n,"name",{value:r,configurable:!0});import{z as t}from"zod";const g=2,v={AUTHENTICATION_REQUIRED:"AUTHENTICATION_REQUIRED",VALIDATION_ERROR:"VALIDATION_ERROR",INTERNAL_ERROR:"INTERNAL_ERROR",INVALID_SUBCOMMAND:"INVALID_SUBCOMMAND",INVALID_FRAME:"INVALID_FRAME"},s=1e3,e=t.literal(g),u=t.enum(["completed","error","skipped"]),d=t.object({v:e,kind:t.literal("step.start"),stepId:t.string().min(1),name:t.string().min(1),index:t.number().int().min(0),total:t.number().int().min(1)}).strict(),b=t.object({v:e,kind:t.literal("step.complete"),stepId:t.string().min(1),name:t.string().min(1),status:u,index:t.number().int().min(0),total:t.number().int().min(1),errorMessage:t.string().optional()}).strict(),h=t.object({v:e,kind:t.literal("warning"),message:t.string().min(1)}).strict(),R=t.object({code:t.string().min(1),message:t.string().min(1),details:t.record(t.string(),t.unknown()).optional()}).strict(),A=t.object({v:e,kind:t.literal("error"),error:R}).strict(),S=t.object({v:e,kind:t.literal("result"),data:t.unknown()}).strict(),I=t.object({v:e,kind:t.literal("auth.browser.required"),url:t.string().url(),externalId:t.string().min(1),region:t.string().min(1),environment:t.string().min(1),reason:t.string().min(1)}).strict(),x=t.object({v:e,kind:t.literal("auth.browser.complete"),awsAccountId:t.string().min(1),accountName:t.string().min(1),roleArn:t.string().min(1)}).strict(),k=t.discriminatedUnion("kind",[d,b,h,A,S,I,x]),c=t.object({path:t.string().min(1),sizeBytes:t.number().int().nonnegative(),collisionMode:t.enum(["create","overwrite"])}).strict(),m=t.object({type:t.enum(["register-application","create-activity","auto-link-repository","writeback-config-path"]),description:t.string().min(1)}).strict(),E=t.enum(["tinkerer","lightweight","standard","resilient","enterprise"]),N=t.enum(["lightweight","standard","resilient","enterprise","none"]),l=t.object({name:t.string().min(1),environment:t.string().min(1).nullable(),source:t.string().min(1)}).strict(),y=t.object({name:t.string().min(1),pattern:t.string().min(1).optional(),patternTier:E.optional(),patternDomain:t.string().min(1).optional(),network:N.optional(),template:t.string().min(1).optional(),docker:t.object({path:t.string().min(1).optional(),context:t.string().min(1).optional(),target:t.string().min(1).optional()}).strict().optional(),git:t.boolean().optional(),github:t.boolean().optional(),repoVisibility:t.enum(["private","public"]).optional(),intent:t.string().max(s).optional()}).strict(),w=t.object({planId:t.string().regex(/^[0-9a-f]{32}$/),normalisedPath:t.string().min(1),resolvedInputs:y,targetAccount:l.nullable(),targetAccountCandidates:t.array(l),filesToWrite:t.array(c),npmPackages:t.array(t.string().min(1)),registryActions:t.array(m),patternUnsupportedForDryRun:t.boolean().optional(),intent:t.string().max(s).optional(),framework:t.string().min(1).optional(),summary:t.string().min(1)}).strict(),f=t.object({filesToWrite:t.array(c),npmPackages:t.array(t.string().min(1)),registryActions:t.array(m),patternUnsupportedForDryRun:t.boolean().optional()}).strict(),O=t.object({path:t.string().min(1),name:t.string().min(1),mode:t.enum(["application","pattern"]),dependenciesInstalled:t.boolean().optional(),warning:t.string().optional(),dryRunArtefacts:f.optional()}).strict(),D=t.object({step:t.string().min(1),current:t.number().int().nonnegative(),total:t.number().int().positive().optional(),message:t.string().min(1)}).strict();function F(n){let r;try{r=JSON.parse(n)}catch(o){return{ok:!1,reason:"json",raw:n,error:o instanceof Error?o.message:String(o)}}const i=k.safeParse(r);return i.success?{ok:!0,frame:i.data}:{ok:!1,reason:"schema",raw:n,error:i.error.message}}a(F,"parseFrame");export{x as AuthBrowserCompleteFrameSchema,I as AuthBrowserRequiredFrameSchema,f as DryRunArtefactsSchema,A as ErrorFrameSchema,c as FileToWriteSchema,s as INTENT_MAX_LENGTH,v as MCP_ERROR_CODES,g as MCP_PROTOCOL_VERSION,k as McpProtocolFrameSchema,m as RegistryActionSchema,y as ResolvedInputsSchema,S as ResultFrameSchema,w as ScaffoldPlanSchema,D as ScaffoldProgressFrameSchema,O as ScaffoldResultDataSchema,b as StepCompleteFrameSchema,d as StepStartFrameSchema,l as TargetAccountSchema,h as WarningFrameSchema,F as parseFrame};
1
+ var l=Object.defineProperty;var a=(e,r)=>l(e,"name",{value:r,configurable:!0});import{z as t}from"zod";import{APP_TYPES as g}from"../appTypes.js";import{DEPLOYABLE_PATTERN_TYPES as u,PATTERN_TIER_VALUES as d}from"../patterns/patternTypes.js";const b=2,L={AUTHENTICATION_REQUIRED:"AUTHENTICATION_REQUIRED",VALIDATION_ERROR:"VALIDATION_ERROR",INTERNAL_ERROR:"INTERNAL_ERROR",INVALID_SUBCOMMAND:"INVALID_SUBCOMMAND",INVALID_FRAME:"INVALID_FRAME"},s=1e3,n=t.literal(b),A=t.enum(["completed","error","skipped"]),R=t.object({v:n,kind:t.literal("step.start"),stepId:t.string().min(1),name:t.string().min(1),index:t.number().int().min(0),total:t.number().int().min(1)}).strict(),S=t.object({v:n,kind:t.literal("step.complete"),stepId:t.string().min(1),name:t.string().min(1),status:A,index:t.number().int().min(0),total:t.number().int().min(1),errorMessage:t.string().optional()}).strict(),E=t.object({v:n,kind:t.literal("warning"),message:t.string().min(1)}).strict(),h=t.object({code:t.string().min(1),message:t.string().min(1),details:t.record(t.string(),t.unknown()).optional()}).strict(),I=t.object({v:n,kind:t.literal("error"),error:h}).strict(),x=t.object({v:n,kind:t.literal("result"),data:t.unknown()}).strict(),T=t.object({v:n,kind:t.literal("auth.browser.required"),url:t.string().url(),externalId:t.string().min(1),region:t.string().min(1),environment:t.string().min(1),reason:t.string().min(1)}).strict(),y=t.object({v:n,kind:t.literal("auth.browser.complete"),awsAccountId:t.string().min(1),accountName:t.string().min(1),roleArn:t.string().min(1)}).strict(),N=t.discriminatedUnion("kind",[R,S,E,I,x,T,y]),m=t.object({path:t.string().min(1),sizeBytes:t.number().int().nonnegative(),collisionMode:t.enum(["create","overwrite"])}).strict(),c=t.object({type:t.enum(["register-application","create-activity","auto-link-repository","writeback-config-path"]),description:t.string().min(1)}).strict(),f=t.enum(d),k=t.enum(u),P=t.enum(g),_=t.enum(["lightweight","standard","resilient","enterprise","none"]),p=t.object({name:t.string().min(1),environment:t.string().min(1).nullable(),source:t.string().min(1)}).strict(),j=t.object({name:t.string().min(1),pattern:k.optional(),patternTier:f.optional(),patternDomain:t.string().min(1).optional(),network:_.optional(),template:t.string().min(1).optional(),docker:t.object({path:t.string().min(1).optional(),context:t.string().min(1).optional(),target:t.string().min(1).optional()}).strict().optional(),type:P.optional(),database:t.boolean().optional(),containerPort:t.number().int().min(1).max(65535).optional(),source:t.string().min(1).optional(),buildCommand:t.string().min(1).optional(),outputDir:t.string().min(1).optional(),git:t.boolean().optional(),github:t.boolean().optional(),repoVisibility:t.enum(["private","public"]).optional(),intent:t.string().max(s).optional()}).strict(),C=t.object({planId:t.string().regex(/^[0-9a-f]{32}$/),normalisedPath:t.string().min(1),resolvedInputs:j,targetAccount:p.nullable(),targetAccountCandidates:t.array(p),filesToWrite:t.array(m),npmPackages:t.array(t.string().min(1)),registryActions:t.array(c),patternUnsupportedForDryRun:t.boolean().optional(),intent:t.string().max(s).optional(),framework:t.string().min(1).optional(),summary:t.string().min(1)}).strict(),D=t.object({filesToWrite:t.array(m),npmPackages:t.array(t.string().min(1)),registryActions:t.array(c),patternUnsupportedForDryRun:t.boolean().optional()}).strict(),M=t.object({path:t.string().min(1),name:t.string().min(1),mode:t.enum(["application","pattern"]),dependenciesInstalled:t.boolean().optional(),warning:t.string().optional(),dryRunArtefacts:D.optional()}).strict(),U=t.object({step:t.string().min(1),current:t.number().int().nonnegative(),total:t.number().int().positive().optional(),message:t.string().min(1)}).strict();function V(e){let r;try{r=JSON.parse(e)}catch(i){return{ok:!1,reason:"json",raw:e,error:i instanceof Error?i.message:String(i)}}const o=N.safeParse(r);return o.success?{ok:!0,frame:o.data}:{ok:!1,reason:"schema",raw:e,error:o.error.message}}a(V,"parseFrame");export{y as AuthBrowserCompleteFrameSchema,T as AuthBrowserRequiredFrameSchema,D as DryRunArtefactsSchema,I as ErrorFrameSchema,m as FileToWriteSchema,s as INTENT_MAX_LENGTH,L as MCP_ERROR_CODES,b as MCP_PROTOCOL_VERSION,N as McpProtocolFrameSchema,c as RegistryActionSchema,j as ResolvedInputsSchema,x as ResultFrameSchema,C as ScaffoldPlanSchema,U as ScaffoldProgressFrameSchema,M as ScaffoldResultDataSchema,S as StepCompleteFrameSchema,R as StepStartFrameSchema,p as TargetAccountSchema,E as WarningFrameSchema,V as parseFrame};
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Application infrastructure stacks.
3
+ *
4
+ * UsEast1Certificates is the app-owned us-east-1 certificate stack (domain
5
+ * gold-plating D3, BYO CloudFront path — components/infrastructure
6
+ * `App.getUsEast1CertificateStack`). It exists only when a CloudFront-facing
7
+ * pattern carries a custom domain outside us-east-1, so ordering derivations
8
+ * include it ONLY when the post-synth manifest proves it
9
+ * (`AppResourceFlags.hasUsEast1Certificates`) — a fixed-set entry would make
10
+ * every deploy attempt a stack that does not exist. It must deploy BEFORE Cdn
11
+ * (the distribution consumes its certificate ARN through cross-region
12
+ * references) and destroy AFTER Cdn.
13
+ *
14
+ * UsEast1CdnLogging is the app-owned us-east-1 CloudFront standard-logging
15
+ * (v2) stack (components/infrastructure `App.getUsEast1CdnLoggingStack`).
16
+ * CloudWatch vends CloudFront delivery sources, destinations and deliveries
17
+ * in us-east-1 only, so a logging CDN whose app deploys elsewhere creates
18
+ * them there. It CONSUMES the Cdn stack's distribution and bucket ARNs
19
+ * through cross-region references, so it is the mirror of the certificate
20
+ * stack: it deploys AFTER Cdn and is destroyed BEFORE it, and it is likewise
21
+ * conditional — a CDN in a us-east-1 app never creates it.
22
+ */
23
+ export declare const APPLICATION_STACKS: {
24
+ readonly NETWORK: "Network";
25
+ readonly DATABASE: "Database";
26
+ readonly STORAGE: "Storage";
27
+ readonly COMPUTE: "Compute";
28
+ readonly US_EAST_1_CERTIFICATES: "UsEast1Certificates";
29
+ readonly CDN: "Cdn";
30
+ readonly US_EAST_1_CDN_LOGGING: "UsEast1CdnLogging";
31
+ readonly MESSAGING: "Messaging";
32
+ };
33
+ export type ApplicationStack = (typeof APPLICATION_STACKS)[keyof typeof APPLICATION_STACKS];
34
+ export declare function isApplicationStack(value: string): value is ApplicationStack;
35
+ /**
36
+ * CloudFormation stack name of one of an application's stacks.
37
+ *
38
+ * This is the derivation `@fjall/components-infrastructure` uses to NAME the
39
+ * stack at synth (`App` keys every stack on `toPascalCase(appName)` and the
40
+ * stack id is the CloudFormation stack name), so it is the only way runtime
41
+ * code may address one: a re-implementation agrees with the construct for a
42
+ * lowercase app name and diverges for `billing-api` or `WebApp`, which is how
43
+ * a hand-rolled `${appName}Network` shipped through typecheck and every
44
+ * kebab-named fixture before failing for the first hyphenated app. Both sides
45
+ * call this function; neither composes the string.
46
+ */
47
+ export declare function getApplicationStackName(appName: string, stack: ApplicationStack): string;
@@ -0,0 +1 @@
1
+ var A=Object.defineProperty;var a=(t,e)=>A(t,"name",{value:e,configurable:!0});import{toPascalCase as S}from"./caseConversion.js";const o={NETWORK:"Network",DATABASE:"Database",STORAGE:"Storage",COMPUTE:"Compute",US_EAST_1_CERTIFICATES:"UsEast1Certificates",CDN:"Cdn",US_EAST_1_CDN_LOGGING:"UsEast1CdnLogging",MESSAGING:"Messaging"},n=new Set(Object.values(o));function i(t){return n.has(t)}a(i,"isApplicationStack");function r(t,e){return`${S(t)}${e}`}a(r,"getApplicationStackName");export{o as APPLICATION_STACKS,r as getApplicationStackName,i as isApplicationStack};
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Export names an application's own stacks emit for the runtime to read back.
3
+ *
4
+ * These names are a contract between `@fjall/components-infrastructure`,
5
+ * which emits them as CloudFormation exports at synth, and the runtime
6
+ * consumers that address the live resources they name: deploy-core's
7
+ * `EcsServiceResolver` (the ECS services `fjall services`, `fjall rollout`,
8
+ * a code-only deploy, a rollback and a restart act on) and the fjall.io
9
+ * webapp's VPC CIDR capture. Both sides MUST call these functions rather than
10
+ * compose or pattern-match the string themselves. The producer once built
11
+ * the service name from `toPascalCase(appName)` while the consumer searched
12
+ * exports for the app name as written, so no hyphenated app could resolve
13
+ * its services; and a substring match on the consumer side returned a
14
+ * sibling app's services whenever one name contained the other.
15
+ *
16
+ * Every builder takes RAW names — the CloudFormation stack name, the
17
+ * cluster's construct id, the service's manifest name, the VPC's construct
18
+ * id — never a pre-sanitised token, so the two sides cannot normalise
19
+ * differently. `stackName` is the stack the output lives in
20
+ * (`getApplicationStackName`), which is what makes the name unique per
21
+ * account and region: export names are a flat namespace, and a name built
22
+ * only from the literal ids inside a pattern is shared by every app that
23
+ * uses that pattern.
24
+ *
25
+ * The `is*ExportName` predicates identify an output that CLAIMS the contract
26
+ * (they read the marker suffix, case-sensitively). A consumer that selects
27
+ * by predicate must then verify the claim by rebuilding the name from the
28
+ * output's own value and refuse the output when the two disagree — the
29
+ * predicate is a filter, never proof.
30
+ */
31
+ /**
32
+ * Qualify an export name with the stack that emits it. The suffix stays
33
+ * last: every builder below composes through this function, and the
34
+ * predicates read the tail.
35
+ */
36
+ export declare function stackScopedExportName(stackName: string, suffix: string): string;
37
+ export declare const DEPLOYABLE_SERVICE_EXPORT_MARKER = "DeployableService";
38
+ export declare const DEPLOYABLE_CLUSTER_EXPORT_MARKER = "DeployableCluster";
39
+ export declare const CLUSTER_ARN_EXPORT_MARKER = "ClusterArn";
40
+ export declare const VPC_CIDR_EXPORT_MARKER = "-vpc-cidr";
41
+ /**
42
+ * Export carrying the ARN of one deployable ECS service.
43
+ *
44
+ * `clusterName` is the ECS cluster's construct id, which is also its physical
45
+ * cluster name; `serviceName` is the service's manifest name, which is also
46
+ * its physical ECS service name. Both therefore reappear verbatim in the
47
+ * exported ARN (`…:service/<clusterName>/<serviceName>`), so a consumer can
48
+ * rebuild this name from the ARN alone and check the export against it.
49
+ */
50
+ export declare function deployableServiceExportName(stackName: string, clusterName: string, serviceName: string): string;
51
+ /**
52
+ * The unqualified form constructs 2.0.0 through 7.x emitted, before 8.0.0
53
+ * qualified every export with its stack. Consumers accept it so an app last
54
+ * deployed on those versions still resolves; nothing emits it any more.
55
+ */
56
+ export declare function legacyDeployableServiceExportName(clusterName: string, serviceName: string): string;
57
+ export declare function isDeployableServiceExportName(exportName: string): boolean;
58
+ /** Export carrying the ARN of a cluster that holds deployable services. */
59
+ export declare function deployableClusterExportName(stackName: string, clusterName: string): string;
60
+ /** Export carrying an ECS cluster's ARN under its plain name. */
61
+ export declare function clusterArnExportName(stackName: string, clusterName: string): string;
62
+ /**
63
+ * Construct id of the VPC `App.getApp(name, { network })` creates in the
64
+ * app's Network stack — the id the VPC CIDR export is keyed on.
65
+ */
66
+ export declare function defaultVpcConstructId(appName: string): string;
67
+ /**
68
+ * Export carrying a VPC's CIDR block. The construct id is load-bearing: a
69
+ * stack holding two VPCs (`app.addNetwork`) would otherwise emit one name
70
+ * twice, and CloudFormation rejects the template.
71
+ */
72
+ export declare function vpcCidrExportName(stackName: string, vpcConstructId: string): string;
73
+ /**
74
+ * The form constructs before 8.0.0 emitted, keyed on the stack alone.
75
+ * Accepted by consumers for apps last deployed on those versions.
76
+ */
77
+ export declare function legacyVpcCidrExportName(stackName: string): string;
78
+ export declare function isVpcCidrExportName(exportName: string): boolean;
@@ -0,0 +1 @@
1
+ var u=Object.defineProperty;var t=(e,r)=>u(e,"name",{value:r,configurable:!0});import{toPascalCase as E}from"./caseConversion.js";function o(e,r){return`${e}${r}`}t(o,"stackScopedExportName");const n="DeployableService",i="DeployableCluster",x="ClusterArn",p="-vpc-cidr";function R(e,r,c){return o(e,`${r}${c}${n}`)}t(R,"deployableServiceExportName");function s(e,r){return`${e}${r}${n}`}t(s,"legacyDeployableServiceExportName");function $(e){return e.endsWith(n)}t($,"isDeployableServiceExportName");function C(e,r){return o(e,`${r}${i}`)}t(C,"deployableClusterExportName");function f(e,r){return o(e,`${r}${x}`)}t(f,"clusterArnExportName");function _(e){return`${E(e)}Vpc`}t(_,"defaultVpcConstructId");function d(e,r){return o(e,`${r}${p}`)}t(d,"vpcCidrExportName");function m(e){return o(e,p)}t(m,"legacyVpcCidrExportName");function N(e){return e.endsWith(p)}t(N,"isVpcCidrExportName");export{x as CLUSTER_ARN_EXPORT_MARKER,i as DEPLOYABLE_CLUSTER_EXPORT_MARKER,n as DEPLOYABLE_SERVICE_EXPORT_MARKER,p as VPC_CIDR_EXPORT_MARKER,f as clusterArnExportName,_ as defaultVpcConstructId,C as deployableClusterExportName,R as deployableServiceExportName,$ as isDeployableServiceExportName,N as isVpcCidrExportName,s as legacyDeployableServiceExportName,m as legacyVpcCidrExportName,o as stackScopedExportName,d as vpcCidrExportName};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A read-only partial record that cannot be empty: the union, over every
3
+ * key of `T`, of "this key required, the rest optional". `{}` is
4
+ * unrepresentable whether or not `T` declares the key optional (the
5
+ * required arm strips `?`, so the carried key is present with its value),
6
+ * a key `T` does not carry fails typecheck on a literal, and each present
7
+ * key keeps its own value type — `T[K]` may differ per key, which is what a
8
+ * plain `Partial<T>` with a non-emptiness check cannot express. The shape a
9
+ * cure map is declared on: the posture registries' `PolicyCure`, and a
10
+ * consumer mirroring the cure map declares its own on this helper rather
11
+ * than re-spelling the union.
12
+ */
13
+ export type NonEmptyPartial<T> = {
14
+ [Key in keyof T]-?: {
15
+ readonly [K in Key]-?: T[K];
16
+ } & {
17
+ readonly [K in Exclude<keyof T, Key>]?: T[K];
18
+ };
19
+ }[keyof T];
@@ -0,0 +1 @@
1
+ const n={first:1},o={second:"two"},t={first:1,second:"two"},s={},e={first:1,third:3},c={second:"two"},i={},d={second:void 0};
@@ -30,7 +30,7 @@
30
30
  * than left behind as a lie. The reverse holds too: a pattern that loses its
31
31
  * construct cannot stay in `recommended`.
32
32
  */
33
- import { PATTERN_REGISTRY, type PatternType, type PatternCreateInput } from "./patternTypes.js";
33
+ import { PATTERN_REGISTRY, type PatternType, type PatternCreateInput, type DeployablePatternType } from "./patternTypes.js";
34
34
  /**
35
35
  * Every framework the detector can name.
36
36
  *
@@ -50,13 +50,6 @@ export declare const FRAMEWORK_VALUES: readonly ["nextjs", "payload", "nextjs+pa
50
50
  export type Framework = (typeof FRAMEWORK_VALUES)[number];
51
51
  export declare const FRAMEWORKS: ReadonlySet<string>;
52
52
  export declare function isFramework(value: unknown): value is Framework;
53
- /**
54
- * The patterns that can deploy end-to-end, derived from the registry's own
55
- * flag at the type level. `DEPLOYABLE_PATTERN_TYPES` is the value-level twin.
56
- */
57
- export type DeployablePatternType = {
58
- [K in PatternType]: (typeof PATTERN_REGISTRY)[K]["deployable"] extends true ? K : never;
59
- }[PatternType];
60
53
  /**
61
54
  * The patterns the generator knows but cannot yet synthesise. Uninhabited once
62
55
  * every pattern is deployable — at which point the `blocked` verdict below
@@ -7,6 +7,6 @@
7
7
  * the webapp bundles) can take the vocabulary without depending on the root
8
8
  * barrel's contents staying pure.
9
9
  */
10
- export { PATTERN_TYPE_VALUES, type PatternType, PATTERN_TYPES, isPatternType, type PatternArtefact, type PatternStackPlacement, type PatternDescriptor, PATTERN_REGISTRY, patternConstructId, DEPLOYABLE_PATTERN_TYPES, type OpenNextPatternType, OPENNEXT_PATTERN_TYPES, isOpenNextPatternType, CLOUDFRONT_PRICE_CLASS_VALUES, type CloudFrontPriceClass, STATIC_SITE_CONFIG_KEYS, type StaticSiteConfigKey, STATIC_SITE_DEPLOY_INJECTED_KEYS, type StaticSiteDeployInjectedKey, STATIC_SITE_CDN_CONFIG_KEYS, type StaticSiteCdnConfigKey, STATIC_SITE_ROUTING_VALUES, STATIC_SITE_ROUTING_INFO, type StaticSiteRouting, type StaticSiteRoutingDescriptor, PATTERN_CREATE_INPUT_VALUES, type PatternCreateInput, PATTERN_CREATE_INPUT_FLAGS } from "./patternTypes.js";
11
- export { FRAMEWORK_VALUES, type Framework, FRAMEWORKS, isFramework, type DeployablePatternType, type UndeployablePatternType, type PatternRecommendation, FRAMEWORK_PATTERN_RECOMMENDATIONS, recommendPatternForFramework, CREATABLE_FRAMEWORKS, requiredInputsForFramework } from "./frameworkPatterns.js";
10
+ export { PATTERN_TYPE_VALUES, type PatternType, PATTERN_TYPES, isPatternType, type PatternArtefact, type PatternStackPlacement, type PatternDescriptor, PATTERN_REGISTRY, patternConstructId, type DeployablePatternType, DEPLOYABLE_PATTERN_TYPES, PATTERN_TIER_VALUES, type PatternTier, type OpenNextPatternType, OPENNEXT_PATTERN_TYPES, isOpenNextPatternType, OPENNEXT_BUCKET_KEYS, type OpenNextBucketKey, isOpenNextBucketKey, patternBucketConstructId, CLOUDFRONT_PRICE_CLASS_VALUES, type CloudFrontPriceClass, STATIC_SITE_CONFIG_KEYS, type StaticSiteConfigKey, STATIC_SITE_DEPLOY_INJECTED_KEYS, type StaticSiteDeployInjectedKey, STATIC_SITE_CDN_CONFIG_KEYS, type StaticSiteCdnConfigKey, STATIC_SITE_ROUTING_VALUES, STATIC_SITE_ROUTING_INFO, type StaticSiteRouting, type StaticSiteRoutingDescriptor, PATTERN_CREATE_INPUT_VALUES, type PatternCreateInput, PATTERN_CREATE_INPUT_FLAGS } from "./patternTypes.js";
11
+ export { FRAMEWORK_VALUES, type Framework, FRAMEWORKS, isFramework, type UndeployablePatternType, type PatternRecommendation, FRAMEWORK_PATTERN_RECOMMENDATIONS, recommendPatternForFramework, CREATABLE_FRAMEWORKS, requiredInputsForFramework } from "./frameworkPatterns.js";
12
12
  export { DEFAULT_FORMS_FROM_LOCAL_PART, defaultFormsFromAddress, defaultFormsCorsOrigin, isAddressAtDomain } from "./staticSiteForms.js";
@@ -1 +1 @@
1
- import{PATTERN_TYPE_VALUES as _,PATTERN_TYPES as A,isPatternType as r,PATTERN_REGISTRY as R,patternConstructId as S,DEPLOYABLE_PATTERN_TYPES as I,OPENNEXT_PATTERN_TYPES as N,isOpenNextPatternType as e,CLOUDFRONT_PRICE_CLASS_VALUES as P,STATIC_SITE_CONFIG_KEYS as O,STATIC_SITE_DEPLOY_INJECTED_KEYS as F,STATIC_SITE_CDN_CONFIG_KEYS as C,STATIC_SITE_ROUTING_VALUES as o,STATIC_SITE_ROUTING_INFO as t,PATTERN_CREATE_INPUT_VALUES as L,PATTERN_CREATE_INPUT_FLAGS as s}from"./patternTypes.js";import{FRAMEWORK_VALUES as U,FRAMEWORKS as a,isFramework as n,FRAMEWORK_PATTERN_RECOMMENDATIONS as Y,recommendPatternForFramework as d,CREATABLE_FRAMEWORKS as i,requiredInputsForFramework as p}from"./frameworkPatterns.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as M,defaultFormsFromAddress as K,defaultFormsCorsOrigin as G,isAddressAtDomain as f}from"./staticSiteForms.js";export{P as CLOUDFRONT_PRICE_CLASS_VALUES,i as CREATABLE_FRAMEWORKS,M as DEFAULT_FORMS_FROM_LOCAL_PART,I as DEPLOYABLE_PATTERN_TYPES,a as FRAMEWORKS,Y as FRAMEWORK_PATTERN_RECOMMENDATIONS,U as FRAMEWORK_VALUES,N as OPENNEXT_PATTERN_TYPES,s as PATTERN_CREATE_INPUT_FLAGS,L as PATTERN_CREATE_INPUT_VALUES,R as PATTERN_REGISTRY,A as PATTERN_TYPES,_ as PATTERN_TYPE_VALUES,C as STATIC_SITE_CDN_CONFIG_KEYS,O as STATIC_SITE_CONFIG_KEYS,F as STATIC_SITE_DEPLOY_INJECTED_KEYS,t as STATIC_SITE_ROUTING_INFO,o as STATIC_SITE_ROUTING_VALUES,G as defaultFormsCorsOrigin,K as defaultFormsFromAddress,f as isAddressAtDomain,n as isFramework,e as isOpenNextPatternType,r as isPatternType,S as patternConstructId,d as recommendPatternForFramework,p as requiredInputsForFramework};
1
+ import{PATTERN_TYPE_VALUES as _,PATTERN_TYPES as A,isPatternType as r,PATTERN_REGISTRY as R,patternConstructId as S,DEPLOYABLE_PATTERN_TYPES as e,PATTERN_TIER_VALUES as N,OPENNEXT_PATTERN_TYPES as t,isOpenNextPatternType as I,OPENNEXT_BUCKET_KEYS as P,isOpenNextBucketKey as O,patternBucketConstructId as C,CLOUDFRONT_PRICE_CLASS_VALUES as F,STATIC_SITE_CONFIG_KEYS as o,STATIC_SITE_DEPLOY_INJECTED_KEYS as L,STATIC_SITE_CDN_CONFIG_KEYS as s,STATIC_SITE_ROUTING_VALUES as n,STATIC_SITE_ROUTING_INFO as U,PATTERN_CREATE_INPUT_VALUES as m,PATTERN_CREATE_INPUT_FLAGS as a}from"./patternTypes.js";import{FRAMEWORK_VALUES as d,FRAMEWORKS as p,isFramework as K,FRAMEWORK_PATTERN_RECOMMENDATIONS as i,recommendPatternForFramework as u,CREATABLE_FRAMEWORKS as D,requiredInputsForFramework as M}from"./frameworkPatterns.js";import{DEFAULT_FORMS_FROM_LOCAL_PART as V,defaultFormsFromAddress as c,defaultFormsCorsOrigin as f,isAddressAtDomain as k}from"./staticSiteForms.js";export{F as CLOUDFRONT_PRICE_CLASS_VALUES,D as CREATABLE_FRAMEWORKS,V as DEFAULT_FORMS_FROM_LOCAL_PART,e as DEPLOYABLE_PATTERN_TYPES,p as FRAMEWORKS,i as FRAMEWORK_PATTERN_RECOMMENDATIONS,d as FRAMEWORK_VALUES,P as OPENNEXT_BUCKET_KEYS,t as OPENNEXT_PATTERN_TYPES,a as PATTERN_CREATE_INPUT_FLAGS,m as PATTERN_CREATE_INPUT_VALUES,R as PATTERN_REGISTRY,N as PATTERN_TIER_VALUES,A as PATTERN_TYPES,_ as PATTERN_TYPE_VALUES,s as STATIC_SITE_CDN_CONFIG_KEYS,o as STATIC_SITE_CONFIG_KEYS,L as STATIC_SITE_DEPLOY_INJECTED_KEYS,U as STATIC_SITE_ROUTING_INFO,n as STATIC_SITE_ROUTING_VALUES,f as defaultFormsCorsOrigin,c as defaultFormsFromAddress,k as isAddressAtDomain,K as isFramework,O as isOpenNextBucketKey,I as isOpenNextPatternType,r as isPatternType,C as patternBucketConstructId,S as patternConstructId,u as recommendPatternForFramework,M as requiredInputsForFramework};
@@ -128,12 +128,54 @@ export declare const PATTERN_REGISTRY: {
128
128
  * detection silently re-admits the synth-fatal id collision.
129
129
  */
130
130
  export declare function patternConstructId(name: string, type: PatternType): string;
131
+ /**
132
+ * The buckets an OpenNext pattern's construct builds, keyed as the pattern
133
+ * statement's `storage` config spells them (`storage.media.versioned`): the
134
+ * build output, the ISR cache and the uploads. The constructs' props type
135
+ * and the generator's config schema are both pinned to this tuple, so a
136
+ * bucket the construct starts building appears in the config vocabulary
137
+ * before either can compile without it.
138
+ */
139
+ export declare const OPENNEXT_BUCKET_KEYS: readonly ["assets", "cache", "media"];
140
+ export type OpenNextBucketKey = (typeof OPENNEXT_BUCKET_KEYS)[number];
141
+ export declare function isOpenNextBucketKey(value: unknown): value is OpenNextBucketKey;
142
+ /**
143
+ * The construct id of one bucket an OpenNext pattern builds — the FIRST
144
+ * ARGUMENT of the `StorageFactory.build` call the construct makes for it:
145
+ * the pattern statement's `name` prop (the workload name the engine lists
146
+ * as `resourceName`), a hyphen, the bucket key. The bucket's CloudFormation
147
+ * logical id is derived from this id, so a consumer matching a logical id
148
+ * back to the statement that owns the bucket derives the same string here
149
+ * rather than re-composing it; a drifted re-composition agrees for every
150
+ * kebab-case name and diverges the first time a name carries a case
151
+ * boundary the id derivation folds.
152
+ */
153
+ export declare function patternBucketConstructId(name: string, bucket: OpenNextBucketKey): string;
154
+ /**
155
+ * The patterns that can deploy end-to-end, derived from the registry's own
156
+ * flag at the type level. `DEPLOYABLE_PATTERN_TYPES` is the value-level twin.
157
+ */
158
+ export type DeployablePatternType = {
159
+ [K in PatternType]: (typeof PATTERN_REGISTRY)[K]["deployable"] extends true ? K : never;
160
+ }[PatternType];
131
161
  /**
132
162
  * Patterns the toolchain may offer for creation, derived from the registry —
133
163
  * pickers and validation reject the rest at create time instead of letting a
134
- * scaffold fail at first deploy.
164
+ * scaffold fail at first deploy. Typed to the deployable subset so an enum
165
+ * built from it (`z.enum(DEPLOYABLE_PATTERN_TYPES)`) infers exactly the
166
+ * members it accepts at runtime.
167
+ */
168
+ export declare const DEPLOYABLE_PATTERN_TYPES: readonly DeployablePatternType[];
169
+ /**
170
+ * Tier presets a pattern may be created with — the ONE spelling the generator
171
+ * (`PatternTierSchema`), the CLI's SKILL vocabulary, the MCP protocol's
172
+ * `ResolvedInputsSchema` and the MCP `plan_app_scaffold` input derive from.
173
+ * Not the infrastructure tiers (`TIER_NAMES`: tinkerer … enterprise), which
174
+ * govern `fjall add … --tier` — the two vocabularies were conflated on the MCP
175
+ * surface until Train 4 of the 2026-09-07 hardening prompt.
135
176
  */
136
- export declare const DEPLOYABLE_PATTERN_TYPES: readonly PatternType[];
177
+ export declare const PATTERN_TIER_VALUES: readonly ["lightweight", "standard", "resilient", "custom"];
178
+ export type PatternTier = (typeof PATTERN_TIER_VALUES)[number];
137
179
  /**
138
180
  * The OpenNext-shaped patterns, derived from the registry at both the type and
139
181
  * the value level — a new OpenNext pattern joins this accept-set by declaring
@@ -1 +1 @@
1
- var n=Object.defineProperty;var e=(t,a)=>n(t,"name",{value:a,configurable:!0});import{toPascalCase as r}from"../naming/caseConversion.js";const s=["payload","nextjs","staticsite"],c=new Set(s);function T(t){return typeof t=="string"&&c.has(t)}e(T,"isPatternType");const S=["source","buildCommand","outputDir"],d={source:"--source",buildCommand:"--build-command",outputDir:"--output-dir"},o={payload:{label:"Payload CMS",constructIdSuffix:"Payload",artefact:"opennext-lambda",stackPlacement:"compute",deployable:!0,requiredCreateInputs:[],synthesises:["AWS::S3::Bucket","AWS::KMS::Key"]},nextjs:{label:"Next.js",constructIdSuffix:"Nextjs",artefact:"opennext-lambda",stackPlacement:"compute",deployable:!1,requiredCreateInputs:[],synthesises:["AWS::S3::Bucket","AWS::KMS::Key"]},staticsite:{label:"Static site",constructIdSuffix:"StaticSite",artefact:"static-assets",stackPlacement:"cdn",deployable:!0,requiredCreateInputs:["source","buildCommand","outputDir"],synthesises:["AWS::S3::Bucket"]}};function E(t,a){return`${r(t)}${o[a].constructIdSuffix}`}e(E,"patternConstructId");const _=s.filter(t=>o[t].deployable),i=s.filter(t=>o[t].artefact==="opennext-lambda"),u=new Set(i);function m(t){return t!=null&&u.has(t)}e(m,"isOpenNextPatternType");const x=["multipage","directory","spa"],P={multipage:{label:"Multi-page (.html files)",summary:"/about serves /about.html"},directory:{label:"Directory index",summary:"/about serves /about/index.html"},spa:{label:"Single-page app",summary:"unknown paths fall back to /index.html"}},I=["PriceClass_100","PriceClass_200","PriceClass_All"],A=["type","name","source","build","routing","security","domain","managedDomain","zoneName","hostedZoneId","forms","cdn","accessGate"],C=["managedDomain"],N=["behaviours","priceClass","invalidateOnDeploy"];export{I as CLOUDFRONT_PRICE_CLASS_VALUES,_ as DEPLOYABLE_PATTERN_TYPES,i as OPENNEXT_PATTERN_TYPES,d as PATTERN_CREATE_INPUT_FLAGS,S as PATTERN_CREATE_INPUT_VALUES,o as PATTERN_REGISTRY,c as PATTERN_TYPES,s as PATTERN_TYPE_VALUES,N as STATIC_SITE_CDN_CONFIG_KEYS,A as STATIC_SITE_CONFIG_KEYS,C as STATIC_SITE_DEPLOY_INJECTED_KEYS,P as STATIC_SITE_ROUTING_INFO,x as STATIC_SITE_ROUTING_VALUES,m as isOpenNextPatternType,T as isPatternType,E as patternConstructId};
1
+ var n=Object.defineProperty;var e=(t,s)=>n(t,"name",{value:s,configurable:!0});import{toPascalCase as r}from"../naming/caseConversion.js";const o=["payload","nextjs","staticsite"],c=new Set(o);function S(t){return typeof t=="string"&&c.has(t)}e(S,"isPatternType");const d=["source","buildCommand","outputDir"],_={source:"--source",buildCommand:"--build-command",outputDir:"--output-dir"},a={payload:{label:"Payload CMS",constructIdSuffix:"Payload",artefact:"opennext-lambda",stackPlacement:"compute",deployable:!0,requiredCreateInputs:[],synthesises:["AWS::S3::Bucket","AWS::KMS::Key"]},nextjs:{label:"Next.js",constructIdSuffix:"Nextjs",artefact:"opennext-lambda",stackPlacement:"compute",deployable:!1,requiredCreateInputs:[],synthesises:["AWS::S3::Bucket","AWS::KMS::Key"]},staticsite:{label:"Static site",constructIdSuffix:"StaticSite",artefact:"static-assets",stackPlacement:"cdn",deployable:!0,requiredCreateInputs:["source","buildCommand","outputDir"],synthesises:["AWS::S3::Bucket"]}};function m(t,s){return`${r(t)}${a[s].constructIdSuffix}`}e(m,"patternConstructId");const i=["assets","cache","media"],u=new Set(i);function x(t){return typeof t=="string"&&u.has(t)}e(x,"isOpenNextBucketKey");function P(t,s){return`${t}-${s}`}e(P,"patternBucketConstructId");const N=o.filter(t=>a[t].deployable),I=["lightweight","standard","resilient","custom"],p=o.filter(t=>a[t].artefact==="opennext-lambda"),l=new Set(p);function A(t){return t!=null&&l.has(t)}e(A,"isOpenNextPatternType");const C=["multipage","directory","spa"],f={multipage:{label:"Multi-page (.html files)",summary:"/about serves /about.html"},directory:{label:"Directory index",summary:"/about serves /about/index.html"},spa:{label:"Single-page app",summary:"unknown paths fall back to /index.html"}},y=["PriceClass_100","PriceClass_200","PriceClass_All"],b=["type","name","source","build","routing","security","domain","managedDomain","zoneName","hostedZoneId","forms","cdn","accessGate"],R=["managedDomain"],h=["behaviours","priceClass","invalidateOnDeploy"];export{y as CLOUDFRONT_PRICE_CLASS_VALUES,N as DEPLOYABLE_PATTERN_TYPES,i as OPENNEXT_BUCKET_KEYS,p as OPENNEXT_PATTERN_TYPES,_ as PATTERN_CREATE_INPUT_FLAGS,d as PATTERN_CREATE_INPUT_VALUES,a as PATTERN_REGISTRY,I as PATTERN_TIER_VALUES,c as PATTERN_TYPES,o as PATTERN_TYPE_VALUES,h as STATIC_SITE_CDN_CONFIG_KEYS,b as STATIC_SITE_CONFIG_KEYS,R as STATIC_SITE_DEPLOY_INJECTED_KEYS,f as STATIC_SITE_ROUTING_INFO,C as STATIC_SITE_ROUTING_VALUES,x as isOpenNextBucketKey,A as isOpenNextPatternType,S as isPatternType,P as patternBucketConstructId,m as patternConstructId};