@stacksjs/ts-cloud 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/package.json +18 -16
  3. package/src/aws/acm.ts +768 -0
  4. package/src/aws/application-autoscaling.ts +845 -0
  5. package/src/aws/bedrock.ts +4074 -0
  6. package/src/aws/client.ts +891 -0
  7. package/src/aws/cloudformation.ts +896 -0
  8. package/src/aws/cloudfront.ts +1531 -0
  9. package/src/aws/cloudwatch-logs.ts +154 -0
  10. package/src/aws/comprehend.ts +839 -0
  11. package/src/aws/connect.ts +1056 -0
  12. package/src/aws/deploy-imap.ts +384 -0
  13. package/src/aws/dynamodb.ts +340 -0
  14. package/src/aws/ec2.ts +1385 -0
  15. package/src/aws/ecr.ts +621 -0
  16. package/src/aws/ecs.ts +615 -0
  17. package/src/aws/elasticache.ts +301 -0
  18. package/src/aws/elbv2.ts +942 -0
  19. package/src/aws/email.ts +928 -0
  20. package/src/aws/eventbridge.ts +248 -0
  21. package/src/aws/iam.ts +1689 -0
  22. package/src/aws/imap-server.ts +2100 -0
  23. package/src/aws/index.ts +213 -0
  24. package/src/aws/kendra.ts +1097 -0
  25. package/src/aws/lambda.ts +786 -0
  26. package/src/aws/opensearch.ts +158 -0
  27. package/src/aws/personalize.ts +977 -0
  28. package/src/aws/polly.ts +559 -0
  29. package/src/aws/rds.ts +888 -0
  30. package/src/aws/rekognition.ts +846 -0
  31. package/src/aws/route53-domains.ts +359 -0
  32. package/src/aws/route53.ts +1046 -0
  33. package/src/aws/s3.ts +2334 -0
  34. package/src/aws/scheduler.ts +571 -0
  35. package/src/aws/secrets-manager.ts +769 -0
  36. package/src/aws/ses.ts +1081 -0
  37. package/src/aws/setup-phone.ts +104 -0
  38. package/src/aws/setup-sms.ts +580 -0
  39. package/src/aws/sms.ts +1735 -0
  40. package/src/aws/smtp-server.ts +531 -0
  41. package/src/aws/sns.ts +758 -0
  42. package/src/aws/sqs.ts +382 -0
  43. package/src/aws/ssm.ts +807 -0
  44. package/src/aws/sts.ts +92 -0
  45. package/src/aws/support.ts +391 -0
  46. package/src/aws/test-imap.ts +86 -0
  47. package/src/aws/textract.ts +780 -0
  48. package/src/aws/transcribe.ts +108 -0
  49. package/src/aws/translate.ts +641 -0
  50. package/src/aws/voice.ts +1379 -0
  51. package/src/config.ts +35 -0
  52. package/src/deploy/index.ts +7 -0
  53. package/src/deploy/static-site-external-dns.ts +945 -0
  54. package/src/deploy/static-site.ts +1175 -0
  55. package/src/dns/cloudflare.ts +548 -0
  56. package/src/dns/godaddy.ts +412 -0
  57. package/src/dns/index.ts +205 -0
  58. package/src/dns/porkbun.ts +362 -0
  59. package/src/dns/route53-adapter.ts +414 -0
  60. package/src/dns/types.ts +119 -0
  61. package/src/dns/validator.ts +369 -0
  62. package/src/generators/index.ts +5 -0
  63. package/src/generators/infrastructure.ts +1660 -0
  64. package/src/index.ts +163 -0
  65. package/src/push/apns.ts +452 -0
  66. package/src/push/fcm.ts +506 -0
  67. package/src/push/index.ts +58 -0
  68. package/src/security/pre-deploy-scanner.ts +655 -0
  69. package/src/ssl/acme-client.ts +478 -0
  70. package/src/ssl/index.ts +7 -0
  71. package/src/ssl/letsencrypt.ts +747 -0
  72. package/src/types.ts +2 -0
  73. package/src/utils/cli.ts +398 -0
  74. package/src/validation/index.ts +5 -0
  75. package/src/validation/template.ts +405 -0
