@fjall/generator 2.20.1 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 139 files minified at 2026-06-23T21:59:10.238Z
1
+ 139 files minified at 2026-06-27T21:57:31.620Z
@@ -34,7 +34,7 @@ $FJALL-CERT ${e}
34
34
  ; www IN CNAME ${e}.
35
35
  ; api IN A ALIAS fjall:ecs:my-api
36
36
  ; @ IN MX 10 mail.${e}.
37
- `}function f(e){return JSON.stringify({name:`domain-${e}`,version:c,scripts:{},devDependencies:{"@types/node":"20.4.9","ts-node":"^10.9.1",typescript:"~5.1.6"},dependencies:{"@fjall/components-infrastructure":`^${c}`,"aws-cdk-lib":"^2.239.0",constructs:"^10.0.0"}},null,2)+`
37
+ `}function f(e){return JSON.stringify({name:`domain-${e}`,version:c,scripts:{},devDependencies:{"@types/node":"20.4.9","ts-node":"^10.9.1",typescript:"~5.1.6"},dependencies:{"@fjall/components-infrastructure":`^${c}`,"aws-cdk-lib":"^2.260.0",constructs:"^10.6.0"}},null,2)+`
38
38
  `}const g=`node_modules/
39
39
  cdk.out/
40
40
  *.d.ts
