@stacksjs/ts-cloud 0.1.2 → 0.1.5
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/README.md +98 -13
- package/dist/aws/acm.d.ts +129 -0
- package/dist/aws/application-autoscaling.d.ts +282 -0
- package/dist/aws/bedrock.d.ts +2292 -0
- package/dist/aws/client.d.ts +79 -0
- package/dist/aws/cloudformation.d.ts +105 -0
- package/dist/aws/cloudfront.d.ts +265 -0
- package/dist/aws/cloudwatch-logs.d.ts +48 -0
- package/dist/aws/comprehend.d.ts +505 -0
- package/dist/aws/connect.d.ts +377 -0
- package/dist/aws/deploy-imap.d.ts +14 -0
- package/dist/aws/dynamodb.d.ts +176 -0
- package/dist/aws/ec2.d.ts +272 -0
- package/dist/aws/ecr.d.ts +149 -0
- package/dist/aws/ecs.d.ts +162 -0
- package/dist/aws/elasticache.d.ts +71 -0
- package/dist/aws/elbv2.d.ts +248 -0
- package/dist/aws/email.d.ts +175 -0
- package/dist/aws/eventbridge.d.ts +142 -0
- package/dist/aws/iam.d.ts +638 -0
- package/dist/aws/imap-server.d.ts +119 -0
- package/{src/aws/index.ts → dist/aws/index.d.ts} +62 -83
- package/{src/aws/kendra.ts → dist/aws/kendra.d.ts} +71 -386
- package/dist/aws/lambda.d.ts +232 -0
- package/dist/aws/opensearch.d.ts +87 -0
- package/dist/aws/personalize.d.ts +516 -0
- package/dist/aws/polly.d.ts +214 -0
- package/dist/aws/rds.d.ts +240 -0
- package/dist/aws/rekognition.d.ts +543 -0
- package/dist/aws/route53-domains.d.ts +113 -0
- package/dist/aws/route53.d.ts +215 -0
- package/dist/aws/s3.d.ts +212 -0
- package/dist/aws/scheduler.d.ts +140 -0
- package/dist/aws/secrets-manager.d.ts +170 -0
- package/dist/aws/ses.d.ts +288 -0
- package/dist/aws/setup-phone.d.ts +0 -0
- package/dist/aws/setup-sms.d.ts +115 -0
- package/dist/aws/sms.d.ts +304 -0
- package/dist/aws/smtp-server.d.ts +61 -0
- package/dist/aws/sns.d.ts +117 -0
- package/dist/aws/sqs.d.ts +65 -0
- package/dist/aws/ssm.d.ts +179 -0
- package/dist/aws/sts.d.ts +15 -0
- package/dist/aws/support.d.ts +104 -0
- package/dist/aws/test-imap.d.ts +0 -0
- package/dist/aws/textract.d.ts +403 -0
- package/dist/aws/transcribe.d.ts +60 -0
- package/dist/aws/translate.d.ts +358 -0
- package/dist/aws/voice.d.ts +219 -0
- package/dist/bin/cli.js +1724 -0
- package/dist/config.d.ts +7 -0
- package/dist/deploy/index.d.ts +2 -0
- package/dist/deploy/static-site-external-dns.d.ts +51 -0
- package/dist/deploy/static-site.d.ts +71 -0
- package/dist/dns/cloudflare.d.ts +52 -0
- package/dist/dns/godaddy.d.ts +38 -0
- package/dist/dns/index.d.ts +45 -0
- package/dist/dns/porkbun.d.ts +18 -0
- package/dist/dns/route53-adapter.d.ts +38 -0
- package/{src/dns/types.ts → dist/dns/types.d.ts} +26 -63
- package/dist/dns/validator.d.ts +78 -0
- package/dist/generators/index.d.ts +1 -0
- package/dist/generators/infrastructure.d.ts +30 -0
- package/{src/index.ts → dist/index.d.ts} +70 -93
- package/dist/index.js +7881 -0
- package/dist/push/apns.d.ts +60 -0
- package/dist/push/fcm.d.ts +117 -0
- package/dist/push/index.d.ts +14 -0
- package/dist/security/pre-deploy-scanner.d.ts +69 -0
- package/dist/ssl/acme-client.d.ts +67 -0
- package/dist/ssl/index.d.ts +2 -0
- package/dist/ssl/letsencrypt.d.ts +48 -0
- package/dist/types.d.ts +1 -0
- package/dist/utils/cli.d.ts +123 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/template.d.ts +23 -0
- package/package.json +8 -8
- package/bin/cli.ts +0 -133
- package/bin/commands/analytics.ts +0 -328
- package/bin/commands/api.ts +0 -379
- package/bin/commands/assets.ts +0 -221
- package/bin/commands/audit.ts +0 -501
- package/bin/commands/backup.ts +0 -682
- package/bin/commands/cache.ts +0 -294
- package/bin/commands/cdn.ts +0 -281
- package/bin/commands/config.ts +0 -202
- package/bin/commands/container.ts +0 -105
- package/bin/commands/cost.ts +0 -208
- package/bin/commands/database.ts +0 -401
- package/bin/commands/deploy.ts +0 -674
- package/bin/commands/domain.ts +0 -397
- package/bin/commands/email.ts +0 -423
- package/bin/commands/environment.ts +0 -285
- package/bin/commands/events.ts +0 -424
- package/bin/commands/firewall.ts +0 -145
- package/bin/commands/function.ts +0 -116
- package/bin/commands/generate.ts +0 -280
- package/bin/commands/git.ts +0 -139
- package/bin/commands/iam.ts +0 -464
- package/bin/commands/index.ts +0 -48
- package/bin/commands/init.ts +0 -120
- package/bin/commands/logs.ts +0 -148
- package/bin/commands/network.ts +0 -579
- package/bin/commands/notify.ts +0 -489
- package/bin/commands/queue.ts +0 -407
- package/bin/commands/scheduler.ts +0 -370
- package/bin/commands/secrets.ts +0 -54
- package/bin/commands/server.ts +0 -629
- package/bin/commands/shared.ts +0 -97
- package/bin/commands/ssl.ts +0 -138
- package/bin/commands/stack.ts +0 -325
- package/bin/commands/status.ts +0 -385
- package/bin/commands/storage.ts +0 -450
- package/bin/commands/team.ts +0 -96
- package/bin/commands/tunnel.ts +0 -489
- package/bin/commands/utils.ts +0 -202
- package/build.ts +0 -15
- package/cloud +0 -2
- package/src/aws/acm.ts +0 -768
- package/src/aws/application-autoscaling.ts +0 -845
- package/src/aws/bedrock.ts +0 -4074
- package/src/aws/client.ts +0 -878
- package/src/aws/cloudformation.ts +0 -896
- package/src/aws/cloudfront.ts +0 -1531
- package/src/aws/cloudwatch-logs.ts +0 -154
- package/src/aws/comprehend.ts +0 -839
- package/src/aws/connect.ts +0 -1056
- package/src/aws/deploy-imap.ts +0 -384
- package/src/aws/dynamodb.ts +0 -340
- package/src/aws/ec2.ts +0 -1385
- package/src/aws/ecr.ts +0 -621
- package/src/aws/ecs.ts +0 -615
- package/src/aws/elasticache.ts +0 -301
- package/src/aws/elbv2.ts +0 -942
- package/src/aws/email.ts +0 -928
- package/src/aws/eventbridge.ts +0 -248
- package/src/aws/iam.ts +0 -1689
- package/src/aws/imap-server.ts +0 -2100
- package/src/aws/lambda.ts +0 -786
- package/src/aws/opensearch.ts +0 -158
- package/src/aws/personalize.ts +0 -977
- package/src/aws/polly.ts +0 -559
- package/src/aws/rds.ts +0 -888
- package/src/aws/rekognition.ts +0 -846
- package/src/aws/route53-domains.ts +0 -359
- package/src/aws/route53.ts +0 -1046
- package/src/aws/s3.ts +0 -2318
- package/src/aws/scheduler.ts +0 -571
- package/src/aws/secrets-manager.ts +0 -769
- package/src/aws/ses.ts +0 -1081
- package/src/aws/setup-phone.ts +0 -104
- package/src/aws/setup-sms.ts +0 -580
- package/src/aws/sms.ts +0 -1735
- package/src/aws/smtp-server.ts +0 -531
- package/src/aws/sns.ts +0 -758
- package/src/aws/sqs.ts +0 -382
- package/src/aws/ssm.ts +0 -807
- package/src/aws/sts.ts +0 -92
- package/src/aws/support.ts +0 -391
- package/src/aws/test-imap.ts +0 -86
- package/src/aws/textract.ts +0 -780
- package/src/aws/transcribe.ts +0 -108
- package/src/aws/translate.ts +0 -641
- package/src/aws/voice.ts +0 -1379
- package/src/config.ts +0 -35
- package/src/deploy/index.ts +0 -7
- package/src/deploy/static-site-external-dns.ts +0 -906
- package/src/deploy/static-site.ts +0 -1125
- package/src/dns/godaddy.ts +0 -412
- package/src/dns/index.ts +0 -183
- package/src/dns/porkbun.ts +0 -362
- package/src/dns/route53-adapter.ts +0 -414
- package/src/dns/validator.ts +0 -369
- package/src/generators/index.ts +0 -5
- package/src/generators/infrastructure.ts +0 -1660
- package/src/push/apns.ts +0 -452
- package/src/push/fcm.ts +0 -506
- package/src/push/index.ts +0 -58
- package/src/ssl/acme-client.ts +0 -478
- package/src/ssl/index.ts +0 -7
- package/src/ssl/letsencrypt.ts +0 -747
- package/src/types.ts +0 -2
- package/src/utils/cli.ts +0 -398
- package/src/validation/index.ts +0 -5
- package/src/validation/template.ts +0 -405
- package/test/index.test.ts +0 -128
- package/tsconfig.json +0 -18
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CloudFormation Template Validation
|
|
3
|
-
* Validates templates before deployment
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { CloudFormationTemplate } from '@stacksjs/ts-cloud-aws-types'
|
|
7
|
-
|
|
8
|
-
export interface ValidationError {
|
|
9
|
-
path: string
|
|
10
|
-
message: string
|
|
11
|
-
severity: 'error' | 'warning'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ValidationResult {
|
|
15
|
-
valid: boolean
|
|
16
|
-
errors: ValidationError[]
|
|
17
|
-
warnings: ValidationError[]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Validate a CloudFormation template
|
|
22
|
-
*/
|
|
23
|
-
export function validateTemplate(template: CloudFormationTemplate): ValidationResult {
|
|
24
|
-
const errors: ValidationError[] = []
|
|
25
|
-
const warnings: ValidationError[] = []
|
|
26
|
-
|
|
27
|
-
// Check required fields
|
|
28
|
-
if (!template.AWSTemplateFormatVersion) {
|
|
29
|
-
errors.push({
|
|
30
|
-
path: 'AWSTemplateFormatVersion',
|
|
31
|
-
message: 'AWSTemplateFormatVersion is required',
|
|
32
|
-
severity: 'error',
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
else if (template.AWSTemplateFormatVersion !== '2010-09-09') {
|
|
36
|
-
warnings.push({
|
|
37
|
-
path: 'AWSTemplateFormatVersion',
|
|
38
|
-
message: 'AWSTemplateFormatVersion should be "2010-09-09"',
|
|
39
|
-
severity: 'warning',
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Check Resources section
|
|
44
|
-
if (!template.Resources || Object.keys(template.Resources).length === 0) {
|
|
45
|
-
errors.push({
|
|
46
|
-
path: 'Resources',
|
|
47
|
-
message: 'At least one resource is required',
|
|
48
|
-
severity: 'error',
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
// Validate each resource
|
|
53
|
-
for (const [logicalId, resource] of Object.entries(template.Resources)) {
|
|
54
|
-
validateResource(logicalId, resource, errors, warnings)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check for circular dependencies
|
|
59
|
-
if (template.Resources) {
|
|
60
|
-
const circularDeps = findCircularDependencies(template.Resources)
|
|
61
|
-
if (circularDeps.length > 0) {
|
|
62
|
-
errors.push({
|
|
63
|
-
path: 'Resources',
|
|
64
|
-
message: `Circular dependencies detected: ${circularDeps.join(' -> ')}`,
|
|
65
|
-
severity: 'error',
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Validate Parameters if present
|
|
71
|
-
if (template.Parameters) {
|
|
72
|
-
for (const [paramName, param] of Object.entries(template.Parameters)) {
|
|
73
|
-
validateParameter(paramName, param, errors, warnings)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Validate Outputs if present
|
|
78
|
-
if (template.Outputs) {
|
|
79
|
-
for (const [outputName, output] of Object.entries(template.Outputs)) {
|
|
80
|
-
validateOutput(outputName, output, errors, warnings)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
valid: errors.length === 0,
|
|
86
|
-
errors,
|
|
87
|
-
warnings,
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Validate a single resource
|
|
93
|
-
*/
|
|
94
|
-
function validateResource(
|
|
95
|
-
logicalId: string,
|
|
96
|
-
resource: any,
|
|
97
|
-
errors: ValidationError[],
|
|
98
|
-
warnings: ValidationError[],
|
|
99
|
-
): void {
|
|
100
|
-
// Check required fields
|
|
101
|
-
if (!resource.Type) {
|
|
102
|
-
errors.push({
|
|
103
|
-
path: `Resources.${logicalId}.Type`,
|
|
104
|
-
message: 'Resource Type is required',
|
|
105
|
-
severity: 'error',
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Check logical ID format
|
|
110
|
-
if (!/^[a-zA-Z0-9]+$/.test(logicalId)) {
|
|
111
|
-
warnings.push({
|
|
112
|
-
path: `Resources.${logicalId}`,
|
|
113
|
-
message: 'Logical ID should only contain alphanumeric characters',
|
|
114
|
-
severity: 'warning',
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Validate resource type format
|
|
119
|
-
if (resource.Type && !resource.Type.startsWith('AWS::') && !resource.Type.startsWith('Custom::')) {
|
|
120
|
-
errors.push({
|
|
121
|
-
path: `Resources.${logicalId}.Type`,
|
|
122
|
-
message: 'Resource Type must start with "AWS::" or "Custom::"',
|
|
123
|
-
severity: 'error',
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check for common mistakes
|
|
128
|
-
if (resource.Properties) {
|
|
129
|
-
// Check for undefined or null values
|
|
130
|
-
for (const [propName, propValue] of Object.entries(resource.Properties)) {
|
|
131
|
-
if (propValue === undefined || propValue === null) {
|
|
132
|
-
warnings.push({
|
|
133
|
-
path: `Resources.${logicalId}.Properties.${propName}`,
|
|
134
|
-
message: 'Property has undefined or null value',
|
|
135
|
-
severity: 'warning',
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Validate DependsOn
|
|
142
|
-
if (resource.DependsOn) {
|
|
143
|
-
if (typeof resource.DependsOn === 'string') {
|
|
144
|
-
if (resource.DependsOn === logicalId) {
|
|
145
|
-
errors.push({
|
|
146
|
-
path: `Resources.${logicalId}.DependsOn`,
|
|
147
|
-
message: 'Resource cannot depend on itself',
|
|
148
|
-
severity: 'error',
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
else if (Array.isArray(resource.DependsOn)) {
|
|
153
|
-
if (resource.DependsOn.includes(logicalId)) {
|
|
154
|
-
errors.push({
|
|
155
|
-
path: `Resources.${logicalId}.DependsOn`,
|
|
156
|
-
message: 'Resource cannot depend on itself',
|
|
157
|
-
severity: 'error',
|
|
158
|
-
})
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Validate a parameter
|
|
166
|
-
*/
|
|
167
|
-
function validateParameter(
|
|
168
|
-
paramName: string,
|
|
169
|
-
param: any,
|
|
170
|
-
errors: ValidationError[],
|
|
171
|
-
warnings: ValidationError[],
|
|
172
|
-
): void {
|
|
173
|
-
if (!param.Type) {
|
|
174
|
-
errors.push({
|
|
175
|
-
path: `Parameters.${paramName}.Type`,
|
|
176
|
-
message: 'Parameter Type is required',
|
|
177
|
-
severity: 'error',
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const validTypes = ['String', 'Number', 'List<Number>', 'CommaDelimitedList', 'AWS::SSM::Parameter::Value<String>']
|
|
182
|
-
if (param.Type && !validTypes.includes(param.Type) && !param.Type.startsWith('AWS::')) {
|
|
183
|
-
warnings.push({
|
|
184
|
-
path: `Parameters.${paramName}.Type`,
|
|
185
|
-
message: `Uncommon parameter type: ${param.Type}`,
|
|
186
|
-
severity: 'warning',
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Check for default value with NoEcho
|
|
191
|
-
if (param.NoEcho && param.Default) {
|
|
192
|
-
warnings.push({
|
|
193
|
-
path: `Parameters.${paramName}`,
|
|
194
|
-
message: 'NoEcho parameters should not have default values',
|
|
195
|
-
severity: 'warning',
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Validate an output
|
|
202
|
-
*/
|
|
203
|
-
function validateOutput(
|
|
204
|
-
outputName: string,
|
|
205
|
-
output: any,
|
|
206
|
-
errors: ValidationError[],
|
|
207
|
-
warnings: ValidationError[],
|
|
208
|
-
): void {
|
|
209
|
-
if (!output.Value) {
|
|
210
|
-
errors.push({
|
|
211
|
-
path: `Outputs.${outputName}.Value`,
|
|
212
|
-
message: 'Output Value is required',
|
|
213
|
-
severity: 'error',
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Find circular dependencies in resources
|
|
220
|
-
*/
|
|
221
|
-
function findCircularDependencies(resources: Record<string, any>): string[] {
|
|
222
|
-
const graph: Record<string, string[]> = {}
|
|
223
|
-
|
|
224
|
-
// Build dependency graph
|
|
225
|
-
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
226
|
-
graph[logicalId] = []
|
|
227
|
-
|
|
228
|
-
// Explicit dependencies (DependsOn)
|
|
229
|
-
if (resource.DependsOn) {
|
|
230
|
-
if (typeof resource.DependsOn === 'string') {
|
|
231
|
-
graph[logicalId].push(resource.DependsOn)
|
|
232
|
-
}
|
|
233
|
-
else if (Array.isArray(resource.DependsOn)) {
|
|
234
|
-
graph[logicalId].push(...resource.DependsOn)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Implicit dependencies (Ref, GetAtt)
|
|
239
|
-
const deps = extractDependencies(resource)
|
|
240
|
-
graph[logicalId].push(...deps)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Detect cycles using DFS
|
|
244
|
-
const visited = new Set<string>()
|
|
245
|
-
const recursionStack = new Set<string>()
|
|
246
|
-
const cycle: string[] = []
|
|
247
|
-
|
|
248
|
-
function dfs(node: string, path: string[]): boolean {
|
|
249
|
-
visited.add(node)
|
|
250
|
-
recursionStack.add(node)
|
|
251
|
-
path.push(node)
|
|
252
|
-
|
|
253
|
-
const neighbors = graph[node] || []
|
|
254
|
-
for (const neighbor of neighbors) {
|
|
255
|
-
if (!visited.has(neighbor)) {
|
|
256
|
-
if (dfs(neighbor, path)) {
|
|
257
|
-
return true
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else if (recursionStack.has(neighbor)) {
|
|
261
|
-
// Cycle detected
|
|
262
|
-
const cycleStart = path.indexOf(neighbor)
|
|
263
|
-
cycle.push(...path.slice(cycleStart), neighbor)
|
|
264
|
-
return true
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
path.pop()
|
|
269
|
-
recursionStack.delete(node)
|
|
270
|
-
return false
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
for (const node of Object.keys(graph)) {
|
|
274
|
-
if (!visited.has(node)) {
|
|
275
|
-
if (dfs(node, [])) {
|
|
276
|
-
return cycle
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return []
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Extract dependencies from a resource (Ref, GetAtt, etc.)
|
|
286
|
-
*/
|
|
287
|
-
function extractDependencies(obj: any, deps: string[] = []): string[] {
|
|
288
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
289
|
-
return deps
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (Array.isArray(obj)) {
|
|
293
|
-
for (const item of obj) {
|
|
294
|
-
extractDependencies(item, deps)
|
|
295
|
-
}
|
|
296
|
-
return deps
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Check for Ref
|
|
300
|
-
if (obj.Ref && typeof obj.Ref === 'string' && !obj.Ref.startsWith('AWS::')) {
|
|
301
|
-
deps.push(obj.Ref)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Check for GetAtt
|
|
305
|
-
if (obj['Fn::GetAtt']) {
|
|
306
|
-
const getAtt = obj['Fn::GetAtt']
|
|
307
|
-
if (Array.isArray(getAtt) && getAtt.length > 0) {
|
|
308
|
-
deps.push(getAtt[0])
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Recurse into object properties
|
|
313
|
-
for (const value of Object.values(obj)) {
|
|
314
|
-
extractDependencies(value, deps)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return deps
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Validate template size
|
|
322
|
-
*/
|
|
323
|
-
export function validateTemplateSize(templateBody: string): ValidationResult {
|
|
324
|
-
const errors: ValidationError[] = []
|
|
325
|
-
const warnings: ValidationError[] = []
|
|
326
|
-
|
|
327
|
-
const sizeInBytes = new TextEncoder().encode(templateBody).length
|
|
328
|
-
|
|
329
|
-
// CloudFormation limits
|
|
330
|
-
const maxBodySize = 51200 // 51,200 bytes (50 KB)
|
|
331
|
-
const maxS3Size = 460800 // 460,800 bytes (450 KB)
|
|
332
|
-
|
|
333
|
-
if (sizeInBytes > maxBodySize) {
|
|
334
|
-
if (sizeInBytes > maxS3Size) {
|
|
335
|
-
errors.push({
|
|
336
|
-
path: 'template',
|
|
337
|
-
message: `Template size (${sizeInBytes} bytes) exceeds maximum allowed size of ${maxS3Size} bytes`,
|
|
338
|
-
severity: 'error',
|
|
339
|
-
})
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
warnings.push({
|
|
343
|
-
path: 'template',
|
|
344
|
-
message: `Template size (${sizeInBytes} bytes) exceeds direct upload limit (${maxBodySize} bytes). You must upload to S3 first.`,
|
|
345
|
-
severity: 'warning',
|
|
346
|
-
})
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
valid: errors.length === 0,
|
|
352
|
-
errors,
|
|
353
|
-
warnings,
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Validate template resource limits
|
|
359
|
-
*/
|
|
360
|
-
export function validateResourceLimits(template: CloudFormationTemplate): ValidationResult {
|
|
361
|
-
const errors: ValidationError[] = []
|
|
362
|
-
const warnings: ValidationError[] = []
|
|
363
|
-
|
|
364
|
-
const resourceCount = template.Resources ? Object.keys(template.Resources).length : 0
|
|
365
|
-
const parameterCount = template.Parameters ? Object.keys(template.Parameters).length : 0
|
|
366
|
-
const outputCount = template.Outputs ? Object.keys(template.Outputs).length : 0
|
|
367
|
-
|
|
368
|
-
// CloudFormation limits
|
|
369
|
-
if (resourceCount > 500) {
|
|
370
|
-
errors.push({
|
|
371
|
-
path: 'Resources',
|
|
372
|
-
message: `Template has ${resourceCount} resources, exceeding the limit of 500`,
|
|
373
|
-
severity: 'error',
|
|
374
|
-
})
|
|
375
|
-
}
|
|
376
|
-
else if (resourceCount > 200) {
|
|
377
|
-
warnings.push({
|
|
378
|
-
path: 'Resources',
|
|
379
|
-
message: `Template has ${resourceCount} resources. Consider using nested stacks for better organization.`,
|
|
380
|
-
severity: 'warning',
|
|
381
|
-
})
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (parameterCount > 200) {
|
|
385
|
-
errors.push({
|
|
386
|
-
path: 'Parameters',
|
|
387
|
-
message: `Template has ${parameterCount} parameters, exceeding the limit of 200`,
|
|
388
|
-
severity: 'error',
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (outputCount > 200) {
|
|
393
|
-
errors.push({
|
|
394
|
-
path: 'Outputs',
|
|
395
|
-
message: `Template has ${outputCount} outputs, exceeding the limit of 200`,
|
|
396
|
-
severity: 'error',
|
|
397
|
-
})
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return {
|
|
401
|
-
valid: errors.length === 0,
|
|
402
|
-
errors,
|
|
403
|
-
warnings,
|
|
404
|
-
}
|
|
405
|
-
}
|
package/test/index.test.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
// Example function to test
|
|
4
|
-
function add(a: number, b: number): number {
|
|
5
|
-
return a + b
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// Example async function to test
|
|
9
|
-
async function fetchData(): Promise<string> {
|
|
10
|
-
return new Promise(resolve => setTimeout(() => resolve('data'), 100))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Example module to mock
|
|
14
|
-
const apiModule = {
|
|
15
|
-
fetchUserData: async (id: number) => {
|
|
16
|
-
// Simulating an API call
|
|
17
|
-
return { id, name: 'John Doe', email: 'john@example.com' }
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe('my awesome package', () => {
|
|
22
|
-
let testValue: number
|
|
23
|
-
|
|
24
|
-
beforeAll(() => {
|
|
25
|
-
// eslint-disable-next-line no-console
|
|
26
|
-
console.log('Running before all tests')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
afterAll(() => {
|
|
30
|
-
// eslint-disable-next-line no-console
|
|
31
|
-
console.log('Running after all tests')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
testValue = 10
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
testValue = 0
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('should demonstrate basic assertion', () => {
|
|
43
|
-
expect(1).toBe(1)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should test the add function', () => {
|
|
47
|
-
expect(add(2, 3)).toBe(5)
|
|
48
|
-
expect(add(-1, 1)).toBe(0)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should work with various matchers', () => {
|
|
52
|
-
expect(true).toBeTruthy()
|
|
53
|
-
expect(false).toBeFalsy()
|
|
54
|
-
expect(null).toBeNull()
|
|
55
|
-
expect(undefined).toBeUndefined()
|
|
56
|
-
expect([1, 2, 3]).toContain(2)
|
|
57
|
-
expect({ name: 'John' }).toHaveProperty('name')
|
|
58
|
-
expect(() => {
|
|
59
|
-
throw new Error('Test error')
|
|
60
|
-
}).toThrow('Test error')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('should handle async tests', async () => {
|
|
64
|
-
const result = await fetchData()
|
|
65
|
-
expect(result).toBe('data')
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should use beforeEach and afterEach', () => {
|
|
69
|
-
expect(testValue).toBe(10)
|
|
70
|
-
testValue += 5
|
|
71
|
-
expect(testValue).toBe(15)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('should work with test function as well', () => {
|
|
75
|
-
expect(true).toBe(true)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should demonstrate spy functionality', () => {
|
|
79
|
-
const consoleSpy = spyOn(console, 'log')
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.log('Test message')
|
|
82
|
-
expect(consoleSpy).toHaveBeenCalledWith('Test message')
|
|
83
|
-
consoleSpy.mockRestore()
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it.todo('should implement this test later', () => {})
|
|
87
|
-
|
|
88
|
-
it.skip('should skip this test', () => {
|
|
89
|
-
// This test will be skipped
|
|
90
|
-
expect(true).toBe(false)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('Mocking example', () => {
|
|
94
|
-
it('should demonstrate mock functionality', async () => {
|
|
95
|
-
// Create a mock function
|
|
96
|
-
const mockFetchUserData = mock((id: number) => {
|
|
97
|
-
return Promise.resolve({ id, name: 'Mocked User', email: 'mocked@example.com' })
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
// Replace the original function with the mock
|
|
101
|
-
apiModule.fetchUserData = mockFetchUserData
|
|
102
|
-
|
|
103
|
-
// Use the mocked function
|
|
104
|
-
const result = await apiModule.fetchUserData(1)
|
|
105
|
-
|
|
106
|
-
// Assert the mock was called with the correct argument
|
|
107
|
-
expect(mockFetchUserData).toHaveBeenCalledWith(1)
|
|
108
|
-
|
|
109
|
-
// Assert the mock returned the expected result
|
|
110
|
-
expect(result).toEqual({ id: 1, name: 'Mocked User', email: 'mocked@example.com' })
|
|
111
|
-
|
|
112
|
-
// Check how many times the mock was called
|
|
113
|
-
expect(mockFetchUserData).toHaveBeenCalledTimes(1)
|
|
114
|
-
|
|
115
|
-
// Reset the mock
|
|
116
|
-
mockFetchUserData.mockReset()
|
|
117
|
-
|
|
118
|
-
// Provide a new implementation for the mock
|
|
119
|
-
mockFetchUserData.mockImplementation((id: number) => {
|
|
120
|
-
return Promise.resolve({ id, name: 'New Mock', email: 'new@example.com' })
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// Use the mock with the new implementation
|
|
124
|
-
const newResult = await apiModule.fetchUserData(2)
|
|
125
|
-
expect(newResult).toEqual({ id: 2, name: 'New Mock', email: 'new@example.com' })
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
})
|
package/tsconfig.json
DELETED