@stacksjs/ts-cloud 0.1.9 → 0.1.14

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 (150) hide show
  1. package/README.md +39 -377
  2. package/dist/bin/cli.js +1047 -424
  3. package/dist/index.d.ts +36 -3
  4. package/dist/index.js +76430 -7096
  5. package/package.json +7 -8
  6. package/dist/aws/acm.d.ts +0 -129
  7. package/dist/aws/application-autoscaling.d.ts +0 -282
  8. package/dist/aws/bedrock.d.ts +0 -2292
  9. package/dist/aws/client.d.ts +0 -79
  10. package/dist/aws/cloudformation.d.ts +0 -105
  11. package/dist/aws/cloudfront.d.ts +0 -265
  12. package/dist/aws/cloudwatch-logs.d.ts +0 -48
  13. package/dist/aws/comprehend.d.ts +0 -505
  14. package/dist/aws/connect.d.ts +0 -377
  15. package/dist/aws/deploy-imap.d.ts +0 -14
  16. package/dist/aws/dynamodb.d.ts +0 -176
  17. package/dist/aws/ec2.d.ts +0 -272
  18. package/dist/aws/ecr.d.ts +0 -149
  19. package/dist/aws/ecs.d.ts +0 -162
  20. package/dist/aws/elasticache.d.ts +0 -71
  21. package/dist/aws/elbv2.d.ts +0 -248
  22. package/dist/aws/email.d.ts +0 -175
  23. package/dist/aws/eventbridge.d.ts +0 -142
  24. package/dist/aws/iam.d.ts +0 -638
  25. package/dist/aws/imap-server.d.ts +0 -119
  26. package/dist/aws/index.d.ts +0 -192
  27. package/dist/aws/kendra.d.ts +0 -782
  28. package/dist/aws/lambda.d.ts +0 -232
  29. package/dist/aws/opensearch.d.ts +0 -87
  30. package/dist/aws/personalize.d.ts +0 -516
  31. package/dist/aws/polly.d.ts +0 -214
  32. package/dist/aws/rds.d.ts +0 -240
  33. package/dist/aws/rekognition.d.ts +0 -543
  34. package/dist/aws/route53-domains.d.ts +0 -113
  35. package/dist/aws/route53.d.ts +0 -215
  36. package/dist/aws/s3.d.ts +0 -212
  37. package/dist/aws/scheduler.d.ts +0 -140
  38. package/dist/aws/secrets-manager.d.ts +0 -170
  39. package/dist/aws/ses.d.ts +0 -288
  40. package/dist/aws/setup-phone.d.ts +0 -0
  41. package/dist/aws/setup-sms.d.ts +0 -115
  42. package/dist/aws/sms.d.ts +0 -304
  43. package/dist/aws/smtp-server.d.ts +0 -61
  44. package/dist/aws/sns.d.ts +0 -117
  45. package/dist/aws/sqs.d.ts +0 -65
  46. package/dist/aws/ssm.d.ts +0 -179
  47. package/dist/aws/sts.d.ts +0 -15
  48. package/dist/aws/support.d.ts +0 -104
  49. package/dist/aws/test-imap.d.ts +0 -0
  50. package/dist/aws/textract.d.ts +0 -403
  51. package/dist/aws/transcribe.d.ts +0 -60
  52. package/dist/aws/translate.d.ts +0 -358
  53. package/dist/aws/voice.d.ts +0 -219
  54. package/dist/config.d.ts +0 -7
  55. package/dist/deploy/index.d.ts +0 -2
  56. package/dist/deploy/static-site-external-dns.d.ts +0 -51
  57. package/dist/deploy/static-site.d.ts +0 -71
  58. package/dist/dns/cloudflare.d.ts +0 -52
  59. package/dist/dns/godaddy.d.ts +0 -38
  60. package/dist/dns/index.d.ts +0 -45
  61. package/dist/dns/porkbun.d.ts +0 -18
  62. package/dist/dns/route53-adapter.d.ts +0 -38
  63. package/dist/dns/types.d.ts +0 -77
  64. package/dist/dns/validator.d.ts +0 -78
  65. package/dist/generators/index.d.ts +0 -1
  66. package/dist/generators/infrastructure.d.ts +0 -30
  67. package/dist/push/apns.d.ts +0 -60
  68. package/dist/push/fcm.d.ts +0 -117
  69. package/dist/push/index.d.ts +0 -14
  70. package/dist/security/pre-deploy-scanner.d.ts +0 -69
  71. package/dist/ssl/acme-client.d.ts +0 -67
  72. package/dist/ssl/index.d.ts +0 -2
  73. package/dist/ssl/letsencrypt.d.ts +0 -48
  74. package/dist/types.d.ts +0 -1
  75. package/dist/utils/cli.d.ts +0 -123
  76. package/dist/validation/index.d.ts +0 -1
  77. package/dist/validation/template.d.ts +0 -23
  78. package/src/aws/acm.ts +0 -768
  79. package/src/aws/application-autoscaling.ts +0 -845
  80. package/src/aws/bedrock.ts +0 -4074
  81. package/src/aws/client.ts +0 -891
  82. package/src/aws/cloudformation.ts +0 -896
  83. package/src/aws/cloudfront.ts +0 -1531
  84. package/src/aws/cloudwatch-logs.ts +0 -154
  85. package/src/aws/comprehend.ts +0 -839
  86. package/src/aws/connect.ts +0 -1056
  87. package/src/aws/deploy-imap.ts +0 -384
  88. package/src/aws/dynamodb.ts +0 -340
  89. package/src/aws/ec2.ts +0 -1385
  90. package/src/aws/ecr.ts +0 -621
  91. package/src/aws/ecs.ts +0 -615
  92. package/src/aws/elasticache.ts +0 -301
  93. package/src/aws/elbv2.ts +0 -942
  94. package/src/aws/email.ts +0 -928
  95. package/src/aws/eventbridge.ts +0 -248
  96. package/src/aws/iam.ts +0 -1689
  97. package/src/aws/imap-server.ts +0 -2100
  98. package/src/aws/index.ts +0 -213
  99. package/src/aws/kendra.ts +0 -1097
  100. package/src/aws/lambda.ts +0 -786
  101. package/src/aws/opensearch.ts +0 -158
  102. package/src/aws/personalize.ts +0 -977
  103. package/src/aws/polly.ts +0 -559
  104. package/src/aws/rds.ts +0 -888
  105. package/src/aws/rekognition.ts +0 -846
  106. package/src/aws/route53-domains.ts +0 -359
  107. package/src/aws/route53.ts +0 -1046
  108. package/src/aws/s3.ts +0 -2334
  109. package/src/aws/scheduler.ts +0 -571
  110. package/src/aws/secrets-manager.ts +0 -769
  111. package/src/aws/ses.ts +0 -1081
  112. package/src/aws/setup-phone.ts +0 -104
  113. package/src/aws/setup-sms.ts +0 -580
  114. package/src/aws/sms.ts +0 -1735
  115. package/src/aws/smtp-server.ts +0 -531
  116. package/src/aws/sns.ts +0 -758
  117. package/src/aws/sqs.ts +0 -382
  118. package/src/aws/ssm.ts +0 -807
  119. package/src/aws/sts.ts +0 -92
  120. package/src/aws/support.ts +0 -391
  121. package/src/aws/test-imap.ts +0 -86
  122. package/src/aws/textract.ts +0 -780
  123. package/src/aws/transcribe.ts +0 -108
  124. package/src/aws/translate.ts +0 -641
  125. package/src/aws/voice.ts +0 -1379
  126. package/src/config.ts +0 -35
  127. package/src/deploy/index.ts +0 -7
  128. package/src/deploy/static-site-external-dns.ts +0 -945
  129. package/src/deploy/static-site.ts +0 -1175
  130. package/src/dns/cloudflare.ts +0 -548
  131. package/src/dns/godaddy.ts +0 -412
  132. package/src/dns/index.ts +0 -205
  133. package/src/dns/porkbun.ts +0 -362
  134. package/src/dns/route53-adapter.ts +0 -414
  135. package/src/dns/types.ts +0 -119
  136. package/src/dns/validator.ts +0 -369
  137. package/src/generators/index.ts +0 -5
  138. package/src/generators/infrastructure.ts +0 -1660
  139. package/src/index.ts +0 -163
  140. package/src/push/apns.ts +0 -452
  141. package/src/push/fcm.ts +0 -506
  142. package/src/push/index.ts +0 -58
  143. package/src/security/pre-deploy-scanner.ts +0 -655
  144. package/src/ssl/acme-client.ts +0 -478
  145. package/src/ssl/index.ts +0 -7
  146. package/src/ssl/letsencrypt.ts +0 -747
  147. package/src/types.ts +0 -2
  148. package/src/utils/cli.ts +0 -398
  149. package/src/validation/index.ts +0 -5
  150. package/src/validation/template.ts +0 -405
