@stacksjs/ts-cloud-core 0.1.1
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.md +21 -0
- package/README.md +321 -0
- package/package.json +31 -0
- package/src/advanced-features.test.ts +465 -0
- package/src/aws/cloudformation.ts +421 -0
- package/src/aws/cloudfront.ts +158 -0
- package/src/aws/credentials.test.ts +132 -0
- package/src/aws/credentials.ts +545 -0
- package/src/aws/index.ts +87 -0
- package/src/aws/s3.test.ts +188 -0
- package/src/aws/s3.ts +1088 -0
- package/src/aws/signature.test.ts +670 -0
- package/src/aws/signature.ts +1155 -0
- package/src/backup/disaster-recovery.test.ts +726 -0
- package/src/backup/disaster-recovery.ts +500 -0
- package/src/backup/index.ts +34 -0
- package/src/backup/manager.test.ts +498 -0
- package/src/backup/manager.ts +432 -0
- package/src/cicd/circleci.ts +430 -0
- package/src/cicd/github-actions.ts +424 -0
- package/src/cicd/gitlab-ci.ts +255 -0
- package/src/cicd/index.ts +8 -0
- package/src/cli/history.ts +396 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/progress.ts +458 -0
- package/src/cli/repl.ts +454 -0
- package/src/cli/suggestions.ts +327 -0
- package/src/cli/table.test.ts +319 -0
- package/src/cli/table.ts +332 -0
- package/src/cloudformation/builder.test.ts +327 -0
- package/src/cloudformation/builder.ts +378 -0
- package/src/cloudformation/builders/api-gateway.ts +449 -0
- package/src/cloudformation/builders/cache.ts +334 -0
- package/src/cloudformation/builders/cdn.ts +278 -0
- package/src/cloudformation/builders/compute.ts +485 -0
- package/src/cloudformation/builders/database.ts +392 -0
- package/src/cloudformation/builders/functions.ts +343 -0
- package/src/cloudformation/builders/messaging.ts +140 -0
- package/src/cloudformation/builders/monitoring.ts +300 -0
- package/src/cloudformation/builders/network.ts +264 -0
- package/src/cloudformation/builders/queue.ts +147 -0
- package/src/cloudformation/builders/security.ts +399 -0
- package/src/cloudformation/builders/storage.ts +285 -0
- package/src/cloudformation/index.ts +30 -0
- package/src/cloudformation/types.ts +173 -0
- package/src/compliance/aws-config.ts +543 -0
- package/src/compliance/cloudtrail.ts +376 -0
- package/src/compliance/compliance.test.ts +423 -0
- package/src/compliance/guardduty.ts +446 -0
- package/src/compliance/index.ts +66 -0
- package/src/compliance/security-hub.ts +456 -0
- package/src/containers/build-optimization.ts +416 -0
- package/src/containers/containers.test.ts +508 -0
- package/src/containers/image-scanning.ts +360 -0
- package/src/containers/index.ts +9 -0
- package/src/containers/registry.ts +293 -0
- package/src/containers/service-mesh.ts +520 -0
- package/src/database/database.test.ts +762 -0
- package/src/database/index.ts +9 -0
- package/src/database/migrations.ts +444 -0
- package/src/database/performance.ts +528 -0
- package/src/database/replicas.ts +534 -0
- package/src/database/users.ts +494 -0
- package/src/dependency-graph.ts +143 -0
- package/src/deployment/ab-testing.ts +582 -0
- package/src/deployment/blue-green.ts +452 -0
- package/src/deployment/canary.ts +500 -0
- package/src/deployment/deployment.test.ts +526 -0
- package/src/deployment/index.ts +61 -0
- package/src/deployment/progressive.ts +62 -0
- package/src/dns/dns.test.ts +641 -0
- package/src/dns/dnssec.ts +315 -0
- package/src/dns/index.ts +8 -0
- package/src/dns/resolver.ts +496 -0
- package/src/dns/routing.ts +593 -0
- package/src/email/advanced/analytics.ts +445 -0
- package/src/email/advanced/index.ts +11 -0
- package/src/email/advanced/rules.ts +465 -0
- package/src/email/advanced/scheduling.ts +352 -0
- package/src/email/advanced/search.ts +412 -0
- package/src/email/advanced/shared-mailboxes.ts +404 -0
- package/src/email/advanced/templates.ts +455 -0
- package/src/email/advanced/threading.ts +281 -0
- package/src/email/analytics.ts +467 -0
- package/src/email/bounce-handling.ts +425 -0
- package/src/email/email.test.ts +431 -0
- package/src/email/handlers/__tests__/inbound.test.ts +38 -0
- package/src/email/handlers/__tests__/outbound.test.ts +37 -0
- package/src/email/handlers/converter.ts +227 -0
- package/src/email/handlers/feedback.ts +228 -0
- package/src/email/handlers/inbound.ts +169 -0
- package/src/email/handlers/outbound.ts +178 -0
- package/src/email/index.ts +15 -0
- package/src/email/reputation.ts +303 -0
- package/src/email/templates.ts +352 -0
- package/src/errors/index.test.ts +434 -0
- package/src/errors/index.ts +416 -0
- package/src/health-checks/index.ts +40 -0
- package/src/index.ts +360 -0
- package/src/intrinsic-functions.ts +118 -0
- package/src/lambda/concurrency.ts +330 -0
- package/src/lambda/destinations.ts +345 -0
- package/src/lambda/dlq.ts +425 -0
- package/src/lambda/index.ts +11 -0
- package/src/lambda/lambda.test.ts +840 -0
- package/src/lambda/layers.ts +263 -0
- package/src/lambda/versions.ts +376 -0
- package/src/lambda/vpc.ts +399 -0
- package/src/local/config.ts +114 -0
- package/src/local/index.ts +6 -0
- package/src/local/mock-aws.ts +351 -0
- package/src/modules/ai.ts +340 -0
- package/src/modules/api.ts +478 -0
- package/src/modules/auth.ts +805 -0
- package/src/modules/cache.ts +417 -0
- package/src/modules/cdn.ts +1062 -0
- package/src/modules/communication.ts +1094 -0
- package/src/modules/compute.ts +3348 -0
- package/src/modules/database.ts +554 -0
- package/src/modules/deployment.ts +1079 -0
- package/src/modules/dns.ts +337 -0
- package/src/modules/email.ts +1538 -0
- package/src/modules/filesystem.ts +515 -0
- package/src/modules/index.ts +32 -0
- package/src/modules/messaging.ts +486 -0
- package/src/modules/monitoring.ts +2086 -0
- package/src/modules/network.ts +664 -0
- package/src/modules/parameter-store.ts +325 -0
- package/src/modules/permissions.ts +1081 -0
- package/src/modules/phone.ts +494 -0
- package/src/modules/queue.ts +1260 -0
- package/src/modules/redirects.ts +464 -0
- package/src/modules/registry.ts +699 -0
- package/src/modules/search.ts +401 -0
- package/src/modules/secrets.ts +416 -0
- package/src/modules/security.ts +731 -0
- package/src/modules/sms.ts +389 -0
- package/src/modules/storage.ts +1120 -0
- package/src/modules/workflow.ts +680 -0
- package/src/multi-account/config.ts +521 -0
- package/src/multi-account/index.ts +7 -0
- package/src/multi-account/manager.ts +427 -0
- package/src/multi-region/cross-region.ts +410 -0
- package/src/multi-region/index.ts +8 -0
- package/src/multi-region/manager.ts +483 -0
- package/src/multi-region/regions.ts +435 -0
- package/src/network-security/index.ts +48 -0
- package/src/observability/index.ts +9 -0
- package/src/observability/logs.ts +522 -0
- package/src/observability/metrics.ts +460 -0
- package/src/observability/observability.test.ts +782 -0
- package/src/observability/synthetics.ts +568 -0
- package/src/observability/xray.ts +358 -0
- package/src/phone/advanced/analytics.ts +349 -0
- package/src/phone/advanced/callbacks.ts +428 -0
- package/src/phone/advanced/index.ts +8 -0
- package/src/phone/advanced/ivr-builder.ts +504 -0
- package/src/phone/advanced/recording.ts +310 -0
- package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
- package/src/phone/handlers/incoming-call.ts +117 -0
- package/src/phone/handlers/missed-call.ts +116 -0
- package/src/phone/handlers/voicemail.ts +179 -0
- package/src/phone/index.ts +9 -0
- package/src/presets/api-backend.ts +134 -0
- package/src/presets/data-pipeline.ts +204 -0
- package/src/presets/extend.test.ts +295 -0
- package/src/presets/extend.ts +297 -0
- package/src/presets/fullstack-app.ts +144 -0
- package/src/presets/index.ts +27 -0
- package/src/presets/jamstack.ts +135 -0
- package/src/presets/microservices.ts +167 -0
- package/src/presets/ml-api.ts +208 -0
- package/src/presets/nodejs-server.ts +104 -0
- package/src/presets/nodejs-serverless.ts +114 -0
- package/src/presets/realtime-app.ts +184 -0
- package/src/presets/static-site.ts +64 -0
- package/src/presets/traditional-web-app.ts +339 -0
- package/src/presets/wordpress.ts +138 -0
- package/src/preview/github.test.ts +249 -0
- package/src/preview/github.ts +297 -0
- package/src/preview/index.ts +37 -0
- package/src/preview/manager.test.ts +440 -0
- package/src/preview/manager.ts +326 -0
- package/src/preview/notifications.test.ts +582 -0
- package/src/preview/notifications.ts +341 -0
- package/src/queue/batch-processing.ts +402 -0
- package/src/queue/dlq-monitoring.ts +402 -0
- package/src/queue/fifo.ts +342 -0
- package/src/queue/index.ts +9 -0
- package/src/queue/management.ts +428 -0
- package/src/queue/queue.test.ts +429 -0
- package/src/resource-mgmt/index.ts +39 -0
- package/src/resource-naming.ts +62 -0
- package/src/s3/index.ts +523 -0
- package/src/schema/cloud-config.schema.json +554 -0
- package/src/schema/index.ts +68 -0
- package/src/security/certificate-manager.ts +492 -0
- package/src/security/index.ts +9 -0
- package/src/security/scanning.ts +545 -0
- package/src/security/secrets-manager.ts +476 -0
- package/src/security/secrets-rotation.ts +456 -0
- package/src/security/security.test.ts +738 -0
- package/src/sms/advanced/ab-testing.ts +389 -0
- package/src/sms/advanced/analytics.ts +336 -0
- package/src/sms/advanced/campaigns.ts +523 -0
- package/src/sms/advanced/chatbot.ts +224 -0
- package/src/sms/advanced/index.ts +10 -0
- package/src/sms/advanced/link-tracking.ts +248 -0
- package/src/sms/advanced/mms.ts +308 -0
- package/src/sms/handlers/__tests__/send.test.ts +40 -0
- package/src/sms/handlers/delivery-status.ts +133 -0
- package/src/sms/handlers/receive.ts +162 -0
- package/src/sms/handlers/send.ts +174 -0
- package/src/sms/index.ts +9 -0
- package/src/stack-diff.ts +389 -0
- package/src/static-site/index.ts +85 -0
- package/src/template-builder.ts +110 -0
- package/src/template-validator.ts +574 -0
- package/src/utils/cache.ts +291 -0
- package/src/utils/diff.ts +269 -0
- package/src/utils/hash.ts +227 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/parallel.ts +294 -0
- package/src/validators/credentials.test.ts +274 -0
- package/src/validators/credentials.ts +233 -0
- package/src/validators/quotas.test.ts +434 -0
- package/src/validators/quotas.ts +217 -0
- package/test/ai.test.ts +327 -0
- package/test/api.test.ts +511 -0
- package/test/auth.test.ts +632 -0
- package/test/cache.test.ts +406 -0
- package/test/cdn.test.ts +247 -0
- package/test/compute.test.ts +861 -0
- package/test/database.test.ts +523 -0
- package/test/deployment.test.ts +499 -0
- package/test/dns.test.ts +270 -0
- package/test/email.test.ts +439 -0
- package/test/filesystem.test.ts +382 -0
- package/test/integration.test.ts +350 -0
- package/test/messaging.test.ts +514 -0
- package/test/monitoring.test.ts +634 -0
- package/test/network.test.ts +425 -0
- package/test/permissions.test.ts +488 -0
- package/test/queue.test.ts +484 -0
- package/test/registry.test.ts +306 -0
- package/test/security.test.ts +462 -0
- package/test/storage.test.ts +463 -0
- package/test/template-validator.test.ts +559 -0
- package/test/workflow.test.ts +592 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lambda Layers Management
|
|
3
|
+
* Reusable code and dependencies for Lambda functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface LambdaLayer {
|
|
7
|
+
id: string
|
|
8
|
+
layerName: string
|
|
9
|
+
layerArn: string
|
|
10
|
+
version: number
|
|
11
|
+
description?: string
|
|
12
|
+
compatibleRuntimes: string[]
|
|
13
|
+
licenseInfo?: string
|
|
14
|
+
content: LayerContent
|
|
15
|
+
size: number // bytes
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface LayerContent {
|
|
19
|
+
type: 's3' | 'zip'
|
|
20
|
+
s3Bucket?: string
|
|
21
|
+
s3Key?: string
|
|
22
|
+
s3ObjectVersion?: string
|
|
23
|
+
zipFile?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LayerVersion {
|
|
27
|
+
id: string
|
|
28
|
+
layerName: string
|
|
29
|
+
version: number
|
|
30
|
+
createdAt: Date
|
|
31
|
+
compatibleRuntimes: string[]
|
|
32
|
+
size: number
|
|
33
|
+
codeHash: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface LayerPermission {
|
|
37
|
+
id: string
|
|
38
|
+
layerName: string
|
|
39
|
+
version: number
|
|
40
|
+
principal: string
|
|
41
|
+
action: 'lambda:GetLayerVersion'
|
|
42
|
+
organizationId?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Lambda layers manager
|
|
47
|
+
*/
|
|
48
|
+
export class LambdaLayersManager {
|
|
49
|
+
private layers: Map<string, LambdaLayer> = new Map()
|
|
50
|
+
private versions: Map<string, LayerVersion> = new Map()
|
|
51
|
+
private permissions: Map<string, LayerPermission> = new Map()
|
|
52
|
+
private layerCounter = 0
|
|
53
|
+
private versionCounter = 0
|
|
54
|
+
private permissionCounter = 0
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create Lambda layer
|
|
58
|
+
*/
|
|
59
|
+
createLayer(layer: Omit<LambdaLayer, 'id' | 'layerArn' | 'version'>): LambdaLayer {
|
|
60
|
+
const id = `layer-${Date.now()}-${this.layerCounter++}`
|
|
61
|
+
const version = 1
|
|
62
|
+
|
|
63
|
+
const lambdaLayer: LambdaLayer = {
|
|
64
|
+
id,
|
|
65
|
+
layerArn: `arn:aws:lambda:us-east-1:123456789012:layer:${layer.layerName}:${version}`,
|
|
66
|
+
version,
|
|
67
|
+
...layer,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.layers.set(id, lambdaLayer)
|
|
71
|
+
|
|
72
|
+
return lambdaLayer
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create Node.js dependencies layer
|
|
77
|
+
*/
|
|
78
|
+
createNodeDependenciesLayer(options: {
|
|
79
|
+
layerName: string
|
|
80
|
+
nodeVersion: string
|
|
81
|
+
s3Bucket: string
|
|
82
|
+
s3Key: string
|
|
83
|
+
}): LambdaLayer {
|
|
84
|
+
return this.createLayer({
|
|
85
|
+
layerName: options.layerName,
|
|
86
|
+
description: 'Node.js dependencies layer',
|
|
87
|
+
compatibleRuntimes: [`nodejs${options.nodeVersion}`],
|
|
88
|
+
content: {
|
|
89
|
+
type: 's3',
|
|
90
|
+
s3Bucket: options.s3Bucket,
|
|
91
|
+
s3Key: options.s3Key,
|
|
92
|
+
},
|
|
93
|
+
size: 5 * 1024 * 1024, // 5MB
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create shared utilities layer
|
|
99
|
+
*/
|
|
100
|
+
createUtilitiesLayer(options: {
|
|
101
|
+
layerName: string
|
|
102
|
+
runtimes: string[]
|
|
103
|
+
s3Bucket: string
|
|
104
|
+
s3Key: string
|
|
105
|
+
}): LambdaLayer {
|
|
106
|
+
return this.createLayer({
|
|
107
|
+
layerName: options.layerName,
|
|
108
|
+
description: 'Shared utilities and helpers',
|
|
109
|
+
compatibleRuntimes: options.runtimes,
|
|
110
|
+
content: {
|
|
111
|
+
type: 's3',
|
|
112
|
+
s3Bucket: options.s3Bucket,
|
|
113
|
+
s3Key: options.s3Key,
|
|
114
|
+
},
|
|
115
|
+
size: 1 * 1024 * 1024, // 1MB
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Publish layer version
|
|
121
|
+
*/
|
|
122
|
+
publishVersion(layerId: string): LayerVersion {
|
|
123
|
+
const layer = this.layers.get(layerId)
|
|
124
|
+
|
|
125
|
+
if (!layer) {
|
|
126
|
+
throw new Error(`Layer not found: ${layerId}`)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const id = `version-${Date.now()}-${this.versionCounter++}`
|
|
130
|
+
const version = layer.version + 1
|
|
131
|
+
|
|
132
|
+
const layerVersion: LayerVersion = {
|
|
133
|
+
id,
|
|
134
|
+
layerName: layer.layerName,
|
|
135
|
+
version,
|
|
136
|
+
createdAt: new Date(),
|
|
137
|
+
compatibleRuntimes: layer.compatibleRuntimes,
|
|
138
|
+
size: layer.size,
|
|
139
|
+
codeHash: this.generateHash(),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.versions.set(id, layerVersion)
|
|
143
|
+
|
|
144
|
+
// Update layer version
|
|
145
|
+
layer.version = version
|
|
146
|
+
layer.layerArn = `arn:aws:lambda:us-east-1:123456789012:layer:${layer.layerName}:${version}`
|
|
147
|
+
|
|
148
|
+
return layerVersion
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Add layer permission
|
|
153
|
+
*/
|
|
154
|
+
addPermission(options: {
|
|
155
|
+
layerName: string
|
|
156
|
+
version: number
|
|
157
|
+
principal: string
|
|
158
|
+
organizationId?: string
|
|
159
|
+
}): LayerPermission {
|
|
160
|
+
const id = `permission-${Date.now()}-${this.permissionCounter++}`
|
|
161
|
+
|
|
162
|
+
const permission: LayerPermission = {
|
|
163
|
+
id,
|
|
164
|
+
layerName: options.layerName,
|
|
165
|
+
version: options.version,
|
|
166
|
+
principal: options.principal,
|
|
167
|
+
action: 'lambda:GetLayerVersion',
|
|
168
|
+
organizationId: options.organizationId,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.permissions.set(id, permission)
|
|
172
|
+
|
|
173
|
+
return permission
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate hash
|
|
178
|
+
*/
|
|
179
|
+
private generateHash(): string {
|
|
180
|
+
return Math.random().toString(36).substring(2, 15)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get layer
|
|
185
|
+
*/
|
|
186
|
+
getLayer(id: string): LambdaLayer | undefined {
|
|
187
|
+
return this.layers.get(id)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* List layers
|
|
192
|
+
*/
|
|
193
|
+
listLayers(): LambdaLayer[] {
|
|
194
|
+
return Array.from(this.layers.values())
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get layer versions
|
|
199
|
+
*/
|
|
200
|
+
getLayerVersions(layerName: string): LayerVersion[] {
|
|
201
|
+
return Array.from(this.versions.values()).filter(v => v.layerName === layerName)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generate CloudFormation for layer
|
|
206
|
+
*/
|
|
207
|
+
generateLayerCF(layer: LambdaLayer): any {
|
|
208
|
+
return {
|
|
209
|
+
Type: 'AWS::Lambda::LayerVersion',
|
|
210
|
+
Properties: {
|
|
211
|
+
LayerName: layer.layerName,
|
|
212
|
+
Description: layer.description,
|
|
213
|
+
Content: layer.content.type === 's3'
|
|
214
|
+
? {
|
|
215
|
+
S3Bucket: layer.content.s3Bucket,
|
|
216
|
+
S3Key: layer.content.s3Key,
|
|
217
|
+
...(layer.content.s3ObjectVersion && {
|
|
218
|
+
S3ObjectVersion: layer.content.s3ObjectVersion,
|
|
219
|
+
}),
|
|
220
|
+
}
|
|
221
|
+
: {
|
|
222
|
+
ZipFile: layer.content.zipFile,
|
|
223
|
+
},
|
|
224
|
+
CompatibleRuntimes: layer.compatibleRuntimes,
|
|
225
|
+
...(layer.licenseInfo && { LicenseInfo: layer.licenseInfo }),
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Generate CloudFormation for layer permission
|
|
232
|
+
*/
|
|
233
|
+
generateLayerPermissionCF(permission: LayerPermission): any {
|
|
234
|
+
return {
|
|
235
|
+
Type: 'AWS::Lambda::LayerVersionPermission',
|
|
236
|
+
Properties: {
|
|
237
|
+
LayerVersionArn: `arn:aws:lambda:us-east-1:123456789012:layer:${permission.layerName}:${permission.version}`,
|
|
238
|
+
Action: permission.action,
|
|
239
|
+
Principal: permission.principal,
|
|
240
|
+
...(permission.organizationId && {
|
|
241
|
+
OrganizationId: permission.organizationId,
|
|
242
|
+
}),
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Clear all data
|
|
249
|
+
*/
|
|
250
|
+
clear(): void {
|
|
251
|
+
this.layers.clear()
|
|
252
|
+
this.versions.clear()
|
|
253
|
+
this.permissions.clear()
|
|
254
|
+
this.layerCounter = 0
|
|
255
|
+
this.versionCounter = 0
|
|
256
|
+
this.permissionCounter = 0
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Global Lambda layers manager instance
|
|
262
|
+
*/
|
|
263
|
+
export const lambdaLayersManager: LambdaLayersManager = new LambdaLayersManager()
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lambda Versions and Aliases
|
|
3
|
+
* Immutable versions and mutable aliases for Lambda functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface LambdaVersion {
|
|
7
|
+
id: string
|
|
8
|
+
functionName: string
|
|
9
|
+
version: string
|
|
10
|
+
functionArn: string
|
|
11
|
+
codeHash: string
|
|
12
|
+
description?: string
|
|
13
|
+
runtime: string
|
|
14
|
+
memorySize: number
|
|
15
|
+
timeout: number
|
|
16
|
+
publishedAt: Date
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface LambdaAlias {
|
|
20
|
+
id: string
|
|
21
|
+
functionName: string
|
|
22
|
+
aliasName: string
|
|
23
|
+
aliasArn: string
|
|
24
|
+
functionVersion: string
|
|
25
|
+
description?: string
|
|
26
|
+
routingConfig?: RoutingConfig
|
|
27
|
+
revisionId?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RoutingConfig {
|
|
31
|
+
additionalVersionWeights?: Record<string, number>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface VersionDeployment {
|
|
35
|
+
id: string
|
|
36
|
+
functionName: string
|
|
37
|
+
fromVersion: string
|
|
38
|
+
toVersion: string
|
|
39
|
+
aliasName: string
|
|
40
|
+
strategy: 'all_at_once' | 'linear' | 'canary'
|
|
41
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed'
|
|
42
|
+
startedAt?: Date
|
|
43
|
+
completedAt?: Date
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Lambda versions manager
|
|
48
|
+
*/
|
|
49
|
+
export class LambdaVersionsManager {
|
|
50
|
+
private versions: Map<string, LambdaVersion> = new Map()
|
|
51
|
+
private aliases: Map<string, LambdaAlias> = new Map()
|
|
52
|
+
private deployments: Map<string, VersionDeployment> = new Map()
|
|
53
|
+
private versionCounter = 0
|
|
54
|
+
private aliasCounter = 0
|
|
55
|
+
private deploymentCounter = 0
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Publish function version
|
|
59
|
+
*/
|
|
60
|
+
publishVersion(options: {
|
|
61
|
+
functionName: string
|
|
62
|
+
description?: string
|
|
63
|
+
runtime: string
|
|
64
|
+
memorySize: number
|
|
65
|
+
timeout: number
|
|
66
|
+
}): LambdaVersion {
|
|
67
|
+
const id = `version-${Date.now()}-${this.versionCounter++}`
|
|
68
|
+
const versionNumber = this.getNextVersionNumber(options.functionName)
|
|
69
|
+
|
|
70
|
+
const version: LambdaVersion = {
|
|
71
|
+
id,
|
|
72
|
+
functionName: options.functionName,
|
|
73
|
+
version: versionNumber.toString(),
|
|
74
|
+
functionArn: `arn:aws:lambda:us-east-1:123456789012:function:${options.functionName}:${versionNumber}`,
|
|
75
|
+
codeHash: this.generateHash(),
|
|
76
|
+
description: options.description,
|
|
77
|
+
runtime: options.runtime,
|
|
78
|
+
memorySize: options.memorySize,
|
|
79
|
+
timeout: options.timeout,
|
|
80
|
+
publishedAt: new Date(),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.versions.set(id, version)
|
|
84
|
+
|
|
85
|
+
return version
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get next version number
|
|
90
|
+
*/
|
|
91
|
+
private getNextVersionNumber(functionName: string): number {
|
|
92
|
+
const existingVersions = Array.from(this.versions.values())
|
|
93
|
+
.filter(v => v.functionName === functionName)
|
|
94
|
+
.map(v => parseInt(v.version))
|
|
95
|
+
|
|
96
|
+
return existingVersions.length > 0 ? Math.max(...existingVersions) + 1 : 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create alias
|
|
101
|
+
*/
|
|
102
|
+
createAlias(alias: Omit<LambdaAlias, 'id' | 'aliasArn' | 'revisionId'>): LambdaAlias {
|
|
103
|
+
const id = `alias-${Date.now()}-${this.aliasCounter++}`
|
|
104
|
+
|
|
105
|
+
const lambdaAlias: LambdaAlias = {
|
|
106
|
+
id,
|
|
107
|
+
aliasArn: `arn:aws:lambda:us-east-1:123456789012:function:${alias.functionName}:${alias.aliasName}`,
|
|
108
|
+
revisionId: this.generateHash(),
|
|
109
|
+
...alias,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.aliases.set(id, lambdaAlias)
|
|
113
|
+
|
|
114
|
+
return lambdaAlias
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create production alias
|
|
119
|
+
*/
|
|
120
|
+
createProductionAlias(options: {
|
|
121
|
+
functionName: string
|
|
122
|
+
version: string
|
|
123
|
+
}): LambdaAlias {
|
|
124
|
+
return this.createAlias({
|
|
125
|
+
functionName: options.functionName,
|
|
126
|
+
aliasName: 'production',
|
|
127
|
+
functionVersion: options.version,
|
|
128
|
+
description: 'Production alias',
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create staging alias
|
|
134
|
+
*/
|
|
135
|
+
createStagingAlias(options: {
|
|
136
|
+
functionName: string
|
|
137
|
+
version: string
|
|
138
|
+
}): LambdaAlias {
|
|
139
|
+
return this.createAlias({
|
|
140
|
+
functionName: options.functionName,
|
|
141
|
+
aliasName: 'staging',
|
|
142
|
+
functionVersion: options.version,
|
|
143
|
+
description: 'Staging alias',
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Update alias
|
|
149
|
+
*/
|
|
150
|
+
updateAlias(aliasId: string, newVersion: string): LambdaAlias {
|
|
151
|
+
const alias = this.aliases.get(aliasId)
|
|
152
|
+
|
|
153
|
+
if (!alias) {
|
|
154
|
+
throw new Error(`Alias not found: ${aliasId}`)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
alias.functionVersion = newVersion
|
|
158
|
+
alias.revisionId = this.generateHash()
|
|
159
|
+
|
|
160
|
+
return alias
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Configure weighted routing
|
|
165
|
+
*/
|
|
166
|
+
configureWeightedRouting(
|
|
167
|
+
aliasId: string,
|
|
168
|
+
weights: Record<string, number>
|
|
169
|
+
): LambdaAlias {
|
|
170
|
+
const alias = this.aliases.get(aliasId)
|
|
171
|
+
|
|
172
|
+
if (!alias) {
|
|
173
|
+
throw new Error(`Alias not found: ${aliasId}`)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
alias.routingConfig = {
|
|
177
|
+
additionalVersionWeights: weights,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return alias
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create canary deployment
|
|
185
|
+
*/
|
|
186
|
+
createCanaryDeployment(options: {
|
|
187
|
+
functionName: string
|
|
188
|
+
fromVersion: string
|
|
189
|
+
toVersion: string
|
|
190
|
+
aliasName: string
|
|
191
|
+
canaryWeight: number
|
|
192
|
+
}): VersionDeployment {
|
|
193
|
+
const id = `deployment-${Date.now()}-${this.deploymentCounter++}`
|
|
194
|
+
|
|
195
|
+
// Find or create alias
|
|
196
|
+
let alias = Array.from(this.aliases.values()).find(
|
|
197
|
+
a => a.functionName === options.functionName && a.aliasName === options.aliasName
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if (!alias) {
|
|
201
|
+
alias = this.createAlias({
|
|
202
|
+
functionName: options.functionName,
|
|
203
|
+
aliasName: options.aliasName,
|
|
204
|
+
functionVersion: options.fromVersion,
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Configure weighted routing
|
|
209
|
+
this.configureWeightedRouting(alias.id, {
|
|
210
|
+
[options.toVersion]: options.canaryWeight,
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
const deployment: VersionDeployment = {
|
|
214
|
+
id,
|
|
215
|
+
functionName: options.functionName,
|
|
216
|
+
fromVersion: options.fromVersion,
|
|
217
|
+
toVersion: options.toVersion,
|
|
218
|
+
aliasName: options.aliasName,
|
|
219
|
+
strategy: 'canary',
|
|
220
|
+
status: 'in_progress',
|
|
221
|
+
startedAt: new Date(),
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.deployments.set(id, deployment)
|
|
225
|
+
|
|
226
|
+
return deployment
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Complete deployment
|
|
231
|
+
*/
|
|
232
|
+
completeDeployment(deploymentId: string): VersionDeployment {
|
|
233
|
+
const deployment = this.deployments.get(deploymentId)
|
|
234
|
+
|
|
235
|
+
if (!deployment) {
|
|
236
|
+
throw new Error(`Deployment not found: ${deploymentId}`)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Update alias to point to new version
|
|
240
|
+
const alias = Array.from(this.aliases.values()).find(
|
|
241
|
+
a =>
|
|
242
|
+
a.functionName === deployment.functionName &&
|
|
243
|
+
a.aliasName === deployment.aliasName
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if (alias) {
|
|
247
|
+
alias.functionVersion = deployment.toVersion
|
|
248
|
+
alias.routingConfig = undefined // Remove weighted routing
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
deployment.status = 'completed'
|
|
252
|
+
deployment.completedAt = new Date()
|
|
253
|
+
|
|
254
|
+
return deployment
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Rollback deployment
|
|
259
|
+
*/
|
|
260
|
+
rollbackDeployment(deploymentId: string): VersionDeployment {
|
|
261
|
+
const deployment = this.deployments.get(deploymentId)
|
|
262
|
+
|
|
263
|
+
if (!deployment) {
|
|
264
|
+
throw new Error(`Deployment not found: ${deploymentId}`)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Revert alias to previous version
|
|
268
|
+
const alias = Array.from(this.aliases.values()).find(
|
|
269
|
+
a =>
|
|
270
|
+
a.functionName === deployment.functionName &&
|
|
271
|
+
a.aliasName === deployment.aliasName
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if (alias) {
|
|
275
|
+
alias.functionVersion = deployment.fromVersion
|
|
276
|
+
alias.routingConfig = undefined
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
deployment.status = 'failed'
|
|
280
|
+
deployment.completedAt = new Date()
|
|
281
|
+
|
|
282
|
+
return deployment
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Generate hash
|
|
287
|
+
*/
|
|
288
|
+
private generateHash(): string {
|
|
289
|
+
return Math.random().toString(36).substring(2, 15)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get version
|
|
294
|
+
*/
|
|
295
|
+
getVersion(id: string): LambdaVersion | undefined {
|
|
296
|
+
return this.versions.get(id)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* List versions
|
|
301
|
+
*/
|
|
302
|
+
listVersions(functionName?: string): LambdaVersion[] {
|
|
303
|
+
const versions = Array.from(this.versions.values())
|
|
304
|
+
return functionName ? versions.filter(v => v.functionName === functionName) : versions
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get alias
|
|
309
|
+
*/
|
|
310
|
+
getAlias(id: string): LambdaAlias | undefined {
|
|
311
|
+
return this.aliases.get(id)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* List aliases
|
|
316
|
+
*/
|
|
317
|
+
listAliases(functionName?: string): LambdaAlias[] {
|
|
318
|
+
const aliases = Array.from(this.aliases.values())
|
|
319
|
+
return functionName ? aliases.filter(a => a.functionName === functionName) : aliases
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Generate CloudFormation for version
|
|
324
|
+
*/
|
|
325
|
+
generateVersionCF(version: LambdaVersion): any {
|
|
326
|
+
return {
|
|
327
|
+
Type: 'AWS::Lambda::Version',
|
|
328
|
+
Properties: {
|
|
329
|
+
FunctionName: version.functionName,
|
|
330
|
+
Description: version.description,
|
|
331
|
+
},
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Generate CloudFormation for alias
|
|
337
|
+
*/
|
|
338
|
+
generateAliasCF(alias: LambdaAlias): any {
|
|
339
|
+
return {
|
|
340
|
+
Type: 'AWS::Lambda::Alias',
|
|
341
|
+
Properties: {
|
|
342
|
+
FunctionName: alias.functionName,
|
|
343
|
+
Name: alias.aliasName,
|
|
344
|
+
FunctionVersion: alias.functionVersion,
|
|
345
|
+
Description: alias.description,
|
|
346
|
+
...(alias.routingConfig && {
|
|
347
|
+
RoutingConfig: {
|
|
348
|
+
AdditionalVersionWeights: Object.entries(
|
|
349
|
+
alias.routingConfig.additionalVersionWeights || {}
|
|
350
|
+
).map(([version, weight]) => ({
|
|
351
|
+
FunctionVersion: version,
|
|
352
|
+
FunctionWeight: weight,
|
|
353
|
+
})),
|
|
354
|
+
},
|
|
355
|
+
}),
|
|
356
|
+
},
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Clear all data
|
|
362
|
+
*/
|
|
363
|
+
clear(): void {
|
|
364
|
+
this.versions.clear()
|
|
365
|
+
this.aliases.clear()
|
|
366
|
+
this.deployments.clear()
|
|
367
|
+
this.versionCounter = 0
|
|
368
|
+
this.aliasCounter = 0
|
|
369
|
+
this.deploymentCounter = 0
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Global Lambda versions manager instance
|
|
375
|
+
*/
|
|
376
|
+
export const lambdaVersionsManager: LambdaVersionsManager = new LambdaVersionsManager()
|