alchemy 0.53.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/bin/alchemy.js +196353 -0
  2. package/bin/alchemy.ts +2 -2
  3. package/bin/commands/init.ts +1190 -0
  4. package/bin/constants.ts +18 -0
  5. package/bin/services/execute-alchemy.ts +7 -2
  6. package/bin/services/package-manager.ts +1 -1
  7. package/bin/types.ts +14 -5
  8. package/lib/aws/ec2/index.d.ts +10 -0
  9. package/lib/aws/ec2/index.d.ts.map +1 -0
  10. package/lib/aws/ec2/index.js +10 -0
  11. package/lib/aws/ec2/index.js.map +1 -0
  12. package/lib/aws/ec2/internet-gateway-attachment.d.ts +150 -0
  13. package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -0
  14. package/lib/aws/ec2/internet-gateway-attachment.js +248 -0
  15. package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -0
  16. package/lib/aws/ec2/internet-gateway.d.ts +132 -0
  17. package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
  18. package/lib/aws/ec2/internet-gateway.js +311 -0
  19. package/lib/aws/ec2/internet-gateway.js.map +1 -0
  20. package/lib/aws/ec2/nat-gateway.d.ts +145 -0
  21. package/lib/aws/ec2/nat-gateway.d.ts.map +1 -0
  22. package/lib/aws/ec2/nat-gateway.js +618 -0
  23. package/lib/aws/ec2/nat-gateway.js.map +1 -0
  24. package/lib/aws/ec2/route-table-association.d.ts +134 -0
  25. package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
  26. package/lib/aws/ec2/route-table-association.js +364 -0
  27. package/lib/aws/ec2/route-table-association.js.map +1 -0
  28. package/lib/aws/ec2/route-table.d.ts +94 -0
  29. package/lib/aws/ec2/route-table.d.ts.map +1 -0
  30. package/lib/aws/ec2/route-table.js +253 -0
  31. package/lib/aws/ec2/route-table.js.map +1 -0
  32. package/lib/aws/ec2/route.d.ts +130 -0
  33. package/lib/aws/ec2/route.d.ts.map +1 -0
  34. package/lib/aws/ec2/route.js +253 -0
  35. package/lib/aws/ec2/route.js.map +1 -0
  36. package/lib/aws/ec2/security-group-rule.d.ts +84 -0
  37. package/lib/aws/ec2/security-group-rule.d.ts.map +1 -0
  38. package/lib/aws/ec2/security-group-rule.js +123 -0
  39. package/lib/aws/ec2/security-group-rule.js.map +1 -0
  40. package/lib/aws/ec2/security-group.d.ts +84 -0
  41. package/lib/aws/ec2/security-group.d.ts.map +1 -0
  42. package/lib/aws/ec2/security-group.js +207 -0
  43. package/lib/aws/ec2/security-group.js.map +1 -0
  44. package/lib/aws/ec2/subnet.d.ts +110 -0
  45. package/lib/aws/ec2/subnet.d.ts.map +1 -0
  46. package/lib/aws/ec2/subnet.js +336 -0
  47. package/lib/aws/ec2/subnet.js.map +1 -0
  48. package/lib/aws/ec2/utils.d.ts +15 -0
  49. package/lib/aws/ec2/utils.d.ts.map +1 -0
  50. package/lib/aws/ec2/utils.js +79 -0
  51. package/lib/aws/ec2/utils.js.map +1 -0
  52. package/lib/aws/ec2/vpc.d.ts +160 -0
  53. package/lib/aws/ec2/vpc.d.ts.map +1 -0
  54. package/lib/aws/ec2/vpc.js +453 -0
  55. package/lib/aws/ec2/vpc.js.map +1 -0
  56. package/lib/aws/index.d.ts +7 -0
  57. package/lib/aws/index.d.ts.map +1 -1
  58. package/lib/aws/index.js +8 -0
  59. package/lib/aws/index.js.map +1 -1
  60. package/lib/aws/utils.d.ts +25 -0
  61. package/lib/aws/utils.d.ts.map +1 -0
  62. package/lib/aws/utils.js +89 -0
  63. package/lib/aws/utils.js.map +1 -0
  64. package/lib/build-date.d.ts +6 -0
  65. package/lib/build-date.d.ts.map +1 -0
  66. package/lib/build-date.js +8 -0
  67. package/lib/build-date.js.map +1 -0
  68. package/lib/cloudflare/astro.d.ts.map +1 -1
  69. package/lib/cloudflare/astro.js +4 -3
  70. package/lib/cloudflare/astro.js.map +1 -1
  71. package/lib/cloudflare/bundle/externals.d.ts +3 -0
  72. package/lib/cloudflare/bundle/externals.d.ts.map +1 -0
  73. package/lib/cloudflare/bundle/externals.js +35 -0
  74. package/lib/cloudflare/bundle/externals.js.map +1 -0
  75. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts +8 -1
  76. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts.map +1 -1
  77. package/lib/cloudflare/bundle/internal-worker-bundle.js +11 -4
  78. package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
  79. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts +4 -1
  80. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts.map +1 -1
  81. package/lib/cloudflare/bundle/plugin-hot-reload.js +27 -11
  82. package/lib/cloudflare/bundle/plugin-hot-reload.js.map +1 -1
  83. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +5 -1
  84. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  85. package/lib/cloudflare/bundle/plugin-wasm.d.ts +6 -2
  86. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
  87. package/lib/cloudflare/bundle/plugin-wasm.js +21 -47
  88. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  89. package/lib/cloudflare/bundle/validate-node-compat.d.ts +2 -1
  90. package/lib/cloudflare/bundle/validate-node-compat.d.ts.map +1 -1
  91. package/lib/cloudflare/bundle/validate-node-compat.js +5 -4
  92. package/lib/cloudflare/bundle/validate-node-compat.js.map +1 -1
  93. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  94. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  95. package/lib/cloudflare/d1-database.js +1 -1
  96. package/lib/cloudflare/d1-database.js.map +1 -1
  97. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  98. package/lib/cloudflare/d1-migrations.js +8 -10
  99. package/lib/cloudflare/d1-migrations.js.map +1 -1
  100. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts +3 -3
  101. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
  102. package/lib/cloudflare/miniflare/miniflare-worker-options.js +23 -6
  103. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
  104. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -1
  105. package/lib/cloudflare/miniflare/miniflare.js +2 -4
  106. package/lib/cloudflare/miniflare/miniflare.js.map +1 -1
  107. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
  108. package/lib/cloudflare/miniflare/remote-binding-proxy.js +4 -6
  109. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
  110. package/lib/cloudflare/react-router.d.ts +4 -0
  111. package/lib/cloudflare/react-router.d.ts.map +1 -1
  112. package/lib/cloudflare/react-router.js +8 -5
  113. package/lib/cloudflare/react-router.js.map +1 -1
  114. package/lib/cloudflare/worker-bundle.d.ts +80 -0
  115. package/lib/cloudflare/worker-bundle.d.ts.map +1 -0
  116. package/lib/cloudflare/worker-bundle.js +266 -0
  117. package/lib/cloudflare/worker-bundle.js.map +1 -0
  118. package/lib/cloudflare/worker.d.ts +1 -1
  119. package/lib/cloudflare/worker.d.ts.map +1 -1
  120. package/lib/cloudflare/worker.js +81 -195
  121. package/lib/cloudflare/worker.js.map +1 -1
  122. package/lib/esbuild/bundle.d.ts +1 -0
  123. package/lib/esbuild/bundle.d.ts.map +1 -1
  124. package/lib/state/cloudflare-state-store.js +1 -5
  125. package/lib/state/cloudflare-state-store.js.map +1 -1
  126. package/lib/state/dofs-state-store/store.d.ts.map +1 -1
  127. package/lib/state/dofs-state-store/store.js +4 -4
  128. package/lib/state/dofs-state-store/store.js.map +1 -1
  129. package/lib/util/params.d.ts +5 -0
  130. package/lib/util/params.d.ts.map +1 -0
  131. package/lib/util/params.js +30 -0
  132. package/lib/util/params.js.map +1 -0
  133. package/lib/util/timeout.d.ts +31 -0
  134. package/lib/util/timeout.d.ts.map +1 -0
  135. package/lib/util/timeout.js +109 -0
  136. package/lib/util/timeout.js.map +1 -0
  137. package/lib/util/xml.d.ts +5 -0
  138. package/lib/util/xml.d.ts.map +1 -0
  139. package/lib/util/xml.js +8 -0
  140. package/lib/util/xml.js.map +1 -0
  141. package/package.json +12 -5
  142. package/src/aws/ec2/index.ts +9 -0
  143. package/src/aws/ec2/internet-gateway-attachment.ts +468 -0
  144. package/src/aws/ec2/internet-gateway.ts +557 -0
  145. package/src/aws/ec2/nat-gateway.ts +1123 -0
  146. package/src/aws/ec2/route-table-association.ts +747 -0
  147. package/src/aws/ec2/route-table.ts +452 -0
  148. package/src/aws/ec2/route.ts +447 -0
  149. package/src/aws/ec2/security-group-rule.ts +229 -0
  150. package/src/aws/ec2/security-group.ts +418 -0
  151. package/src/aws/ec2/subnet.ts +625 -0
  152. package/src/aws/ec2/utils.ts +102 -0
  153. package/src/aws/ec2/vpc.ts +969 -0
  154. package/src/aws/index.ts +9 -0
  155. package/src/aws/utils.ts +137 -0
  156. package/src/build-date.ts +8 -0
  157. package/src/cloudflare/astro.ts +4 -3
  158. package/src/cloudflare/bundle/externals.ts +36 -0
  159. package/src/cloudflare/bundle/internal-worker-bundle.ts +12 -3
  160. package/src/cloudflare/bundle/plugin-hot-reload.ts +32 -12
  161. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +6 -1
  162. package/src/cloudflare/bundle/plugin-wasm.ts +26 -54
  163. package/src/cloudflare/bundle/validate-node-compat.ts +6 -5
  164. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  165. package/src/cloudflare/d1-database.ts +1 -1
  166. package/src/cloudflare/d1-migrations.ts +19 -18
  167. package/src/cloudflare/miniflare/miniflare-worker-options.ts +27 -13
  168. package/src/cloudflare/miniflare/miniflare.ts +2 -4
  169. package/src/cloudflare/miniflare/remote-binding-proxy.ts +5 -7
  170. package/src/cloudflare/react-router.ts +14 -6
  171. package/src/cloudflare/worker-bundle.ts +364 -0
  172. package/src/cloudflare/worker.ts +104 -241
  173. package/src/state/cloudflare-state-store.ts +1 -5
  174. package/src/state/dofs-state-store/store.ts +4 -4
  175. package/src/util/params.ts +33 -0
  176. package/src/util/timeout.ts +178 -0
  177. package/src/util/xml.ts +7 -0
  178. package/templates/nuxt/package.json +1 -1
  179. package/templates/nuxt/types/env.d.ts +11 -0
  180. package/templates/react-router/workers/app.ts +2 -2
  181. package/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc +385 -0
  182. package/templates/rwsdk/wrangler.jsonc +10 -7
  183. package/templates/typescript/tsconfig.json +1 -2
  184. package/bin/alchemy.mjs +0 -51766
  185. package/lib/cloudflare/bundle/esbuild.d.ts +0 -16
  186. package/lib/cloudflare/bundle/esbuild.d.ts.map +0 -1
  187. package/lib/cloudflare/bundle/esbuild.js +0 -159
  188. package/lib/cloudflare/bundle/esbuild.js.map +0 -1
  189. package/lib/cloudflare/bundle/fs.d.ts +0 -18
  190. package/lib/cloudflare/bundle/fs.d.ts.map +0 -1
  191. package/lib/cloudflare/bundle/fs.js +0 -78
  192. package/lib/cloudflare/bundle/fs.js.map +0 -1
  193. package/lib/cloudflare/bundle/index.d.ts +0 -3
  194. package/lib/cloudflare/bundle/index.d.ts.map +0 -1
  195. package/lib/cloudflare/bundle/index.js +0 -2
  196. package/lib/cloudflare/bundle/index.js.map +0 -1
  197. package/lib/cloudflare/bundle/inline.d.ts +0 -12
  198. package/lib/cloudflare/bundle/inline.d.ts.map +0 -1
  199. package/lib/cloudflare/bundle/inline.js +0 -32
  200. package/lib/cloudflare/bundle/inline.js.map +0 -1
  201. package/lib/cloudflare/bundle/normalize.d.ts +0 -21
  202. package/lib/cloudflare/bundle/normalize.d.ts.map +0 -1
  203. package/lib/cloudflare/bundle/normalize.js +0 -49
  204. package/lib/cloudflare/bundle/normalize.js.map +0 -1
  205. package/lib/cloudflare/bundle/shared.d.ts +0 -30
  206. package/lib/cloudflare/bundle/shared.d.ts.map +0 -1
  207. package/lib/cloudflare/bundle/shared.js +0 -34
  208. package/lib/cloudflare/bundle/shared.js.map +0 -1
  209. package/src/cloudflare/bundle/esbuild.ts +0 -194
  210. package/src/cloudflare/bundle/fs.ts +0 -103
  211. package/src/cloudflare/bundle/index.ts +0 -6
  212. package/src/cloudflare/bundle/inline.ts +0 -42
  213. package/src/cloudflare/bundle/normalize.ts +0 -76
  214. package/src/cloudflare/bundle/shared.ts +0 -73
