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,468 @@
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 { InternetGateway } from "./internet-gateway.ts";
12
+ import { callEC2Api, createEC2Client } from "./utils.ts";
13
+ import type { Vpc } from "./vpc.ts";
14
+
15
+ /**
16
+ * Properties for creating or updating an Internet Gateway Attachment
17
+ */
18
+ export interface InternetGatewayAttachmentProps {
19
+ /**
20
+ * The Internet Gateway to attach
21
+ */
22
+ internetGateway: InternetGateway | string;
23
+
24
+ /**
25
+ * The VPC to attach the Internet Gateway to
26
+ */
27
+ vpc: Vpc | string;
28
+
29
+ /**
30
+ * Timeout configuration for Internet Gateway Attachment operations
31
+ * @default Internet Gateway Attachment-specific sensible defaults (60 attempts, 2000ms delay)
32
+ */
33
+ timeout?: Partial<TimeoutConfig>;
34
+ }
35
+
36
+ /**
37
+ * Output returned after Internet Gateway Attachment creation/update
38
+ */
39
+ export interface InternetGatewayAttachment
40
+ extends Resource<"aws::InternetGatewayAttachment">,
41
+ InternetGatewayAttachmentProps {
42
+ /**
43
+ * The ID of the Internet Gateway
44
+ */
45
+ internetGatewayId: string;
46
+
47
+ /**
48
+ * The ID of the VPC
49
+ */
50
+ vpcId: string;
51
+
52
+ /**
53
+ * The current state of the attachment
54
+ */
55
+ state: "attaching" | "attached" | "detaching" | "detached";
56
+ }
57
+
58
+ /**
59
+ * AWS Internet Gateway Attachment Resource
60
+ *
61
+ * Manages the attachment of an Internet Gateway to a VPC, enabling internet connectivity
62
+ * for the VPC. This is a separate resource to ensure proper dependency ordering and
63
+ * lifecycle management between Internet Gateways and VPCs.
64
+ *
65
+ * An Internet Gateway attachment is required for any VPC that needs internet access.
66
+ * The attachment creates a logical connection between the Internet Gateway and the VPC,
67
+ * allowing traffic to flow between the VPC and the internet when combined with proper
68
+ * routing configuration.
69
+ *
70
+ * @example
71
+ * // Basic Internet Gateway attachment for internet connectivity
72
+ * const vpc = await Vpc("main-vpc", {
73
+ * cidrBlock: "10.0.0.0/16"
74
+ * });
75
+ *
76
+ * const igw = await InternetGateway("main-igw", {});
77
+ *
78
+ * const attachment = await InternetGatewayAttachment("main-igw-attachment", {
79
+ * internetGateway: igw,
80
+ * vpc: vpc
81
+ * });
82
+ *
83
+ * @example
84
+ * // Internet Gateway attachment with custom timeout configuration
85
+ * // for environments with slower AWS API responses
86
+ * const attachment = await InternetGatewayAttachment("slow-env-attachment", {
87
+ * internetGateway: "igw-1234567890abcdef0",
88
+ * vpc: "vpc-0987654321fedcba0",
89
+ * timeout: {
90
+ * maxAttempts: 120,
91
+ * delayMs: 3000
92
+ * }
93
+ * });
94
+ *
95
+ * @example
96
+ * // Complete setup for a VPC with internet access including routing
97
+ * const vpc = await Vpc("web-vpc", {
98
+ * cidrBlock: "10.0.0.0/16",
99
+ * enableDnsHostnames: true,
100
+ * enableDnsSupport: true
101
+ * });
102
+ *
103
+ * const igw = await InternetGateway("web-igw", {});
104
+ *
105
+ * const attachment = await InternetGatewayAttachment("web-igw-attachment", {
106
+ * internetGateway: igw,
107
+ * vpc: vpc
108
+ * });
109
+ *
110
+ * // Create public subnet and route table for internet access
111
+ * const publicSubnet = await Subnet("public-subnet", {
112
+ * vpc,
113
+ * cidrBlock: "10.0.1.0/24",
114
+ * availabilityZone: "us-east-1a"
115
+ * });
116
+ *
117
+ * const publicRouteTable = await RouteTable("public-rt", {
118
+ * vpc: vpc
119
+ * });
120
+ *
121
+ * const internetRoute = await Route("internet-route", {
122
+ * routeTable: publicRouteTable,
123
+ * destinationCidrBlock: "0.0.0.0/0",
124
+ * gateway: igw
125
+ * });
126
+ */
127
+ export const InternetGatewayAttachment = Resource(
128
+ "aws::InternetGatewayAttachment",
129
+ async function (
130
+ this: Context<InternetGatewayAttachment>,
131
+ _id: string,
132
+ props: InternetGatewayAttachmentProps,
133
+ ): Promise<InternetGatewayAttachment> {
134
+ const client = await createEC2Client();
135
+ const timeoutConfig = mergeTimeoutConfig(
136
+ INTERNET_GATEWAY_ATTACHMENT_TIMEOUT,
137
+ props.timeout,
138
+ );
139
+
140
+ const internetGatewayId =
141
+ typeof props.internetGateway === "string"
142
+ ? props.internetGateway
143
+ : props.internetGateway.internetGatewayId;
144
+ const vpcId = typeof props.vpc === "string" ? props.vpc : props.vpc.vpcId;
145
+
146
+ if (this.phase === "delete") {
147
+ if (this.output?.internetGatewayId && this.output?.vpcId) {
148
+ logger.log(
149
+ `🗑️ Detaching Internet Gateway: ${this.output.internetGatewayId} from VPC: ${this.output.vpcId}`,
150
+ );
151
+ await ignore("Gateway.NotAttached", async () => {
152
+ await ignore("InvalidInternetGatewayID.NotFound", async () => {
153
+ await ignore("InvalidVpcID.NotFound", async () => {
154
+ await callEC2Api(
155
+ client,
156
+ "DetachInternetGateway",
157
+ parseInternetGatewayAttachmentXmlResponse,
158
+ {
159
+ InternetGatewayId: this.output!.internetGatewayId,
160
+ VpcId: this.output!.vpcId,
161
+ },
162
+ );
163
+ });
164
+ });
165
+ });
166
+
167
+ const checkDetached = async () => {
168
+ try {
169
+ const response =
170
+ await callEC2Api<DescribeInternetGatewaysApiResponse>(
171
+ client,
172
+ "DescribeInternetGateways",
173
+ parseInternetGatewayAttachmentXmlResponse,
174
+ {
175
+ "InternetGatewayId.1": this.output!.internetGatewayId,
176
+ },
177
+ );
178
+ const igw = response.InternetGateways?.[0];
179
+ if (!igw) {
180
+ return undefined;
181
+ }
182
+ return igw.Attachments?.find(
183
+ (att) => att.VpcId === this.output!.vpcId,
184
+ );
185
+ } catch (error: any) {
186
+ if (error.code === "InvalidInternetGatewayID.NotFound") {
187
+ return undefined;
188
+ }
189
+ throw error;
190
+ }
191
+ };
192
+
193
+ await waitForResourceState(
194
+ checkDetached,
195
+ (attachment) => !attachment,
196
+ timeoutConfig,
197
+ `${this.output.internetGatewayId} to ${this.output.vpcId}`,
198
+ "Internet Gateway Attachment",
199
+ "to be detached",
200
+ );
201
+
202
+ logger.log(
203
+ ` ✅ Internet Gateway ${this.output.internetGatewayId} detached from VPC ${this.output.vpcId}`,
204
+ );
205
+ }
206
+ return this.destroy();
207
+ }
208
+
209
+ // Check if already attached
210
+ if (this.phase === "update" && this.output?.internetGatewayId) {
211
+ // Verify the attachment still exists
212
+ const response = await callEC2Api<DescribeInternetGatewaysApiResponse>(
213
+ client,
214
+ "DescribeInternetGateways",
215
+ parseInternetGatewayAttachmentXmlResponse,
216
+ {
217
+ "InternetGatewayId.1": this.output.internetGatewayId,
218
+ },
219
+ );
220
+
221
+ const igw = response.InternetGateways?.[0];
222
+ const attachment = igw?.Attachments?.find((att) => att.VpcId === vpcId);
223
+
224
+ if (attachment) {
225
+ return this({
226
+ internetGatewayId,
227
+ vpcId,
228
+ state: "attached",
229
+ ...props,
230
+ });
231
+ }
232
+ }
233
+
234
+ // Check if already attached before creating
235
+ const checkResponse = await callEC2Api<DescribeInternetGatewaysApiResponse>(
236
+ client,
237
+ "DescribeInternetGateways",
238
+ parseInternetGatewayAttachmentXmlResponse,
239
+ {
240
+ "InternetGatewayId.1": internetGatewayId,
241
+ },
242
+ );
243
+
244
+ const existingIgw = checkResponse.InternetGateways?.[0];
245
+ const existingAttachment = existingIgw?.Attachments?.find(
246
+ (att) => att.VpcId === vpcId,
247
+ );
248
+
249
+ if (!existingAttachment) {
250
+ // Create the attachment only if it doesn't exist
251
+ await callEC2Api(
252
+ client,
253
+ "AttachInternetGateway",
254
+ parseInternetGatewayAttachmentXmlResponse,
255
+ {
256
+ InternetGatewayId: internetGatewayId,
257
+ VpcId: vpcId,
258
+ },
259
+ );
260
+ }
261
+
262
+ // Wait for attachment to complete
263
+ const checkAttached = async () => {
264
+ try {
265
+ const response = await callEC2Api<DescribeInternetGatewaysApiResponse>(
266
+ client,
267
+ "DescribeInternetGateways",
268
+ parseInternetGatewayAttachmentXmlResponse,
269
+ {
270
+ "InternetGatewayId.1": internetGatewayId,
271
+ },
272
+ );
273
+ const igw = response.InternetGateways?.[0];
274
+ if (!igw) {
275
+ return undefined;
276
+ }
277
+ return igw.Attachments?.find((att) => att.VpcId === vpcId);
278
+ } catch (error: any) {
279
+ if (error.code === "InvalidInternetGatewayID.NotFound") {
280
+ return undefined;
281
+ }
282
+ throw error;
283
+ }
284
+ };
285
+
286
+ const isAttached = (attachment: DescribedAttachment | undefined) => {
287
+ return (
288
+ attachment?.State === "attached" || attachment?.State === "available"
289
+ );
290
+ };
291
+
292
+ await waitForResourceState(
293
+ checkAttached,
294
+ isAttached,
295
+ timeoutConfig,
296
+ `${internetGatewayId} to ${vpcId}`,
297
+ "Internet Gateway Attachment",
298
+ "to be attached",
299
+ );
300
+
301
+ return this({
302
+ internetGatewayId,
303
+ vpcId,
304
+ state: "attached",
305
+ ...props,
306
+ });
307
+ },
308
+ );
309
+
310
+ /**
311
+ * Possible states for an Internet Gateway attachment.
312
+ */
313
+ type AttachmentState =
314
+ | "attaching"
315
+ | "attached"
316
+ | "detaching"
317
+ | "detached"
318
+ | "available";
319
+
320
+ /**
321
+ * Represents an attachment as described by the EC2 API.
322
+ */
323
+ interface DescribedAttachment {
324
+ VpcId: string;
325
+ State: AttachmentState;
326
+ }
327
+
328
+ /**
329
+ * Represents a tag as described by the EC2 API.
330
+ */
331
+ interface DescribedTag {
332
+ Key: string;
333
+ Value: string;
334
+ }
335
+
336
+ /**
337
+ * Represents an Internet Gateway as described by the EC2 API.
338
+ */
339
+ interface DescribedInternetGateway {
340
+ InternetGatewayId: string;
341
+ State: "available";
342
+ Attachments: DescribedAttachment[];
343
+ Tags?: DescribedTag[];
344
+ OwnerId: string;
345
+ }
346
+
347
+ /**
348
+ * The structure of the response from the DescribeInternetGateways API call.
349
+ */
350
+ export interface DescribeInternetGatewaysApiResponse {
351
+ InternetGateways: DescribedInternetGateway[];
352
+ }
353
+
354
+ /**
355
+ * Represents a tag item parsed from the raw XML response.
356
+ */
357
+ interface XmlTagItem {
358
+ key: string;
359
+ value: string;
360
+ }
361
+
362
+ /**
363
+ * Represents an attachment item parsed from the raw XML response.
364
+ */
365
+ interface XmlAttachmentItem {
366
+ vpcId: string;
367
+ state: AttachmentState;
368
+ }
369
+
370
+ /**
371
+ * Represents an Internet Gateway item parsed from the raw XML response.
372
+ */
373
+ interface XmlInternetGatewayItem {
374
+ internetGatewayId: string;
375
+ ownerId: string;
376
+ attachmentSet?: {
377
+ item: XmlAttachmentItem | XmlAttachmentItem[];
378
+ };
379
+ tagSet?: {
380
+ item: XmlTagItem | XmlTagItem[];
381
+ };
382
+ }
383
+
384
+ /**
385
+ * Internet Gateway Attachment timeout constants
386
+ */
387
+ export const INTERNET_GATEWAY_ATTACHMENT_TIMEOUT: TimeoutConfig = {
388
+ maxAttempts: 60,
389
+ delayMs: 2000, // 2 seconds - IGW attachments are usually fast
390
+ };
391
+
392
+ /**
393
+ * Parse XML responses for Internet Gateway Attachment operations
394
+ */
395
+ function parseInternetGatewayAttachmentXmlResponse<T>(xmlText: string): T {
396
+ const parser = new XMLParser({
397
+ ignoreAttributes: true,
398
+ parseAttributeValue: true,
399
+ parseTagValue: true,
400
+ trimValues: true,
401
+ });
402
+
403
+ const parsed = parser.parse(xmlText);
404
+ const result: Record<string, unknown> = {};
405
+
406
+ // Handle success responses
407
+ if (parsed.AttachInternetGatewayResponse) {
408
+ result.success = true;
409
+ result.return = parsed.AttachInternetGatewayResponse.return;
410
+ }
411
+
412
+ if (parsed.DetachInternetGatewayResponse) {
413
+ result.success = true;
414
+ result.return = parsed.DetachInternetGatewayResponse.return;
415
+ }
416
+
417
+ // Handle DescribeInternetGatewaysResponse
418
+ if (parsed.DescribeInternetGatewaysResponse) {
419
+ const internetGatewaySet =
420
+ parsed.DescribeInternetGatewaysResponse.internetGatewaySet;
421
+ if (internetGatewaySet?.item) {
422
+ const igws: XmlInternetGatewayItem[] = Array.isArray(
423
+ internetGatewaySet.item,
424
+ )
425
+ ? internetGatewaySet.item
426
+ : [internetGatewaySet.item];
427
+
428
+ result.InternetGateways = igws.map((igw): DescribedInternetGateway => {
429
+ const attachments = igw.attachmentSet?.item
430
+ ? Array.isArray(igw.attachmentSet.item)
431
+ ? igw.attachmentSet.item
432
+ : [igw.attachmentSet.item]
433
+ : [];
434
+
435
+ const tags = igw.tagSet?.item
436
+ ? Array.isArray(igw.tagSet.item)
437
+ ? igw.tagSet.item
438
+ : [igw.tagSet.item]
439
+ : [];
440
+
441
+ return {
442
+ InternetGatewayId: igw.internetGatewayId,
443
+ State: "available", // IGWs are always available once created
444
+ Attachments: attachments.map(
445
+ (attachment): DescribedAttachment => ({
446
+ VpcId: attachment.vpcId,
447
+ State: attachment.state,
448
+ }),
449
+ ),
450
+ Tags:
451
+ tags.length > 0
452
+ ? tags.map(
453
+ (tag): DescribedTag => ({
454
+ Key: tag.key,
455
+ Value: tag.value,
456
+ }),
457
+ )
458
+ : undefined,
459
+ OwnerId: igw.ownerId,
460
+ };
461
+ });
462
+ } else {
463
+ result.InternetGateways = [];
464
+ }
465
+ }
466
+
467
+ return result as T;
468
+ }