@@ -1,4 +1,4 @@
1
- export { planApplicationResources, type GenerationOptions, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
1
+ export { planApplicationResources, toUserServiceConfigs, type GenerationOptions, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
2
2
  export { type OpenNextResourceOptions, planOpenNextResources, } from "./openNextPlanning.js";
3
3
  export { type ConnectionType, applyComputeConnections, applyServiceConnections, } from "./resourceConnections.js";
4
4
  export { type AddableResourceType, type AddResourceOptions, type DatabaseAddOptions, type ProxyAddOptions, type S3AddOptions, type NetworkAddOptions, type EcsComputeAddOptions, type LambdaComputeAddOptions, type CdnAddOptions, type TunnelAddOptions, addResourceToPlan, validateResourceAddition, listAvailableResources, } from "./resourceAddition.js";
@@ -1 +1 @@
1
- import{planApplicationResources as r,generateInfrastructureFromPlan as n}from"./resourcePlanning.js";import{planOpenNextResources as a}from"./openNextPlanning.js";import{applyComputeConnections as s,applyServiceConnections as c}from"./resourceConnections.js";import{addResourceToPlan as i,validateResourceAddition as u,listAvailableResources as m}from"./resourceAddition.js";import{generateResourceChange as x}from"./generateResourceChange.js";export{i as addResourceToPlan,s as applyComputeConnections,c as applyServiceConnections,n as generateInfrastructureFromPlan,x as generateResourceChange,m as listAvailableResources,r as planApplicationResources,a as planOpenNextResources,u as validateResourceAddition};
1
+ import{planApplicationResources as r,toUserServiceConfigs as n,generateInfrastructureFromPlan as t}from"./resourcePlanning.js";import{planOpenNextResources as p}from"./openNextPlanning.js";import{applyComputeConnections as c,applyServiceConnections as i}from"./resourceConnections.js";import{addResourceToPlan as u,validateResourceAddition as f,listAvailableResources as m}from"./resourceAddition.js";import{generateResourceChange as R}from"./generateResourceChange.js";export{u as addResourceToPlan,c as applyComputeConnections,i as applyServiceConnections,t as generateInfrastructureFromPlan,R as generateResourceChange,m as listAvailableResources,r as planApplicationResources,p as planOpenNextResources,n as toUserServiceConfigs,f as validateResourceAddition};
@@ -1,6 +1,15 @@
1
1
  import { type UserServiceConfig, type AppType } from "../presets/tierPresets.js";
2
2
  import { type Result } from "../types/Result.js";
3
3
  import type { ApplicationResourcePlan } from "../schemas/resourceSchemas.js";
4
+ import type { ApplicationServiceConfig } from "../schemas/computeSchemas.js";
5
+ /**
6
+ * Adapt the wire-level service config (`ApplicationServiceConfig`, validated by
7
+ * `ApplicationServiceConfigSchema`) into the planner's `UserServiceConfig`. The
8
+ * two shapes are structurally identical; this is the single shared seam every
9
+ * consumer (CLI `applicationGenerator`, webapp scaffold) routes through, so the
10
+ * wire contract is mapped one way and cannot drift per-consumer.
11
+ */
12
+ export declare function toUserServiceConfigs(services?: ApplicationServiceConfig[]): UserServiceConfig[] | undefined;
4
13
  export declare function planApplicationResources(appName: string, appType: AppType, includeDatabase?: boolean, services?: UserServiceConfig[], snapshotOptions?: {
5
14
  snapshotIdentifier?: string;
6
15
  snapshotUsername?: string;
@@ -1 +1 @@
1
- import{TIER_PRESETS as m,getDefaultDatabaseTypeForTier as C,applyTierDefaultsToServices as g}from"../presets/tierPresets.js";import{COMPUTE_TYPE as b,DEFAULT_CONTAINER_PORT as l}from"../schemas/constants.js";import{success as u,failure as P}from"../types/Result.js";import{injectCustomCodeBlocks as k}from"../ast/mutation/astCodeInjection.js";import{toVariableName as d,toValidDatabaseName as D,generateImports as h,generateAppInit as T,generateTags as E,generateNetworkCode as f,generateDatabaseCode as R,generateClickHouseCode as I,generateS3Code as S,generateDynamoDBCode as p,generateSQSCode as w,generateComputeCode as A,generateCDNCode as B,collectCdnReferencedResources as U,usesPatternApproach as j,generatePatternCodeWithComments as x}from"../generation/index.js";function L(e,n,r=!0,s,t){const a={appName:e,type:n,database:[],s3:[],compute:[]};if(n==="custom")return a;const o=n,c=m[o];return{...a,database:y(e,c,o,r,t),compute:F(e,c,o,r,s),network:c.network,backup:c.backup}}function y(e,n,r,s,t){const a=C(r),o=n.database[a];return!s||o===null?[]:[{name:e,type:a,databaseName:D(e),variableName:`${d(e)}Database`,...o,...t?.snapshotIdentifier!==void 0&&{snapshotIdentifier:t.snapshotIdentifier},...t?.snapshotUsername!==void 0&&{snapshotUsername:t.snapshotUsername}}]}function _(e,n){if(!(e===void 0&&n===void 0))return{scaling:{...e!==void 0&&{minCapacity:e},...n!==void 0&&{maxCapacity:n}}}}function $(e,n){const{minCapacity:r,maxCapacity:s,...t}=e,a=n?.containerPort??l,o=n?.docker;return{...t,...o?.path!==void 0&&{docker:{path:o.path,...o.context!==void 0&&{context:o.context},...o.target!==void 0&&{target:o.target}}},...n?.needsDatabaseConnection!==void 0&&{needsDatabaseConnection:n.needsDatabaseConnection},containers:[{port:a}],..._(r,s)}}function F(e,n,r,s,t){const a=n.compute.ecs,o=t&&t.length>0?g(r,t):a.services.map((c,i)=>({...c,name:i===0?"api":`service-${i+1}`}));return[{name:e,type:b.ECS,needsConnection:s,connectedDatabase:s?[e]:void 0,variableName:`${d(e)}Compute`,cluster:a.cluster,services:o.map((c,i)=>$(c,t?.[i]))}]}function Q(e,n){const r=[h(e),T(e),E(e)];if(j(e))r.push(x(e),f(e));else{const t=U(e);r.push(f(e),R(e),I(e),S(e),p(e),w(e),A(e,t),B(e))}const s=r.join("");if(e.customCodeBlocks&&e.customCodeBlocks.length>0){const t=k(s,e.customCodeBlocks,n?.resourceMapping);return t.success?u(t.content):P(new Error(`Custom code injection failed: ${t.error??"unknown error"}`))}return u(s)}export{Q as generateInfrastructureFromPlan,L as planApplicationResources};
1
+ import{TIER_PRESETS as g,getDefaultDatabaseTypeForTier as b,applyTierDefaultsToServices as l}from"../presets/tierPresets.js";import{COMPUTE_TYPE as k,DEFAULT_CONTAINER_PORT as P}from"../schemas/constants.js";import{success as u,failure as D}from"../types/Result.js";import{injectCustomCodeBlocks as T}from"../ast/mutation/astCodeInjection.js";import{toVariableName as f,toValidDatabaseName as h,generateImports as p,generateAppInit as v,generateTags as E,generateNetworkCode as m,generateDatabaseCode as R,generateClickHouseCode as S,generateS3Code as y,generateDynamoDBCode as I,generateSQSCode as w,generateComputeCode as A,generateCDNCode as B,collectCdnReferencedResources as U,usesPatternApproach as x,generatePatternCodeWithComments as j}from"../generation/index.js";function Y(e){return e?.map(n=>({name:n.name,...n.serviceType!==void 0&&{serviceType:n.serviceType},...n.docker!==void 0&&{docker:n.docker},...n.containerPort!==void 0&&{containerPort:n.containerPort},...n.needsDatabaseConnection!==void 0&&{needsDatabaseConnection:n.needsDatabaseConnection},...n.routing!==void 0&&{routing:n.routing}}))}function q(e,n,r=!0,c,t){const i={appName:e,type:n,database:[],s3:[],compute:[]};if(n==="custom")return i;const s=n,o=g[s];return{...i,database:_(e,o,s,r,t),compute:H(e,o,s,r,c),network:o.network,backup:o.backup}}function _(e,n,r,c,t){const i=b(r),s=n.database[i];return!c||s===null?[]:[{name:e,type:i,databaseName:h(e),variableName:`${f(e)}Database`,...s,...t?.snapshotIdentifier!==void 0&&{snapshotIdentifier:t.snapshotIdentifier},...t?.snapshotUsername!==void 0&&{snapshotUsername:t.snapshotUsername}}]}function $(e,n){if(!(e===void 0&&n===void 0))return{scaling:{...e!==void 0&&{minCapacity:e},...n!==void 0&&{maxCapacity:n}}}}function F(e,n){const{minCapacity:r,maxCapacity:c,...t}=e,i=n?.serviceType==="worker",s=n?.containerPort??P,o=n?.docker;return{...t,...o?.path!==void 0&&{docker:{path:o.path,...o.context!==void 0&&{context:o.context},...o.target!==void 0&&{target:o.target}}},...n?.needsDatabaseConnection!==void 0&&{needsDatabaseConnection:n.needsDatabaseConnection},containers:i?[{}]:[{port:s}],...$(r,c)}}function N(e,n){return n||e?.directAccess===!0?e:{...e,loadBalancer:!1}}function H(e,n,r,c,t){const i=n.compute.ecs,o=(t&&t.length>0?l(r,t):i.services.map((d,a)=>({...d,name:a===0?"api":`service-${a+1}`}))).map((d,a)=>F(d,t?.[a])),C=o.some(d=>d.containers?.some(a=>a.port!==void 0));return[{name:e,type:k.ECS,needsConnection:c,connectedDatabase:c?[e]:void 0,variableName:`${f(e)}Compute`,cluster:N(i.cluster,C),services:o}]}function z(e,n){const r=[p(e),v(e),E(e)];if(x(e))r.push(j(e),m(e));else{const t=U(e);r.push(m(e),R(e),S(e),y(e),I(e),w(e),A(e,t),B(e))}const c=r.join("");if(e.customCodeBlocks&&e.customCodeBlocks.length>0){const t=T(c,e.customCodeBlocks,n?.resourceMapping);return t.success?u(t.content):D(new Error(`Custom code injection failed: ${t.error??"unknown error"}`))}return u(c)}export{z as generateInfrastructureFromPlan,q as planApplicationResources,Y as toUserServiceConfigs};
@@ -1 +1 @@
1
- import{TIER_NAMES as d}from"../schemas/sharedTypes.js";import{TIER_NAMES as h,CUSTOM_TIER as L,APP_TYPES as M}from"../schemas/sharedTypes.js";const i="service",c="0 4 * * ? *",u="0 5 * * ? *",r=Object.freeze({tinkerer:{displayName:"Tinkerer",description:"Free tier eligible, minimal cost for experimentation",database:{Instance:{instanceType:"t4g.micro",multiAz:!1,proxy:!1,readReplica:!1,publiclyAccessible:!0},Aurora:null,GlobalAurora:null,ClickHouse:null},compute:{ecs:{services:[{name:i,capacityProvider:"EC2",desiredCount:1,minCapacity:1,maxCapacity:1,ec2Config:{instanceType:"t4g.micro",amiHardwareType:"ARM",minCapacity:1,maxCapacity:1,memoryLimitMiB:400}}],cluster:{directAccess:!0}},lambda:{timeout:30,memory:128}},network:{maxAzs:2,natGateways:!1,flowLogs:!1},backup:!1},lightweight:{displayName:"Lightweight",description:"Streamlined single-AZ deployment for cost efficiency",database:{Instance:{instanceType:"t4g.small",multiAz:!1},Aurora:{readers:!1},GlobalAurora:null,ClickHouse:null},compute:{ecs:{services:[{name:i,capacityProvider:"FARGATE_SPOT",cpu:256,memoryLimitMiB:512,desiredCount:1,minCapacity:1,maxCapacity:3}]},lambda:{timeout:30,memory:256}},network:{maxAzs:2,natGateways:{count:1},flowLogs:!1},backup:!1},standard:{displayName:"Standard",description:"Production-ready with sensible defaults",database:{Instance:{instanceType:"t4g.large"},Aurora:{readers:{count:1}},GlobalAurora:{readers:{count:1}},ClickHouse:{instanceType:"t4g.medium",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:14}},compute:{ecs:{services:[{name:i,capacityProvider:"FARGATE",cpu:512,memoryLimitMiB:1024,desiredCount:2,minCapacity:2,maxCapacity:5}]},lambda:{timeout:60,memory:256}},network:{maxAzs:3,natGateways:{count:1},flowLogs:{},vpcEndpoints:{interface:{ecr:!0}}},backup:!1},resilient:{displayName:"Resilient",description:"High availability with enhanced monitoring and scaling",database:{Instance:{instanceType:"r7g.large",encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0}},Aurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:30},GlobalAurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:30,enableGlobalWriteForwarding:!0},ClickHouse:{instanceType:"m6g.large",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:14}},compute:{ecs:{services:[{name:i,capacityProvider:"FARGATE",cpu:1024,memoryLimitMiB:2048,desiredCount:4,minCapacity:4,maxCapacity:20}]},lambda:{timeout:120,memory:512}},network:{maxAzs:3,natGateways:{count:3},flowLogs:{retentionDays:90},vpcEndpoints:{interface:{ecr:!0,secretsManager:!0}}},backup:{tier:"resilient"}},enterprise:{displayName:"Enterprise",description:"Maximum capability with all features enabled",database:{Instance:{instanceType:"r7g.xlarge",encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},readReplica:{}},Aurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:35},GlobalAurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:35,enableGlobalWriteForwarding:!0},ClickHouse:{instanceType:"m6g.xlarge",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:30}},compute:{ecs:{services:[{name:i,capacityProvider:"FARGATE",cpu:2048,memoryLimitMiB:4096,desiredCount:6,minCapacity:6,maxCapacity:100}]},lambda:{timeout:300,memory:1024}},network:{maxAzs:3,natGateways:{count:3},flowLogs:{destination:"s3",retentionDays:365},vpcEndpoints:{interface:{ecr:!0,secretsManager:!0,kms:!0,cloudwatchLogs:!0}}},backup:{tier:"enterprise"}}});function C(e,t){return r[e].database[t]}function f(e){return r[e].compute.ecs}function T(e){return r[e].network}function y(e){return d.filter(t=>r[t].database[e]!==null)}function A(e){return y(e).map(t=>({label:r[t].displayName,value:t,description:r[t].description}))}function x(e){switch(e){case"tinkerer":case"lightweight":case"standard":return"Instance";case"resilient":case"enterprise":return"Aurora"}}function k(e,t){const s=r[e].compute.ecs,a=s.services[0],m=!s.cluster?.directAccess&&s.cluster?.loadBalancer!==!1;return t.map((n,p)=>{const l=p===0;let o;return m&&(n.routing?o=n.routing:t.length>1&&(o={path:l?"/*":`/${n.name.toLowerCase()}/*`,...l?{}:{priority:100+p}})),{name:n.name,capacityProvider:a.capacityProvider,ec2Config:a.ec2Config,cpu:a.cpu,memoryLimitMiB:a.memoryLimitMiB,desiredCount:a.desiredCount,minCapacity:a.minCapacity,maxCapacity:a.maxCapacity,routing:o}})}export{M as APP_TYPES,L as CUSTOM_TIER,h as TIER_NAMES,r as TIER_PRESETS,k as applyTierDefaultsToServices,y as getAvailableTiersForDatabase,C as getDatabasePreset,x as getDefaultDatabaseTypeForTier,f as getEcsPreset,T as getNetworkPreset,A as getTierOptionsForDatabase};
1
+ import{TIER_NAMES as g}from"../schemas/sharedTypes.js";import{TIER_NAMES as S,CUSTOM_TIER as K,APP_TYPES as I}from"../schemas/sharedTypes.js";const n="service",c="0 4 * * ? *",u="0 5 * * ? *",i=Object.freeze({tinkerer:{displayName:"Tinkerer",description:"Free tier eligible, minimal cost for experimentation",database:{Instance:{instanceType:"t4g.micro",multiAz:!1,proxy:!1,readReplica:!1,publiclyAccessible:!0},Aurora:null,GlobalAurora:null,ClickHouse:null},compute:{ecs:{services:[{name:n,capacityProvider:"EC2",desiredCount:1,minCapacity:1,maxCapacity:1,ec2Config:{instanceType:"t4g.micro",amiHardwareType:"ARM",minCapacity:1,maxCapacity:1,memoryLimitMiB:400}}],cluster:{directAccess:!0}},lambda:{timeout:30,memory:128}},network:{maxAzs:2,natGateways:!1,flowLogs:!1},backup:!1},lightweight:{displayName:"Lightweight",description:"Streamlined single-AZ deployment for cost efficiency",database:{Instance:{instanceType:"t4g.small",multiAz:!1},Aurora:{readers:!1},GlobalAurora:null,ClickHouse:null},compute:{ecs:{services:[{name:n,capacityProvider:"FARGATE_SPOT",cpu:256,memoryLimitMiB:512,desiredCount:1,minCapacity:1,maxCapacity:3}]},lambda:{timeout:30,memory:256}},network:{maxAzs:2,natGateways:{count:1},flowLogs:!1},backup:!1},standard:{displayName:"Standard",description:"Production-ready with sensible defaults",database:{Instance:{instanceType:"t4g.large"},Aurora:{readers:{count:1}},GlobalAurora:{readers:{count:1}},ClickHouse:{instanceType:"t4g.medium",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:14}},compute:{ecs:{services:[{name:n,capacityProvider:"FARGATE",cpu:512,memoryLimitMiB:1024,desiredCount:2,minCapacity:2,maxCapacity:5}]},lambda:{timeout:60,memory:256}},network:{maxAzs:3,natGateways:{count:1},flowLogs:{},vpcEndpoints:{interface:{ecr:!0}}},backup:!1},resilient:{displayName:"Resilient",description:"High availability with enhanced monitoring and scaling",database:{Instance:{instanceType:"r7g.large",encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0}},Aurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:30},GlobalAurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:30,enableGlobalWriteForwarding:!0},ClickHouse:{instanceType:"m6g.large",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:14}},compute:{ecs:{services:[{name:n,capacityProvider:"FARGATE",cpu:1024,memoryLimitMiB:2048,desiredCount:4,minCapacity:4,maxCapacity:20}]},lambda:{timeout:120,memory:512}},network:{maxAzs:3,natGateways:{count:3},flowLogs:{retentionDays:90},vpcEndpoints:{interface:{ecr:!0,secretsManager:!0}}},backup:{tier:"resilient"}},enterprise:{displayName:"Enterprise",description:"Maximum capability with all features enabled",database:{Instance:{instanceType:"r7g.xlarge",encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},readReplica:{}},Aurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:35},GlobalAurora:{readers:{count:2},encryption:{storageKey:{useCMK:!0}},databaseInsights:{mode:"advanced",encryptionKey:{useCMK:!0}},proxy:{requireTLS:!0},backupRetention:35,enableGlobalWriteForwarding:!0},ClickHouse:{instanceType:"m6g.xlarge",coldTier:{mode:"s3"},optimiseSchedule:c,backupSchedule:u,backupRetentionDays:30}},compute:{ecs:{services:[{name:n,capacityProvider:"FARGATE",cpu:2048,memoryLimitMiB:4096,desiredCount:6,minCapacity:6,maxCapacity:100}]},lambda:{timeout:300,memory:1024}},network:{maxAzs:3,natGateways:{count:3},flowLogs:{destination:"s3",retentionDays:365},vpcEndpoints:{interface:{ecr:!0,secretsManager:!0,kms:!0,cloudwatchLogs:!0}}},backup:{tier:"enterprise"}}});function k(e,t){return i[e].database[t]}function w(e){return i[e].compute.ecs}function x(e){return i[e].network}function C(e){return g.filter(t=>i[t].database[e]!==null)}function E(e){return C(e).map(t=>({label:i[t].displayName,value:t,description:i[t].description}))}function v(e){switch(e){case"tinkerer":case"lightweight":case"standard":return"Instance";case"resilient":case"enterprise":return"Aurora"}}function h(e,t){const s=i[e].compute.ecs,r=s.services[0],d=!s.cluster?.directAccess&&s.cluster?.loadBalancer!==!1,m=t.filter(a=>a.serviceType!=="worker").length,y=t.findIndex(a=>a.serviceType!=="worker");return t.map((a,p)=>{const l=p===y,b=a.serviceType==="worker";let o;return d&&!b&&(a.routing?o=a.routing:m>1&&(o={path:l?"/*":`/${a.name.toLowerCase()}/*`,...l?{}:{priority:100+p}})),{name:a.name,capacityProvider:r.capacityProvider,ec2Config:r.ec2Config,cpu:r.cpu,memoryLimitMiB:r.memoryLimitMiB,desiredCount:r.desiredCount,minCapacity:r.minCapacity,maxCapacity:r.maxCapacity,routing:o}})}export{I as APP_TYPES,K as CUSTOM_TIER,S as TIER_NAMES,i as TIER_PRESETS,h as applyTierDefaultsToServices,C as getAvailableTiersForDatabase,k as getDatabasePreset,v as getDefaultDatabaseTypeForTier,w as getEcsPreset,x as getNetworkPreset,E as getTierOptionsForDatabase};
@@ -287,6 +287,8 @@ export interface TierPreset {
287
287
  */
288
288
  export interface UserServiceConfig {
289
289
  name: string;
290
+ /** "worker" omits the inbound port + ALB route; "web" (default) keeps them. */
291
+ serviceType?: "web" | "worker";
290
292
  docker?: DockerBuildPartial;
291
293
  containerPort?: number;
292
294
  needsDatabaseConnection?: boolean;
@@ -909,6 +909,10 @@ export declare const ApplicationGeneratorSchema: z.ZodObject<{
909
909
  }, z.core.$strict>]>>;
910
910
  services: z.ZodOptional<z.ZodArray<z.ZodObject<{
911
911
  name: z.ZodString;
912
+ serviceType: z.ZodOptional<z.ZodEnum<{
913
+ web: "web";
914
+ worker: "worker";
915
+ }>>;
912
916
  docker: z.ZodOptional<z.ZodObject<{
913
917
  path: z.ZodOptional<z.ZodString>;
914
918
  context: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -803,6 +803,10 @@ export declare const ApplicationServiceRoutingSchema: z.ZodObject<{
803
803
  */
804
804
  export declare const ApplicationServiceConfigSchema: z.ZodObject<{
805
805
  name: z.ZodString;
806
+ serviceType: z.ZodOptional<z.ZodEnum<{
807
+ web: "web";
808
+ worker: "worker";
809
+ }>>;
806
810
  docker: z.ZodOptional<z.ZodObject<{
807
811
  path: z.ZodOptional<z.ZodString>;
808
812
  context: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -1 +1 @@
1
- import{z as e}from"zod";import{VALIDATION_PATTERNS as m,VALIDATION_MESSAGES as o}from"../validation/patterns.js";import{COMPUTE_TYPES as h,ECS_CAPACITY_PROVIDERS as f,EC2_INSTANCE_TYPES as j,HTTP_METHODS as k,MIN_LAMBDA_MEMORY as W,MAX_LAMBDA_MEMORY as w,MIN_LAMBDA_TIMEOUT as S,MAX_LAMBDA_TIMEOUT as g,MIN_ECS_CAPACITY as x,MAX_ECS_CAPACITY as O,MAX_SCALING_CAPACITY as p,MAX_WARM_POOL_SIZE as V,constIncludes as K,COMPUTE_ARCHITECTURES as F,FUNCTION_URL_AUTH_TYPES as L,DEPLOYMENT_TYPES as Z,AMI_HARDWARE_TYPES as z,LOAD_BALANCER_TYPES as Q,SCALING_TYPES as $}from"./constants.js";import{validateArchitectureMatch as q,getArchitectureForInstanceType as J}from"./instanceTypeArchitecture.js";import{optionalOrDisabled as ee,CAPACITY_REFINEMENT as r,memoryLimitMiBSchema as I,ResourceNameSchema as c,AppNameSchema as s,PortSchema as T,ExtraPropertySchema as l,EnvironmentRecordSchema as E,SecretsImportRecordSchema as oe}from"./baseSchemas.js";import{DockerBuildSchema as A}from"@fjall/util/manifest/schemas";import{EcsServiceAlarmConfigSchema as ne,LambdaAlarmConfigSchema as te}from"./alarmSchemas.js";function ie(n){return!(n.image!==void 0&&n.docker!==void 0)}const H=e.number().int(o.CAPACITY.MIN.INTEGER).min(x,o.GENERATOR_CAPACITY.MIN.MIN).max(O,o.GENERATOR_CAPACITY.MIN.MAX),D=e.number().int(o.CAPACITY.MAX.INTEGER).min(x,o.GENERATOR_CAPACITY.MAX.MIN).max(O,o.GENERATOR_CAPACITY.MAX.MAX),N=e.number().int(o.LAMBDA.MEMORY.INTEGER).min(W,o.LAMBDA.MEMORY.MIN).max(w,o.LAMBDA.MEMORY.MAX).refine(n=>n===128||n%64===0,o.LAMBDA.MEMORY.MULTIPLE),G=e.string().regex(m.LAMBDA_RUNTIME_IDENTIFIER,o.LAMBDA.RUNTIME),d=e.number().int(o.CPU.INTEGER).min(256,o.CPU.MIN).max(4096,o.CPU.MAX),M=e.number().int(o.CAPACITY.DESIRED.INTEGER).min(0,o.CAPACITY.DESIRED.MIN).max(p,o.CAPACITY.DESIRED.MAX),C=e.object({connectToDatabase:e.array(e.string()).optional()}).strict(),ae=e.object({minSize:e.number().int(o.CAPACITY.WARM_POOL.MIN_SIZE.INTEGER).min(0,o.CAPACITY.WARM_POOL.MIN_SIZE.MIN).max(V,o.CAPACITY.WARM_POOL.MIN_SIZE.MAX).optional(),reuseOnScaleIn:e.boolean().optional()}).strict(),_=e.object({instanceType:e.string().optional(),amiHardwareType:e.enum(z).optional(),minCapacity:e.number().int(o.CAPACITY.MIN.INTEGER).min(0,o.CAPACITY.MIN.MIN_0).max(p,o.CAPACITY.MIN.MAX).optional(),maxCapacity:e.number().int(o.CAPACITY.MAX.INTEGER).min(1,o.CAPACITY.MAX.MIN_1).max(p,o.CAPACITY.MAX.MAX).optional(),memoryLimitMiB:I(128).optional(),warmPool:ae.optional(),extraProperties:e.array(l).optional()}).strict().superRefine((n,i)=>{if(!n.instanceType||!n.amiHardwareType)return;const t=q(n.instanceType,n.amiHardwareType);t.valid||i.addIssue({code:"custom",message:t.message??o.ARCHITECTURE_MISMATCH,path:["instanceType"]})}).superRefine((n,i)=>{n.minCapacity===0&&!n.warmPool&&i.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL_REQUIRED,path:["minCapacity"]}),n.warmPool?.minSize!==void 0&&n.maxCapacity!==void 0&&n.warmPool.minSize>n.maxCapacity&&i.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL.MIN_SIZE_EXCEEDS_MAX_CAPACITY,path:["warmPool","minSize"]})}),re=e.enum(h).describe(`Compute type must be one of: ${h.join(", ")}`),y=e.enum(f).describe(`ECS capacity provider must be one of: ${f.join(", ")}`),b=e.string().refine(n=>K(j,n),{message:o.INSTANCE_TYPE}),Y=e.object({sourceType:e.enum(["sqs","dynamodb","eventbridge"]),sourceRef:e.string().describe("Name of the source resource"),batchSize:e.number().int(o.BATCH_SIZE.INTEGER).min(1,o.BATCH_SIZE.MIN).max(1e4,o.BATCH_SIZE.MAX).optional(),startingPosition:e.enum(["TRIM_HORIZON","LATEST"]).optional(),maxBatchingWindow:e.number().int(o.BATCHING_WINDOW.INTEGER).min(0,o.BATCHING_WINDOW.MIN).max(300,o.BATCHING_WINDOW.MAX).optional(),reportBatchItemFailures:e.boolean().optional()}).strict(),ce=e.object({name:e.string().min(1,o.REQUIRED.CONTAINER_NAME).optional(),image:e.string().optional(),port:T.optional(),environment:E.optional(),secretsImport:oe.optional(),command:e.array(e.string()).optional(),entryPoint:e.array(e.string()).optional(),essential:e.boolean().optional(),healthCheck:e.object({command:e.array(e.string()),interval:e.number().int(o.HEALTH_CHECK.INTERVAL.INTEGER).min(5,o.HEALTH_CHECK.INTERVAL.MIN).max(300,o.HEALTH_CHECK.INTERVAL.MAX).optional(),timeout:e.number().int(o.HEALTH_CHECK.TIMEOUT.INTEGER).min(2,o.HEALTH_CHECK.TIMEOUT.MIN).max(60,o.HEALTH_CHECK.TIMEOUT.MAX).optional(),retries:e.number().int(o.HEALTH_CHECK.RETRIES.INTEGER).min(1,o.HEALTH_CHECK.RETRIES.MIN).max(10,o.HEALTH_CHECK.RETRIES.MAX).optional(),startPeriod:e.number().int(o.HEALTH_CHECK.START_PERIOD.INTEGER).min(0,o.HEALTH_CHECK.START_PERIOD.MIN).max(300,o.HEALTH_CHECK.START_PERIOD.MAX).optional()}).strict().optional(),ssmSecrets:e.array(e.string().regex(m.SECRET_NAME,o.SECRET_NAME)).optional(),extraProperties:e.array(l).optional()}).strict(),U=e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(m.ECS_SERVICE_NAME,o.ECS_SERVICE_NAME),v=e.object({domain:e.string().optional(),loadBalancer:e.union([e.literal(!1),e.enum(Q)]).optional(),directAccess:e.boolean().optional(),extraProperties:e.array(l).optional()}).strict().refine(n=>!(n.directAccess&&n.domain),{message:o.DIRECT_ACCESS.NO_DOMAIN,path:["directAccess"]}).refine(n=>!(n.directAccess&&typeof n.loadBalancer=="string"),{message:o.DIRECT_ACCESS.NO_LOAD_BALANCER}).describe("Cluster-level configuration"),u=e.object({path:e.string().optional(),host:e.string().optional(),priority:e.number().int(o.PRIORITY.INTEGER).min(1,o.PRIORITY.MIN).max(5e4,o.PRIORITY.MAX).optional(),healthCheckPath:e.string().optional()}).strict().describe("Path/host-based routing configuration"),se=e.object({desiredCount:M.optional(),minCapacity:e.number().int(o.CAPACITY.MIN.INTEGER).min(0,o.CAPACITY.MIN.MIN_0).max(p,o.CAPACITY.MIN.MAX).optional(),maxCapacity:e.number().int(o.CAPACITY.MAX.INTEGER).min(1,o.CAPACITY.MAX.MIN_1).max(p,o.CAPACITY.MAX.MAX).optional(),scalingType:e.enum($).optional()}).strict().refine(r.check,r.params).describe("ECS service scaling configuration"),B=e.object({name:U,docker:A.optional(),needsDatabaseConnection:e.boolean().optional(),needsStorageConnection:e.boolean().optional(),needsMessagingConnection:e.boolean().optional(),image:e.string().optional(),containers:e.array(ce).optional(),routing:e.union([u,e.array(u).min(1)]).optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),desiredCount:M.optional(),scaling:ee(se).optional(),capacityProvider:y,ec2Config:_.optional(),ssmSecretsPath:e.string().regex(m.SSM_PATH,o.SSM_PATH).optional(),extraProperties:e.array(l).optional(),alarms:ne.optional()}).strict().superRefine((n,i)=>{ie(n)||i.addIssue({code:"custom",path:["docker"],message:o.IMAGE_DOCKER_MUTEX})}).describe("Configuration for a service in an ECS cluster"),Re=e.object({name:c,type:re,needsConnection:e.boolean(),connectedDatabase:e.array(e.string()).optional(),connectedStorage:e.array(e.string()).optional(),connectedMessaging:e.array(e.string()).optional(),cluster:v.optional(),services:e.array(B).optional(),docker:A.partial().optional(),deployment:e.enum(Z).optional(),codePath:e.string().optional(),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),handler:e.string().optional(),runtime:G.optional(),environment:E.optional(),secrets:E.optional(),eventSources:e.array(Y).optional(),functionUrl:e.object({authType:e.enum(L)}).strict().optional(),description:e.string().optional(),scheduleExpression:e.string().optional(),architecture:e.enum(F).optional(),ephemeralStorageSize:e.number().int(o.EPHEMERAL_STORAGE.INTEGER).min(512,o.EPHEMERAL_STORAGE.MIN).max(10240,o.EPHEMERAL_STORAGE.MAX).optional(),functionName:e.string().optional(),ssmSecrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),alarms:te.optional(),instanceType:b.optional(),enableSSH:e.boolean().optional(),keyName:e.string().optional(),userData:e.string().optional(),securityGroups:e.array(e.string()).optional(),variableName:e.string().optional(),extraProperties:e.array(l).optional()}).strict().superRefine((n,i)=>{if(n.type==="ecs"&&n.services)for(let t=0;t<n.services.length;t++){const a=n.services[t];a?.ec2Config?.instanceType&&!a.ec2Config.amiHardwareType&&J(a.ec2Config.instanceType)!=="ARM"&&i.addIssue({code:"custom",message:`Service '${a.name??t}' uses instance type '${a.ec2Config.instanceType}' (x86), which requires Docker images built for linux/amd64. The default build targets linux/arm64 (Graviton). Use a Graviton instance type (t4g, c6g, r6g, m6g, etc.) or set amiHardwareType: "STANDARD" to acknowledge x86.`,path:["services",t,"ec2Config","instanceType"]})}n.type==="lambda"&&n.deployment==="container"&&n.architecture==="X86_64"&&i.addIssue({code:"custom",message:"Lambda architecture 'X86_64' requires Docker images built for linux/amd64, but the default build targets linux/arm64. Use 'ARM_64' for Graviton Lambdas (recommended) or ensure your build pipeline targets linux/amd64.",path:["architecture"]})}),Se=e.object({appName:s,clusterName:c,containerPort:T,nameProvidedByFlag:e.boolean().optional(),ecrRepository:e.string().optional(),capacityProvider:y.optional(),useAppEcr:e.boolean().optional(),desiredCount:M.optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),minCapacity:H.optional(),maxCapacity:D.optional(),ec2Config:_.optional(),connectionConfig:C.optional()}).strict().refine(r.check,r.params),ge=e.object({appName:s,functionName:c,nameProvidedByFlag:e.boolean().optional(),lambdaType:e.enum(["basic","web","custom"]).optional(),ecrRepository:e.string().optional(),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),enableFunctionUrl:e.boolean().optional(),description:e.string().optional(),enableCors:e.boolean().optional(),corsOrigins:e.string().optional(),corsHeaders:e.string().optional(),corsMethods:e.array(e.enum(k)).optional(),corsCredentials:e.boolean().optional(),connectionConfig:C.optional()}).strict(),Ne=e.object({appName:s,instanceName:c,nameProvidedByFlag:e.boolean().optional(),instanceType:b.optional(),enableSSH:e.boolean().optional(),minCapacity:H.optional(),maxCapacity:D.optional(),connectionConfig:C.optional()}).strict().refine(r.check,r.params),de=e.object({appName:s,clusterName:c,serviceName:U,capacityProvider:y,containerPort:T.optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),desiredCount:M.optional(),ec2Config:_.optional(),routing:e.union([u,e.array(u).min(1)]).optional(),docker:A.partial().optional(),nameProvidedByFlag:e.boolean().optional(),connectionConfig:C.optional()}).strict(),P=e.object({appName:s,computeName:c,nameProvidedByFlag:e.boolean().optional(),connectionConfig:C.optional()}).strict(),me=P.extend({type:e.literal("ecs"),cluster:v.optional(),services:e.array(B).min(1,o.SERVICE.MIN_REQUIRED),docker:A.partial().optional()}).strict().refine(n=>{const i=n.services.filter(t=>t.containers?.some(a=>a.port));return i.length>1?i.every(t=>(Array.isArray(t.routing)?t.routing:t.routing?[t.routing]:[]).some(R=>R.path||R.host)):!0},{message:o.SERVICE.ROUTING_REQUIRED}).refine(n=>{const i=n.services.map(t=>t.name);return new Set(i).size===i.length},{message:o.SERVICE.UNIQUE_WITHIN_CLUSTER}),pe=P.extend({type:e.literal("lambda"),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),handler:e.string().optional(),runtime:G.optional(),environment:E.optional(),secrets:E.optional(),description:e.string().optional(),functionUrl:e.union([e.object({authType:e.enum(L).optional()}).strict(),e.literal(!1)]).optional(),eventSources:e.array(Y).optional()}).strict(),le=P.extend({type:e.literal("ec2"),instanceType:b.optional(),enableSSH:e.boolean().optional(),keyName:e.string().optional(),userData:e.string().optional(),securityGroups:e.array(e.string()).optional()}).strict(),_e=e.discriminatedUnion("type",[me,pe,le]),X=e.object({path:e.string().min(1,o.REQUIRED.ROUTING_PATH),priority:e.number().int(o.PRIORITY.INTEGER).min(1,o.PRIORITY.MIN).max(5e4,o.PRIORITY.MAX).optional()}).strict(),ye=e.object({name:e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(m.RESOURCE_NAME,o.RESOURCE_NAME),docker:A.partial().optional(),containerPort:T.optional(),needsDatabaseConnection:e.boolean().optional(),routing:e.union([X,e.array(X).min(1)]).optional()}).strict(),be=e.object({appName:s,instanceType:e.string().optional()}).strict();export{de as AddServiceGeneratorSchema,ye as ApplicationServiceConfigSchema,X as ApplicationServiceRoutingSchema,_e as ComputeGeneratorSchema,Re as ComputeResourcePlanSchema,re as ComputeTypeSchema,ce as ContainerConfigSchema,Ne as EC2GeneratorSchema,Se as ECSGeneratorSchema,_ as Ec2ConfigSchema,y as EcsCapacityProviderSchema,v as EcsClusterConfigSchema,u as EcsRoutingConfigSchema,se as EcsScalingConfigSchema,B as EcsServiceConfigSchema,U as EcsServiceNameSchema,b as InstanceTypeSchema,Y as LambdaEventSourceSchema,ge as LambdaGeneratorSchema,N as LambdaMemorySchema,G as LambdaRuntimeSchema,be as TunnelGeneratorSchema,ae as WarmPoolSchema};
1
+ import{z as e}from"zod";import{VALIDATION_PATTERNS as m,VALIDATION_MESSAGES as o}from"../validation/patterns.js";import{COMPUTE_TYPES as h,ECS_CAPACITY_PROVIDERS as f,EC2_INSTANCE_TYPES as k,HTTP_METHODS as j,MIN_LAMBDA_MEMORY as W,MAX_LAMBDA_MEMORY as w,MIN_LAMBDA_TIMEOUT as S,MAX_LAMBDA_TIMEOUT as g,MIN_ECS_CAPACITY as x,MAX_ECS_CAPACITY as O,MAX_SCALING_CAPACITY as p,MAX_WARM_POOL_SIZE as V,constIncludes as K,COMPUTE_ARCHITECTURES as F,FUNCTION_URL_AUTH_TYPES as L,DEPLOYMENT_TYPES as Z,AMI_HARDWARE_TYPES as z,LOAD_BALANCER_TYPES as Q,SCALING_TYPES as $}from"./constants.js";import{validateArchitectureMatch as q,getArchitectureForInstanceType as J}from"./instanceTypeArchitecture.js";import{optionalOrDisabled as ee,CAPACITY_REFINEMENT as r,memoryLimitMiBSchema as I,ResourceNameSchema as c,AppNameSchema as s,PortSchema as T,ExtraPropertySchema as l,EnvironmentRecordSchema as E,SecretsImportRecordSchema as oe}from"./baseSchemas.js";import{DockerBuildSchema as A}from"@fjall/util/manifest/schemas";import{EcsServiceAlarmConfigSchema as ne,LambdaAlarmConfigSchema as te}from"./alarmSchemas.js";function ie(n){return!(n.image!==void 0&&n.docker!==void 0)}const H=e.number().int(o.CAPACITY.MIN.INTEGER).min(x,o.GENERATOR_CAPACITY.MIN.MIN).max(O,o.GENERATOR_CAPACITY.MIN.MAX),D=e.number().int(o.CAPACITY.MAX.INTEGER).min(x,o.GENERATOR_CAPACITY.MAX.MIN).max(O,o.GENERATOR_CAPACITY.MAX.MAX),N=e.number().int(o.LAMBDA.MEMORY.INTEGER).min(W,o.LAMBDA.MEMORY.MIN).max(w,o.LAMBDA.MEMORY.MAX).refine(n=>n===128||n%64===0,o.LAMBDA.MEMORY.MULTIPLE),G=e.string().regex(m.LAMBDA_RUNTIME_IDENTIFIER,o.LAMBDA.RUNTIME),d=e.number().int(o.CPU.INTEGER).min(256,o.CPU.MIN).max(4096,o.CPU.MAX),u=e.number().int(o.CAPACITY.DESIRED.INTEGER).min(0,o.CAPACITY.DESIRED.MIN).max(p,o.CAPACITY.DESIRED.MAX),C=e.object({connectToDatabase:e.array(e.string()).optional()}).strict(),ae=e.object({minSize:e.number().int(o.CAPACITY.WARM_POOL.MIN_SIZE.INTEGER).min(0,o.CAPACITY.WARM_POOL.MIN_SIZE.MIN).max(V,o.CAPACITY.WARM_POOL.MIN_SIZE.MAX).optional(),reuseOnScaleIn:e.boolean().optional()}).strict(),_=e.object({instanceType:e.string().optional(),amiHardwareType:e.enum(z).optional(),minCapacity:e.number().int(o.CAPACITY.MIN.INTEGER).min(0,o.CAPACITY.MIN.MIN_0).max(p,o.CAPACITY.MIN.MAX).optional(),maxCapacity:e.number().int(o.CAPACITY.MAX.INTEGER).min(1,o.CAPACITY.MAX.MIN_1).max(p,o.CAPACITY.MAX.MAX).optional(),memoryLimitMiB:I(128).optional(),warmPool:ae.optional(),extraProperties:e.array(l).optional()}).strict().superRefine((n,t)=>{if(!n.instanceType||!n.amiHardwareType)return;const i=q(n.instanceType,n.amiHardwareType);i.valid||t.addIssue({code:"custom",message:i.message??o.ARCHITECTURE_MISMATCH,path:["instanceType"]})}).superRefine((n,t)=>{n.minCapacity===0&&!n.warmPool&&t.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL_REQUIRED,path:["minCapacity"]}),n.warmPool?.minSize!==void 0&&n.maxCapacity!==void 0&&n.warmPool.minSize>n.maxCapacity&&t.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL.MIN_SIZE_EXCEEDS_MAX_CAPACITY,path:["warmPool","minSize"]})}),re=e.enum(h).describe(`Compute type must be one of: ${h.join(", ")}`),y=e.enum(f).describe(`ECS capacity provider must be one of: ${f.join(", ")}`),b=e.string().refine(n=>K(k,n),{message:o.INSTANCE_TYPE}),Y=e.object({sourceType:e.enum(["sqs","dynamodb","eventbridge"]),sourceRef:e.string().describe("Name of the source resource"),batchSize:e.number().int(o.BATCH_SIZE.INTEGER).min(1,o.BATCH_SIZE.MIN).max(1e4,o.BATCH_SIZE.MAX).optional(),startingPosition:e.enum(["TRIM_HORIZON","LATEST"]).optional(),maxBatchingWindow:e.number().int(o.BATCHING_WINDOW.INTEGER).min(0,o.BATCHING_WINDOW.MIN).max(300,o.BATCHING_WINDOW.MAX).optional(),reportBatchItemFailures:e.boolean().optional()}).strict(),ce=e.object({name:e.string().min(1,o.REQUIRED.CONTAINER_NAME).optional(),image:e.string().optional(),port:T.optional(),environment:E.optional(),secretsImport:oe.optional(),command:e.array(e.string()).optional(),entryPoint:e.array(e.string()).optional(),essential:e.boolean().optional(),healthCheck:e.object({command:e.array(e.string()),interval:e.number().int(o.HEALTH_CHECK.INTERVAL.INTEGER).min(5,o.HEALTH_CHECK.INTERVAL.MIN).max(300,o.HEALTH_CHECK.INTERVAL.MAX).optional(),timeout:e.number().int(o.HEALTH_CHECK.TIMEOUT.INTEGER).min(2,o.HEALTH_CHECK.TIMEOUT.MIN).max(60,o.HEALTH_CHECK.TIMEOUT.MAX).optional(),retries:e.number().int(o.HEALTH_CHECK.RETRIES.INTEGER).min(1,o.HEALTH_CHECK.RETRIES.MIN).max(10,o.HEALTH_CHECK.RETRIES.MAX).optional(),startPeriod:e.number().int(o.HEALTH_CHECK.START_PERIOD.INTEGER).min(0,o.HEALTH_CHECK.START_PERIOD.MIN).max(300,o.HEALTH_CHECK.START_PERIOD.MAX).optional()}).strict().optional(),ssmSecrets:e.array(e.string().regex(m.SECRET_NAME,o.SECRET_NAME)).optional(),extraProperties:e.array(l).optional()}).strict(),v=e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(m.ECS_SERVICE_NAME,o.ECS_SERVICE_NAME),U=e.object({domain:e.string().optional(),loadBalancer:e.union([e.literal(!1),e.enum(Q)]).optional(),directAccess:e.boolean().optional(),extraProperties:e.array(l).optional()}).strict().refine(n=>!(n.directAccess&&n.domain),{message:o.DIRECT_ACCESS.NO_DOMAIN,path:["directAccess"]}).refine(n=>!(n.directAccess&&typeof n.loadBalancer=="string"),{message:o.DIRECT_ACCESS.NO_LOAD_BALANCER}).describe("Cluster-level configuration"),M=e.object({path:e.string().optional(),host:e.string().optional(),priority:e.number().int(o.PRIORITY.INTEGER).min(1,o.PRIORITY.MIN).max(5e4,o.PRIORITY.MAX).optional(),healthCheckPath:e.string().optional()}).strict().describe("Path/host-based routing configuration"),se=e.object({desiredCount:u.optional(),minCapacity:e.number().int(o.CAPACITY.MIN.INTEGER).min(0,o.CAPACITY.MIN.MIN_0).max(p,o.CAPACITY.MIN.MAX).optional(),maxCapacity:e.number().int(o.CAPACITY.MAX.INTEGER).min(1,o.CAPACITY.MAX.MIN_1).max(p,o.CAPACITY.MAX.MAX).optional(),scalingType:e.enum($).optional()}).strict().refine(r.check,r.params).describe("ECS service scaling configuration"),B=e.object({name:v,docker:A.optional(),needsDatabaseConnection:e.boolean().optional(),needsStorageConnection:e.boolean().optional(),needsMessagingConnection:e.boolean().optional(),image:e.string().optional(),containers:e.array(ce).optional(),routing:e.union([M,e.array(M).min(1)]).optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),desiredCount:u.optional(),scaling:ee(se).optional(),capacityProvider:y,ec2Config:_.optional(),ssmSecretsPath:e.string().regex(m.SSM_PATH,o.SSM_PATH).optional(),extraProperties:e.array(l).optional(),alarms:ne.optional()}).strict().superRefine((n,t)=>{ie(n)||t.addIssue({code:"custom",path:["docker"],message:o.IMAGE_DOCKER_MUTEX})}).describe("Configuration for a service in an ECS cluster"),Re=e.object({name:c,type:re,needsConnection:e.boolean(),connectedDatabase:e.array(e.string()).optional(),connectedStorage:e.array(e.string()).optional(),connectedMessaging:e.array(e.string()).optional(),cluster:U.optional(),services:e.array(B).optional(),docker:A.partial().optional(),deployment:e.enum(Z).optional(),codePath:e.string().optional(),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),handler:e.string().optional(),runtime:G.optional(),environment:E.optional(),secrets:E.optional(),eventSources:e.array(Y).optional(),functionUrl:e.object({authType:e.enum(L)}).strict().optional(),description:e.string().optional(),scheduleExpression:e.string().optional(),architecture:e.enum(F).optional(),ephemeralStorageSize:e.number().int(o.EPHEMERAL_STORAGE.INTEGER).min(512,o.EPHEMERAL_STORAGE.MIN).max(10240,o.EPHEMERAL_STORAGE.MAX).optional(),functionName:e.string().optional(),ssmSecrets:e.array(e.string()).optional(),ssmSecretsPath:e.string().optional(),alarms:te.optional(),instanceType:b.optional(),enableSSH:e.boolean().optional(),keyName:e.string().optional(),userData:e.string().optional(),securityGroups:e.array(e.string()).optional(),variableName:e.string().optional(),extraProperties:e.array(l).optional()}).strict().superRefine((n,t)=>{if(n.type==="ecs"&&n.services)for(let i=0;i<n.services.length;i++){const a=n.services[i];a?.ec2Config?.instanceType&&!a.ec2Config.amiHardwareType&&J(a.ec2Config.instanceType)!=="ARM"&&t.addIssue({code:"custom",message:`Service '${a.name??i}' uses instance type '${a.ec2Config.instanceType}' (x86), which requires Docker images built for linux/amd64. The default build targets linux/arm64 (Graviton). Use a Graviton instance type (t4g, c6g, r6g, m6g, etc.) or set amiHardwareType: "STANDARD" to acknowledge x86.`,path:["services",i,"ec2Config","instanceType"]})}n.type==="lambda"&&n.deployment==="container"&&n.architecture==="X86_64"&&t.addIssue({code:"custom",message:"Lambda architecture 'X86_64' requires Docker images built for linux/amd64, but the default build targets linux/arm64. Use 'ARM_64' for Graviton Lambdas (recommended) or ensure your build pipeline targets linux/amd64.",path:["architecture"]})}),Se=e.object({appName:s,clusterName:c,containerPort:T,nameProvidedByFlag:e.boolean().optional(),ecrRepository:e.string().optional(),capacityProvider:y.optional(),useAppEcr:e.boolean().optional(),desiredCount:u.optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),minCapacity:H.optional(),maxCapacity:D.optional(),ec2Config:_.optional(),connectionConfig:C.optional()}).strict().refine(r.check,r.params),ge=e.object({appName:s,functionName:c,nameProvidedByFlag:e.boolean().optional(),lambdaType:e.enum(["basic","web","custom"]).optional(),ecrRepository:e.string().optional(),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),enableFunctionUrl:e.boolean().optional(),description:e.string().optional(),enableCors:e.boolean().optional(),corsOrigins:e.string().optional(),corsHeaders:e.string().optional(),corsMethods:e.array(e.enum(j)).optional(),corsCredentials:e.boolean().optional(),connectionConfig:C.optional()}).strict(),Ne=e.object({appName:s,instanceName:c,nameProvidedByFlag:e.boolean().optional(),instanceType:b.optional(),enableSSH:e.boolean().optional(),minCapacity:H.optional(),maxCapacity:D.optional(),connectionConfig:C.optional()}).strict().refine(r.check,r.params),de=e.object({appName:s,clusterName:c,serviceName:v,capacityProvider:y,containerPort:T.optional(),cpu:d.optional(),memoryLimitMiB:I(512).optional(),desiredCount:u.optional(),ec2Config:_.optional(),routing:e.union([M,e.array(M).min(1)]).optional(),docker:A.partial().optional(),nameProvidedByFlag:e.boolean().optional(),connectionConfig:C.optional()}).strict(),P=e.object({appName:s,computeName:c,nameProvidedByFlag:e.boolean().optional(),connectionConfig:C.optional()}).strict(),me=P.extend({type:e.literal("ecs"),cluster:U.optional(),services:e.array(B).min(1,o.SERVICE.MIN_REQUIRED),docker:A.partial().optional()}).strict().refine(n=>{const t=n.services.filter(i=>i.containers?.some(a=>a.port));return t.length>1?t.every(i=>(Array.isArray(i.routing)?i.routing:i.routing?[i.routing]:[]).some(R=>R.path||R.host)):!0},{message:o.SERVICE.ROUTING_REQUIRED}).refine(n=>{const t=n.services.map(i=>i.name);return new Set(t).size===t.length},{message:o.SERVICE.UNIQUE_WITHIN_CLUSTER}),pe=P.extend({type:e.literal("lambda"),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(S,o.LAMBDA.TIMEOUT.MIN).max(g,o.LAMBDA.TIMEOUT.MAX).optional(),memory:N.optional(),handler:e.string().optional(),runtime:G.optional(),environment:E.optional(),secrets:E.optional(),description:e.string().optional(),functionUrl:e.union([e.object({authType:e.enum(L).optional()}).strict(),e.literal(!1)]).optional(),eventSources:e.array(Y).optional()}).strict(),le=P.extend({type:e.literal("ec2"),instanceType:b.optional(),enableSSH:e.boolean().optional(),keyName:e.string().optional(),userData:e.string().optional(),securityGroups:e.array(e.string()).optional()}).strict(),_e=e.discriminatedUnion("type",[me,pe,le]),X=e.object({path:e.string().min(1,o.REQUIRED.ROUTING_PATH),priority:e.number().int(o.PRIORITY.INTEGER).min(1,o.PRIORITY.MIN).max(5e4,o.PRIORITY.MAX).optional()}).strict(),ye=e.object({name:e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(m.RESOURCE_NAME,o.RESOURCE_NAME),serviceType:e.enum(["web","worker"]).optional(),docker:A.partial().optional(),containerPort:T.optional(),needsDatabaseConnection:e.boolean().optional(),routing:e.union([X,e.array(X).min(1)]).optional()}).strict().superRefine((n,t)=>{n.serviceType==="worker"&&(n.containerPort!==void 0&&t.addIssue({code:"custom",path:["containerPort"],message:"Worker services are not reachable inbound; remove containerPort."}),n.routing!==void 0&&t.addIssue({code:"custom",path:["routing"],message:"Worker services have no ALB route; remove routing."}))}),be=e.object({appName:s,instanceType:e.string().optional()}).strict();export{de as AddServiceGeneratorSchema,ye as ApplicationServiceConfigSchema,X as ApplicationServiceRoutingSchema,_e as ComputeGeneratorSchema,Re as ComputeResourcePlanSchema,re as ComputeTypeSchema,ce as ContainerConfigSchema,Ne as EC2GeneratorSchema,Se as ECSGeneratorSchema,_ as Ec2ConfigSchema,y as EcsCapacityProviderSchema,U as EcsClusterConfigSchema,M as EcsRoutingConfigSchema,se as EcsScalingConfigSchema,B as EcsServiceConfigSchema,v as EcsServiceNameSchema,b as InstanceTypeSchema,Y as LambdaEventSourceSchema,ge as LambdaGeneratorSchema,N as LambdaMemorySchema,G as LambdaRuntimeSchema,be as TunnelGeneratorSchema,ae as WarmPoolSchema};
@@ -1 +1 @@
1
- export declare const GENERATOR_VERSION = "2.20.0";
1
+ export declare const GENERATOR_VERSION = "2.20.1";
@@ -1 +1 @@
1
- const E="2.20.0";export{E as GENERATOR_VERSION};
1
+ const E="2.20.1";export{E as GENERATOR_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/generator",
3
- "version": "2.20.1",
3
+ "version": "2.21.0",
4
4
  "description": "Pure infrastructure generation logic for Fjall",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -17,6 +17,10 @@
17
17
  "./detection": {
18
18
  "types": "./dist/src/detection/index.d.ts",
19
19
  "default": "./dist/src/detection/index.js"
20
+ },
21
+ "./schemas": {
22
+ "types": "./dist/src/schemas/index.d.ts",
23
+ "default": "./dist/src/schemas/index.js"
20
24
  }
21
25
  },
22
26
  "files": [
@@ -43,7 +47,7 @@
43
47
  },
44
48
  "license": "SEE LICENSE IN LICENSE",
45
49
  "dependencies": {
46
- "@fjall/util": "^2.20.1",
50
+ "@fjall/util": "^2.21.0",
47
51
  "ast-types": "^0.16.1",
48
52
  "recast": "^0.23.11",
49
53
  "ts-morph": "^28.0.0",
@@ -59,5 +63,5 @@
59
63
  "typescript-eslint": "^8.59.1",
60
64
  "vitest": "^4.1.5"
61
65
  },
62
- "gitHead": "397880361ea533450ea5c888f7e7b9593254f663"
66
+ "gitHead": "1b8902876c40697183ce8cbc25a0556b11d0ec38"
63
67
  }