package/src/aws/elbv2.ts DELETED
@@ -1,942 +0,0 @@
1
- /**
2
- * AWS Elastic Load Balancing V2 (ELBv2) Operations
3
- * Direct API calls without AWS CLI dependency
4
- *
5
- * Supports Application Load Balancers (ALB), Network Load Balancers (NLB),
6
- * and Gateway Load Balancers (GWLB)
7
- */
8
-
9
- import { AWSClient } from './client'
10
-
11
- export interface LoadBalancer {
12
- LoadBalancerArn?: string
13
- DNSName?: string
14
- CanonicalHostedZoneId?: string
15
- CreatedTime?: string
16
- LoadBalancerName?: string
17
- Scheme?: 'internet-facing' | 'internal'
18
- VpcId?: string
19
- State?: {
20
- Code?: 'active' | 'provisioning' | 'active_impaired' | 'failed'
21
- Reason?: string
22
- }
23
- Type?: 'application' | 'network' | 'gateway'
24
- AvailabilityZones?: AvailabilityZone[]
25
- SecurityGroups?: string[]
26
- IpAddressType?: 'ipv4' | 'dualstack'
27
- }
28
-
29
- export interface AvailabilityZone {
30
- ZoneName?: string
31
- SubnetId?: string
32
- OutpostId?: string
33
- LoadBalancerAddresses?: LoadBalancerAddress[]
34
- }
35
-
36
- export interface LoadBalancerAddress {
37
- IpAddress?: string
38
- AllocationId?: string
39
- PrivateIPv4Address?: string
40
- IPv6Address?: string
41
- }
42
-
43
- export interface TargetGroup {
44
- TargetGroupArn?: string
45
- TargetGroupName?: string
46
- Protocol?: string
47
- Port?: number
48
- VpcId?: string
49
- HealthCheckProtocol?: string
50
- HealthCheckPort?: string
51
- HealthCheckEnabled?: boolean
52
- HealthCheckIntervalSeconds?: number
53
- HealthCheckTimeoutSeconds?: number
54
- HealthyThresholdCount?: number
55
- UnhealthyThresholdCount?: number
56
- HealthCheckPath?: string
57
- Matcher?: {
58
- HttpCode?: string
59
- GrpcCode?: string
60
- }
61
- LoadBalancerArns?: string[]
62
- TargetType?: 'instance' | 'ip' | 'lambda' | 'alb'
63
- ProtocolVersion?: string
64
- IpAddressType?: 'ipv4' | 'ipv6'
65
- }
66
-
67
- export interface Listener {
68
- ListenerArn?: string
69
- LoadBalancerArn?: string
70
- Port?: number
71
- Protocol?: string
72
- Certificates?: Certificate[]
73
- SslPolicy?: string
74
- DefaultActions?: Action[]
75
- AlpnPolicy?: string[]
76
- }
77
-
78
- export interface Certificate {
79
- CertificateArn?: string
80
- IsDefault?: boolean
81
- }
82
-
83
- export interface Action {
84
- Type?: 'forward' | 'redirect' | 'fixed-response' | 'authenticate-oidc' | 'authenticate-cognito'
85
- TargetGroupArn?: string
86
- Order?: number
87
- RedirectConfig?: {
88
- Protocol?: string
89
- Port?: string
90
- Host?: string
91
- Path?: string
92
- Query?: string
93
- StatusCode?: 'HTTP_301' | 'HTTP_302'
94
- }
95
- FixedResponseConfig?: {
96
- MessageBody?: string
97
- StatusCode?: string
98
- ContentType?: string
99
- }
100
- ForwardConfig?: {
101
- TargetGroups?: Array<{
102
- TargetGroupArn?: string
103
- Weight?: number
104
- }>
105
- TargetGroupStickinessConfig?: {
106
- Enabled?: boolean
107
- DurationSeconds?: number
108
- }
109
- }
110
- }
111
-
112
- export interface Rule {
113
- RuleArn?: string
114
- Priority?: string
115
- Conditions?: Condition[]
116
- Actions?: Action[]
117
- IsDefault?: boolean
118
- }
119
-
120
- export interface Condition {
121
- Field?: string
122
- Values?: string[]
123
- HostHeaderConfig?: { Values?: string[] }
124
- PathPatternConfig?: { Values?: string[] }
125
- HttpHeaderConfig?: { HttpHeaderName?: string, Values?: string[] }
126
- QueryStringConfig?: { Values?: Array<{ Key?: string, Value?: string }> }
127
- HttpRequestMethodConfig?: { Values?: string[] }
128
- SourceIpConfig?: { Values?: string[] }
129
- }
130
-
131
- export interface TargetHealthDescription {
132
- Target?: {
133
- Id?: string
134
- Port?: number
135
- AvailabilityZone?: string
136
- }
137
- HealthCheckPort?: string
138
- TargetHealth?: {
139
- State?: 'initial' | 'healthy' | 'unhealthy' | 'unused' | 'draining' | 'unavailable'
140
- Reason?: string
141
- Description?: string
142
- }
143
- }
144
-
145
- /**
146
- * ELBv2 client for managing Application, Network, and Gateway Load Balancers
147
- */
148
- export class ELBv2Client {
149
- private client: AWSClient
150
- private region: string
151
-
152
- constructor(region: string = 'us-east-1') {
153
- this.region = region
154
- this.client = new AWSClient()
155
- }
156
-
157
- /**
158
- * Describe load balancers
159
- */
160
- async describeLoadBalancers(options?: {
161
- LoadBalancerArns?: string[]
162
- Names?: string[]
163
- Marker?: string
164
- PageSize?: number
165
- }): Promise<{ LoadBalancers?: LoadBalancer[], NextMarker?: string }> {
166
- const params: Record<string, any> = {}
167
-
168
- if (options?.LoadBalancerArns) {
169
- options.LoadBalancerArns.forEach((arn, index) => {
170
- params[`LoadBalancerArns.member.${index + 1}`] = arn
171
- })
172
- }
173
-
174
- if (options?.Names) {
175
- options.Names.forEach((name, index) => {
176
- params[`Names.member.${index + 1}`] = name
177
- })
178
- }
179
-
180
- if (options?.Marker) {
181
- params.Marker = options.Marker
182
- }
183
-
184
- if (options?.PageSize) {
185
- params.PageSize = options.PageSize
186
- }
187
-
188
- const result = await this.client.request({
189
- service: 'elasticloadbalancing',
190
- region: this.region,
191
- method: 'POST',
192
- path: '/',
193
- headers: {
194
- 'Content-Type': 'application/x-www-form-urlencoded',
195
- },
196
- body: this.buildFormBody('DescribeLoadBalancers', params),
197
- })
198
-
199
- return this.normalizeResult(result, 'DescribeLoadBalancersResult')
200
- }
201
-
202
- /**
203
- * Describe target groups
204
- */
205
- async describeTargetGroups(options?: {
206
- LoadBalancerArn?: string
207
- TargetGroupArns?: string[]
208
- Names?: string[]
209
- Marker?: string
210
- PageSize?: number
211
- }): Promise<{ TargetGroups?: TargetGroup[], NextMarker?: string }> {
212
- const params: Record<string, any> = {}
213
-
214
- if (options?.LoadBalancerArn) {
215
- params.LoadBalancerArn = options.LoadBalancerArn
216
- }
217
-
218
- if (options?.TargetGroupArns) {
219
- options.TargetGroupArns.forEach((arn, index) => {
220
- params[`TargetGroupArns.member.${index + 1}`] = arn
221
- })
222
- }
223
-
224
- if (options?.Names) {
225
- options.Names.forEach((name, index) => {
226
- params[`Names.member.${index + 1}`] = name
227
- })
228
- }
229
-
230
- if (options?.Marker) {
231
- params.Marker = options.Marker
232
- }
233
-
234
- if (options?.PageSize) {
235
- params.PageSize = options.PageSize
236
- }
237
-
238
- const result = await this.client.request({
239
- service: 'elasticloadbalancing',
240
- region: this.region,
241
- method: 'POST',
242
- path: '/',
243
- headers: {
244
- 'Content-Type': 'application/x-www-form-urlencoded',
245
- },
246
- body: this.buildFormBody('DescribeTargetGroups', params),
247
- })
248
-
249
- return this.normalizeResult(result, 'DescribeTargetGroupsResult')
250
- }
251
-
252
- /**
253
- * Describe target health
254
- */
255
- async describeTargetHealth(options: {
256
- TargetGroupArn: string
257
- Targets?: Array<{ Id: string, Port?: number, AvailabilityZone?: string }>
258
- }): Promise<{ TargetHealthDescriptions?: TargetHealthDescription[] }> {
259
- const params: Record<string, any> = {
260
- TargetGroupArn: options.TargetGroupArn,
261
- }
262
-
263
- if (options.Targets) {
264
- options.Targets.forEach((target, index) => {
265
- params[`Targets.member.${index + 1}.Id`] = target.Id
266
- if (target.Port) {
267
- params[`Targets.member.${index + 1}.Port`] = target.Port
268
- }
269
- if (target.AvailabilityZone) {
270
- params[`Targets.member.${index + 1}.AvailabilityZone`] = target.AvailabilityZone
271
- }
272
- })
273
- }
274
-
275
- const result = await this.client.request({
276
- service: 'elasticloadbalancing',
277
- region: this.region,
278
- method: 'POST',
279
- path: '/',
280
- headers: {
281
- 'Content-Type': 'application/x-www-form-urlencoded',
282
- },
283
- body: this.buildFormBody('DescribeTargetHealth', params),
284
- })
285
-
286
- return this.normalizeResult(result, 'DescribeTargetHealthResult')
287
- }
288
-
289
- /**
290
- * Describe listeners
291
- */
292
- async describeListeners(options?: {
293
- LoadBalancerArn?: string
294
- ListenerArns?: string[]
295
- Marker?: string
296
- PageSize?: number
297
- }): Promise<{ Listeners?: Listener[], NextMarker?: string }> {
298
- const params: Record<string, any> = {}
299
-
300
- if (options?.LoadBalancerArn) {
301
- params.LoadBalancerArn = options.LoadBalancerArn
302
- }
303
-
304
- if (options?.ListenerArns) {
305
- options.ListenerArns.forEach((arn, index) => {
306
- params[`ListenerArns.member.${index + 1}`] = arn
307
- })
308
- }
309
-
310
- if (options?.Marker) {
311
- params.Marker = options.Marker
312
- }
313
-
314
- if (options?.PageSize) {
315
- params.PageSize = options.PageSize
316
- }
317
-
318
- const result = await this.client.request({
319
- service: 'elasticloadbalancing',
320
- region: this.region,
321
- method: 'POST',
322
- path: '/',
323
- headers: {
324
- 'Content-Type': 'application/x-www-form-urlencoded',
325
- },
326
- body: this.buildFormBody('DescribeListeners', params),
327
- })
328
-
329
- return this.normalizeResult(result, 'DescribeListenersResult')
330
- }
331
-
332
- /**
333
- * Describe rules for a listener
334
- */
335
- async describeRules(options?: {
336
- ListenerArn?: string
337
- RuleArns?: string[]
338
- Marker?: string
339
- PageSize?: number
340
- }): Promise<{ Rules?: Rule[], NextMarker?: string }> {
341
- const params: Record<string, any> = {}
342
-
343
- if (options?.ListenerArn) {
344
- params.ListenerArn = options.ListenerArn
345
- }
346
-
347
- if (options?.RuleArns) {
348
- options.RuleArns.forEach((arn, index) => {
349
- params[`RuleArns.member.${index + 1}`] = arn
350
- })
351
- }
352
-
353
- if (options?.Marker) {
354
- params.Marker = options.Marker
355
- }
356
-
357
- if (options?.PageSize) {
358
- params.PageSize = options.PageSize
359
- }
360
-
361
- const result = await this.client.request({
362
- service: 'elasticloadbalancing',
363
- region: this.region,
364
- method: 'POST',
365
- path: '/',
366
- headers: {
367
- 'Content-Type': 'application/x-www-form-urlencoded',
368
- },
369
- body: this.buildFormBody('DescribeRules', params),
370
- })
371
-
372
- return this.normalizeResult(result, 'DescribeRulesResult')
373
- }
374
-
375
- /**
376
- * Describe load balancer attributes
377
- */
378
- async describeLoadBalancerAttributes(loadBalancerArn: string): Promise<{ Attributes?: Array<{ Key: string, Value: string }> }> {
379
- const params = {
380
- LoadBalancerArn: loadBalancerArn,
381
- }
382
-
383
- const result = await this.client.request({
384
- service: 'elasticloadbalancing',
385
- region: this.region,
386
- method: 'POST',
387
- path: '/',
388
- headers: {
389
- 'Content-Type': 'application/x-www-form-urlencoded',
390
- },
391
- body: this.buildFormBody('DescribeLoadBalancerAttributes', params),
392
- })
393
-
394
- return this.normalizeResult(result, 'DescribeLoadBalancerAttributesResult')
395
- }
396
-
397
- /**
398
- * Describe target group attributes
399
- */
400
- async describeTargetGroupAttributes(targetGroupArn: string): Promise<{ Attributes?: Array<{ Key: string, Value: string }> }> {
401
- const params = {
402
- TargetGroupArn: targetGroupArn,
403
- }
404
-
405
- const result = await this.client.request({
406
- service: 'elasticloadbalancing',
407
- region: this.region,
408
- method: 'POST',
409
- path: '/',
410
- headers: {
411
- 'Content-Type': 'application/x-www-form-urlencoded',
412
- },
413
- body: this.buildFormBody('DescribeTargetGroupAttributes', params),
414
- })
415
-
416
- return this.normalizeResult(result, 'DescribeTargetGroupAttributesResult')
417
- }
418
-
419
- /**
420
- * Create a load balancer
421
- */
422
- async createLoadBalancer(options: {
423
- Name: string
424
- Subnets?: string[]
425
- SubnetMappings?: Array<{
426
- SubnetId: string
427
- AllocationId?: string
428
- PrivateIPv4Address?: string
429
- IPv6Address?: string
430
- }>
431
- SecurityGroups?: string[]
432
- Scheme?: 'internet-facing' | 'internal'
433
- Type?: 'application' | 'network' | 'gateway'
434
- IpAddressType?: 'ipv4' | 'dualstack'
435
- Tags?: Array<{ Key: string, Value: string }>
436
- }): Promise<{ LoadBalancers?: LoadBalancer[] }> {
437
- const params: Record<string, any> = {
438
- Name: options.Name,
439
- }
440
-
441
- if (options.Subnets) {
442
- options.Subnets.forEach((subnet, index) => {
443
- params[`Subnets.member.${index + 1}`] = subnet
444
- })
445
- }
446
-
447
- if (options.SubnetMappings) {
448
- options.SubnetMappings.forEach((mapping, index) => {
449
- params[`SubnetMappings.member.${index + 1}.SubnetId`] = mapping.SubnetId
450
- if (mapping.AllocationId) {
451
- params[`SubnetMappings.member.${index + 1}.AllocationId`] = mapping.AllocationId
452
- }
453
- if (mapping.PrivateIPv4Address) {
454
- params[`SubnetMappings.member.${index + 1}.PrivateIPv4Address`] = mapping.PrivateIPv4Address
455
- }
456
- if (mapping.IPv6Address) {
457
- params[`SubnetMappings.member.${index + 1}.IPv6Address`] = mapping.IPv6Address
458
- }
459
- })
460
- }
461
-
462
- if (options.SecurityGroups) {
463
- options.SecurityGroups.forEach((sg, index) => {
464
- params[`SecurityGroups.member.${index + 1}`] = sg
465
- })
466
- }
467
-
468
- if (options.Scheme) {
469
- params.Scheme = options.Scheme
470
- }
471
-
472
- if (options.Type) {
473
- params.Type = options.Type
474
- }
475
-
476
- if (options.IpAddressType) {
477
- params.IpAddressType = options.IpAddressType
478
- }
479
-
480
- if (options.Tags) {
481
- options.Tags.forEach((tag, index) => {
482
- params[`Tags.member.${index + 1}.Key`] = tag.Key
483
- params[`Tags.member.${index + 1}.Value`] = tag.Value
484
- })
485
- }
486
-
487
- const result = await this.client.request({
488
- service: 'elasticloadbalancing',
489
- region: this.region,
490
- method: 'POST',
491
- path: '/',
492
- headers: {
493
- 'Content-Type': 'application/x-www-form-urlencoded',
494
- },
495
- body: this.buildFormBody('CreateLoadBalancer', params),
496
- })
497
-
498
- return this.normalizeResult(result, 'CreateLoadBalancerResult')
499
- }
500
-
501
- /**
502
- * Delete a load balancer
503
- */
504
- async deleteLoadBalancer(loadBalancerArn: string): Promise<void> {
505
- const params = {
506
- LoadBalancerArn: loadBalancerArn,
507
- }
508
-
509
- await this.client.request({
510
- service: 'elasticloadbalancing',
511
- region: this.region,
512
- method: 'POST',
513
- path: '/',
514
- headers: {
515
- 'Content-Type': 'application/x-www-form-urlencoded',
516
- },
517
- body: this.buildFormBody('DeleteLoadBalancer', params),
518
- })
519
- }
520
-
521
- /**
522
- * Create a target group
523
- */
524
- async createTargetGroup(options: {
525
- Name: string
526
- Protocol?: string
527
- ProtocolVersion?: string
528
- Port?: number
529
- VpcId?: string
530
- HealthCheckProtocol?: string
531
- HealthCheckPort?: string
532
- HealthCheckEnabled?: boolean
533
- HealthCheckPath?: string
534
- HealthCheckIntervalSeconds?: number
535
- HealthCheckTimeoutSeconds?: number
536
- HealthyThresholdCount?: number
537
- UnhealthyThresholdCount?: number
538
- Matcher?: { HttpCode?: string, GrpcCode?: string }
539
- TargetType?: 'instance' | 'ip' | 'lambda' | 'alb'
540
- Tags?: Array<{ Key: string, Value: string }>
541
- IpAddressType?: 'ipv4' | 'ipv6'
542
- }): Promise<{ TargetGroups?: TargetGroup[] }> {
543
- const params: Record<string, any> = {
544
- Name: options.Name,
545
- }
546
-
547
- if (options.Protocol) params.Protocol = options.Protocol
548
- if (options.ProtocolVersion) params.ProtocolVersion = options.ProtocolVersion
549
- if (options.Port) params.Port = options.Port
550
- if (options.VpcId) params.VpcId = options.VpcId
551
- if (options.HealthCheckProtocol) params.HealthCheckProtocol = options.HealthCheckProtocol
552
- if (options.HealthCheckPort) params.HealthCheckPort = options.HealthCheckPort
553
- if (options.HealthCheckEnabled !== undefined) params.HealthCheckEnabled = options.HealthCheckEnabled
554
- if (options.HealthCheckPath) params.HealthCheckPath = options.HealthCheckPath
555
- if (options.HealthCheckIntervalSeconds) params.HealthCheckIntervalSeconds = options.HealthCheckIntervalSeconds
556
- if (options.HealthCheckTimeoutSeconds) params.HealthCheckTimeoutSeconds = options.HealthCheckTimeoutSeconds
557
- if (options.HealthyThresholdCount) params.HealthyThresholdCount = options.HealthyThresholdCount
558
- if (options.UnhealthyThresholdCount) params.UnhealthyThresholdCount = options.UnhealthyThresholdCount
559
- if (options.TargetType) params.TargetType = options.TargetType
560
- if (options.IpAddressType) params.IpAddressType = options.IpAddressType
561
-
562
- if (options.Matcher) {
563
- if (options.Matcher.HttpCode) params['Matcher.HttpCode'] = options.Matcher.HttpCode
564
- if (options.Matcher.GrpcCode) params['Matcher.GrpcCode'] = options.Matcher.GrpcCode
565
- }
566
-
567
- if (options.Tags) {
568
- options.Tags.forEach((tag, index) => {
569
- params[`Tags.member.${index + 1}.Key`] = tag.Key
570
- params[`Tags.member.${index + 1}.Value`] = tag.Value
571
- })
572
- }
573
-
574
- const result = await this.client.request({
575
- service: 'elasticloadbalancing',
576
- region: this.region,
577
- method: 'POST',
578
- path: '/',
579
- headers: {
580
- 'Content-Type': 'application/x-www-form-urlencoded',
581
- },
582
- body: this.buildFormBody('CreateTargetGroup', params),
583
- })
584
-
585
- return this.normalizeResult(result, 'CreateTargetGroupResult')
586
- }
587
-
588
- /**
589
- * Delete a target group
590
- */
591
- async deleteTargetGroup(targetGroupArn: string): Promise<void> {
592
- const params = {
593
- TargetGroupArn: targetGroupArn,
594
- }
595
-
596
- await this.client.request({
597
- service: 'elasticloadbalancing',
598
- region: this.region,
599
- method: 'POST',
600
- path: '/',
601
- headers: {
602
- 'Content-Type': 'application/x-www-form-urlencoded',
603
- },
604
- body: this.buildFormBody('DeleteTargetGroup', params),
605
- })
606
- }
607
-
608
- /**
609
- * Register targets with a target group
610
- */
611
- async registerTargets(options: {
612
- TargetGroupArn: string
613
- Targets: Array<{ Id: string, Port?: number, AvailabilityZone?: string }>
614
- }): Promise<void> {
615
- const params: Record<string, any> = {
616
- TargetGroupArn: options.TargetGroupArn,
617
- }
618
-
619
- options.Targets.forEach((target, index) => {
620
- params[`Targets.member.${index + 1}.Id`] = target.Id
621
- if (target.Port) {
622
- params[`Targets.member.${index + 1}.Port`] = target.Port
623
- }
624
- if (target.AvailabilityZone) {
625
- params[`Targets.member.${index + 1}.AvailabilityZone`] = target.AvailabilityZone
626
- }
627
- })
628
-
629
- await this.client.request({
630
- service: 'elasticloadbalancing',
631
- region: this.region,
632
- method: 'POST',
633
- path: '/',
634
- headers: {
635
- 'Content-Type': 'application/x-www-form-urlencoded',
636
- },
637
- body: this.buildFormBody('RegisterTargets', params),
638
- })
639
- }
640
-
641
- /**
642
- * Deregister targets from a target group
643
- */
644
- async deregisterTargets(options: {
645
- TargetGroupArn: string
646
- Targets: Array<{ Id: string, Port?: number, AvailabilityZone?: string }>
647
- }): Promise<void> {
648
- const params: Record<string, any> = {
649
- TargetGroupArn: options.TargetGroupArn,
650
- }
651
-
652
- options.Targets.forEach((target, index) => {
653
- params[`Targets.member.${index + 1}.Id`] = target.Id
654
- if (target.Port) {
655
- params[`Targets.member.${index + 1}.Port`] = target.Port
656
- }
657
- if (target.AvailabilityZone) {
658
- params[`Targets.member.${index + 1}.AvailabilityZone`] = target.AvailabilityZone
659
- }
660
- })
661
-
662
- await this.client.request({
663
- service: 'elasticloadbalancing',
664
- region: this.region,
665
- method: 'POST',
666
- path: '/',
667
- headers: {
668
- 'Content-Type': 'application/x-www-form-urlencoded',
669
- },
670
- body: this.buildFormBody('DeregisterTargets', params),
671
- })
672
- }
673
-
674
- /**
675
- * Create a listener
676
- */
677
- async createListener(options: {
678
- LoadBalancerArn: string
679
- Protocol?: string
680
- Port: number
681
- SslPolicy?: string
682
- Certificates?: Array<{ CertificateArn: string }>
683
- DefaultActions: Array<{
684
- Type: 'forward' | 'redirect' | 'fixed-response'
685
- TargetGroupArn?: string
686
- Order?: number
687
- RedirectConfig?: {
688
- Protocol?: string
689
- Port?: string
690
- Host?: string
691
- Path?: string
692
- Query?: string
693
- StatusCode: 'HTTP_301' | 'HTTP_302'
694
- }
695
- FixedResponseConfig?: {
696
- MessageBody?: string
697
- StatusCode: string
698
- ContentType?: string
699
- }
700
- }>
701
- AlpnPolicy?: string[]
702
- Tags?: Array<{ Key: string, Value: string }>
703
- }): Promise<{ Listeners?: Listener[] }> {
704
- const params: Record<string, any> = {
705
- LoadBalancerArn: options.LoadBalancerArn,
706
- Port: options.Port,
707
- }
708
-
709
- if (options.Protocol) params.Protocol = options.Protocol
710
- if (options.SslPolicy) params.SslPolicy = options.SslPolicy
711
-
712
- if (options.Certificates) {
713
- options.Certificates.forEach((cert, index) => {
714
- params[`Certificates.member.${index + 1}.CertificateArn`] = cert.CertificateArn
715
- })
716
- }
717
-
718
- options.DefaultActions.forEach((action, index) => {
719
- params[`DefaultActions.member.${index + 1}.Type`] = action.Type
720
- if (action.TargetGroupArn) {
721
- params[`DefaultActions.member.${index + 1}.TargetGroupArn`] = action.TargetGroupArn
722
- }
723
- if (action.Order !== undefined) {
724
- params[`DefaultActions.member.${index + 1}.Order`] = action.Order
725
- }
726
- if (action.RedirectConfig) {
727
- const rc = action.RedirectConfig
728
- if (rc.Protocol) params[`DefaultActions.member.${index + 1}.RedirectConfig.Protocol`] = rc.Protocol
729
- if (rc.Port) params[`DefaultActions.member.${index + 1}.RedirectConfig.Port`] = rc.Port
730
- if (rc.Host) params[`DefaultActions.member.${index + 1}.RedirectConfig.Host`] = rc.Host
731
- if (rc.Path) params[`DefaultActions.member.${index + 1}.RedirectConfig.Path`] = rc.Path
732
- if (rc.Query) params[`DefaultActions.member.${index + 1}.RedirectConfig.Query`] = rc.Query
733
- params[`DefaultActions.member.${index + 1}.RedirectConfig.StatusCode`] = rc.StatusCode
734
- }
735
- if (action.FixedResponseConfig) {
736
- const fr = action.FixedResponseConfig
737
- if (fr.MessageBody) params[`DefaultActions.member.${index + 1}.FixedResponseConfig.MessageBody`] = fr.MessageBody
738
- params[`DefaultActions.member.${index + 1}.FixedResponseConfig.StatusCode`] = fr.StatusCode
739
- if (fr.ContentType) params[`DefaultActions.member.${index + 1}.FixedResponseConfig.ContentType`] = fr.ContentType
740
- }
741
- })
742
-
743
- if (options.AlpnPolicy) {
744
- options.AlpnPolicy.forEach((policy, index) => {
745
- params[`AlpnPolicy.member.${index + 1}`] = policy
746
- })
747
- }
748
-
749
- if (options.Tags) {
750
- options.Tags.forEach((tag, index) => {
751
- params[`Tags.member.${index + 1}.Key`] = tag.Key
752
- params[`Tags.member.${index + 1}.Value`] = tag.Value
753
- })
754
- }
755
-
756
- const result = await this.client.request({
757
- service: 'elasticloadbalancing',
758
- region: this.region,
759
- method: 'POST',
760
- path: '/',
761
- headers: {
762
- 'Content-Type': 'application/x-www-form-urlencoded',
763
- },
764
- body: this.buildFormBody('CreateListener', params),
765
- })
766
-
767
- return this.normalizeResult(result, 'CreateListenerResult')
768
- }
769
-
770
- /**
771
- * Delete a listener
772
- */
773
- async deleteListener(listenerArn: string): Promise<void> {
774
- const params = {
775
- ListenerArn: listenerArn,
776
- }
777
-
778
- await this.client.request({
779
- service: 'elasticloadbalancing',
780
- region: this.region,
781
- method: 'POST',
782
- path: '/',
783
- headers: {
784
- 'Content-Type': 'application/x-www-form-urlencoded',
785
- },
786
- body: this.buildFormBody('DeleteListener', params),
787
- })
788
- }
789
-
790
- /**
791
- * Modify listener
792
- */
793
- async modifyListener(options: {
794
- ListenerArn: string
795
- Port?: number
796
- Protocol?: string
797
- SslPolicy?: string
798
- Certificates?: Array<{ CertificateArn: string }>
799
- DefaultActions?: Action[]
800
- AlpnPolicy?: string[]
801
- }): Promise<{ Listeners?: Listener[] }> {
802
- const params: Record<string, any> = {
803
- ListenerArn: options.ListenerArn,
804
- }
805
-
806
- if (options.Port) params.Port = options.Port
807
- if (options.Protocol) params.Protocol = options.Protocol
808
- if (options.SslPolicy) params.SslPolicy = options.SslPolicy
809
-
810
- if (options.Certificates) {
811
- options.Certificates.forEach((cert, index) => {
812
- params[`Certificates.member.${index + 1}.CertificateArn`] = cert.CertificateArn
813
- })
814
- }
815
-
816
- if (options.DefaultActions) {
817
- options.DefaultActions.forEach((action, index) => {
818
- if (action.Type) params[`DefaultActions.member.${index + 1}.Type`] = action.Type
819
- if (action.TargetGroupArn) params[`DefaultActions.member.${index + 1}.TargetGroupArn`] = action.TargetGroupArn
820
- if (action.Order !== undefined) params[`DefaultActions.member.${index + 1}.Order`] = action.Order
821
- })
822
- }
823
-
824
- if (options.AlpnPolicy) {
825
- options.AlpnPolicy.forEach((policy, index) => {
826
- params[`AlpnPolicy.member.${index + 1}`] = policy
827
- })
828
- }
829
-
830
- const result = await this.client.request({
831
- service: 'elasticloadbalancing',
832
- region: this.region,
833
- method: 'POST',
834
- path: '/',
835
- headers: {
836
- 'Content-Type': 'application/x-www-form-urlencoded',
837
- },
838
- body: this.buildFormBody('ModifyListener', params),
839
- })
840
-
841
- return this.normalizeResult(result, 'ModifyListenerResult')
842
- }
843
-
844
- /**
845
- * Build form URL encoded body for ELBv2 API
846
- */
847
- private buildFormBody(action: string, params: Record<string, any>): string {
848
- const formParams: Record<string, string> = {
849
- Action: action,
850
- Version: '2015-12-01',
851
- }
852
-
853
- // Flatten params
854
- for (const [key, value] of Object.entries(params)) {
855
- if (value !== undefined && value !== null) {
856
- formParams[key] = String(value)
857
- }
858
- }
859
-
860
- return Object.entries(formParams)
861
- .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
862
- .join('&')
863
- }
864
-
865
- /**
866
- * Normalize the parsed XML result from AWS API
867
- * The client parses XML to JSON, so we need to extract the result
868
- */
869
- private normalizeResult(parsed: any, resultKey: string): any {
870
- // The AWS response is wrapped like: { DescribeLoadBalancersResponse: { DescribeLoadBalancersResult: {...} } }
871
- // fast-xml-parser returns: { DescribeLoadBalancersResult: {...}, ResponseMetadata: {...} }
872
-
873
- // Try direct access first
874
- if (parsed && parsed[resultKey]) {
875
- return this.normalizeArrays(parsed[resultKey])
876
- }
877
-
878
- // Try accessing through response wrapper
879
- const responseKey = resultKey.replace('Result', 'Response')
880
- if (parsed && parsed[responseKey] && parsed[responseKey][resultKey]) {
881
- return this.normalizeArrays(parsed[responseKey][resultKey])
882
- }
883
-
884
- // Return parsed as-is if no wrapper found
885
- return this.normalizeArrays(parsed)
886
- }
887
-
888
- /**
889
- * Normalize arrays in the response
890
- * AWS XML parsing sometimes returns single items as objects instead of arrays
891
- */
892
- private normalizeArrays(obj: any): any {
893
- if (!obj || typeof obj !== 'object') {
894
- return obj
895
- }
896
-
897
- // Handle arrays
898
- if (Array.isArray(obj)) {
899
- return obj.map(item => this.normalizeArrays(item))
900
- }
901
-
902
- const result: any = {}
903
- for (const [key, value] of Object.entries(obj)) {
904
- // Known array fields that should always be arrays
905
- const arrayFields = [
906
- 'LoadBalancers', 'TargetGroups', 'Listeners', 'Rules',
907
- 'TargetHealthDescriptions', 'Attributes', 'SecurityGroups',
908
- 'AvailabilityZones', 'Certificates', 'DefaultActions',
909
- 'Conditions', 'Actions', 'member'
910
- ]
911
-
912
- if (key === 'member') {
913
- // AWS returns arrays as { member: [...] } or { member: {...} }
914
- if (Array.isArray(value)) {
915
- return value.map(item => this.normalizeArrays(item))
916
- }
917
- return [this.normalizeArrays(value)]
918
- }
919
-
920
- if (arrayFields.includes(key)) {
921
- if (value && typeof value === 'object' && 'member' in (value as any)) {
922
- const memberValue = (value as any).member
923
- result[key] = Array.isArray(memberValue)
924
- ? memberValue.map((item: any) => this.normalizeArrays(item))
925
- : [this.normalizeArrays(memberValue)]
926
- } else if (Array.isArray(value)) {
927
- result[key] = value.map(item => this.normalizeArrays(item))
928
- } else if (value) {
929
- result[key] = [this.normalizeArrays(value)]
930
- } else {
931
- result[key] = []
932
- }
933
- } else if (typeof value === 'object') {
934
- result[key] = this.normalizeArrays(value)
935
- } else {
936
- result[key] = value
937
- }
938
- }
939
-
940
- return result
941
- }
942
- }