@fjall/generator 0.89.5 → 0.94.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/LICENSE +50 -21
- package/README.md +28 -0
- package/dist/.minified +1 -0
- package/dist/src/ast/astCdnParser.d.ts +5 -0
- package/dist/src/ast/astCdnParser.js +1 -114
- package/dist/src/ast/astCommonParser.d.ts +6 -17
- package/dist/src/ast/astCommonParser.js +1 -351
- package/dist/src/ast/astComputeConnectionParser.d.ts +18 -0
- package/dist/src/ast/astComputeConnectionParser.js +1 -0
- package/dist/src/ast/astComputeParser.d.ts +6 -0
- package/dist/src/ast/astComputeParser.js +1 -473
- package/dist/src/ast/astComputeParserHelpers.d.ts +21 -0
- package/dist/src/ast/astComputeParserHelpers.js +1 -0
- package/dist/src/ast/astDatabaseParser.d.ts +9 -24
- package/dist/src/ast/astDatabaseParser.js +1 -275
- package/dist/src/ast/astDomainParser.d.ts +139 -0
- package/dist/src/ast/astDomainParser.js +1 -0
- package/dist/src/ast/astDynamoDBParser.d.ts +35 -0
- package/dist/src/ast/astDynamoDBParser.js +1 -0
- package/dist/src/ast/astExpressionEvaluator.d.ts +23 -0
- package/dist/src/ast/astExpressionEvaluator.js +1 -0
- package/dist/src/ast/astInfrastructureParser.d.ts +12 -49
- package/dist/src/ast/astInfrastructureParser.js +1 -552
- package/dist/src/ast/astMessagingParser.d.ts +5 -0
- package/dist/src/ast/astMessagingParser.js +1 -78
- package/dist/src/ast/astNetworkParser.d.ts +6 -0
- package/dist/src/ast/astNetworkParser.js +1 -219
- package/dist/src/ast/astPatternParser.d.ts +6 -0
- package/dist/src/ast/astPatternParser.js +1 -155
- package/dist/src/ast/astPlanConverter.d.ts +11 -0
- package/dist/src/ast/astPlanConverter.js +2 -0
- package/dist/src/ast/astStatementClassifier.d.ts +24 -0
- package/dist/src/ast/astStatementClassifier.js +1 -0
- package/dist/src/ast/astStatementQueries.d.ts +21 -0
- package/dist/src/ast/astStatementQueries.js +3 -0
- package/dist/src/ast/astStorageParser.d.ts +5 -0
- package/dist/src/ast/astStorageParser.js +1 -164
- package/dist/src/ast/astSurgicalModification.js +19 -400
- package/dist/src/ast/astTestHelpers.d.ts +635 -0
- package/dist/src/ast/astTestHelpers.js +1 -0
- package/dist/src/ast/index.d.ts +1 -0
- package/dist/src/ast/index.js +1 -6
- package/dist/src/aws/regions.js +1 -254
- package/dist/src/codemod/_internal.d.ts +12 -0
- package/dist/src/codemod/_internal.js +1 -0
- package/dist/src/codemod/edits/addResource/bodyIndex.d.ts +34 -0
- package/dist/src/codemod/edits/addResource/bodyIndex.js +1 -0
- package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +7 -0
- package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -0
- package/dist/src/codemod/edits/addResource.d.ts +9 -0
- package/dist/src/codemod/edits/addResource.js +1 -0
- package/dist/src/codemod/edits/ensureImports.d.ts +26 -0
- package/dist/src/codemod/edits/ensureImports.js +1 -0
- package/dist/src/codemod/edits/findInsertionPosition.d.ts +39 -0
- package/dist/src/codemod/edits/findInsertionPosition.js +1 -0
- package/dist/src/codemod/edits/index.d.ts +5 -0
- package/dist/src/codemod/edits/index.js +1 -0
- package/dist/src/codemod/edits/modifyResource/literalConversion.d.ts +37 -0
- package/dist/src/codemod/edits/modifyResource/literalConversion.js +1 -0
- package/dist/src/codemod/edits/modifyResource.d.ts +9 -0
- package/dist/src/codemod/edits/modifyResource.js +1 -0
- package/dist/src/codemod/edits/removeResource/commentHeuristic.d.ts +31 -0
- package/dist/src/codemod/edits/removeResource/commentHeuristic.js +1 -0
- package/dist/src/codemod/edits/removeResource/importPruning.d.ts +8 -0
- package/dist/src/codemod/edits/removeResource/importPruning.js +1 -0
- package/dist/src/codemod/edits/removeResource.d.ts +10 -0
- package/dist/src/codemod/edits/removeResource.js +1 -0
- package/dist/src/codemod/fileRewriter/builders.d.ts +57 -0
- package/dist/src/codemod/fileRewriter/builders.js +1 -0
- package/dist/src/codemod/fileRewriter/index.d.ts +4 -0
- package/dist/src/codemod/fileRewriter/index.js +1 -0
- package/dist/src/codemod/fileRewriter/locateByRange.d.ts +65 -0
- package/dist/src/codemod/fileRewriter/locateByRange.js +1 -0
- package/dist/src/codemod/fileRewriter/parse.d.ts +18 -0
- package/dist/src/codemod/fileRewriter/parse.js +2 -0
- package/dist/src/codemod/fileRewriter/print.d.ts +46 -0
- package/dist/src/codemod/fileRewriter/print.js +4 -0
- package/dist/src/codemod/historyPaths.d.ts +2 -0
- package/dist/src/codemod/historyPaths.js +1 -0
- package/dist/src/codemod/index.d.ts +7 -0
- package/dist/src/codemod/index.js +1 -0
- package/dist/src/codemod/listResources.d.ts +4 -0
- package/dist/src/codemod/listResources.js +1 -0
- package/dist/src/codemod/registry.d.ts +42 -0
- package/dist/src/codemod/registry.js +1 -0
- package/dist/src/codemod/semanticIndex/findReferences.d.ts +15 -0
- package/dist/src/codemod/semanticIndex/findReferences.js +2 -0
- package/dist/src/codemod/semanticIndex/index.d.ts +4 -0
- package/dist/src/codemod/semanticIndex/index.js +1 -0
- package/dist/src/codemod/semanticIndex/listImports.d.ts +24 -0
- package/dist/src/codemod/semanticIndex/listImports.js +1 -0
- package/dist/src/codemod/semanticIndex/locateByShape.d.ts +28 -0
- package/dist/src/codemod/semanticIndex/locateByShape.js +1 -0
- package/dist/src/codemod/semanticIndex/projectCache.d.ts +14 -0
- package/dist/src/codemod/semanticIndex/projectCache.js +1 -0
- package/dist/src/codemod/types.d.ts +172 -0
- package/dist/src/codemod/types.js +1 -0
- package/dist/src/dns/bindParser.js +2 -224
- package/dist/src/dns/bindWriter.js +3 -52
- package/dist/src/dns/domainFileGenerator.d.ts +20 -0
- package/dist/src/dns/domainFileGenerator.js +207 -0
- package/dist/src/dns/domainRecords.d.ts +164 -0
- package/dist/src/dns/domainRecords.js +1 -0
- package/dist/src/dns/index.d.ts +2 -1
- package/dist/src/dns/index.js +1 -4
- package/dist/src/dns/types.js +1 -52
- package/dist/src/generation/common.js +6 -161
- package/dist/src/generation/compute.js +82 -590
- package/dist/src/generation/database.js +12 -198
- package/dist/src/generation/generatePatternCode.d.ts +58 -0
- package/dist/src/generation/generatePatternCode.js +33 -0
- package/dist/src/generation/index.js +1 -20
- package/dist/src/generation/infrastructure.d.ts +1 -5
- package/dist/src/generation/infrastructure.js +35 -377
- package/dist/src/generation/messagingConnections.js +1 -73
- package/dist/src/generation/storage.d.ts +0 -15
- package/dist/src/generation/storage.js +35 -168
- package/dist/src/generation/storageConnections.js +1 -75
- package/dist/src/planning/generateResourceChange.d.ts +21 -0
- package/dist/src/planning/generateResourceChange.js +1 -0
- package/dist/src/planning/index.d.ts +3 -0
- package/dist/src/planning/index.js +1 -1
- package/dist/src/planning/resourceAddition.d.ts +154 -0
- package/dist/src/planning/resourceAddition.js +1 -0
- package/dist/src/planning/resourceConnections.d.ts +19 -0
- package/dist/src/planning/resourceConnections.js +1 -0
- package/dist/src/planning/resourcePlanning.js +1 -214
- package/dist/src/presets/index.js +1 -3
- package/dist/src/presets/patternTierPresets.js +1 -131
- package/dist/src/presets/storagePresets.js +1 -36
- package/dist/src/presets/tierPresets.d.ts +5 -8
- package/dist/src/presets/tierPresets.js +1 -384
- package/dist/src/presets/tierTypes.d.ts +1 -1
- package/dist/src/presets/tierTypes.js +0 -7
- package/dist/src/schemas/alarmSchemas.d.ts +19 -0
- package/dist/src/schemas/alarmSchemas.js +1 -0
- package/dist/src/schemas/applicationSchemas.d.ts +22 -6
- package/dist/src/schemas/applicationSchemas.js +1 -80
- package/dist/src/schemas/baseSchemas.d.ts +8 -3
- package/dist/src/schemas/baseSchemas.js +2 -248
- package/dist/src/schemas/cdnSchemas.js +1 -62
- package/dist/src/schemas/computeSchemas.d.ts +25 -3
- package/dist/src/schemas/computeSchemas.js +1 -727
- package/dist/src/schemas/constants.d.ts +5 -7
- package/dist/src/schemas/constants.js +1 -218
- package/dist/src/schemas/databaseSchemas.d.ts +6 -1
- package/dist/src/schemas/databaseSchemas.js +1 -366
- package/dist/src/schemas/index.js +1 -3
- package/dist/src/schemas/instanceTypeArchitecture.js +1 -75
- package/dist/src/schemas/messagingSchemas.js +1 -29
- package/dist/src/schemas/networkSchemas.js +1 -125
- package/dist/src/schemas/patternSchemas.d.ts +1 -1
- package/dist/src/schemas/patternSchemas.js +1 -294
- package/dist/src/schemas/resourceSchemas.d.ts +1 -0
- package/dist/src/schemas/resourceSchemas.js +1 -28
- package/dist/src/schemas/sharedTypes.d.ts +18 -0
- package/dist/src/schemas/sharedTypes.js +1 -0
- package/dist/src/schemas/storageSchemas.d.ts +1 -0
- package/dist/src/schemas/storageSchemas.js +1 -119
- package/dist/src/types/Result.js +1 -31
- package/dist/src/util/errorUtils.js +1 -1
- package/dist/src/validation/patterns.d.ts +9 -0
- package/dist/src/validation/patterns.js +1 -369
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +29 -9
- package/dist/src/dns/infrastructureWriter.d.ts +0 -2
- package/dist/src/dns/infrastructureWriter.js +0 -58
|
@@ -1,727 +1 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { VALIDATION_PATTERNS, VALIDATION_MESSAGES, } from "../validation/patterns.js";
|
|
3
|
-
import { COMPUTE_TYPES, ECS_CAPACITY_PROVIDERS, EC2_INSTANCE_TYPES, HTTP_METHODS, MIN_LAMBDA_MEMORY, MAX_LAMBDA_MEMORY, MIN_LAMBDA_TIMEOUT, MAX_LAMBDA_TIMEOUT, MIN_ECS_CAPACITY, MAX_ECS_CAPACITY, MAX_SCALING_CAPACITY, MAX_WARM_POOL_SIZE, constIncludes, COMPUTE_ARCHITECTURES, } from "./constants.js";
|
|
4
|
-
import { validateArchitectureMatch, getArchitectureForInstanceType, } from "./instanceTypeArchitecture.js";
|
|
5
|
-
import { optionalOrDisabled, CAPACITY_REFINEMENT, memoryLimitMiBSchema, ResourceNameSchema, AppNameSchema, PortSchema, ExtraPropertySchema, EnvironmentRecordSchema, SecretsImportRecordSchema, } from "./baseSchemas.js";
|
|
6
|
-
/** Reusable generator capacity schemas for min/max capacity. */
|
|
7
|
-
const GeneratorMinCapacitySchema = z
|
|
8
|
-
.number()
|
|
9
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MIN.INTEGER)
|
|
10
|
-
.min(MIN_ECS_CAPACITY, VALIDATION_MESSAGES.GENERATOR_CAPACITY.MIN.MIN)
|
|
11
|
-
.max(MAX_ECS_CAPACITY, VALIDATION_MESSAGES.GENERATOR_CAPACITY.MIN.MAX);
|
|
12
|
-
const GeneratorMaxCapacitySchema = z
|
|
13
|
-
.number()
|
|
14
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MAX.INTEGER)
|
|
15
|
-
.min(MIN_ECS_CAPACITY, VALIDATION_MESSAGES.GENERATOR_CAPACITY.MAX.MIN)
|
|
16
|
-
.max(MAX_ECS_CAPACITY, VALIDATION_MESSAGES.GENERATOR_CAPACITY.MAX.MAX);
|
|
17
|
-
/** Reusable Lambda memory validation with multiple-of-64 constraint. */
|
|
18
|
-
export const LambdaMemorySchema = z
|
|
19
|
-
.number()
|
|
20
|
-
.int(VALIDATION_MESSAGES.LAMBDA.MEMORY.INTEGER)
|
|
21
|
-
.min(MIN_LAMBDA_MEMORY, VALIDATION_MESSAGES.LAMBDA.MEMORY.MIN)
|
|
22
|
-
.max(MAX_LAMBDA_MEMORY, VALIDATION_MESSAGES.LAMBDA.MEMORY.MAX)
|
|
23
|
-
.refine((mem) => mem === 128 || mem % 64 === 0, VALIDATION_MESSAGES.LAMBDA.MEMORY.MULTIPLE);
|
|
24
|
-
/** Reusable ECS CPU units validation (256-4096). */
|
|
25
|
-
const EcsCpuSchema = z
|
|
26
|
-
.number()
|
|
27
|
-
.int(VALIDATION_MESSAGES.CPU.INTEGER)
|
|
28
|
-
.min(256, VALIDATION_MESSAGES.CPU.MIN)
|
|
29
|
-
.max(4096, VALIDATION_MESSAGES.CPU.MAX);
|
|
30
|
-
/** Reusable ECS desired count validation (0 to MAX_SCALING_CAPACITY). */
|
|
31
|
-
const EcsDesiredCountSchema = z
|
|
32
|
-
.number()
|
|
33
|
-
.int(VALIDATION_MESSAGES.CAPACITY.DESIRED.INTEGER)
|
|
34
|
-
.min(0, VALIDATION_MESSAGES.CAPACITY.DESIRED.MIN)
|
|
35
|
-
.max(MAX_SCALING_CAPACITY, VALIDATION_MESSAGES.CAPACITY.DESIRED.MAX);
|
|
36
|
-
export const WarmPoolSchema = z
|
|
37
|
-
.object({
|
|
38
|
-
/** Minimum number of instances to keep in the warm pool. Default: 1 */
|
|
39
|
-
minSize: z
|
|
40
|
-
.number()
|
|
41
|
-
.int(VALIDATION_MESSAGES.CAPACITY.WARM_POOL.MIN_SIZE.INTEGER)
|
|
42
|
-
.min(0, VALIDATION_MESSAGES.CAPACITY.WARM_POOL.MIN_SIZE.MIN)
|
|
43
|
-
.max(MAX_WARM_POOL_SIZE, VALIDATION_MESSAGES.CAPACITY.WARM_POOL.MIN_SIZE.MAX)
|
|
44
|
-
.optional(),
|
|
45
|
-
/** Reuse instances on scale-in instead of terminating. Default: true */
|
|
46
|
-
reuseOnScaleIn: z.boolean().optional(),
|
|
47
|
-
})
|
|
48
|
-
.strict();
|
|
49
|
-
export const Ec2ConfigSchema = z
|
|
50
|
-
.object({
|
|
51
|
-
instanceType: z.string().optional(),
|
|
52
|
-
amiHardwareType: z.enum(["ARM", "STANDARD"]).optional(),
|
|
53
|
-
minCapacity: z
|
|
54
|
-
.number()
|
|
55
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MIN.INTEGER)
|
|
56
|
-
.min(0, VALIDATION_MESSAGES.CAPACITY.MIN.MIN_0)
|
|
57
|
-
.max(MAX_SCALING_CAPACITY, VALIDATION_MESSAGES.CAPACITY.MIN.MAX)
|
|
58
|
-
.optional(),
|
|
59
|
-
maxCapacity: z
|
|
60
|
-
.number()
|
|
61
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MAX.INTEGER)
|
|
62
|
-
.min(1, VALIDATION_MESSAGES.CAPACITY.MAX.MIN_1)
|
|
63
|
-
.max(MAX_SCALING_CAPACITY, VALIDATION_MESSAGES.CAPACITY.MAX.MAX)
|
|
64
|
-
.optional(),
|
|
65
|
-
memoryLimitMiB: memoryLimitMiBSchema(128).optional(),
|
|
66
|
-
/** Warm pool configuration for faster instance start times. */
|
|
67
|
-
warmPool: WarmPoolSchema.optional(),
|
|
68
|
-
})
|
|
69
|
-
.strict()
|
|
70
|
-
.superRefine((data, ctx) => {
|
|
71
|
-
if (!data.instanceType || !data.amiHardwareType)
|
|
72
|
-
return;
|
|
73
|
-
const result = validateArchitectureMatch(data.instanceType, data.amiHardwareType);
|
|
74
|
-
if (!result.valid) {
|
|
75
|
-
ctx.addIssue({
|
|
76
|
-
code: "custom",
|
|
77
|
-
message: result.message ?? VALIDATION_MESSAGES.ARCHITECTURE_MISMATCH,
|
|
78
|
-
path: ["instanceType"],
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
.superRefine((data, ctx) => {
|
|
83
|
-
if (data.minCapacity === 0 && !data.warmPool) {
|
|
84
|
-
ctx.addIssue({
|
|
85
|
-
code: "custom",
|
|
86
|
-
message: VALIDATION_MESSAGES.CAPACITY.WARM_POOL_REQUIRED,
|
|
87
|
-
path: ["minCapacity"],
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (data.warmPool?.minSize !== undefined &&
|
|
91
|
-
data.maxCapacity !== undefined &&
|
|
92
|
-
data.warmPool.minSize > data.maxCapacity) {
|
|
93
|
-
ctx.addIssue({
|
|
94
|
-
code: "custom",
|
|
95
|
-
message: VALIDATION_MESSAGES.CAPACITY.WARM_POOL.MIN_SIZE_EXCEEDS_MAX_CAPACITY,
|
|
96
|
-
path: ["warmPool", "minSize"],
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
export const ComputeTypeSchema = z
|
|
101
|
-
.enum(COMPUTE_TYPES)
|
|
102
|
-
.describe(`Compute type must be one of: ${COMPUTE_TYPES.join(", ")}`);
|
|
103
|
-
export const EcsCapacityProviderSchema = z
|
|
104
|
-
.enum(ECS_CAPACITY_PROVIDERS)
|
|
105
|
-
.describe(`ECS capacity provider must be one of: ${ECS_CAPACITY_PROVIDERS.join(", ")}`);
|
|
106
|
-
export const InstanceTypeSchema = z
|
|
107
|
-
.string()
|
|
108
|
-
.refine((type) => constIncludes(EC2_INSTANCE_TYPES, type), {
|
|
109
|
-
message: VALIDATION_MESSAGES.INSTANCE_TYPE,
|
|
110
|
-
});
|
|
111
|
-
export const LambdaEventSourceSchema = z
|
|
112
|
-
.object({
|
|
113
|
-
sourceType: z.enum(["sqs", "dynamodb", "eventbridge"]),
|
|
114
|
-
sourceRef: z.string().describe("Name of the source resource"),
|
|
115
|
-
batchSize: z
|
|
116
|
-
.number()
|
|
117
|
-
.int(VALIDATION_MESSAGES.BATCH_SIZE.INTEGER)
|
|
118
|
-
.min(1, VALIDATION_MESSAGES.BATCH_SIZE.MIN)
|
|
119
|
-
.max(10000, VALIDATION_MESSAGES.BATCH_SIZE.MAX)
|
|
120
|
-
.optional(),
|
|
121
|
-
startingPosition: z.enum(["TRIM_HORIZON", "LATEST"]).optional(),
|
|
122
|
-
maxBatchingWindow: z
|
|
123
|
-
.number()
|
|
124
|
-
.int(VALIDATION_MESSAGES.BATCHING_WINDOW.INTEGER)
|
|
125
|
-
.min(0, VALIDATION_MESSAGES.BATCHING_WINDOW.MIN)
|
|
126
|
-
.max(300, VALIDATION_MESSAGES.BATCHING_WINDOW.MAX)
|
|
127
|
-
.optional(),
|
|
128
|
-
reportBatchItemFailures: z.boolean().optional(),
|
|
129
|
-
})
|
|
130
|
-
.strict();
|
|
131
|
-
/**
|
|
132
|
-
* Container configuration for ECS tasks.
|
|
133
|
-
* For single-container services, name is optional and auto-generated.
|
|
134
|
-
* For multi-container tasks, first container with a port gets load balancer registration.
|
|
135
|
-
*/
|
|
136
|
-
export const ContainerConfigSchema = z
|
|
137
|
-
.object({
|
|
138
|
-
/** Container name. Optional for single-container services (auto-generated). */
|
|
139
|
-
name: z
|
|
140
|
-
.string()
|
|
141
|
-
.min(1, VALIDATION_MESSAGES.REQUIRED.CONTAINER_NAME)
|
|
142
|
-
.optional(),
|
|
143
|
-
/** Container image (ECR repo name or public image URL) */
|
|
144
|
-
image: z.string().optional(),
|
|
145
|
-
/** Port the container listens on */
|
|
146
|
-
port: PortSchema.optional(),
|
|
147
|
-
/** Environment variables */
|
|
148
|
-
environment: EnvironmentRecordSchema.optional(),
|
|
149
|
-
/** Secrets imported from AWS Secrets Manager */
|
|
150
|
-
secretsImport: SecretsImportRecordSchema.optional(),
|
|
151
|
-
/** Command to run in the container */
|
|
152
|
-
command: z.array(z.string()).optional(),
|
|
153
|
-
/** Entry point for the container */
|
|
154
|
-
entryPoint: z.array(z.string()).optional(),
|
|
155
|
-
/** Whether this container is essential (default: true) */
|
|
156
|
-
essential: z.boolean().optional(),
|
|
157
|
-
/** Health check configuration */
|
|
158
|
-
healthCheck: z
|
|
159
|
-
.object({
|
|
160
|
-
command: z.array(z.string()),
|
|
161
|
-
interval: z
|
|
162
|
-
.number()
|
|
163
|
-
.int(VALIDATION_MESSAGES.HEALTH_CHECK.INTERVAL.INTEGER)
|
|
164
|
-
.min(5, VALIDATION_MESSAGES.HEALTH_CHECK.INTERVAL.MIN)
|
|
165
|
-
.max(300, VALIDATION_MESSAGES.HEALTH_CHECK.INTERVAL.MAX)
|
|
166
|
-
.optional(),
|
|
167
|
-
timeout: z
|
|
168
|
-
.number()
|
|
169
|
-
.int(VALIDATION_MESSAGES.HEALTH_CHECK.TIMEOUT.INTEGER)
|
|
170
|
-
.min(2, VALIDATION_MESSAGES.HEALTH_CHECK.TIMEOUT.MIN)
|
|
171
|
-
.max(60, VALIDATION_MESSAGES.HEALTH_CHECK.TIMEOUT.MAX)
|
|
172
|
-
.optional(),
|
|
173
|
-
retries: z
|
|
174
|
-
.number()
|
|
175
|
-
.int(VALIDATION_MESSAGES.HEALTH_CHECK.RETRIES.INTEGER)
|
|
176
|
-
.min(1, VALIDATION_MESSAGES.HEALTH_CHECK.RETRIES.MIN)
|
|
177
|
-
.max(10, VALIDATION_MESSAGES.HEALTH_CHECK.RETRIES.MAX)
|
|
178
|
-
.optional(),
|
|
179
|
-
startPeriod: z
|
|
180
|
-
.number()
|
|
181
|
-
.int(VALIDATION_MESSAGES.HEALTH_CHECK.START_PERIOD.INTEGER)
|
|
182
|
-
.min(0, VALIDATION_MESSAGES.HEALTH_CHECK.START_PERIOD.MIN)
|
|
183
|
-
.max(300, VALIDATION_MESSAGES.HEALTH_CHECK.START_PERIOD.MAX)
|
|
184
|
-
.optional(),
|
|
185
|
-
})
|
|
186
|
-
.strict()
|
|
187
|
-
.optional(),
|
|
188
|
-
/**
|
|
189
|
-
* SSM secrets to inject into the container.
|
|
190
|
-
* Secret names must start with a letter or underscore (e.g., API_KEY, db.password).
|
|
191
|
-
* Used with ssmSecretsPath on the service to construct full SSM parameter paths.
|
|
192
|
-
*/
|
|
193
|
-
ssmSecrets: z
|
|
194
|
-
.array(z
|
|
195
|
-
.string()
|
|
196
|
-
.regex(VALIDATION_PATTERNS.SECRET_NAME, VALIDATION_MESSAGES.SECRET_NAME))
|
|
197
|
-
.optional(),
|
|
198
|
-
extraProperties: z.array(ExtraPropertySchema).optional(),
|
|
199
|
-
})
|
|
200
|
-
.strict();
|
|
201
|
-
/**
|
|
202
|
-
* Service name schema.
|
|
203
|
-
* Must be a valid identifier for ECS services.
|
|
204
|
-
*/
|
|
205
|
-
export const EcsServiceNameSchema = z
|
|
206
|
-
.string()
|
|
207
|
-
.min(1, VALIDATION_MESSAGES.REQUIRED.SERVICE_NAME)
|
|
208
|
-
.max(255, VALIDATION_MESSAGES.MAX_LENGTH.SERVICE_NAME)
|
|
209
|
-
.regex(VALIDATION_PATTERNS.ECS_SERVICE_NAME, VALIDATION_MESSAGES.ECS_SERVICE_NAME);
|
|
210
|
-
/**
|
|
211
|
-
* Cluster-level configuration.
|
|
212
|
-
* Controls the shared ALB for all services.
|
|
213
|
-
*/
|
|
214
|
-
export const EcsClusterConfigSchema = z
|
|
215
|
-
.object({
|
|
216
|
-
/** Domain for HTTPS access. Creates ACM certificate + Route53 DNS. */
|
|
217
|
-
domain: z.string().optional(),
|
|
218
|
-
/**
|
|
219
|
-
* Load balancer configuration.
|
|
220
|
-
* - false: No ALB (for workers/internal services)
|
|
221
|
-
* - "public": Internet-facing ALB (default)
|
|
222
|
-
* - "internal": VPC-only ALB
|
|
223
|
-
*/
|
|
224
|
-
loadBalancer: z
|
|
225
|
-
.union([z.literal(false), z.literal("public"), z.literal("internal")])
|
|
226
|
-
.optional(),
|
|
227
|
-
/** Enable direct EC2 access without ALB. Opens container ports on security group. */
|
|
228
|
-
directAccess: z.boolean().optional(),
|
|
229
|
-
})
|
|
230
|
-
.strict()
|
|
231
|
-
.refine((data) => !(data.directAccess && data.domain), {
|
|
232
|
-
message: VALIDATION_MESSAGES.DIRECT_ACCESS.NO_DOMAIN,
|
|
233
|
-
path: ["directAccess"],
|
|
234
|
-
})
|
|
235
|
-
.refine((data) => !(data.directAccess && typeof data.loadBalancer === "string"), {
|
|
236
|
-
message: VALIDATION_MESSAGES.DIRECT_ACCESS.NO_LOAD_BALANCER,
|
|
237
|
-
})
|
|
238
|
-
.describe("Cluster-level configuration");
|
|
239
|
-
/**
|
|
240
|
-
* Routing configuration for path/host-based routing on the ALB.
|
|
241
|
-
* Required when cluster has multiple services with ports.
|
|
242
|
-
*/
|
|
243
|
-
export const EcsRoutingConfigSchema = z
|
|
244
|
-
.object({
|
|
245
|
-
/** Path pattern for routing (e.g., "/api/*", "/users/*") */
|
|
246
|
-
path: z.string().optional(),
|
|
247
|
-
/** Host header for routing (e.g., "api.example.com") */
|
|
248
|
-
host: z.string().optional(),
|
|
249
|
-
/** Priority for this routing rule (1-50000). Lower = higher priority. */
|
|
250
|
-
priority: z
|
|
251
|
-
.number()
|
|
252
|
-
.int(VALIDATION_MESSAGES.PRIORITY.INTEGER)
|
|
253
|
-
.min(1, VALIDATION_MESSAGES.PRIORITY.MIN)
|
|
254
|
-
.max(50000, VALIDATION_MESSAGES.PRIORITY.MAX)
|
|
255
|
-
.optional(),
|
|
256
|
-
/** Health check path for this service's target group. Default: "/" */
|
|
257
|
-
healthCheckPath: z.string().optional(),
|
|
258
|
-
})
|
|
259
|
-
.strict()
|
|
260
|
-
.describe("Path/host-based routing configuration");
|
|
261
|
-
/**
|
|
262
|
-
* Scaling configuration for ECS services.
|
|
263
|
-
*/
|
|
264
|
-
export const EcsScalingConfigSchema = z
|
|
265
|
-
.object({
|
|
266
|
-
desiredCount: EcsDesiredCountSchema.optional(),
|
|
267
|
-
minCapacity: z
|
|
268
|
-
.number()
|
|
269
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MIN.INTEGER)
|
|
270
|
-
.min(0, VALIDATION_MESSAGES.CAPACITY.MIN.MIN_0)
|
|
271
|
-
.max(MAX_SCALING_CAPACITY, VALIDATION_MESSAGES.CAPACITY.MIN.MAX)
|
|
272
|
-
.optional(),
|
|
273
|
-
maxCapacity: z
|
|
274
|
-
.number()
|
|
275
|
-
.int(VALIDATION_MESSAGES.CAPACITY.MAX.INTEGER)
|
|
276
|
-
.min(1, VALIDATION_MESSAGES.CAPACITY.MAX.MIN_1)
|
|
277
|
-
.max(MAX_SCALING_CAPACITY, VALIDATION_MESSAGES.CAPACITY.MAX.MAX)
|
|
278
|
-
.optional(),
|
|
279
|
-
scalingType: z.enum(["CPU", "MEMORY"]).optional(),
|
|
280
|
-
})
|
|
281
|
-
.strict()
|
|
282
|
-
.refine(CAPACITY_REFINEMENT.check, CAPACITY_REFINEMENT.params)
|
|
283
|
-
.describe("ECS service scaling configuration");
|
|
284
|
-
/**
|
|
285
|
-
* Configuration for a service in an ECS cluster.
|
|
286
|
-
* Each service gets its own task definition, scaling, and target group.
|
|
287
|
-
*/
|
|
288
|
-
export const EcsServiceConfigSchema = z
|
|
289
|
-
.object({
|
|
290
|
-
/** Service name (unique within cluster) */
|
|
291
|
-
name: EcsServiceNameSchema,
|
|
292
|
-
/** Path to Dockerfile for this service (e.g., "./Dockerfile.frontend") */
|
|
293
|
-
dockerfilePath: z.string().optional(),
|
|
294
|
-
/** Docker build target stage for multi-stage Dockerfiles (e.g., "api", "frontend") */
|
|
295
|
-
dockerTarget: z.string().optional(),
|
|
296
|
-
/** Whether this service needs database connection */
|
|
297
|
-
needsDatabaseConnection: z.boolean().optional(),
|
|
298
|
-
/** Whether this service needs storage connection */
|
|
299
|
-
needsStorageConnection: z.boolean().optional(),
|
|
300
|
-
/** Whether this service needs messaging connection */
|
|
301
|
-
needsMessagingConnection: z.boolean().optional(),
|
|
302
|
-
/** Container image (ECR repo name or public image URL) */
|
|
303
|
-
image: z.string().optional(),
|
|
304
|
-
/**
|
|
305
|
-
* Container configuration(s) for this service.
|
|
306
|
-
* For single-container services, container name is optional and auto-generated.
|
|
307
|
-
* For multi-container tasks, first container with a port is the primary container.
|
|
308
|
-
*/
|
|
309
|
-
containers: z.array(ContainerConfigSchema).optional(),
|
|
310
|
-
/** Routing rules for this service on the cluster's ALB. Single rule or array of rules. */
|
|
311
|
-
routing: z
|
|
312
|
-
.union([EcsRoutingConfigSchema, z.array(EcsRoutingConfigSchema).min(1)])
|
|
313
|
-
.optional(),
|
|
314
|
-
/** CPU units for this service's tasks (256-4096) */
|
|
315
|
-
cpu: EcsCpuSchema.optional(),
|
|
316
|
-
/** Memory in MiB for this service's tasks (512-30720) */
|
|
317
|
-
memoryLimitMiB: memoryLimitMiBSchema(512).optional(),
|
|
318
|
-
/** Desired number of tasks. Default: 2 */
|
|
319
|
-
desiredCount: EcsDesiredCountSchema.optional(),
|
|
320
|
-
/**
|
|
321
|
-
* Scaling configuration.
|
|
322
|
-
* - Omit: enabled with defaults
|
|
323
|
-
* - false: disabled
|
|
324
|
-
*/
|
|
325
|
-
scaling: optionalOrDisabled(EcsScalingConfigSchema).optional(),
|
|
326
|
-
/**
|
|
327
|
-
* Capacity provider for this service. REQUIRED.
|
|
328
|
-
* - "FARGATE": Serverless containers (default for most tiers)
|
|
329
|
-
* - "FARGATE_SPOT": Serverless with Spot pricing (cost savings)
|
|
330
|
-
* - "EC2": EC2-backed containers (for tinkerer tier or specific needs)
|
|
331
|
-
*/
|
|
332
|
-
capacityProvider: EcsCapacityProviderSchema,
|
|
333
|
-
/**
|
|
334
|
-
* EC2 capacity configuration for this service.
|
|
335
|
-
* Only used when service capacityProvider is "EC2".
|
|
336
|
-
* Creates a dedicated ASG if config differs from existing ASGs.
|
|
337
|
-
*/
|
|
338
|
-
ec2Config: Ec2ConfigSchema.optional(),
|
|
339
|
-
/**
|
|
340
|
-
* SSM Parameter Store path for secrets.
|
|
341
|
-
* If containers have ssmSecrets defined, this path is used as the base path.
|
|
342
|
-
* Format: /<app>/<cluster>/<service>
|
|
343
|
-
*/
|
|
344
|
-
ssmSecretsPath: z
|
|
345
|
-
.string()
|
|
346
|
-
.regex(VALIDATION_PATTERNS.SSM_PATH, VALIDATION_MESSAGES.SSM_PATH)
|
|
347
|
-
.optional(),
|
|
348
|
-
extraProperties: z.array(ExtraPropertySchema).optional(),
|
|
349
|
-
})
|
|
350
|
-
.strict()
|
|
351
|
-
.describe("Configuration for a service in an ECS cluster");
|
|
352
|
-
/**
|
|
353
|
-
* Compute resource plan schema.
|
|
354
|
-
* Used by the code generator to produce infrastructure code.
|
|
355
|
-
*/
|
|
356
|
-
export const ComputeResourcePlanSchema = z
|
|
357
|
-
.object({
|
|
358
|
-
name: ResourceNameSchema,
|
|
359
|
-
type: ComputeTypeSchema,
|
|
360
|
-
needsConnection: z.boolean(),
|
|
361
|
-
connectedDatabase: z.array(z.string()).optional(),
|
|
362
|
-
connectedStorage: z.array(z.string()).optional(),
|
|
363
|
-
connectedMessaging: z.array(z.string()).optional(),
|
|
364
|
-
// ECS-specific (multi-service structure)
|
|
365
|
-
/** Cluster configuration (domain, loadBalancer, directAccess) */
|
|
366
|
-
cluster: EcsClusterConfigSchema.optional(),
|
|
367
|
-
/** Services in this cluster. Each service specifies its own capacityProvider. */
|
|
368
|
-
services: z.array(EcsServiceConfigSchema).optional(),
|
|
369
|
-
dockerfilePath: z.string().optional(),
|
|
370
|
-
// Lambda-specific
|
|
371
|
-
/** Deployment type: "container" (ECR image) or "code" (Code.fromAsset) */
|
|
372
|
-
deployment: z.enum(["container", "code"]).optional(),
|
|
373
|
-
/** Path to Lambda code directory (for code deployment) */
|
|
374
|
-
codePath: z.string().optional(),
|
|
375
|
-
timeout: z
|
|
376
|
-
.number()
|
|
377
|
-
.int(VALIDATION_MESSAGES.LAMBDA.TIMEOUT.INTEGER)
|
|
378
|
-
.min(MIN_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MIN)
|
|
379
|
-
.max(MAX_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MAX)
|
|
380
|
-
.optional(),
|
|
381
|
-
memory: LambdaMemorySchema.optional(),
|
|
382
|
-
handler: z.string().optional(),
|
|
383
|
-
runtime: z.string().optional(),
|
|
384
|
-
environment: EnvironmentRecordSchema.optional(),
|
|
385
|
-
secrets: EnvironmentRecordSchema.optional(),
|
|
386
|
-
eventSources: z.array(LambdaEventSourceSchema).optional(),
|
|
387
|
-
/** Lambda function URL configuration for direct HTTP access */
|
|
388
|
-
functionUrl: z
|
|
389
|
-
.object({
|
|
390
|
-
authType: z.enum(["NONE", "AWS_IAM"]),
|
|
391
|
-
})
|
|
392
|
-
.strict()
|
|
393
|
-
.optional(),
|
|
394
|
-
/** Lambda function description */
|
|
395
|
-
description: z.string().optional(),
|
|
396
|
-
/** EventBridge schedule expression (e.g. "rate(5 minutes)") */
|
|
397
|
-
scheduleExpression: z.string().optional(),
|
|
398
|
-
/** CPU architecture (e.g. "ARM_64", "X86_64") */
|
|
399
|
-
architecture: z.enum(COMPUTE_ARCHITECTURES).optional(),
|
|
400
|
-
/** Ephemeral storage size in MiB (512-10240) */
|
|
401
|
-
ephemeralStorageSize: z
|
|
402
|
-
.number()
|
|
403
|
-
.int(VALIDATION_MESSAGES.EPHEMERAL_STORAGE.INTEGER)
|
|
404
|
-
.min(512, VALIDATION_MESSAGES.EPHEMERAL_STORAGE.MIN)
|
|
405
|
-
.max(10240, VALIDATION_MESSAGES.EPHEMERAL_STORAGE.MAX)
|
|
406
|
-
.optional(),
|
|
407
|
-
/** Override auto-generated function name */
|
|
408
|
-
functionName: z.string().optional(),
|
|
409
|
-
/** SSM Parameter Store secret names (Lambda-specific, string array) */
|
|
410
|
-
ssmSecrets: z.array(z.string()).optional(),
|
|
411
|
-
/** SSM Parameter Store path prefix for secrets */
|
|
412
|
-
ssmSecretsPath: z.string().optional(),
|
|
413
|
-
// EC2-specific (standalone EC2, not ECS EC2)
|
|
414
|
-
instanceType: InstanceTypeSchema.optional(),
|
|
415
|
-
enableSSH: z.boolean().optional(),
|
|
416
|
-
keyName: z.string().optional(),
|
|
417
|
-
userData: z.string().optional(),
|
|
418
|
-
securityGroups: z.array(z.string()).optional(),
|
|
419
|
-
// Round-trip preservation
|
|
420
|
-
variableName: z.string().optional(),
|
|
421
|
-
extraProperties: z.array(ExtraPropertySchema).optional(),
|
|
422
|
-
})
|
|
423
|
-
.strict()
|
|
424
|
-
.superRefine((data, ctx) => {
|
|
425
|
-
// ECS with EC2 services: validate instance types match ARM architecture for Docker builds.
|
|
426
|
-
// Skip when amiHardwareType is explicitly set — the user is deliberately choosing the architecture.
|
|
427
|
-
if (data.type === "ecs" && data.services) {
|
|
428
|
-
for (let i = 0; i < data.services.length; i++) {
|
|
429
|
-
const service = data.services[i];
|
|
430
|
-
if (service?.ec2Config?.instanceType &&
|
|
431
|
-
!service.ec2Config.amiHardwareType) {
|
|
432
|
-
const arch = getArchitectureForInstanceType(service.ec2Config.instanceType);
|
|
433
|
-
if (arch !== "ARM") {
|
|
434
|
-
ctx.addIssue({
|
|
435
|
-
code: "custom",
|
|
436
|
-
message: `Service '${service.name ?? i}' uses instance type '${service.ec2Config.instanceType}' (x86), ` +
|
|
437
|
-
`which requires Docker images built for linux/amd64. The default build targets linux/arm64 (Graviton). ` +
|
|
438
|
-
`Use a Graviton instance type (t4g, c6g, r6g, m6g, etc.) or set amiHardwareType: "STANDARD" to acknowledge x86.`,
|
|
439
|
-
path: ["services", i, "ec2Config", "instanceType"],
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
// Container Lambda: warn when explicit X86_64 is set without the matching Docker platform
|
|
446
|
-
if (data.type === "lambda" &&
|
|
447
|
-
data.deployment === "container" &&
|
|
448
|
-
data.architecture === "X86_64") {
|
|
449
|
-
ctx.addIssue({
|
|
450
|
-
code: "custom",
|
|
451
|
-
message: `Lambda architecture 'X86_64' requires Docker images built for linux/amd64, ` +
|
|
452
|
-
`but the default build targets linux/arm64. Use 'ARM_64' for Graviton Lambdas (recommended) ` +
|
|
453
|
-
`or ensure your build pipeline targets linux/amd64.`,
|
|
454
|
-
path: ["architecture"],
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
export const ECSGeneratorSchema = z
|
|
459
|
-
.object({
|
|
460
|
-
appName: AppNameSchema,
|
|
461
|
-
clusterName: ResourceNameSchema,
|
|
462
|
-
containerPort: PortSchema,
|
|
463
|
-
nameProvidedByFlag: z.boolean().optional(),
|
|
464
|
-
ecrRepository: z.string().optional(),
|
|
465
|
-
capacityProvider: EcsCapacityProviderSchema.optional(),
|
|
466
|
-
useAppEcr: z.boolean().optional(),
|
|
467
|
-
desiredCount: EcsDesiredCountSchema.optional(),
|
|
468
|
-
cpu: EcsCpuSchema.optional(),
|
|
469
|
-
memoryLimitMiB: memoryLimitMiBSchema(512).optional(),
|
|
470
|
-
minCapacity: GeneratorMinCapacitySchema.optional(),
|
|
471
|
-
maxCapacity: GeneratorMaxCapacitySchema.optional(),
|
|
472
|
-
ec2Config: Ec2ConfigSchema.optional(),
|
|
473
|
-
connectionConfig: z
|
|
474
|
-
.object({
|
|
475
|
-
connectToDatabase: z.array(z.string()).optional(),
|
|
476
|
-
})
|
|
477
|
-
.strict()
|
|
478
|
-
.optional(),
|
|
479
|
-
})
|
|
480
|
-
.strict()
|
|
481
|
-
.refine(CAPACITY_REFINEMENT.check, CAPACITY_REFINEMENT.params);
|
|
482
|
-
export const LambdaGeneratorSchema = z
|
|
483
|
-
.object({
|
|
484
|
-
appName: AppNameSchema,
|
|
485
|
-
functionName: ResourceNameSchema,
|
|
486
|
-
nameProvidedByFlag: z.boolean().optional(),
|
|
487
|
-
lambdaType: z.enum(["basic", "web", "custom"]).optional(),
|
|
488
|
-
ecrRepository: z.string().optional(),
|
|
489
|
-
timeout: z
|
|
490
|
-
.number()
|
|
491
|
-
.int(VALIDATION_MESSAGES.LAMBDA.TIMEOUT.INTEGER)
|
|
492
|
-
.min(MIN_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MIN)
|
|
493
|
-
.max(MAX_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MAX)
|
|
494
|
-
.optional(),
|
|
495
|
-
memory: LambdaMemorySchema.optional(),
|
|
496
|
-
enableFunctionUrl: z.boolean().optional(),
|
|
497
|
-
description: z.string().optional(),
|
|
498
|
-
enableCors: z.boolean().optional(),
|
|
499
|
-
corsOrigins: z.string().optional(),
|
|
500
|
-
corsHeaders: z.string().optional(),
|
|
501
|
-
corsMethods: z.array(z.enum(HTTP_METHODS)).optional(),
|
|
502
|
-
corsCredentials: z.boolean().optional(),
|
|
503
|
-
connectionConfig: z
|
|
504
|
-
.object({
|
|
505
|
-
connectToDatabase: z.array(z.string()).optional(),
|
|
506
|
-
})
|
|
507
|
-
.strict()
|
|
508
|
-
.optional(),
|
|
509
|
-
})
|
|
510
|
-
.strict();
|
|
511
|
-
export const EC2GeneratorSchema = z
|
|
512
|
-
.object({
|
|
513
|
-
appName: AppNameSchema,
|
|
514
|
-
instanceName: ResourceNameSchema,
|
|
515
|
-
nameProvidedByFlag: z.boolean().optional(),
|
|
516
|
-
instanceType: InstanceTypeSchema.optional(),
|
|
517
|
-
enableSSH: z.boolean().optional(),
|
|
518
|
-
minCapacity: GeneratorMinCapacitySchema.optional(),
|
|
519
|
-
maxCapacity: GeneratorMaxCapacitySchema.optional(),
|
|
520
|
-
connectionConfig: z
|
|
521
|
-
.object({
|
|
522
|
-
connectToDatabase: z.array(z.string()).optional(),
|
|
523
|
-
})
|
|
524
|
-
.strict()
|
|
525
|
-
.optional(),
|
|
526
|
-
})
|
|
527
|
-
.strict()
|
|
528
|
-
.refine(CAPACITY_REFINEMENT.check, CAPACITY_REFINEMENT.params);
|
|
529
|
-
/**
|
|
530
|
-
* Schema for adding a service to an existing ECS cluster.
|
|
531
|
-
* Used by the `fjall add service` command.
|
|
532
|
-
*/
|
|
533
|
-
export const AddServiceGeneratorSchema = z
|
|
534
|
-
.object({
|
|
535
|
-
appName: AppNameSchema,
|
|
536
|
-
clusterName: ResourceNameSchema,
|
|
537
|
-
serviceName: EcsServiceNameSchema,
|
|
538
|
-
capacityProvider: EcsCapacityProviderSchema,
|
|
539
|
-
containerPort: PortSchema.optional(),
|
|
540
|
-
cpu: EcsCpuSchema.optional(),
|
|
541
|
-
memoryLimitMiB: memoryLimitMiBSchema(512).optional(),
|
|
542
|
-
desiredCount: EcsDesiredCountSchema.optional(),
|
|
543
|
-
ec2Config: Ec2ConfigSchema.optional(),
|
|
544
|
-
routing: z
|
|
545
|
-
.union([EcsRoutingConfigSchema, z.array(EcsRoutingConfigSchema).min(1)])
|
|
546
|
-
.optional(),
|
|
547
|
-
dockerfilePath: z.string().optional(),
|
|
548
|
-
dockerTarget: z.string().optional(),
|
|
549
|
-
nameProvidedByFlag: z.boolean().optional(),
|
|
550
|
-
connectionConfig: z
|
|
551
|
-
.object({
|
|
552
|
-
connectToDatabase: z.array(z.string()).optional(),
|
|
553
|
-
})
|
|
554
|
-
.strict()
|
|
555
|
-
.optional(),
|
|
556
|
-
})
|
|
557
|
-
.strict();
|
|
558
|
-
// Compute generator schemas - discriminated union by type
|
|
559
|
-
const ComputeGeneratorBaseSchema = z
|
|
560
|
-
.object({
|
|
561
|
-
appName: AppNameSchema,
|
|
562
|
-
computeName: ResourceNameSchema,
|
|
563
|
-
nameProvidedByFlag: z.boolean().optional(),
|
|
564
|
-
connectionConfig: z
|
|
565
|
-
.object({
|
|
566
|
-
connectToDatabase: z.array(z.string()).optional(),
|
|
567
|
-
})
|
|
568
|
-
.strict()
|
|
569
|
-
.optional(),
|
|
570
|
-
})
|
|
571
|
-
.strict();
|
|
572
|
-
/**
|
|
573
|
-
* ECS-specific schema.
|
|
574
|
-
* Supports multi-service clusters with shared ALB.
|
|
575
|
-
*
|
|
576
|
-
* @example
|
|
577
|
-
* // Single service
|
|
578
|
-
* { type: "ecs", services: [{ name: "web", containers: [{ port: 3000 }] }] }
|
|
579
|
-
*
|
|
580
|
-
* @example
|
|
581
|
-
* // Multi-service with routing
|
|
582
|
-
* {
|
|
583
|
-
* type: "ecs",
|
|
584
|
-
* cluster: { domain: "api.example.com" },
|
|
585
|
-
* services: [
|
|
586
|
-
* { name: "users", containers: [{ port: 3000 }], routing: { path: "/users/*" } },
|
|
587
|
-
* { name: "orders", containers: [{ port: 3001 }], routing: { path: "/orders/*" } }
|
|
588
|
-
* ]
|
|
589
|
-
* }
|
|
590
|
-
*
|
|
591
|
-
* @example
|
|
592
|
-
* // Worker cluster (no ALB)
|
|
593
|
-
* {
|
|
594
|
-
* type: "ecs",
|
|
595
|
-
* cluster: { loadBalancer: false },
|
|
596
|
-
* services: [{ name: "processor" }]
|
|
597
|
-
* }
|
|
598
|
-
*/
|
|
599
|
-
const EcsComputeGeneratorSchema = ComputeGeneratorBaseSchema.extend({
|
|
600
|
-
type: z.literal("ecs"),
|
|
601
|
-
/**
|
|
602
|
-
* Cluster configuration.
|
|
603
|
-
* Controls the shared ALB for all services.
|
|
604
|
-
*/
|
|
605
|
-
cluster: EcsClusterConfigSchema.optional(),
|
|
606
|
-
/**
|
|
607
|
-
* Services in this cluster.
|
|
608
|
-
* Each service gets its own task definition, scaling, and target group.
|
|
609
|
-
* Each service MUST specify its own capacityProvider.
|
|
610
|
-
*/
|
|
611
|
-
services: z
|
|
612
|
-
.array(EcsServiceConfigSchema)
|
|
613
|
-
.min(1, VALIDATION_MESSAGES.SERVICE.MIN_REQUIRED),
|
|
614
|
-
/** Path to Dockerfile for building custom image */
|
|
615
|
-
dockerfilePath: z.string().optional(),
|
|
616
|
-
})
|
|
617
|
-
.strict()
|
|
618
|
-
.refine((data) => {
|
|
619
|
-
// Validate: multiple services with ports require routing config
|
|
620
|
-
const servicesWithPorts = data.services.filter((s) => s.containers?.some((c) => c.port));
|
|
621
|
-
if (servicesWithPorts.length > 1) {
|
|
622
|
-
return servicesWithPorts.every((s) => {
|
|
623
|
-
const rules = Array.isArray(s.routing)
|
|
624
|
-
? s.routing
|
|
625
|
-
: s.routing
|
|
626
|
-
? [s.routing]
|
|
627
|
-
: [];
|
|
628
|
-
return rules.some((r) => r.path || r.host);
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
return true;
|
|
632
|
-
}, { message: VALIDATION_MESSAGES.SERVICE.ROUTING_REQUIRED })
|
|
633
|
-
.refine((data) => {
|
|
634
|
-
// Validate: unique service names
|
|
635
|
-
const names = data.services.map((s) => s.name);
|
|
636
|
-
return new Set(names).size === names.length;
|
|
637
|
-
}, { message: VALIDATION_MESSAGES.SERVICE.UNIQUE_WITHIN_CLUSTER });
|
|
638
|
-
/**
|
|
639
|
-
* Lambda-specific schema.
|
|
640
|
-
*/
|
|
641
|
-
const LambdaComputeGeneratorSchema = ComputeGeneratorBaseSchema.extend({
|
|
642
|
-
type: z.literal("lambda"),
|
|
643
|
-
timeout: z
|
|
644
|
-
.number()
|
|
645
|
-
.int(VALIDATION_MESSAGES.LAMBDA.TIMEOUT.INTEGER)
|
|
646
|
-
.min(MIN_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MIN)
|
|
647
|
-
.max(MAX_LAMBDA_TIMEOUT, VALIDATION_MESSAGES.LAMBDA.TIMEOUT.MAX)
|
|
648
|
-
.optional(),
|
|
649
|
-
memory: LambdaMemorySchema.optional(),
|
|
650
|
-
handler: z.string().optional(),
|
|
651
|
-
runtime: z.string().optional(),
|
|
652
|
-
environment: EnvironmentRecordSchema.optional(),
|
|
653
|
-
secrets: EnvironmentRecordSchema.optional(),
|
|
654
|
-
description: z.string().optional(),
|
|
655
|
-
functionUrl: z
|
|
656
|
-
.union([
|
|
657
|
-
z.object({ authType: z.enum(["AWS_IAM", "NONE"]).optional() }).strict(),
|
|
658
|
-
z.literal(false),
|
|
659
|
-
])
|
|
660
|
-
.optional(),
|
|
661
|
-
eventSources: z.array(LambdaEventSourceSchema).optional(),
|
|
662
|
-
}).strict();
|
|
663
|
-
/**
|
|
664
|
-
* EC2 (standalone instance) specific schema.
|
|
665
|
-
*/
|
|
666
|
-
const Ec2ComputeGeneratorSchema = ComputeGeneratorBaseSchema.extend({
|
|
667
|
-
type: z.literal("ec2"),
|
|
668
|
-
instanceType: InstanceTypeSchema.optional(),
|
|
669
|
-
enableSSH: z.boolean().optional(),
|
|
670
|
-
keyName: z.string().optional(),
|
|
671
|
-
userData: z.string().optional(),
|
|
672
|
-
securityGroups: z.array(z.string()).optional(),
|
|
673
|
-
}).strict();
|
|
674
|
-
/**
|
|
675
|
-
* Compute generator schema using discriminated union.
|
|
676
|
-
* Ensures type-specific fields are only allowed for the correct compute type.
|
|
677
|
-
*/
|
|
678
|
-
export const ComputeGeneratorSchema = z.discriminatedUnion("type", [
|
|
679
|
-
EcsComputeGeneratorSchema,
|
|
680
|
-
LambdaComputeGeneratorSchema,
|
|
681
|
-
Ec2ComputeGeneratorSchema,
|
|
682
|
-
]);
|
|
683
|
-
/**
|
|
684
|
-
* Simple routing configuration for application generator services.
|
|
685
|
-
* Matches what the UI sends (path is required, priority optional).
|
|
686
|
-
*/
|
|
687
|
-
export const ApplicationServiceRoutingSchema = z
|
|
688
|
-
.object({
|
|
689
|
-
/** Path pattern for routing (e.g., "/api/*", "/*") */
|
|
690
|
-
path: z.string().min(1, VALIDATION_MESSAGES.REQUIRED.ROUTING_PATH),
|
|
691
|
-
/** Priority for this routing rule (lower = higher precedence) */
|
|
692
|
-
priority: z
|
|
693
|
-
.number()
|
|
694
|
-
.int(VALIDATION_MESSAGES.PRIORITY.INTEGER)
|
|
695
|
-
.min(1, VALIDATION_MESSAGES.PRIORITY.MIN)
|
|
696
|
-
.max(50000, VALIDATION_MESSAGES.PRIORITY.MAX)
|
|
697
|
-
.optional(),
|
|
698
|
-
})
|
|
699
|
-
.strict();
|
|
700
|
-
/**
|
|
701
|
-
* Service configuration for application generator.
|
|
702
|
-
* Used to configure multi-service ECS applications.
|
|
703
|
-
*/
|
|
704
|
-
export const ApplicationServiceConfigSchema = z
|
|
705
|
-
.object({
|
|
706
|
-
name: z
|
|
707
|
-
.string()
|
|
708
|
-
.min(1, VALIDATION_MESSAGES.REQUIRED.SERVICE_NAME)
|
|
709
|
-
.max(255, VALIDATION_MESSAGES.MAX_LENGTH.SERVICE_NAME)
|
|
710
|
-
.regex(VALIDATION_PATTERNS.RESOURCE_NAME, VALIDATION_MESSAGES.RESOURCE_NAME),
|
|
711
|
-
dockerfilePath: z.string().optional(),
|
|
712
|
-
containerPort: PortSchema.optional(),
|
|
713
|
-
needsDatabaseConnection: z.boolean().optional(),
|
|
714
|
-
routing: z
|
|
715
|
-
.union([
|
|
716
|
-
ApplicationServiceRoutingSchema,
|
|
717
|
-
z.array(ApplicationServiceRoutingSchema).min(1),
|
|
718
|
-
])
|
|
719
|
-
.optional(),
|
|
720
|
-
})
|
|
721
|
-
.strict();
|
|
722
|
-
export const TunnelGeneratorSchema = z
|
|
723
|
-
.object({
|
|
724
|
-
appName: AppNameSchema,
|
|
725
|
-
instanceType: z.string().optional(),
|
|
726
|
-
})
|
|
727
|
-
.strict();
|
|
1
|
+
import{z as e}from"zod";import{VALIDATION_PATTERNS as l,VALIDATION_MESSAGES as o}from"../validation/patterns.js";import{COMPUTE_TYPES as _,ECS_CAPACITY_PROVIDERS as P,EC2_INSTANCE_TYPES as v,HTTP_METHODS as U,MIN_LAMBDA_MEMORY as X,MAX_LAMBDA_MEMORY as B,MIN_LAMBDA_TIMEOUT as M,MAX_LAMBDA_TIMEOUT as u,MIN_ECS_CAPACITY as h,MAX_ECS_CAPACITY as f,MAX_SCALING_CAPACITY as p,MAX_WARM_POOL_SIZE as j,constIncludes as W,COMPUTE_ARCHITECTURES as k}from"./constants.js";import{validateArchitectureMatch as w,getArchitectureForInstanceType as V}from"./instanceTypeArchitecture.js";import{optionalOrDisabled as K,CAPACITY_REFINEMENT as r,memoryLimitMiBSchema as E,ResourceNameSchema as c,AppNameSchema as s,PortSchema as A,ExtraPropertySchema as g,EnvironmentRecordSchema as m,SecretsImportRecordSchema as F}from"./baseSchemas.js";import{EcsServiceAlarmConfigSchema as Z,LambdaAlarmConfigSchema as z}from"./alarmSchemas.js";const O=e.number().int(o.CAPACITY.MIN.INTEGER).min(h,o.GENERATOR_CAPACITY.MIN.MIN).max(f,o.GENERATOR_CAPACITY.MIN.MAX),x=e.number().int(o.CAPACITY.MAX.INTEGER).min(h,o.GENERATOR_CAPACITY.MAX.MIN).max(f,o.GENERATOR_CAPACITY.MAX.MAX),R=e.number().int(o.LAMBDA.MEMORY.INTEGER).min(X,o.LAMBDA.MEMORY.MIN).max(B,o.LAMBDA.MEMORY.MAX).refine(t=>t===128||t%64===0,o.LAMBDA.MEMORY.MULTIPLE),S=e.number().int(o.CPU.INTEGER).min(256,o.CPU.MIN).max(4096,o.CPU.MAX),C=e.number().int(o.CAPACITY.DESIRED.INTEGER).min(0,o.CAPACITY.DESIRED.MIN).max(p,o.CAPACITY.DESIRED.MAX),Q=e.object({minSize:e.number().int(o.CAPACITY.WARM_POOL.MIN_SIZE.INTEGER).min(0,o.CAPACITY.WARM_POOL.MIN_SIZE.MIN).max(j,o.CAPACITY.WARM_POOL.MIN_SIZE.MAX).optional(),reuseOnScaleIn:e.boolean().optional()}).strict(),N=e.object({instanceType:e.string().optional(),amiHardwareType:e.enum(["ARM","STANDARD"]).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:E(128).optional(),warmPool:Q.optional()}).strict().superRefine((t,i)=>{if(!t.instanceType||!t.amiHardwareType)return;const n=w(t.instanceType,t.amiHardwareType);n.valid||i.addIssue({code:"custom",message:n.message??o.ARCHITECTURE_MISMATCH,path:["instanceType"]})}).superRefine((t,i)=>{t.minCapacity===0&&!t.warmPool&&i.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL_REQUIRED,path:["minCapacity"]}),t.warmPool?.minSize!==void 0&&t.maxCapacity!==void 0&&t.warmPool.minSize>t.maxCapacity&&i.addIssue({code:"custom",message:o.CAPACITY.WARM_POOL.MIN_SIZE_EXCEEDS_MAX_CAPACITY,path:["warmPool","minSize"]})}),$=e.enum(_).describe(`Compute type must be one of: ${_.join(", ")}`),d=e.enum(P).describe(`ECS capacity provider must be one of: ${P.join(", ")}`),b=e.string().refine(t=>W(v,t),{message:o.INSTANCE_TYPE}),H=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(),q=e.object({name:e.string().min(1,o.REQUIRED.CONTAINER_NAME).optional(),image:e.string().optional(),port:A.optional(),environment:m.optional(),secretsImport:F.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(l.SECRET_NAME,o.SECRET_NAME)).optional(),extraProperties:e.array(g).optional()}).strict(),L=e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(l.ECS_SERVICE_NAME,o.ECS_SERVICE_NAME),D=e.object({domain:e.string().optional(),loadBalancer:e.union([e.literal(!1),e.literal("public"),e.literal("internal")]).optional(),directAccess:e.boolean().optional()}).strict().refine(t=>!(t.directAccess&&t.domain),{message:o.DIRECT_ACCESS.NO_DOMAIN,path:["directAccess"]}).refine(t=>!(t.directAccess&&typeof t.loadBalancer=="string"),{message:o.DIRECT_ACCESS.NO_LOAD_BALANCER}).describe("Cluster-level configuration"),I=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"),J=e.object({desiredCount:C.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(["CPU","MEMORY"]).optional()}).strict().refine(r.check,r.params).describe("ECS service scaling configuration"),G=e.object({name:L,dockerfilePath:e.string().optional(),dockerTarget:e.string().optional(),needsDatabaseConnection:e.boolean().optional(),needsStorageConnection:e.boolean().optional(),needsMessagingConnection:e.boolean().optional(),image:e.string().optional(),containers:e.array(q).optional(),routing:e.union([I,e.array(I).min(1)]).optional(),cpu:S.optional(),memoryLimitMiB:E(512).optional(),desiredCount:C.optional(),scaling:K(J).optional(),capacityProvider:d,ec2Config:N.optional(),ssmSecretsPath:e.string().regex(l.SSM_PATH,o.SSM_PATH).optional(),extraProperties:e.array(g).optional(),alarms:Z.optional()}).strict().describe("Configuration for a service in an ECS cluster"),pe=e.object({name:c,type:$,needsConnection:e.boolean(),connectedDatabase:e.array(e.string()).optional(),connectedStorage:e.array(e.string()).optional(),connectedMessaging:e.array(e.string()).optional(),cluster:D.optional(),services:e.array(G).optional(),dockerfilePath:e.string().optional(),deployment:e.enum(["container","code"]).optional(),codePath:e.string().optional(),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(M,o.LAMBDA.TIMEOUT.MIN).max(u,o.LAMBDA.TIMEOUT.MAX).optional(),memory:R.optional(),handler:e.string().optional(),runtime:e.string().optional(),environment:m.optional(),secrets:m.optional(),eventSources:e.array(H).optional(),functionUrl:e.object({authType:e.enum(["NONE","AWS_IAM"])}).strict().optional(),description:e.string().optional(),scheduleExpression:e.string().optional(),architecture:e.enum(k).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:z.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(g).optional()}).strict().superRefine((t,i)=>{if(t.type==="ecs"&&t.services)for(let n=0;n<t.services.length;n++){const a=t.services[n];a?.ec2Config?.instanceType&&!a.ec2Config.amiHardwareType&&V(a.ec2Config.instanceType)!=="ARM"&&i.addIssue({code:"custom",message:`Service '${a.name??n}' 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",n,"ec2Config","instanceType"]})}t.type==="lambda"&&t.deployment==="container"&&t.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"]})}),me=e.object({appName:s,clusterName:c,containerPort:A,nameProvidedByFlag:e.boolean().optional(),ecrRepository:e.string().optional(),capacityProvider:d.optional(),useAppEcr:e.boolean().optional(),desiredCount:C.optional(),cpu:S.optional(),memoryLimitMiB:E(512).optional(),minCapacity:O.optional(),maxCapacity:x.optional(),ec2Config:N.optional(),connectionConfig:e.object({connectToDatabase:e.array(e.string()).optional()}).strict().optional()}).strict().refine(r.check,r.params),le=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(M,o.LAMBDA.TIMEOUT.MIN).max(u,o.LAMBDA.TIMEOUT.MAX).optional(),memory:R.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(U)).optional(),corsCredentials:e.boolean().optional(),connectionConfig:e.object({connectToDatabase:e.array(e.string()).optional()}).strict().optional()}).strict(),Ee=e.object({appName:s,instanceName:c,nameProvidedByFlag:e.boolean().optional(),instanceType:b.optional(),enableSSH:e.boolean().optional(),minCapacity:O.optional(),maxCapacity:x.optional(),connectionConfig:e.object({connectToDatabase:e.array(e.string()).optional()}).strict().optional()}).strict().refine(r.check,r.params),Ae=e.object({appName:s,clusterName:c,serviceName:L,capacityProvider:d,containerPort:A.optional(),cpu:S.optional(),memoryLimitMiB:E(512).optional(),desiredCount:C.optional(),ec2Config:N.optional(),routing:e.union([I,e.array(I).min(1)]).optional(),dockerfilePath:e.string().optional(),dockerTarget:e.string().optional(),nameProvidedByFlag:e.boolean().optional(),connectionConfig:e.object({connectToDatabase:e.array(e.string()).optional()}).strict().optional()}).strict(),y=e.object({appName:s,computeName:c,nameProvidedByFlag:e.boolean().optional(),connectionConfig:e.object({connectToDatabase:e.array(e.string()).optional()}).strict().optional()}).strict(),ee=y.extend({type:e.literal("ecs"),cluster:D.optional(),services:e.array(G).min(1,o.SERVICE.MIN_REQUIRED),dockerfilePath:e.string().optional()}).strict().refine(t=>{const i=t.services.filter(n=>n.containers?.some(a=>a.port));return i.length>1?i.every(n=>(Array.isArray(n.routing)?n.routing:n.routing?[n.routing]:[]).some(T=>T.path||T.host)):!0},{message:o.SERVICE.ROUTING_REQUIRED}).refine(t=>{const i=t.services.map(n=>n.name);return new Set(i).size===i.length},{message:o.SERVICE.UNIQUE_WITHIN_CLUSTER}),oe=y.extend({type:e.literal("lambda"),timeout:e.number().int(o.LAMBDA.TIMEOUT.INTEGER).min(M,o.LAMBDA.TIMEOUT.MIN).max(u,o.LAMBDA.TIMEOUT.MAX).optional(),memory:R.optional(),handler:e.string().optional(),runtime:e.string().optional(),environment:m.optional(),secrets:m.optional(),description:e.string().optional(),functionUrl:e.union([e.object({authType:e.enum(["AWS_IAM","NONE"]).optional()}).strict(),e.literal(!1)]).optional(),eventSources:e.array(H).optional()}).strict(),te=y.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(),Ce=e.discriminatedUnion("type",[ee,oe,te]),Y=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(),Ie=e.object({name:e.string().min(1,o.REQUIRED.SERVICE_NAME).max(255,o.MAX_LENGTH.SERVICE_NAME).regex(l.RESOURCE_NAME,o.RESOURCE_NAME),dockerfilePath:e.string().optional(),containerPort:A.optional(),needsDatabaseConnection:e.boolean().optional(),routing:e.union([Y,e.array(Y).min(1)]).optional()}).strict(),Te=e.object({appName:s,instanceType:e.string().optional()}).strict();export{Ae as AddServiceGeneratorSchema,Ie as ApplicationServiceConfigSchema,Y as ApplicationServiceRoutingSchema,Ce as ComputeGeneratorSchema,pe as ComputeResourcePlanSchema,$ as ComputeTypeSchema,q as ContainerConfigSchema,Ee as EC2GeneratorSchema,me as ECSGeneratorSchema,N as Ec2ConfigSchema,d as EcsCapacityProviderSchema,D as EcsClusterConfigSchema,I as EcsRoutingConfigSchema,J as EcsScalingConfigSchema,G as EcsServiceConfigSchema,L as EcsServiceNameSchema,b as InstanceTypeSchema,H as LambdaEventSourceSchema,le as LambdaGeneratorSchema,R as LambdaMemorySchema,Te as TunnelGeneratorSchema,Q as WarmPoolSchema};
|