@@ -0,0 +1,1123 @@
1
+ import { XMLParser } from "fast-xml-parser";
2
+ import type { Context } from "../../context.ts";
3
+ import { Resource } from "../../resource.ts";
4
+ import { ignore } from "../../util/ignore.ts";
5
+ import { logger } from "../../util/logger.ts";
6
+ import {
7
+ mergeTimeoutConfig,
8
+ type TimeoutConfig,
9
+ waitForResourceState,
10
+ } from "../../util/timeout.ts";
11
+ import type { Subnet } from "./subnet.ts";
12
+ import { callEC2Api, createEC2Client } from "./utils.ts";
13
+
14
+ /**
15
+ * Properties for creating or updating a NAT Gateway
16
+ */
17
+ export interface NatGatewayProps {
18
+ /**
19
+ * The subnet to create the NAT Gateway in (must be a public subnet)
20
+ */
21
+ subnet: Subnet | string;
22
+
23
+ /**
24
+ * The allocation ID of an existing Elastic IP address
25
+ * If not provided, a new Elastic IP will be allocated
26
+ */
27
+ allocationId?: string;
28
+
29
+ /**
30
+ * The connectivity type for the NAT Gateway
31
+ * @default "public"
32
+ */
33
+ connectivityType?: "public" | "private";
34
+
35
+ /**
36
+ * Tags to apply to the NAT Gateway
37
+ */
38
+ tags?: Record<string, string>;
39
+
40
+ /**
41
+ * Timeout configuration for NAT Gateway operations
42
+ * @default NAT Gateway-specific sensible defaults (60 attempts, 5000ms delay)
43
+ */
44
+ timeout?: Partial<TimeoutConfig>;
45
+ }
46
+
47
+ /**
48
+ * Output returned after NAT Gateway creation/update
49
+ */
50
+ export interface NatGateway
51
+ extends Resource<"aws::NatGateway">,
52
+ NatGatewayProps {
53
+ /**
54
+ * The ID of the NAT Gateway
55
+ */
56
+ natGatewayId: string;
57
+
58
+ /**
59
+ * The ID of the subnet the NAT Gateway is in
60
+ */
61
+ subnetId: string;
62
+
63
+ /**
64
+ * The ID of the VPC the NAT Gateway belongs to
65
+ */
66
+ vpcId: string;
67
+
68
+ /**
69
+ * The current state of the NAT Gateway
70
+ */
71
+ state: "pending" | "failed" | "available" | "deleting" | "deleted";
72
+
73
+ /**
74
+ * The allocation ID of the Elastic IP address
75
+ */
76
+ allocationId: string;
77
+
78
+ /**
79
+ * The public IP address of the NAT Gateway
80
+ */
81
+ publicIp?: string;
82
+
83
+ /**
84
+ * The private IP address of the NAT Gateway
85
+ */
86
+ privateIp?: string;
87
+
88
+ /**
89
+ * Whether the Elastic IP was created by this resource
90
+ */
91
+ createdElasticIp: boolean;
92
+ }
93
+
94
+ /**
95
+ * AWS NAT Gateway Resource
96
+ *
97
+ * Creates and manages NAT Gateways that provide outbound internet access
98
+ * for instances in private subnets. Automatically allocates an Elastic IP
99
+ * if one is not provided. NAT Gateways are slow resources that can take
100
+ * up to 10 minutes to become available.
101
+ *
102
+ * @example
103
+ * // Create a NAT Gateway with automatic Elastic IP allocation
104
+ * const natGateway = await NatGateway("main-nat", {
105
+ * subnet: publicSubnet,
106
+ * tags: {
107
+ * Name: "main-nat-gateway",
108
+ * Environment: "production"
109
+ * }
110
+ * });
111
+ *
112
+ * @example
113
+ * // Create a NAT Gateway with existing Elastic IP
114
+ * const natGateway = await NatGateway("custom-nat", {
115
+ * subnet: "subnet-12345678",
116
+ * allocationId: "eipalloc-12345678",
117
+ * connectivityType: "public",
118
+ * tags: {
119
+ * Name: "custom-nat-gateway"
120
+ * }
121
+ * });
122
+ *
123
+ * @example
124
+ * // Create a private NAT Gateway (for VPC-to-VPC communication)
125
+ * const privateNat = await NatGateway("private-nat", {
126
+ * subnet: privateSubnet,
127
+ * connectivityType: "private",
128
+ * tags: {
129
+ * Name: "private-nat-gateway",
130
+ * Type: "internal"
131
+ * }
132
+ * });
133
+ *
134
+ * @example
135
+ * // Create a NAT Gateway with custom timeout configuration
136
+ * const slowNat = await NatGateway("slow-nat", {
137
+ * subnet: publicSubnet,
138
+ * timeout: {
139
+ * maxAttempts: 180, // Wait up to 15 minutes
140
+ * delayMs: 5000 // Check every 5 seconds
141
+ * },
142
+ * tags: {
143
+ * Name: "slow-nat-gateway"
144
+ * }
145
+ * });
146
+ *
147
+ * @example
148
+ * // Use NAT Gateway in route table for private subnet internet access
149
+ * const privateRoute = await Route("private-internet", {
150
+ * routeTable: privateRouteTable,
151
+ * destinationCidrBlock: "0.0.0.0/0",
152
+ * natGateway: natGateway,
153
+ * tags: {
154
+ * Name: "private-to-internet"
155
+ * }
156
+ * });
157
+ */
158
+ export const NatGateway = Resource(
159
+ "aws::NatGateway",
160
+ async function (
161
+ this: Context<NatGateway>,
162
+ _id: string,
163
+ props: NatGatewayProps,
164
+ ): Promise<NatGateway> {
165
+ const subnetId =
166
+ typeof props.subnet === "string" ? props.subnet : props.subnet.subnetId;
167
+ const timeoutConfig = mergeTimeoutConfig(
168
+ NAT_GATEWAY_TIMEOUT,
169
+ props.timeout,
170
+ );
171
+
172
+ if (this.phase === "delete") {
173
+ if (this.output?.natGatewayId) {
174
+ logger.log(`🗑️ Deleting NAT Gateway: ${this.output.natGatewayId}`);
175
+ // Delete NAT Gateway if it exists
176
+ await ignore("InvalidNatGatewayID.NotFound", async () => {
177
+ const client = await createEC2Client();
178
+ await callEC2Api(
179
+ client,
180
+ "DeleteNatGateway",
181
+ parseNatGatewayXmlResponse,
182
+ convertDeleteNatGatewayParamsToAwsFormat({
183
+ NatGatewayId: this.output!.natGatewayId,
184
+ }),
185
+ );
186
+ });
187
+
188
+ // Wait for NAT Gateway to be deleted AND network interfaces to be cleaned up
189
+ logger.log(
190
+ ` Waiting for NAT Gateway ${this.output.natGatewayId} to be fully deleted...`,
191
+ );
192
+ await waitForNatGatewayFullyDeleted(
193
+ this.output.natGatewayId,
194
+ timeoutConfig,
195
+ );
196
+
197
+ logger.log(
198
+ ` ✅ NAT Gateway ${this.output.natGatewayId} deletion completed`,
199
+ );
200
+
201
+ // Release Elastic IP if we created it and it exists
202
+ if (this.output.createdElasticIp && this.output.allocationId) {
203
+ logger.log(` Releasing Elastic IP: ${this.output.allocationId}`);
204
+ await ignore(
205
+ ["InvalidAllocationID.NotFound", "InvalidAddress.NotFound"],
206
+ async () => {
207
+ const eipClient = await createEC2Client();
208
+ await callEC2Api(
209
+ eipClient,
210
+ "ReleaseAddress",
211
+ parseNatGatewayXmlResponse,
212
+ convertReleaseAddressParamsToAwsFormat({
213
+ AllocationId: this.output!.allocationId,
214
+ }),
215
+ );
216
+ },
217
+ );
218
+ // Wait for the EIP to be fully released
219
+ await waitForEipReleased(this.output.allocationId, timeoutConfig);
220
+ logger.log(
221
+ ` ✅ Elastic IP ${this.output.allocationId} released successfully`,
222
+ );
223
+ }
224
+ }
225
+ return this.destroy();
226
+ }
227
+
228
+ let natGateway: AwsNatGateway;
229
+ let allocationId = props.allocationId;
230
+ let createdElasticIp = false;
231
+ let publicIp: string | undefined;
232
+
233
+ if (this.phase === "update" && this.output?.natGatewayId) {
234
+ // Get existing NAT Gateway
235
+ const client = await createEC2Client();
236
+ const response = await callEC2Api<DescribeNatGatewaysResponse>(
237
+ client,
238
+ "DescribeNatGateways",
239
+ parseNatGatewayXmlResponse,
240
+ convertDescribeNatGatewaysParamsToAwsFormat({
241
+ NatGatewayIds: [this.output.natGatewayId],
242
+ }),
243
+ );
244
+
245
+ if (!response.NatGateways?.[0]) {
246
+ throw new Error(`NAT Gateway ${this.output.natGatewayId} not found`);
247
+ }
248
+
249
+ natGateway = response.NatGateways[0];
250
+ allocationId = this.output.allocationId;
251
+ createdElasticIp = this.output.createdElasticIp;
252
+ publicIp = this.output.publicIp;
253
+ } else {
254
+ // Allocate Elastic IP if not provided
255
+ if (!allocationId) {
256
+ const eipParams: AllocateAddressParams = {
257
+ Domain: "vpc",
258
+ };
259
+
260
+ // Add tags if specified
261
+ if (props.tags) {
262
+ eipParams.TagSpecifications = [
263
+ {
264
+ ResourceType: "elastic-ip",
265
+ Tags: Object.entries(props.tags).map(([key, value]) => ({
266
+ Key: `${key}-eip`,
267
+ Value: value,
268
+ })),
269
+ },
270
+ ];
271
+ }
272
+
273
+ const client = await createEC2Client();
274
+ const eipResponse = await callEC2Api<AllocateAddressResponse>(
275
+ client,
276
+ "AllocateAddress",
277
+ parseNatGatewayXmlResponse,
278
+ convertAllocateAddressParamsToAwsFormat(eipParams),
279
+ );
280
+
281
+ if (!eipResponse.AllocationId) {
282
+ throw new Error("Failed to allocate Elastic IP");
283
+ }
284
+
285
+ allocationId = eipResponse.AllocationId;
286
+ publicIp = eipResponse.PublicIp;
287
+ createdElasticIp = true;
288
+ } else {
289
+ // Get public IP of existing allocation
290
+ const client = await createEC2Client();
291
+ const addressResponse = await callEC2Api<DescribeAddressesResponse>(
292
+ client,
293
+ "DescribeAddresses",
294
+ parseNatGatewayXmlResponse,
295
+ convertDescribeAddressesParamsToAwsFormat({
296
+ AllocationIds: [allocationId],
297
+ }),
298
+ );
299
+ publicIp = addressResponse.Addresses?.[0]?.PublicIp;
300
+ }
301
+
302
+ // Create NAT Gateway
303
+ const createParams: CreateNatGatewayParams = {
304
+ SubnetId: subnetId,
305
+ AllocationId: allocationId,
306
+ ConnectivityType: props.connectivityType || "public",
307
+ };
308
+
309
+ // Add tags if specified
310
+ if (props.tags) {
311
+ createParams.TagSpecifications = [
312
+ {
313
+ ResourceType: "natgateway",
314
+ Tags: Object.entries(props.tags).map(([key, value]) => ({
315
+ Key: key,
316
+ Value: value,
317
+ })),
318
+ },
319
+ ];
320
+ }
321
+
322
+ const natClient = await createEC2Client();
323
+ const response = await callEC2Api<CreateNatGatewayResponse>(
324
+ natClient,
325
+ "CreateNatGateway",
326
+ parseNatGatewayXmlResponse,
327
+ convertCreateNatGatewayParamsToAwsFormat(createParams),
328
+ );
329
+
330
+ if (!response.NatGateway) {
331
+ throw new Error("Failed to create NAT Gateway");
332
+ }
333
+
334
+ natGateway = response.NatGateway;
335
+
336
+ // Wait for NAT Gateway to be available
337
+ await waitForNatGatewayAvailable(natGateway.NatGatewayId!, timeoutConfig);
338
+ }
339
+
340
+ const result = this({
341
+ natGatewayId: natGateway.NatGatewayId!,
342
+ subnetId: natGateway.SubnetId!,
343
+ vpcId: natGateway.VpcId!,
344
+ state: natGateway.State,
345
+ allocationId: allocationId!,
346
+ publicIp,
347
+ privateIp: natGateway.NatGatewayAddresses?.[0]?.PrivateIp,
348
+ createdElasticIp,
349
+ ...props,
350
+ subnet: subnetId,
351
+ });
352
+ return result;
353
+ },
354
+ );
355
+
356
+ /**
357
+ * Wait for NAT Gateway to be in available state
358
+ */
359
+ async function waitForNatGatewayAvailable(
360
+ natGatewayId: string,
361
+ timeoutConfig: TimeoutConfig,
362
+ ): Promise<void> {
363
+ const checkFunction = async () => {
364
+ const client = await createEC2Client();
365
+ const response = await callEC2Api<DescribeNatGatewaysResponse>(
366
+ client,
367
+ "DescribeNatGateways",
368
+ parseNatGatewayXmlResponse,
369
+ convertDescribeNatGatewaysParamsToAwsFormat({
370
+ NatGatewayIds: [natGatewayId],
371
+ }),
372
+ );
373
+ return response.NatGateways?.[0];
374
+ };
375
+
376
+ const isReady = (natGateway: AwsNatGateway | undefined) => {
377
+ // Check for non-retryable failure states
378
+ if (natGateway?.State === "failed") {
379
+ throw new NatGatewayNonRetryableError(
380
+ `NAT Gateway ${natGatewayId} failed to create`,
381
+ );
382
+ }
383
+ return natGateway?.State === "available";
384
+ };
385
+
386
+ await waitForResourceState(
387
+ checkFunction,
388
+ isReady,
389
+ timeoutConfig,
390
+ natGatewayId,
391
+ "NAT Gateway",
392
+ "is now available",
393
+ );
394
+ }
395
+ /**
396
+ * Wait for NAT Gateway to be deleted
397
+ */
398
+ async function waitForNatGatewayDeleted(
399
+ natGatewayId: string,
400
+ timeoutConfig: TimeoutConfig,
401
+ ): Promise<void> {
402
+ const checkFunction = async () => {
403
+ try {
404
+ const client = await createEC2Client();
405
+ const response = await callEC2Api<DescribeNatGatewaysResponse>(
406
+ client,
407
+ "DescribeNatGateways",
408
+ parseNatGatewayXmlResponse,
409
+ convertDescribeNatGatewaysParamsToAwsFormat({
410
+ NatGatewayIds: [natGatewayId],
411
+ }),
412
+ );
413
+ return response.NatGateways?.[0];
414
+ } catch (error: any) {
415
+ // If NAT Gateway is not found, it's been deleted - return undefined
416
+ if (
417
+ error.code === "InvalidNatGatewayID.NotFound" ||
418
+ error.code === "InvalidNatGateway.NotFound" ||
419
+ error.code === "NatGatewayNotFound"
420
+ ) {
421
+ return undefined;
422
+ }
423
+ throw error;
424
+ }
425
+ };
426
+
427
+ const isReady = (natGateway: AwsNatGateway | undefined) => {
428
+ // NAT Gateway is deleted if it doesn't exist or is in deleted state
429
+ return !natGateway || natGateway.State === "deleted";
430
+ };
431
+
432
+ await waitForResourceState(
433
+ checkFunction,
434
+ isReady,
435
+ timeoutConfig,
436
+ natGatewayId,
437
+ "NAT Gateway",
438
+ "deletion completed",
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Parse XML response for DescribeNetworkInterfaces
444
+ */
445
+ function parseDescribeNetworkInterfacesResponse(
446
+ xmlText: string,
447
+ ): DescribeNetworkInterfacesResponse {
448
+ const parser = new XMLParser({
449
+ ignoreAttributes: true,
450
+ parseAttributeValue: true,
451
+ parseTagValue: true,
452
+ trimValues: true,
453
+ });
454
+ const parsed = parser.parse(xmlText);
455
+ const result: DescribeNetworkInterfacesResponse = { NetworkInterfaces: [] };
456
+ if (parsed.DescribeNetworkInterfacesResponse) {
457
+ const networkInterfaceSet =
458
+ parsed.DescribeNetworkInterfacesResponse.networkInterfaceSet;
459
+ if (networkInterfaceSet?.item) {
460
+ const items: RawNetworkInterfaceXmlItem[] = Array.isArray(
461
+ networkInterfaceSet.item,
462
+ )
463
+ ? networkInterfaceSet.item
464
+ : [networkInterfaceSet.item];
465
+
466
+ result.NetworkInterfaces = items.map(
467
+ (item): AwsNetworkInterface => ({
468
+ NetworkInterfaceId: item.networkInterfaceId,
469
+ Description: item.description,
470
+ Status: item.status as AwsNetworkInterface["Status"],
471
+ }),
472
+ );
473
+ }
474
+ }
475
+ return result;
476
+ }
477
+
478
+ /**
479
+ * Wait for an Elastic IP to be released.
480
+ */
481
+ async function waitForEipReleased(
482
+ allocationId: string,
483
+ timeoutConfig: TimeoutConfig,
484
+ ): Promise<void> {
485
+ const checkFunction = async () => {
486
+ try {
487
+ const eipClient = await createEC2Client();
488
+ const addressResponse = await callEC2Api<DescribeAddressesResponse>(
489
+ eipClient,
490
+ "DescribeAddresses",
491
+ parseNatGatewayXmlResponse,
492
+ convertDescribeAddressesParamsToAwsFormat({
493
+ AllocationIds: [allocationId],
494
+ }),
495
+ );
496
+ return addressResponse.Addresses?.[0];
497
+ } catch (error: any) {
498
+ if (
499
+ error.code === "InvalidAllocationID.NotFound" ||
500
+ error.code === "InvalidAddress.NotFound"
501
+ ) {
502
+ return undefined;
503
+ }
504
+ throw error;
505
+ }
506
+ };
507
+
508
+ const isReady = (address: AwsAddress | undefined) => {
509
+ return !address;
510
+ };
511
+
512
+ await waitForResourceState(
513
+ checkFunction,
514
+ isReady,
515
+ timeoutConfig,
516
+ allocationId,
517
+ "Elastic IP",
518
+ "to be released",
519
+ );
520
+ }
521
+
522
+ /**
523
+ * Wait for NAT Gateway to be fully deleted including network interface cleanup
524
+ */
525
+ async function waitForNatGatewayFullyDeleted(
526
+ natGatewayId: string,
527
+ timeoutConfig: TimeoutConfig,
528
+ ): Promise<void> {
529
+ // First wait for the NAT Gateway itself to be deleted
530
+ await waitForNatGatewayDeleted(natGatewayId, timeoutConfig);
531
+
532
+ // Then wait for network interfaces to be cleaned up
533
+ const checkNetworkInterfaces = async () => {
534
+ try {
535
+ const client = await createEC2Client();
536
+ const response = await callEC2Api<DescribeNetworkInterfacesResponse>(
537
+ client,
538
+ "DescribeNetworkInterfaces",
539
+ parseDescribeNetworkInterfacesResponse,
540
+ {
541
+ "Filter.1.Name": "description",
542
+ "Filter.1.Value.1": `Interface for NAT Gateway ${natGatewayId}`,
543
+ },
544
+ );
545
+
546
+ return response.NetworkInterfaces || [];
547
+ } catch (error: any) {
548
+ // If we can't check network interfaces, assume cleanup is complete
549
+ logger.log(
550
+ ` Could not check network interfaces for NAT Gateway ${natGatewayId}, assuming cleanup complete`,
551
+ error,
552
+ );
553
+ return [];
554
+ }
555
+ };
556
+
557
+ const isNetworkInterfacesCleanedUp = (
558
+ networkInterfaces: AwsNetworkInterface[],
559
+ ) => {
560
+ if (networkInterfaces.length > 0) {
561
+ logger.log(
562
+ ` NAT Gateway ${natGatewayId} still has ${networkInterfaces.length} network interfaces, waiting for cleanup...`,
563
+ );
564
+ return false;
565
+ }
566
+ return true;
567
+ };
568
+
569
+ await waitForResourceState(
570
+ checkNetworkInterfaces,
571
+ isNetworkInterfacesCleanedUp,
572
+ timeoutConfig,
573
+ natGatewayId,
574
+ "NAT Gateway network interfaces",
575
+ "cleanup completed",
576
+ );
577
+ }
578
+
579
+ /**
580
+ * Non-retryable error for NAT Gateway operations
581
+ */
582
+ export class NatGatewayNonRetryableError extends Error {
583
+ constructor(message: string) {
584
+ super(message);
585
+ this.name = "NatGatewayNonRetryableError";
586
+ }
587
+ }
588
+
589
+ /**
590
+ * NAT Gateway timeout constants
591
+ */
592
+ export const NAT_GATEWAY_TIMEOUT: TimeoutConfig = {
593
+ maxAttempts: 120, // NAT Gateways are slow resources - can take up to 10 minutes
594
+ delayMs: 5000, // Check every 5 seconds
595
+ };
596
+
597
+ /**
598
+ * AWS NAT Gateway and related API response types
599
+ */
600
+ interface AwsNatGatewayAddress {
601
+ AllocationId?: string;
602
+ NetworkInterfaceId?: string;
603
+ PrivateIp?: string;
604
+ PublicIp?: string;
605
+ }
606
+
607
+ interface AwsTag {
608
+ Key: string;
609
+ Value: string;
610
+ }
611
+
612
+ interface AwsNatGateway {
613
+ NatGatewayId: string;
614
+ SubnetId: string;
615
+ VpcId: string;
616
+ State: "pending" | "failed" | "available" | "deleting" | "deleted";
617
+ ConnectivityType?: "public" | "private";
618
+ NatGatewayAddresses?: AwsNatGatewayAddress[];
619
+ Tags?: AwsTag[];
620
+ CreateTime?: string;
621
+ DeleteTime?: string;
622
+ FailureCode?: string;
623
+ FailureMessage?: string;
624
+ }
625
+
626
+ interface AwsAddress {
627
+ AllocationId: string;
628
+ PublicIp: string;
629
+ PrivateIpAddress?: string;
630
+ Domain: string;
631
+ }
632
+
633
+ interface AwsNetworkInterface {
634
+ NetworkInterfaceId: string;
635
+ Description?: string;
636
+ Status: "available" | "attaching" | "in-use" | "detaching";
637
+ }
638
+
639
+ interface CreateNatGatewayResponse {
640
+ NatGateway: AwsNatGateway;
641
+ }
642
+
643
+ interface DescribeNatGatewaysResponse {
644
+ NatGateways: AwsNatGateway[];
645
+ }
646
+
647
+ interface DescribeAddressesResponse {
648
+ Addresses: AwsAddress[];
649
+ }
650
+
651
+ interface DescribeNetworkInterfacesResponse {
652
+ NetworkInterfaces: AwsNetworkInterface[];
653
+ }
654
+
655
+ /**
656
+ * Raw XML parsing interfaces for type safety
657
+ */
658
+ interface RawNatGatewayAddressXmlItem {
659
+ allocationId?: string;
660
+ networkInterfaceId?: string;
661
+ privateIp?: string;
662
+ publicIp?: string;
663
+ }
664
+
665
+ interface RawTagXmlItem {
666
+ key: string;
667
+ value: string;
668
+ }
669
+
670
+ interface RawNatGatewayXmlItem {
671
+ natGatewayId: string;
672
+ subnetId: string;
673
+ vpcId: string;
674
+ state: string;
675
+ connectivityType?: string;
676
+ natGatewayAddressSet?: {
677
+ item?: RawNatGatewayAddressXmlItem | RawNatGatewayAddressXmlItem[];
678
+ };
679
+ tagSet?: {
680
+ item?: RawTagXmlItem | RawTagXmlItem[];
681
+ };
682
+ createTime?: string;
683
+ deleteTime?: string;
684
+ failureCode?: string;
685
+ failureMessage?: string;
686
+ }
687
+
688
+ interface RawAddressXmlItem {
689
+ allocationId: string;
690
+ publicIp: string;
691
+ privateIpAddress?: string;
692
+ domain: string;
693
+ }
694
+
695
+ interface RawNetworkInterfaceXmlItem {
696
+ networkInterfaceId: string;
697
+ description?: string;
698
+ status: string;
699
+ }
700
+
701
+ /**
702
+ * Parse XML responses specifically for NAT Gateway operations
703
+ */
704
+ function parseNatGatewayXmlResponse<T>(xmlText: string): T {
705
+ const parser = new XMLParser({
706
+ ignoreAttributes: true,
707
+ parseAttributeValue: true,
708
+ parseTagValue: true,
709
+ trimValues: true,
710
+ });
711
+
712
+ const parsed = parser.parse(xmlText);
713
+ const result: Record<string, unknown> = {};
714
+
715
+ // Parse CreateNatGatewayResponse
716
+ if (parsed.CreateNatGatewayResponse) {
717
+ const natGateway = parsed.CreateNatGatewayResponse
718
+ .natGateway as RawNatGatewayXmlItem;
719
+ if (natGateway) {
720
+ const addresses = natGateway.natGatewayAddressSet?.item
721
+ ? Array.isArray(natGateway.natGatewayAddressSet.item)
722
+ ? natGateway.natGatewayAddressSet.item
723
+ : [natGateway.natGatewayAddressSet.item]
724
+ : [];
725
+
726
+ const tags = natGateway.tagSet?.item
727
+ ? Array.isArray(natGateway.tagSet.item)
728
+ ? natGateway.tagSet.item
729
+ : [natGateway.tagSet.item]
730
+ : [];
731
+
732
+ result.NatGateway = {
733
+ NatGatewayId: natGateway.natGatewayId,
734
+ SubnetId: natGateway.subnetId,
735
+ VpcId: natGateway.vpcId,
736
+ State: natGateway.state as AwsNatGateway["State"],
737
+ ConnectivityType: natGateway.connectivityType as
738
+ | "public"
739
+ | "private"
740
+ | undefined,
741
+ NatGatewayAddresses: addresses.map(
742
+ (address): AwsNatGatewayAddress => ({
743
+ AllocationId: address.allocationId,
744
+ NetworkInterfaceId: address.networkInterfaceId,
745
+ PrivateIp: address.privateIp,
746
+ PublicIp: address.publicIp,
747
+ }),
748
+ ),
749
+ Tags: tags.map(
750
+ (tag): AwsTag => ({
751
+ Key: tag.key,
752
+ Value: tag.value,
753
+ }),
754
+ ),
755
+ CreateTime: natGateway.createTime,
756
+ DeleteTime: natGateway.deleteTime,
757
+ FailureCode: natGateway.failureCode,
758
+ FailureMessage: natGateway.failureMessage,
759
+ };
760
+ }
761
+ }
762
+
763
+ // Parse DescribeNatGatewaysResponse
764
+ if (parsed.DescribeNatGatewaysResponse) {
765
+ const natGatewaySet = parsed.DescribeNatGatewaysResponse.natGatewaySet;
766
+ if (natGatewaySet?.item) {
767
+ const natGateways: RawNatGatewayXmlItem[] = Array.isArray(
768
+ natGatewaySet.item,
769
+ )
770
+ ? natGatewaySet.item
771
+ : [natGatewaySet.item];
772
+ result.NatGateways = natGateways.map((natGateway): AwsNatGateway => {
773
+ const addresses = natGateway.natGatewayAddressSet?.item
774
+ ? Array.isArray(natGateway.natGatewayAddressSet.item)
775
+ ? natGateway.natGatewayAddressSet.item
776
+ : [natGateway.natGatewayAddressSet.item]
777
+ : [];
778
+
779
+ const tags = natGateway.tagSet?.item
780
+ ? Array.isArray(natGateway.tagSet.item)
781
+ ? natGateway.tagSet.item
782
+ : [natGateway.tagSet.item]
783
+ : [];
784
+
785
+ return {
786
+ NatGatewayId: natGateway.natGatewayId,
787
+ SubnetId: natGateway.subnetId,
788
+ VpcId: natGateway.vpcId,
789
+ State: natGateway.state as AwsNatGateway["State"],
790
+ ConnectivityType: natGateway.connectivityType as
791
+ | "public"
792
+ | "private"
793
+ | undefined,
794
+ NatGatewayAddresses: addresses.map(
795
+ (address): AwsNatGatewayAddress => ({
796
+ AllocationId: address.allocationId,
797
+ NetworkInterfaceId: address.networkInterfaceId,
798
+ PrivateIp: address.privateIp,
799
+ PublicIp: address.publicIp,
800
+ }),
801
+ ),
802
+ Tags: tags.map(
803
+ (tag): AwsTag => ({
804
+ Key: tag.key,
805
+ Value: tag.value,
806
+ }),
807
+ ),
808
+ CreateTime: natGateway.createTime,
809
+ DeleteTime: natGateway.deleteTime,
810
+ FailureCode: natGateway.failureCode,
811
+ FailureMessage: natGateway.failureMessage,
812
+ };
813
+ });
814
+ } else {
815
+ result.NatGateways = [];
816
+ }
817
+ }
818
+
819
+ // Parse AllocateAddressResponse (for EIP allocation)
820
+ if (parsed.AllocateAddressResponse) {
821
+ result.AllocationId = parsed.AllocateAddressResponse.allocationId;
822
+ result.PublicIp = parsed.AllocateAddressResponse.publicIp;
823
+ }
824
+
825
+ // Parse DescribeAddressesResponse (for EIP lookup)
826
+ if (parsed.DescribeAddressesResponse) {
827
+ const addressesSet = parsed.DescribeAddressesResponse.addressesSet;
828
+ if (addressesSet?.item) {
829
+ const addresses: RawAddressXmlItem[] = Array.isArray(addressesSet.item)
830
+ ? addressesSet.item
831
+ : [addressesSet.item];
832
+ result.Addresses = addresses.map(
833
+ (address): AwsAddress => ({
834
+ AllocationId: address.allocationId,
835
+ PublicIp: address.publicIp,
836
+ PrivateIpAddress: address.privateIpAddress,
837
+ Domain: address.domain,
838
+ }),
839
+ );
840
+ } else {
841
+ result.Addresses = [];
842
+ }
843
+ }
844
+
845
+ // Handle success responses
846
+ if (parsed.DeleteNatGatewayResponse || parsed.ReleaseAddressResponse) {
847
+ result.success = true;
848
+ }
849
+
850
+ return result as T;
851
+ }
852
+
853
+ interface AllocateAddressResponse {
854
+ AllocationId: string;
855
+ PublicIp: string;
856
+ }
857
+
858
+ /**
859
+ * Comprehensive AWS NAT Gateway API Parameter Types
860
+ */
861
+
862
+ // Common structures
863
+ interface TagSpecification {
864
+ ResourceType: string;
865
+ Tags: Array<{
866
+ Key: string;
867
+ Value: string;
868
+ }>;
869
+ }
870
+
871
+ interface Filter {
872
+ Name: string;
873
+ Values: string[];
874
+ }
875
+
876
+ // NAT Gateway API Request Parameters
877
+ interface CreateNatGatewayParams {
878
+ SubnetId: string;
879
+ AllocationId?: string;
880
+ ConnectivityType?: "public" | "private";
881
+ TagSpecifications?: TagSpecification[];
882
+ DryRun?: boolean;
883
+ ClientToken?: string;
884
+ }
885
+
886
+ interface DescribeNatGatewaysParams {
887
+ NatGatewayIds?: string[];
888
+ Filters?: Filter[];
889
+ NextToken?: string;
890
+ MaxResults?: number;
891
+ DryRun?: boolean;
892
+ }
893
+
894
+ interface DeleteNatGatewayParams {
895
+ NatGatewayId: string;
896
+ DryRun?: boolean;
897
+ }
898
+
899
+ // Elastic IP API Request Parameters
900
+ interface AllocateAddressParams {
901
+ Domain?: "vpc" | "standard";
902
+ Address?: string;
903
+ PublicIpv4Pool?: string;
904
+ NetworkBorderGroup?: string;
905
+ CustomerOwnedIpv4Pool?: string;
906
+ TagSpecifications?: TagSpecification[];
907
+ DryRun?: boolean;
908
+ }
909
+
910
+ interface DescribeAddressesParams {
911
+ AllocationIds?: string[];
912
+ PublicIps?: string[];
913
+ Filters?: Filter[];
914
+ DryRun?: boolean;
915
+ }
916
+
917
+ interface ReleaseAddressParams {
918
+ AllocationId?: string;
919
+ PublicIp?: string;
920
+ NetworkBorderGroup?: string;
921
+ DryRun?: boolean;
922
+ }
923
+
924
+ /**
925
+ * Helper functions to convert typed parameters to AWS API format
926
+ */
927
+ function convertCreateNatGatewayParamsToAwsFormat(
928
+ params: CreateNatGatewayParams,
929
+ ): Record<string, string> {
930
+ const awsParams: Record<string, string> = {
931
+ SubnetId: params.SubnetId,
932
+ };
933
+
934
+ if (params.AllocationId !== undefined) {
935
+ awsParams.AllocationId = params.AllocationId;
936
+ }
937
+
938
+ if (params.ConnectivityType !== undefined) {
939
+ awsParams.ConnectivityType = params.ConnectivityType;
940
+ }
941
+
942
+ if (params.DryRun !== undefined) {
943
+ awsParams.DryRun = params.DryRun.toString();
944
+ }
945
+
946
+ if (params.ClientToken !== undefined) {
947
+ awsParams.ClientToken = params.ClientToken;
948
+ }
949
+
950
+ // Handle TagSpecifications
951
+ if (params.TagSpecifications) {
952
+ params.TagSpecifications.forEach((tagSpec, specIndex) => {
953
+ const specNum = specIndex + 1;
954
+ awsParams[`TagSpecification.${specNum}.ResourceType`] =
955
+ tagSpec.ResourceType;
956
+
957
+ tagSpec.Tags.forEach((tag, tagIndex) => {
958
+ const tagNum = tagIndex + 1;
959
+ awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Key`] = tag.Key;
960
+ awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Value`] =
961
+ tag.Value;
962
+ });
963
+ });
964
+ }
965
+
966
+ return awsParams;
967
+ }
968
+
969
+ function convertDescribeNatGatewaysParamsToAwsFormat(
970
+ params: DescribeNatGatewaysParams,
971
+ ): Record<string, string> {
972
+ const awsParams: Record<string, string> = {};
973
+
974
+ if (params.NatGatewayIds) {
975
+ params.NatGatewayIds.forEach((id, index) => {
976
+ awsParams[`NatGatewayId.${index + 1}`] = id;
977
+ });
978
+ }
979
+
980
+ if (params.Filters) {
981
+ params.Filters.forEach((filter, filterIndex) => {
982
+ const filterNum = filterIndex + 1;
983
+ awsParams[`Filter.${filterNum}.Name`] = filter.Name;
984
+ filter.Values.forEach((value, valueIndex) => {
985
+ awsParams[`Filter.${filterNum}.Value.${valueIndex + 1}`] = value;
986
+ });
987
+ });
988
+ }
989
+
990
+ if (params.NextToken !== undefined) {
991
+ awsParams.NextToken = params.NextToken;
992
+ }
993
+
994
+ if (params.MaxResults !== undefined) {
995
+ awsParams.MaxResults = params.MaxResults.toString();
996
+ }
997
+
998
+ if (params.DryRun !== undefined) {
999
+ awsParams.DryRun = params.DryRun.toString();
1000
+ }
1001
+
1002
+ return awsParams;
1003
+ }
1004
+
1005
+ function convertDeleteNatGatewayParamsToAwsFormat(
1006
+ params: DeleteNatGatewayParams,
1007
+ ): Record<string, string> {
1008
+ const awsParams: Record<string, string> = {
1009
+ NatGatewayId: params.NatGatewayId,
1010
+ };
1011
+
1012
+ if (params.DryRun !== undefined) {
1013
+ awsParams.DryRun = params.DryRun.toString();
1014
+ }
1015
+
1016
+ return awsParams;
1017
+ }
1018
+
1019
+ function convertAllocateAddressParamsToAwsFormat(
1020
+ params: AllocateAddressParams,
1021
+ ): Record<string, string> {
1022
+ const awsParams: Record<string, string> = {};
1023
+
1024
+ if (params.Domain !== undefined) {
1025
+ awsParams.Domain = params.Domain;
1026
+ }
1027
+
1028
+ if (params.Address !== undefined) {
1029
+ awsParams.Address = params.Address;
1030
+ }
1031
+
1032
+ if (params.PublicIpv4Pool !== undefined) {
1033
+ awsParams.PublicIpv4Pool = params.PublicIpv4Pool;
1034
+ }
1035
+
1036
+ if (params.NetworkBorderGroup !== undefined) {
1037
+ awsParams.NetworkBorderGroup = params.NetworkBorderGroup;
1038
+ }
1039
+
1040
+ if (params.CustomerOwnedIpv4Pool !== undefined) {
1041
+ awsParams.CustomerOwnedIpv4Pool = params.CustomerOwnedIpv4Pool;
1042
+ }
1043
+
1044
+ if (params.DryRun !== undefined) {
1045
+ awsParams.DryRun = params.DryRun.toString();
1046
+ }
1047
+
1048
+ // Handle TagSpecifications
1049
+ if (params.TagSpecifications) {
1050
+ params.TagSpecifications.forEach((tagSpec, specIndex) => {
1051
+ const specNum = specIndex + 1;
1052
+ awsParams[`TagSpecification.${specNum}.ResourceType`] =
1053
+ tagSpec.ResourceType;
1054
+
1055
+ tagSpec.Tags.forEach((tag, tagIndex) => {
1056
+ const tagNum = tagIndex + 1;
1057
+ awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Key`] = tag.Key;
1058
+ awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Value`] =
1059
+ tag.Value;
1060
+ });
1061
+ });
1062
+ }
1063
+
1064
+ return awsParams;
1065
+ }
1066
+
1067
+ function convertDescribeAddressesParamsToAwsFormat(
1068
+ params: DescribeAddressesParams,
1069
+ ): Record<string, string> {
1070
+ const awsParams: Record<string, string> = {};
1071
+
1072
+ if (params.AllocationIds) {
1073
+ params.AllocationIds.forEach((id, index) => {
1074
+ awsParams[`AllocationId.${index + 1}`] = id;
1075
+ });
1076
+ }
1077
+
1078
+ if (params.PublicIps) {
1079
+ params.PublicIps.forEach((ip, index) => {
1080
+ awsParams[`PublicIp.${index + 1}`] = ip;
1081
+ });
1082
+ }
1083
+
1084
+ if (params.Filters) {
1085
+ params.Filters.forEach((filter, filterIndex) => {
1086
+ const filterNum = filterIndex + 1;
1087
+ awsParams[`Filter.${filterNum}.Name`] = filter.Name;
1088
+ filter.Values.forEach((value, valueIndex) => {
1089
+ awsParams[`Filter.${filterNum}.Value.${valueIndex + 1}`] = value;
1090
+ });
1091
+ });
1092
+ }
1093
+
1094
+ if (params.DryRun !== undefined) {
1095
+ awsParams.DryRun = params.DryRun.toString();
1096
+ }
1097
+
1098
+ return awsParams;
1099
+ }
1100
+
1101
+ function convertReleaseAddressParamsToAwsFormat(
1102
+ params: ReleaseAddressParams,
1103
+ ): Record<string, string> {
1104
+ const awsParams: Record<string, string> = {};
1105
+
1106
+ if (params.AllocationId !== undefined) {
1107
+ awsParams.AllocationId = params.AllocationId;
1108
+ }
1109
+
1110
+ if (params.PublicIp !== undefined) {
1111
+ awsParams.PublicIp = params.PublicIp;
1112
+ }
1113
+
1114
+ if (params.NetworkBorderGroup !== undefined) {
1115
+ awsParams.NetworkBorderGroup = params.NetworkBorderGroup;
1116
+ }
1117
+
1118
+ if (params.DryRun !== undefined) {
1119
+ awsParams.DryRun = params.DryRun.toString();
1120
+ }
1121
+
1122
+ return awsParams;
1123
+ }