@@ -0,0 +1,786 @@
1
+ /**
2
+ * AWS Lambda Operations
3
+ * Direct API calls without AWS SDK dependency
4
+ */
5
+
6
+ import { AWSClient } from './client'
7
+ import { deflateRawSync } from 'zlib'
8
+
9
+ /**
10
+ * Create a minimal ZIP file containing a single file
11
+ * This is a pure implementation without external dependencies
12
+ */
13
+ function createZipFile(filename: string, content: string | Buffer): Buffer {
14
+ const data = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content
15
+ const compressedData = deflateRawSync(data)
16
+
17
+ // Calculate CRC32
18
+ const crc32 = calculateCrc32(data)
19
+
20
+ // Current date/time in DOS format
21
+ const now = new Date()
22
+ const dosTime = ((now.getHours() << 11) | (now.getMinutes() << 5) | (now.getSeconds() >> 1)) & 0xffff
23
+ const dosDate = (((now.getFullYear() - 1980) << 9) | ((now.getMonth() + 1) << 5) | now.getDate()) & 0xffff
24
+
25
+ const filenameBuffer = Buffer.from(filename, 'utf-8')
26
+
27
+ // Local file header
28
+ const localHeader = Buffer.alloc(30 + filenameBuffer.length)
29
+ localHeader.writeUInt32LE(0x04034b50, 0) // Local file header signature
30
+ localHeader.writeUInt16LE(20, 4) // Version needed to extract
31
+ localHeader.writeUInt16LE(0, 6) // General purpose bit flag
32
+ localHeader.writeUInt16LE(8, 8) // Compression method (deflate)
33
+ localHeader.writeUInt16LE(dosTime, 10) // Last mod file time
34
+ localHeader.writeUInt16LE(dosDate, 12) // Last mod file date
35
+ localHeader.writeUInt32LE(crc32, 14) // CRC-32
36
+ localHeader.writeUInt32LE(compressedData.length, 18) // Compressed size
37
+ localHeader.writeUInt32LE(data.length, 22) // Uncompressed size
38
+ localHeader.writeUInt16LE(filenameBuffer.length, 26) // File name length
39
+ localHeader.writeUInt16LE(0, 28) // Extra field length
40
+ filenameBuffer.copy(localHeader, 30)
41
+
42
+ // Central directory header
43
+ const centralHeader = Buffer.alloc(46 + filenameBuffer.length)
44
+ centralHeader.writeUInt32LE(0x02014b50, 0) // Central directory signature
45
+ centralHeader.writeUInt16LE(20, 4) // Version made by
46
+ centralHeader.writeUInt16LE(20, 6) // Version needed to extract
47
+ centralHeader.writeUInt16LE(0, 8) // General purpose bit flag
48
+ centralHeader.writeUInt16LE(8, 10) // Compression method
49
+ centralHeader.writeUInt16LE(dosTime, 12) // Last mod file time
50
+ centralHeader.writeUInt16LE(dosDate, 14) // Last mod file date
51
+ centralHeader.writeUInt32LE(crc32, 16) // CRC-32
52
+ centralHeader.writeUInt32LE(compressedData.length, 20) // Compressed size
53
+ centralHeader.writeUInt32LE(data.length, 24) // Uncompressed size
54
+ centralHeader.writeUInt16LE(filenameBuffer.length, 28) // File name length
55
+ centralHeader.writeUInt16LE(0, 30) // Extra field length
56
+ centralHeader.writeUInt16LE(0, 32) // File comment length
57
+ centralHeader.writeUInt16LE(0, 34) // Disk number start
58
+ centralHeader.writeUInt16LE(0, 36) // Internal file attributes
59
+ centralHeader.writeUInt32LE(0, 38) // External file attributes
60
+ centralHeader.writeUInt32LE(0, 42) // Relative offset of local header
61
+ filenameBuffer.copy(centralHeader, 46)
62
+
63
+ // End of central directory record
64
+ const centralDirOffset = localHeader.length + compressedData.length
65
+ const centralDirSize = centralHeader.length
66
+ const endRecord = Buffer.alloc(22)
67
+ endRecord.writeUInt32LE(0x06054b50, 0) // End of central directory signature
68
+ endRecord.writeUInt16LE(0, 4) // Number of this disk
69
+ endRecord.writeUInt16LE(0, 6) // Disk where central directory starts
70
+ endRecord.writeUInt16LE(1, 8) // Number of central directory records on this disk
71
+ endRecord.writeUInt16LE(1, 10) // Total number of central directory records
72
+ endRecord.writeUInt32LE(centralDirSize, 12) // Size of central directory
73
+ endRecord.writeUInt32LE(centralDirOffset, 16) // Offset of start of central directory
74
+ endRecord.writeUInt16LE(0, 20) // Comment length
75
+
76
+ return Buffer.concat([localHeader, compressedData, centralHeader, endRecord])
77
+ }
78
+
79
+ /**
80
+ * Calculate CRC32 checksum
81
+ */
82
+ function calculateCrc32(data: Buffer): number {
83
+ // CRC32 lookup table
84
+ const table: number[] = []
85
+ for (let i = 0; i < 256; i++) {
86
+ let c = i
87
+ for (let j = 0; j < 8; j++) {
88
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
89
+ }
90
+ table[i] = c
91
+ }
92
+
93
+ let crc = 0xffffffff
94
+ for (let i = 0; i < data.length; i++) {
95
+ crc = table[(crc ^ data[i]) & 0xff] ^ (crc >>> 8)
96
+ }
97
+ return (crc ^ 0xffffffff) >>> 0
98
+ }
99
+
100
+ export interface LambdaFunctionConfiguration {
101
+ FunctionName?: string
102
+ FunctionArn?: string
103
+ Runtime?: string
104
+ Role?: string
105
+ Handler?: string
106
+ CodeSize?: number
107
+ Description?: string
108
+ Timeout?: number
109
+ MemorySize?: number
110
+ LastModified?: string
111
+ CodeSha256?: string
112
+ Version?: string
113
+ State?: 'Pending' | 'Active' | 'Inactive' | 'Failed'
114
+ StateReason?: string
115
+ StateReasonCode?: string
116
+ Environment?: {
117
+ Variables?: Record<string, string>
118
+ }
119
+ Architectures?: ('x86_64' | 'arm64')[]
120
+ }
121
+
122
+ export interface InvokeResult {
123
+ StatusCode?: number
124
+ FunctionError?: string
125
+ LogResult?: string
126
+ Payload?: string
127
+ ExecutedVersion?: string
128
+ }
129
+
130
+ export interface CreateFunctionParams {
131
+ FunctionName: string
132
+ Runtime: 'nodejs18.x' | 'nodejs20.x' | 'python3.11' | 'python3.12' | string
133
+ Role: string
134
+ Handler: string
135
+ Code: {
136
+ ZipFile?: string // Base64 encoded
137
+ S3Bucket?: string
138
+ S3Key?: string
139
+ S3ObjectVersion?: string
140
+ }
141
+ Description?: string
142
+ Timeout?: number
143
+ MemorySize?: number
144
+ Environment?: {
145
+ Variables: Record<string, string>
146
+ }
147
+ Tags?: Record<string, string>
148
+ Architectures?: ('x86_64' | 'arm64')[]
149
+ EphemeralStorage?: {
150
+ Size: number
151
+ }
152
+ }
153
+
154
+ export interface UpdateFunctionCodeParams {
155
+ FunctionName: string
156
+ ZipFile?: string // Base64 encoded
157
+ S3Bucket?: string
158
+ S3Key?: string
159
+ S3ObjectVersion?: string
160
+ Publish?: boolean
161
+ Architectures?: ('x86_64' | 'arm64')[]
162
+ }
163
+
164
+ export interface AddPermissionParams {
165
+ FunctionName: string
166
+ StatementId: string
167
+ Action: string
168
+ Principal: string
169
+ SourceArn?: string
170
+ SourceAccount?: string
171
+ }
172
+
173
+ /**
174
+ * Lambda service management using direct API calls
175
+ */
176
+ export class LambdaClient {
177
+ private client: AWSClient
178
+ private region: string
179
+
180
+ constructor(region: string = 'us-east-1') {
181
+ this.region = region
182
+ this.client = new AWSClient()
183
+ }
184
+
185
+ /**
186
+ * Create a new Lambda function
187
+ */
188
+ async createFunction(params: CreateFunctionParams): Promise<LambdaFunctionConfiguration> {
189
+ const result = await this.client.request({
190
+ service: 'lambda',
191
+ region: this.region,
192
+ method: 'POST',
193
+ path: '/2015-03-31/functions',
194
+ headers: {
195
+ 'Content-Type': 'application/json',
196
+ },
197
+ body: JSON.stringify(params),
198
+ })
199
+
200
+ return result
201
+ }
202
+
203
+ /**
204
+ * Get function configuration
205
+ */
206
+ async getFunction(functionName: string): Promise<{
207
+ Configuration?: LambdaFunctionConfiguration
208
+ Code?: {
209
+ RepositoryType?: string
210
+ Location?: string
211
+ }
212
+ Tags?: Record<string, string>
213
+ }> {
214
+ const result = await this.client.request({
215
+ service: 'lambda',
216
+ region: this.region,
217
+ method: 'GET',
218
+ path: `/2015-03-31/functions/${encodeURIComponent(functionName)}`,
219
+ headers: {
220
+ 'Content-Type': 'application/json',
221
+ },
222
+ })
223
+
224
+ return result
225
+ }
226
+
227
+ /**
228
+ * Update function code
229
+ */
230
+ async updateFunctionCode(params: UpdateFunctionCodeParams): Promise<LambdaFunctionConfiguration> {
231
+ const { FunctionName, ...rest } = params
232
+ const result = await this.client.request({
233
+ service: 'lambda',
234
+ region: this.region,
235
+ method: 'PUT',
236
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/code`,
237
+ headers: {
238
+ 'Content-Type': 'application/json',
239
+ },
240
+ body: JSON.stringify(rest),
241
+ })
242
+
243
+ return result
244
+ }
245
+
246
+ /**
247
+ * Update function code with inline JavaScript/TypeScript code
248
+ * Automatically creates a zip file with the code as index.js
249
+ */
250
+ async updateFunctionCodeInline(functionName: string, code: string, filename: string = 'index.js'): Promise<LambdaFunctionConfiguration> {
251
+ const zipBuffer = createZipFile(filename, code)
252
+ return this.updateFunctionCode({
253
+ FunctionName: functionName,
254
+ ZipFile: zipBuffer.toString('base64'),
255
+ })
256
+ }
257
+
258
+ /**
259
+ * Update function configuration
260
+ */
261
+ async updateFunctionConfiguration(params: {
262
+ FunctionName: string
263
+ Runtime?: string
264
+ Role?: string
265
+ Handler?: string
266
+ Description?: string
267
+ Timeout?: number
268
+ MemorySize?: number
269
+ Environment?: { Variables: Record<string, string> }
270
+ }): Promise<LambdaFunctionConfiguration> {
271
+ const { FunctionName, ...rest } = params
272
+ const result = await this.client.request({
273
+ service: 'lambda',
274
+ region: this.region,
275
+ method: 'PUT',
276
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/configuration`,
277
+ headers: {
278
+ 'Content-Type': 'application/json',
279
+ },
280
+ body: JSON.stringify(rest),
281
+ })
282
+
283
+ return result
284
+ }
285
+
286
+ /**
287
+ * Delete a Lambda function
288
+ */
289
+ async deleteFunction(functionName: string): Promise<void> {
290
+ await this.client.request({
291
+ service: 'lambda',
292
+ region: this.region,
293
+ method: 'DELETE',
294
+ path: `/2015-03-31/functions/${encodeURIComponent(functionName)}`,
295
+ headers: {
296
+ 'Content-Type': 'application/json',
297
+ },
298
+ })
299
+ }
300
+
301
+ /**
302
+ * Invoke a Lambda function
303
+ */
304
+ async invoke(params: {
305
+ FunctionName: string
306
+ InvocationType?: 'RequestResponse' | 'Event' | 'DryRun'
307
+ Payload?: string | object
308
+ LogType?: 'None' | 'Tail'
309
+ }): Promise<InvokeResult> {
310
+ const { FunctionName, InvocationType = 'RequestResponse', Payload, LogType } = params
311
+
312
+ const headers: Record<string, string> = {
313
+ 'Content-Type': 'application/json',
314
+ 'X-Amz-Invocation-Type': InvocationType,
315
+ }
316
+
317
+ if (LogType) {
318
+ headers['X-Amz-Log-Type'] = LogType
319
+ }
320
+
321
+ const body = Payload
322
+ ? typeof Payload === 'string'
323
+ ? Payload
324
+ : JSON.stringify(Payload)
325
+ : undefined
326
+
327
+ const result = await this.client.request({
328
+ service: 'lambda',
329
+ region: this.region,
330
+ method: 'POST',
331
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/invocations`,
332
+ headers,
333
+ body,
334
+ returnHeaders: true,
335
+ })
336
+
337
+ return {
338
+ StatusCode: result.statusCode || 200,
339
+ FunctionError: result.headers?.['x-amz-function-error'],
340
+ LogResult: result.headers?.['x-amz-log-result'],
341
+ Payload: typeof result.body === 'string' ? result.body : JSON.stringify(result.body),
342
+ ExecutedVersion: result.headers?.['x-amz-executed-version'],
343
+ }
344
+ }
345
+
346
+ /**
347
+ * List Lambda functions
348
+ */
349
+ async listFunctions(params?: {
350
+ MaxItems?: number
351
+ Marker?: string
352
+ FunctionVersion?: 'ALL'
353
+ }): Promise<{
354
+ Functions?: LambdaFunctionConfiguration[]
355
+ NextMarker?: string
356
+ }> {
357
+ const queryParams: Record<string, string> = {}
358
+ if (params?.MaxItems) queryParams.MaxItems = String(params.MaxItems)
359
+ if (params?.Marker) queryParams.Marker = params.Marker
360
+ if (params?.FunctionVersion) queryParams.FunctionVersion = params.FunctionVersion
361
+
362
+ const result = await this.client.request({
363
+ service: 'lambda',
364
+ region: this.region,
365
+ method: 'GET',
366
+ path: '/2015-03-31/functions',
367
+ queryParams: Object.keys(queryParams).length > 0 ? queryParams : undefined,
368
+ headers: {
369
+ 'Content-Type': 'application/json',
370
+ },
371
+ })
372
+
373
+ return result
374
+ }
375
+
376
+ /**
377
+ * Add permission to Lambda function (resource-based policy)
378
+ */
379
+ async addPermission(params: AddPermissionParams): Promise<{ Statement?: string }> {
380
+ const { FunctionName, ...rest } = params
381
+ const result = await this.client.request({
382
+ service: 'lambda',
383
+ region: this.region,
384
+ method: 'POST',
385
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/policy`,
386
+ headers: {
387
+ 'Content-Type': 'application/json',
388
+ },
389
+ body: JSON.stringify(rest),
390
+ })
391
+
392
+ return result
393
+ }
394
+
395
+ /**
396
+ * Remove permission from Lambda function
397
+ */
398
+ async removePermission(functionName: string, statementId: string): Promise<void> {
399
+ await this.client.request({
400
+ service: 'lambda',
401
+ region: this.region,
402
+ method: 'DELETE',
403
+ path: `/2015-03-31/functions/${encodeURIComponent(functionName)}/policy/${encodeURIComponent(statementId)}`,
404
+ headers: {
405
+ 'Content-Type': 'application/json',
406
+ },
407
+ })
408
+ }
409
+
410
+ /**
411
+ * Publish a version of the function
412
+ */
413
+ async publishVersion(params: {
414
+ FunctionName: string
415
+ Description?: string
416
+ CodeSha256?: string
417
+ }): Promise<LambdaFunctionConfiguration> {
418
+ const { FunctionName, ...rest } = params
419
+ const result = await this.client.request({
420
+ service: 'lambda',
421
+ region: this.region,
422
+ method: 'POST',
423
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/versions`,
424
+ headers: {
425
+ 'Content-Type': 'application/json',
426
+ },
427
+ body: JSON.stringify(rest),
428
+ })
429
+
430
+ return result
431
+ }
432
+
433
+ /**
434
+ * Create an alias for a function version
435
+ */
436
+ async createAlias(params: {
437
+ FunctionName: string
438
+ Name: string
439
+ FunctionVersion: string
440
+ Description?: string
441
+ }): Promise<{
442
+ AliasArn?: string
443
+ Name?: string
444
+ FunctionVersion?: string
445
+ Description?: string
446
+ }> {
447
+ const { FunctionName, ...rest } = params
448
+ const result = await this.client.request({
449
+ service: 'lambda',
450
+ region: this.region,
451
+ method: 'POST',
452
+ path: `/2015-03-31/functions/${encodeURIComponent(FunctionName)}/aliases`,
453
+ headers: {
454
+ 'Content-Type': 'application/json',
455
+ },
456
+ body: JSON.stringify(rest),
457
+ })
458
+
459
+ return result
460
+ }
461
+
462
+ /**
463
+ * Wait for function to become active
464
+ */
465
+ async waitForFunctionActive(functionName: string, maxWaitSeconds: number = 60): Promise<LambdaFunctionConfiguration> {
466
+ const startTime = Date.now()
467
+ const maxWaitMs = maxWaitSeconds * 1000
468
+
469
+ while (Date.now() - startTime < maxWaitMs) {
470
+ try {
471
+ const response = await this.getFunction(functionName)
472
+ const state = response.Configuration?.State
473
+
474
+ if (state === 'Active') {
475
+ return response.Configuration!
476
+ }
477
+
478
+ if (state === 'Failed') {
479
+ throw new Error(`Function ${functionName} failed: ${response.Configuration?.StateReason}`)
480
+ }
481
+
482
+ // Wait 2 seconds before checking again
483
+ await new Promise(resolve => setTimeout(resolve, 2000))
484
+ }
485
+ catch (error: any) {
486
+ if (error.code === 'ResourceNotFoundException') {
487
+ await new Promise(resolve => setTimeout(resolve, 2000))
488
+ continue
489
+ }
490
+ throw error
491
+ }
492
+ }
493
+
494
+ throw new Error(`Timeout waiting for function ${functionName} to become active`)
495
+ }
496
+
497
+ /**
498
+ * Check if function exists
499
+ */
500
+ async functionExists(functionName: string): Promise<boolean> {
501
+ try {
502
+ await this.getFunction(functionName)
503
+ return true
504
+ }
505
+ catch (error: any) {
506
+ if (error.code === 'ResourceNotFoundException' || error.statusCode === 404) {
507
+ return false
508
+ }
509
+ throw error
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Create a function URL for the Lambda function
515
+ */
516
+ async createFunctionUrl(params: {
517
+ FunctionName: string
518
+ AuthType: 'NONE' | 'AWS_IAM'
519
+ Cors?: {
520
+ AllowOrigins?: string[]
521
+ AllowMethods?: string[]
522
+ AllowHeaders?: string[]
523
+ ExposeHeaders?: string[]
524
+ MaxAge?: number
525
+ AllowCredentials?: boolean
526
+ }
527
+ InvokeMode?: 'BUFFERED' | 'RESPONSE_STREAM'
528
+ }): Promise<{
529
+ FunctionUrl?: string
530
+ FunctionArn?: string
531
+ AuthType?: string
532
+ CreationTime?: string
533
+ }> {
534
+ const { FunctionName, ...rest } = params
535
+ const result = await this.client.request({
536
+ service: 'lambda',
537
+ region: this.region,
538
+ method: 'POST',
539
+ path: `/2021-10-31/functions/${encodeURIComponent(FunctionName)}/url`,
540
+ headers: {
541
+ 'Content-Type': 'application/json',
542
+ },
543
+ body: JSON.stringify(rest),
544
+ })
545
+
546
+ return result
547
+ }
548
+
549
+ /**
550
+ * Get function URL configuration
551
+ */
552
+ async getFunctionUrl(functionName: string): Promise<{
553
+ FunctionUrl?: string
554
+ FunctionArn?: string
555
+ AuthType?: string
556
+ Cors?: {
557
+ AllowOrigins?: string[]
558
+ AllowMethods?: string[]
559
+ AllowHeaders?: string[]
560
+ }
561
+ CreationTime?: string
562
+ } | null> {
563
+ try {
564
+ const result = await this.client.request({
565
+ service: 'lambda',
566
+ region: this.region,
567
+ method: 'GET',
568
+ path: `/2021-10-31/functions/${encodeURIComponent(functionName)}/url`,
569
+ headers: {
570
+ 'Content-Type': 'application/json',
571
+ },
572
+ })
573
+ return result
574
+ }
575
+ catch (error: any) {
576
+ if (error.statusCode === 404) {
577
+ return null
578
+ }
579
+ throw error
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Delete function URL configuration
585
+ */
586
+ async deleteFunctionUrl(functionName: string): Promise<void> {
587
+ await this.client.request({
588
+ service: 'lambda',
589
+ region: this.region,
590
+ method: 'DELETE',
591
+ path: `/2021-10-31/functions/${encodeURIComponent(functionName)}/url`,
592
+ headers: {
593
+ 'Content-Type': 'application/json',
594
+ },
595
+ })
596
+ }
597
+
598
+ /**
599
+ * Create function with inline code (convenience method)
600
+ */
601
+ async createFunctionWithCode(params: {
602
+ FunctionName: string
603
+ Runtime: 'nodejs18.x' | 'nodejs20.x' | 'python3.11' | 'python3.12' | string
604
+ Role: string
605
+ Handler: string
606
+ Code: string
607
+ Filename?: string
608
+ Description?: string
609
+ Timeout?: number
610
+ MemorySize?: number
611
+ Environment?: { Variables: Record<string, string> }
612
+ }): Promise<LambdaFunctionConfiguration> {
613
+ const { Code, Filename = 'index.js', ...rest } = params
614
+ const zipBuffer = createZipFile(Filename, Code)
615
+
616
+ return this.createFunction({
617
+ ...rest,
618
+ Code: {
619
+ ZipFile: zipBuffer.toString('base64'),
620
+ },
621
+ })
622
+ }
623
+
624
+ /**
625
+ * Add permission for Function URL public access
626
+ */
627
+ async addFunctionUrlPermission(functionName: string): Promise<{ Statement?: string }> {
628
+ return this.addPermission({
629
+ FunctionName: functionName,
630
+ StatementId: 'FunctionURLAllowPublicAccess',
631
+ Action: 'lambda:InvokeFunctionUrl',
632
+ Principal: '*',
633
+ // @ts-ignore - FunctionUrlAuthType is a valid parameter
634
+ FunctionUrlAuthType: 'NONE',
635
+ })
636
+ }
637
+
638
+ // =========================================================================
639
+ // Layer Operations
640
+ // =========================================================================
641
+
642
+ /**
643
+ * Publish a new Lambda layer version
644
+ */
645
+ async publishLayerVersion(params: {
646
+ LayerName: string
647
+ Description?: string
648
+ Content: {
649
+ S3Bucket?: string
650
+ S3Key?: string
651
+ ZipFile?: string // Base64 encoded zip
652
+ }
653
+ CompatibleRuntimes?: string[]
654
+ CompatibleArchitectures?: ('x86_64' | 'arm64')[]
655
+ LicenseInfo?: string
656
+ }): Promise<{
657
+ LayerArn?: string
658
+ LayerVersionArn?: string
659
+ Description?: string
660
+ Version?: number
661
+ CompatibleRuntimes?: string[]
662
+ CompatibleArchitectures?: string[]
663
+ }> {
664
+ const { LayerName, ...rest } = params
665
+ const result = await this.client.request({
666
+ service: 'lambda',
667
+ region: this.region,
668
+ method: 'POST',
669
+ path: `/2018-10-31/layers/${encodeURIComponent(LayerName)}/versions`,
670
+ headers: {
671
+ 'Content-Type': 'application/json',
672
+ },
673
+ body: JSON.stringify(rest),
674
+ })
675
+
676
+ return result
677
+ }
678
+
679
+ /**
680
+ * List layer versions
681
+ */
682
+ async listLayerVersions(layerName: string, params?: {
683
+ CompatibleRuntime?: string
684
+ CompatibleArchitecture?: 'x86_64' | 'arm64'
685
+ MaxItems?: number
686
+ Marker?: string
687
+ }): Promise<{
688
+ LayerVersions?: Array<{
689
+ LayerVersionArn?: string
690
+ Version?: number
691
+ Description?: string
692
+ CompatibleRuntimes?: string[]
693
+ CompatibleArchitectures?: string[]
694
+ }>
695
+ NextMarker?: string
696
+ }> {
697
+ const queryParams: Record<string, string> = {}
698
+ if (params?.CompatibleRuntime) queryParams.CompatibleRuntime = params.CompatibleRuntime
699
+ if (params?.CompatibleArchitecture) queryParams.CompatibleArchitecture = params.CompatibleArchitecture
700
+ if (params?.MaxItems) queryParams.MaxItems = String(params.MaxItems)
701
+ if (params?.Marker) queryParams.Marker = params.Marker
702
+
703
+ const result = await this.client.request({
704
+ service: 'lambda',
705
+ region: this.region,
706
+ method: 'GET',
707
+ path: `/2018-10-31/layers/${encodeURIComponent(layerName)}/versions`,
708
+ queryParams: Object.keys(queryParams).length > 0 ? queryParams : undefined,
709
+ headers: {
710
+ 'Content-Type': 'application/json',
711
+ },
712
+ })
713
+
714
+ return result
715
+ }
716
+
717
+ /**
718
+ * Get layer version details
719
+ */
720
+ async getLayerVersion(layerName: string, versionNumber: number): Promise<{
721
+ LayerArn?: string
722
+ LayerVersionArn?: string
723
+ Description?: string
724
+ Version?: number
725
+ CompatibleRuntimes?: string[]
726
+ CompatibleArchitectures?: string[]
727
+ Content?: {
728
+ Location?: string
729
+ CodeSha256?: string
730
+ CodeSize?: number
731
+ }
732
+ }> {
733
+ const result = await this.client.request({
734
+ service: 'lambda',
735
+ region: this.region,
736
+ method: 'GET',
737
+ path: `/2018-10-31/layers/${encodeURIComponent(layerName)}/versions/${versionNumber}`,
738
+ headers: {
739
+ 'Content-Type': 'application/json',
740
+ },
741
+ })
742
+
743
+ return result
744
+ }
745
+
746
+ /**
747
+ * Add permission to a layer version (e.g., make it public)
748
+ */
749
+ async addLayerVersionPermission(params: {
750
+ LayerName: string
751
+ VersionNumber: number
752
+ StatementId: string
753
+ Action: string
754
+ Principal: string
755
+ OrganizationId?: string
756
+ }): Promise<{ Statement?: string; RevisionId?: string }> {
757
+ const { LayerName, VersionNumber, ...rest } = params
758
+ const result = await this.client.request({
759
+ service: 'lambda',
760
+ region: this.region,
761
+ method: 'POST',
762
+ path: `/2018-10-31/layers/${encodeURIComponent(LayerName)}/versions/${VersionNumber}/policy`,
763
+ headers: {
764
+ 'Content-Type': 'application/json',
765
+ },
766
+ body: JSON.stringify(rest),
767
+ })
768
+
769
+ return result
770
+ }
771
+
772
+ /**
773
+ * Delete a layer version
774
+ */
775
+ async deleteLayerVersion(layerName: string, versionNumber: number): Promise<void> {
776
+ await this.client.request({
777
+ service: 'lambda',
778
+ region: this.region,
779
+ method: 'DELETE',
780
+ path: `/2018-10-31/layers/${encodeURIComponent(layerName)}/versions/${versionNumber}`,
781
+ headers: {
782
+ 'Content-Type': 'application/json',
783
+ },
784
+ })
785
+ }
786
+ }