alchemy 0.60.2 → 0.62.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.
- package/README.md +1 -2
- package/bin/alchemy.js +1480 -1498
- package/bin/commands/init.ts +94 -25
- package/bin/services/execute-alchemy.ts +32 -23
- package/lib/alchemy.d.ts +2 -2
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +1 -0
- package/lib/alchemy.js.map +1 -1
- package/lib/aws/client-props.d.ts +149 -0
- package/lib/aws/client-props.d.ts.map +1 -0
- package/lib/aws/client-props.js +1 -0
- package/lib/aws/client-props.js.map +1 -0
- package/lib/aws/credentials.d.ts +68 -5
- package/lib/aws/credentials.d.ts.map +1 -1
- package/lib/aws/credentials.js +148 -0
- package/lib/aws/credentials.js.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.d.ts +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.js +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +68 -40
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway.js +68 -40
- package/lib/aws/ec2/internet-gateway.js.map +1 -1
- package/lib/aws/ec2/nat-gateway.d.ts +99 -18
- package/lib/aws/ec2/nat-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/nat-gateway.js +113 -37
- package/lib/aws/ec2/nat-gateway.js.map +1 -1
- package/lib/aws/ec2/route-table-association.d.ts +1 -1
- package/lib/aws/ec2/route-table-association.js +1 -1
- package/lib/aws/ec2/route-table.d.ts +64 -1
- package/lib/aws/ec2/route-table.d.ts.map +1 -1
- package/lib/aws/ec2/route-table.js +64 -1
- package/lib/aws/ec2/route-table.js.map +1 -1
- package/lib/aws/ec2/route.d.ts +60 -1
- package/lib/aws/ec2/route.d.ts.map +1 -1
- package/lib/aws/ec2/route.js +60 -1
- package/lib/aws/ec2/route.js.map +1 -1
- package/lib/aws/ec2/security-group.d.ts +96 -3
- package/lib/aws/ec2/security-group.d.ts.map +1 -1
- package/lib/aws/ec2/security-group.js +96 -3
- package/lib/aws/ec2/security-group.js.map +1 -1
- package/lib/aws/ec2/subnet.d.ts +61 -3
- package/lib/aws/ec2/subnet.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.js +61 -3
- package/lib/aws/ec2/subnet.js.map +1 -1
- package/lib/aws/ec2/utils.d.ts +10 -2
- package/lib/aws/ec2/utils.d.ts.map +1 -1
- package/lib/aws/ec2/utils.js +68 -7
- package/lib/aws/ec2/utils.js.map +1 -1
- package/lib/aws/ec2/vpc.d.ts +50 -2
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.js +50 -2
- package/lib/aws/ec2/vpc.js.map +1 -1
- package/lib/aws/index.d.ts +2 -0
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +2 -0
- package/lib/aws/index.js.map +1 -1
- package/lib/cloudflare/api-response.js +2 -2
- package/lib/cloudflare/api-response.js.map +1 -1
- package/lib/cloudflare/api.d.ts +19 -0
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +4 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/assets.d.ts.map +1 -1
- package/lib/cloudflare/assets.js +3 -4
- package/lib/cloudflare/assets.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +5 -3
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +28 -20
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-compat-warning.js +7 -7
- package/lib/cloudflare/bundle/plugin-compat-warning.js.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-migrations.js +3 -2
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/index.d.ts +2 -1
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -1
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +2 -2
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +16 -3
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +3 -0
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/oauth.js +3 -3
- package/lib/cloudflare/oauth.js.map +1 -1
- package/lib/cloudflare/quick-tunnel.d.ts +17 -0
- package/lib/cloudflare/quick-tunnel.d.ts.map +1 -0
- package/lib/cloudflare/quick-tunnel.js +26 -0
- package/lib/cloudflare/quick-tunnel.js.map +1 -0
- package/lib/cloudflare/sveltekit/plugin.d.ts +2 -2
- package/lib/cloudflare/sveltekit/plugin.d.ts.map +1 -1
- package/lib/cloudflare/sveltekit/plugin.js +5 -4
- package/lib/cloudflare/sveltekit/plugin.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +26 -97
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/worker-bundle.js +11 -9
- package/lib/cloudflare/worker-bundle.js.map +1 -1
- package/lib/cloudflare/worker-tail.d.ts.map +1 -1
- package/lib/cloudflare/worker-tail.js +6 -6
- package/lib/cloudflare/worker-tail.js.map +1 -1
- package/lib/resource.d.ts +1 -0
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +13 -2
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +38 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +44 -19
- package/lib/scope.js.map +1 -1
- package/lib/state/cloudflare-state-store.d.ts.map +1 -1
- package/lib/state/cloudflare-state-store.js +2 -1
- package/lib/state/cloudflare-state-store.js.map +1 -1
- package/lib/util/idempotent-spawn.d.ts +24 -0
- package/lib/util/idempotent-spawn.d.ts.map +1 -0
- package/lib/util/idempotent-spawn.js +237 -0
- package/lib/util/idempotent-spawn.js.map +1 -0
- package/lib/util/index.d.ts +2 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +2 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/safe-fetch.d.ts +24 -0
- package/lib/util/safe-fetch.d.ts.map +1 -1
- package/lib/util/safe-fetch.js +72 -0
- package/lib/util/safe-fetch.js.map +1 -1
- package/package.json +9 -5
- package/src/alchemy.ts +3 -2
- package/src/aws/client-props.ts +156 -0
- package/src/aws/credentials.ts +165 -4
- package/src/aws/ec2/internet-gateway-attachment.ts +59 -4
- package/src/aws/ec2/internet-gateway.ts +70 -41
- package/src/aws/ec2/nat-gateway.ts +123 -33
- package/src/aws/ec2/route-table-association.ts +1 -1
- package/src/aws/ec2/route-table.ts +66 -2
- package/src/aws/ec2/route.ts +62 -2
- package/src/aws/ec2/security-group.ts +98 -4
- package/src/aws/ec2/subnet.ts +63 -4
- package/src/aws/ec2/utils.ts +73 -7
- package/src/aws/ec2/vpc.ts +52 -3
- package/src/aws/index.ts +2 -0
- package/src/cloudflare/api-response.ts +2 -2
- package/src/cloudflare/api.ts +23 -0
- package/src/cloudflare/assets.ts +3 -4
- package/src/cloudflare/bucket.ts +39 -29
- package/src/cloudflare/bundle/plugin-compat-warning.ts +7 -7
- package/src/cloudflare/cloudflare-env-proxy.ts +2 -2
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-migrations.ts +4 -5
- package/src/cloudflare/index.ts +2 -1
- package/src/cloudflare/kv-namespace.ts +26 -27
- package/src/cloudflare/miniflare/build-worker-options.ts +16 -3
- package/src/cloudflare/miniflare/miniflare-controller.ts +4 -0
- package/src/cloudflare/oauth.ts +3 -3
- package/src/cloudflare/quick-tunnel.ts +30 -0
- package/src/cloudflare/sveltekit/plugin.ts +5 -5
- package/src/cloudflare/website.ts +30 -111
- package/src/cloudflare/worker-bundle.ts +11 -9
- package/src/cloudflare/worker-tail.ts +6 -8
- package/src/resource.ts +19 -2
- package/src/scope.ts +111 -20
- package/src/state/cloudflare-state-store.ts +2 -1
- package/src/util/idempotent-spawn.ts +303 -0
- package/src/util/index.ts +1 -0
- package/src/util/safe-fetch.ts +114 -0
- package/templates/rwsdk/package.json +1 -1
package/lib/util/safe-fetch.js
CHANGED
|
@@ -52,4 +52,76 @@ async function dispatcher() {
|
|
|
52
52
|
}
|
|
53
53
|
return agent.value;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Fetch with exponential backoff retry logic for 404 responses, expecting 200 OK.
|
|
57
|
+
* This is a convenience wrapper around fetchAndExpectStatus with expectedStatus=200.
|
|
58
|
+
*
|
|
59
|
+
* @param input URL or Request object
|
|
60
|
+
* @param init RequestInit options
|
|
61
|
+
* @param maxAttempts Maximum number of retry attempts (default: 10)
|
|
62
|
+
* @param maxWaitTime Maximum total wait time in milliseconds (default: 30000)
|
|
63
|
+
* @returns The successful Response
|
|
64
|
+
*/
|
|
65
|
+
export async function fetchAndExpectOK(input, init, maxAttempts = 10, maxWaitTime = 30000) {
|
|
66
|
+
return fetchAndExpectStatus(input, init, 200, maxAttempts, maxWaitTime);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Fetch and expect a specific status code with retry logic.
|
|
70
|
+
* Cloudflare's control plane is eventually consistent, so resources may
|
|
71
|
+
* return 404 immediately after creation before becoming available.
|
|
72
|
+
*
|
|
73
|
+
* @param input URL or Request object
|
|
74
|
+
* @param init RequestInit options
|
|
75
|
+
* @param expectedStatus Expected HTTP status code (default: 200)
|
|
76
|
+
* @param maxAttempts Maximum number of retry attempts (default: 10)
|
|
77
|
+
* @param maxWaitTime Maximum total wait time in milliseconds (default: 30000)
|
|
78
|
+
* @returns The successful Response
|
|
79
|
+
*/
|
|
80
|
+
export async function fetchAndExpectStatus(input, init, expectedStatus = 200, maxAttempts = 20, maxWaitTime = 30000) {
|
|
81
|
+
let attempt = 0;
|
|
82
|
+
let lastError;
|
|
83
|
+
while (attempt < maxAttempts) {
|
|
84
|
+
try {
|
|
85
|
+
const response = await safeFetch(input, init);
|
|
86
|
+
// If we get the expected status, return it
|
|
87
|
+
if (response.status === expectedStatus) {
|
|
88
|
+
return response;
|
|
89
|
+
}
|
|
90
|
+
// If we get a 500, 404 when expecting something else, retry with backoff
|
|
91
|
+
if (response.status >= 500 ||
|
|
92
|
+
(response.status === 404 && expectedStatus !== 404)) {
|
|
93
|
+
attempt++;
|
|
94
|
+
if (attempt >= maxAttempts) {
|
|
95
|
+
throw new Error(`Expected status ${expectedStatus} but got 404 after ${maxAttempts} attempts. This may indicate eventual consistency issues.`);
|
|
96
|
+
}
|
|
97
|
+
const backoffTime = Math.min(attempt * 1500, maxWaitTime / maxAttempts);
|
|
98
|
+
console.log(`Expected status ${expectedStatus} but got 404, retrying in ${backoffTime}ms (attempt ${attempt}/${maxAttempts})...`);
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// For other status codes, throw immediately
|
|
103
|
+
throw new Error(`Expected status ${expectedStatus} but got ${response.status}: ${response.statusText}`);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
lastError = error;
|
|
108
|
+
// If it's not an HTTP error, retry with backoff
|
|
109
|
+
if (!error.message.startsWith("Expected status") &&
|
|
110
|
+
!error.message.startsWith("HTTP ")) {
|
|
111
|
+
attempt++;
|
|
112
|
+
if (attempt >= maxAttempts) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
const backoffTime = Math.min(attempt * 1500, maxWaitTime / maxAttempts);
|
|
116
|
+
console.log(`Network error, retrying in ${backoffTime}ms (attempt ${attempt}/${maxAttempts}): ${error.message}`);
|
|
117
|
+
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// For status code mismatches, throw immediately
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
throw lastError || new Error(`Failed after ${maxAttempts} attempts`);
|
|
126
|
+
}
|
|
55
127
|
//# sourceMappingURL=safe-fetch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/util/safe-fetch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAA2B,EAC3B,UAAuB,EAAE,EACzB,OAAO,GAAG,EAAE;IAEZ,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE;gBACtB,GAAG,OAAO;gBACV,oEAAoE;gBACpE,UAAU,EAAE,MAAM,UAAU,EAAE;aAChB,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACxB,SAAS,GAAG,GAAG,CAAC;YAChB,MAAM,WAAW,GACf,uBAAuB,CAAC,GAAG,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAErE,IAAI,CAAC,WAAW,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxC,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,QAAQ,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,sBAAsB;QAChF,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAQ;IACvC,OAAO,CACL,GAAG,EAAE,IAAI,KAAK,gBAAgB;QAC9B,GAAG,EAAE,IAAI,KAAK,YAAY;QAC1B,GAAG,EAAE,IAAI,KAAK,yBAAyB;QACvC,GAAG,EAAE,IAAI,KAAK,OAAO;QACrB,GAAG,EAAE,IAAI,KAAK,YAAY,CAC3B,CAAC;AACJ,CAAC;AAED,IAAI,KAIS,CAAC;AAEd,KAAK,UAAU,UAAU;IACvB,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,IAAI,CAAC;QACH,uCAAuC;QACvC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,gEAAgE;YAChE,KAAK,GAAG;gBACN,KAAK,EAAE,IAAI,KAAK,CAAC;oBACf,UAAU,EAAE,CAAC,EAAE,gDAAgD;oBAC/D,2DAA2D;iBAC5D,CAAC;aACH,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC"}
|
|
1
|
+
{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/util/safe-fetch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAA2B,EAC3B,UAAuB,EAAE,EACzB,OAAO,GAAG,EAAE;IAEZ,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE;gBACtB,GAAG,OAAO;gBACV,oEAAoE;gBACpE,UAAU,EAAE,MAAM,UAAU,EAAE;aAChB,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACxB,SAAS,GAAG,GAAG,CAAC;YAChB,MAAM,WAAW,GACf,uBAAuB,CAAC,GAAG,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAErE,IAAI,CAAC,WAAW,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxC,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,SAAS,OAAO,IAAI,OAAO,QAAQ,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,sBAAsB;QAChF,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAQ;IACvC,OAAO,CACL,GAAG,EAAE,IAAI,KAAK,gBAAgB;QAC9B,GAAG,EAAE,IAAI,KAAK,YAAY;QAC1B,GAAG,EAAE,IAAI,KAAK,yBAAyB;QACvC,GAAG,EAAE,IAAI,KAAK,OAAO;QACrB,GAAG,EAAE,IAAI,KAAK,YAAY,CAC3B,CAAC;AACJ,CAAC;AAED,IAAI,KAIS,CAAC;AAEd,KAAK,UAAU,UAAU;IACvB,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,IAAI,CAAC;QACH,uCAAuC;QACvC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,gEAAgE;YAChE,KAAK,GAAG;gBACN,KAAK,EAAE,IAAI,KAAK,CAAC;oBACf,UAAU,EAAE,CAAC,EAAE,gDAAgD;oBAC/D,2DAA2D;iBAC5D,CAAC;aACH,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA6B,EAC7B,IAAkB,EAClB,WAAW,GAAG,EAAE,EAChB,WAAW,GAAG,KAAK;IAEnB,OAAO,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAA6B,EAC7B,IAAkB,EAClB,cAAc,GAAG,GAAG,EACpB,WAAW,GAAG,EAAE,EAChB,WAAW,GAAG,KAAK;IAEnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAA4B,CAAC;IAEjC,OAAO,OAAO,GAAG,WAAW,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAE9C,2CAA2C;YAC3C,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;gBACvC,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,yEAAyE;YACzE,IACE,QAAQ,CAAC,MAAM,IAAI,GAAG;gBACtB,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,cAAc,KAAK,GAAG,CAAC,EACnD,CAAC;gBACD,OAAO,EAAE,CAAC;gBAEV,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;oBAC3B,MAAM,IAAI,KAAK,CACb,mBAAmB,cAAc,sBAAsB,WAAW,2DAA2D,CAC9H,CAAC;gBACJ,CAAC;gBAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC,CAAC;gBAExE,OAAO,CAAC,GAAG,CACT,mBAAmB,cAAc,6BAA6B,WAAW,eAAe,OAAO,IAAI,WAAW,MAAM,CACrH,CAAC;gBAEF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;gBACjE,SAAS;YACX,CAAC;YAED,4CAA4C;YAC5C,MAAM,IAAI,KAAK,CACb,mBAAmB,cAAc,YAAY,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,CACvF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,SAAS,GAAG,KAAK,CAAC;gBAElB,gDAAgD;gBAChD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC;oBAC5C,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAClC,CAAC;oBACD,OAAO,EAAE,CAAC;oBAEV,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;wBAC3B,MAAM;oBACR,CAAC;oBAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,OAAO,GAAG,IAAI,EACd,WAAW,GAAG,WAAW,CAC1B,CAAC;oBAEF,OAAO,CAAC,GAAG,CACT,8BAA8B,WAAW,eAAe,OAAO,IAAI,WAAW,MAAM,KAAK,CAAC,OAAO,EAAE,CACpG,CAAC;oBAEF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;oBACjE,SAAS;gBACX,CAAC;YACH,CAAC;YAED,gDAAgD;YAChD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,gBAAgB,WAAW,WAAW,CAAC,CAAC;AACvE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
6
6
|
"homepage": "https://alchemy.run",
|
|
@@ -149,6 +149,10 @@
|
|
|
149
149
|
"bun": "./src/state/index.ts",
|
|
150
150
|
"import": "./lib/state/index.js"
|
|
151
151
|
},
|
|
152
|
+
"./util": {
|
|
153
|
+
"bun": "./src/util/index.ts",
|
|
154
|
+
"import": "./lib/util/index.js"
|
|
155
|
+
},
|
|
152
156
|
"./web/astro": {
|
|
153
157
|
"bun": "./src/web/astro.ts",
|
|
154
158
|
"import": "./lib/web/astro.js"
|
|
@@ -168,7 +172,7 @@
|
|
|
168
172
|
},
|
|
169
173
|
"dependencies": {
|
|
170
174
|
"@aws-sdk/credential-providers": "^3.0.0",
|
|
171
|
-
"@cloudflare/unenv-preset": "^2.
|
|
175
|
+
"@cloudflare/unenv-preset": "^2.6.1",
|
|
172
176
|
"@cloudflare/workers-shared": "^0.17.5",
|
|
173
177
|
"@iarna/toml": "^2.2.5",
|
|
174
178
|
"@smithy/node-config-provider": "^4.0.0",
|
|
@@ -180,9 +184,10 @@
|
|
|
180
184
|
"execa": "^9.6.0",
|
|
181
185
|
"fast-json-patch": "^3.1.1",
|
|
182
186
|
"fast-xml-parser": "^5.2.5",
|
|
187
|
+
"find-process": "^2.0.0",
|
|
183
188
|
"glob": "^10.0.0",
|
|
189
|
+
"picocolors": "^1.1.1",
|
|
184
190
|
"jszip": "^3.0.0",
|
|
185
|
-
"kleur": "^4.1.5",
|
|
186
191
|
"libsodium-wrappers": "^0.7.15",
|
|
187
192
|
"miniflare": "^4.20250712.0",
|
|
188
193
|
"neverthrow": "^8.2.0",
|
|
@@ -191,7 +196,7 @@
|
|
|
191
196
|
"pathe": "^2.0.3",
|
|
192
197
|
"signal-exit": "^4.1.0",
|
|
193
198
|
"turndown": "^7.0.0",
|
|
194
|
-
"unenv": "2.0.0-rc.
|
|
199
|
+
"unenv": "2.0.0-rc.19",
|
|
195
200
|
"ws": "^8.18.3",
|
|
196
201
|
"yaml": "^2.0.0"
|
|
197
202
|
},
|
|
@@ -259,7 +264,6 @@
|
|
|
259
264
|
"nitro-cloudflare-dev": "^0.2.2",
|
|
260
265
|
"nitropack": "^2.12.4",
|
|
261
266
|
"openpgp": "^6.1.0",
|
|
262
|
-
"picocolors": "^1.1.1",
|
|
263
267
|
"prettier": "^3.5.3",
|
|
264
268
|
"rwsdk": "^0.1.28",
|
|
265
269
|
"trpc-cli": "^0.10.2",
|
package/src/alchemy.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type PendingResource,
|
|
16
16
|
} from "./resource.ts";
|
|
17
17
|
import { isRuntime } from "./runtime/global.ts";
|
|
18
|
-
import { DEFAULT_STAGE, Scope } from "./scope.ts";
|
|
18
|
+
import { DEFAULT_STAGE, Scope, type ProviderCredentials } from "./scope.ts";
|
|
19
19
|
import { secret } from "./secret.ts";
|
|
20
20
|
import type { StateStoreType } from "./state.ts";
|
|
21
21
|
import type { LoggerApi } from "./util/cli.ts";
|
|
@@ -193,6 +193,7 @@ If this is a mistake, you can disable this check by setting the ALCHEMY_CI_STATE
|
|
|
193
193
|
});
|
|
194
194
|
onExit((code) => {
|
|
195
195
|
root.cleanup().then(() => {
|
|
196
|
+
code = code === 130 ? 0 : (code ?? 0);
|
|
196
197
|
process.exit(code);
|
|
197
198
|
});
|
|
198
199
|
return true;
|
|
@@ -419,7 +420,7 @@ export interface ScopeOptions extends AlchemyOptions {
|
|
|
419
420
|
enter: boolean;
|
|
420
421
|
}
|
|
421
422
|
|
|
422
|
-
export interface RunOptions extends AlchemyOptions {
|
|
423
|
+
export interface RunOptions extends AlchemyOptions, ProviderCredentials {
|
|
423
424
|
/**
|
|
424
425
|
* @default false
|
|
425
426
|
*/
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { Secret } from "../secret.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AWS client properties for credential configuration.
|
|
5
|
+
*
|
|
6
|
+
* This interface defines the properties that can be used to configure AWS credentials
|
|
7
|
+
* at different levels of your application. It supports the three-tier credential resolution
|
|
8
|
+
* system (global → scope → resource) and enables multi-account and multi-region deployments.
|
|
9
|
+
*
|
|
10
|
+
* These properties can be specified at:
|
|
11
|
+
* 1. Resource level - directly in resource creation parameters
|
|
12
|
+
* 2. Scope level - in alchemy.run() options with `aws` property
|
|
13
|
+
* 3. Global level - through environment variables (AWS_REGION, AWS_PROFILE, etc.)
|
|
14
|
+
*
|
|
15
|
+
* The credential resolution follows a precedence order where resource-level credentials
|
|
16
|
+
* override scope-level credentials, which override global environment variables.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Resource-level credential overrides
|
|
21
|
+
* const vpc = await Vpc("main-vpc", {
|
|
22
|
+
* cidrBlock: "10.0.0.0/16",
|
|
23
|
+
* region: "us-west-2", // Override region
|
|
24
|
+
* profile: "production", // Override profile
|
|
25
|
+
* tags: { Name: "main-vpc" }
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // Scope-level credential configuration
|
|
32
|
+
* await alchemy.run("production", {
|
|
33
|
+
* aws: {
|
|
34
|
+
* region: "us-west-2",
|
|
35
|
+
* profile: "production",
|
|
36
|
+
* }
|
|
37
|
+
* }, async () => {
|
|
38
|
+
* // Resources created here will use the scope credentials by default
|
|
39
|
+
* const vpc = await Vpc("main-vpc", {
|
|
40
|
+
* cidrBlock: "10.0.0.0/16"
|
|
41
|
+
* });
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export interface AwsClientProps {
|
|
46
|
+
/**
|
|
47
|
+
* AWS access key ID for authentication.
|
|
48
|
+
*
|
|
49
|
+
* This corresponds to the AWS_ACCESS_KEY_ID environment variable.
|
|
50
|
+
* When provided at the resource or scope level, it overrides the environment variable.
|
|
51
|
+
*/
|
|
52
|
+
accessKeyId?: Secret<string>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* AWS secret access key for authentication.
|
|
56
|
+
*
|
|
57
|
+
* This corresponds to the AWS_SECRET_ACCESS_KEY environment variable.
|
|
58
|
+
* When provided at the resource or scope level, it overrides the environment variable.
|
|
59
|
+
*/
|
|
60
|
+
secretAccessKey?: Secret<string>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* AWS session token for temporary credentials.
|
|
64
|
+
*
|
|
65
|
+
* This corresponds to the AWS_SESSION_TOKEN environment variable.
|
|
66
|
+
* Used with temporary credentials from STS or when assuming a role.
|
|
67
|
+
*/
|
|
68
|
+
sessionToken?: Secret<string>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* AWS region to use for API calls.
|
|
72
|
+
*
|
|
73
|
+
* This corresponds to the AWS_REGION or AWS_DEFAULT_REGION environment variables.
|
|
74
|
+
* When provided at the resource or scope level, it overrides the environment variables.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* // Resource in a specific region
|
|
79
|
+
* const euVpc = await Vpc("eu-vpc", {
|
|
80
|
+
* cidrBlock: "10.0.0.0/16",
|
|
81
|
+
* region: "eu-west-1",
|
|
82
|
+
* tags: { Name: "eu-vpc" }
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
region?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* AWS profile name to use from credentials file.
|
|
90
|
+
*
|
|
91
|
+
* This corresponds to the AWS_PROFILE environment variable.
|
|
92
|
+
* Profiles are defined in ~/.aws/credentials or ~/.aws/config files.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // Resource using a specific profile
|
|
97
|
+
* const prodVpc = await Vpc("prod-vpc", {
|
|
98
|
+
* cidrBlock: "10.0.0.0/16",
|
|
99
|
+
* profile: "production-account",
|
|
100
|
+
* tags: { Name: "prod-vpc" }
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
profile?: string;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* AWS role ARN to assume for authentication.
|
|
108
|
+
*
|
|
109
|
+
* This corresponds to the AWS_ROLE_ARN environment variable.
|
|
110
|
+
* Used for cross-account access or for assuming roles with specific permissions.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // Resource using role assumption
|
|
115
|
+
* const crossAccountVpc = await Vpc("cross-account-vpc", {
|
|
116
|
+
* cidrBlock: "10.0.0.0/16",
|
|
117
|
+
* roleArn: "arn:aws:iam::123456789012:role/DeploymentRole",
|
|
118
|
+
* roleSessionName: "alchemy-deployment",
|
|
119
|
+
* tags: { Name: "cross-account-vpc" }
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
roleArn?: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* External ID to use when assuming a role.
|
|
127
|
+
*
|
|
128
|
+
* This corresponds to the AWS_EXTERNAL_ID environment variable.
|
|
129
|
+
* Used as an additional security measure when assuming roles across accounts.
|
|
130
|
+
*/
|
|
131
|
+
externalId?: string;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Session name to use when assuming a role.
|
|
135
|
+
*
|
|
136
|
+
* This corresponds to the AWS_ROLE_SESSION_NAME environment variable.
|
|
137
|
+
* Helps identify who/what is using the assumed role in AWS CloudTrail logs.
|
|
138
|
+
*/
|
|
139
|
+
roleSessionName?: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* AWS scope extensions - adds AWS credential support to scope options.
|
|
143
|
+
* This uses TypeScript module augmentation to extend the ProviderCredentials interface.
|
|
144
|
+
* Since ScopeOptions and RunOptions both extend ProviderCredentials,
|
|
145
|
+
* they automatically inherit these properties.
|
|
146
|
+
*/
|
|
147
|
+
declare module "../scope.ts" {
|
|
148
|
+
interface ProviderCredentials {
|
|
149
|
+
/**
|
|
150
|
+
* AWS credentials configuration for this scope.
|
|
151
|
+
* All AWS resources created within this scope will inherit these credentials
|
|
152
|
+
* unless overridden at the resource level.
|
|
153
|
+
*/
|
|
154
|
+
aws?: AwsClientProps;
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/aws/credentials.ts
CHANGED
|
@@ -1,6 +1,167 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { alchemy } from "../alchemy.ts";
|
|
2
|
+
import { isSecret } from "../secret.ts";
|
|
3
|
+
import type { AwsClientProps } from "./client-props.ts";
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Validate AWS client properties to ensure they are strings.
|
|
7
|
+
* This follows the same pattern as Cloudflare credential validation.
|
|
8
|
+
*/
|
|
9
|
+
function validateAwsClientProps(props: AwsClientProps, context: string): void {
|
|
10
|
+
const validKeys = [
|
|
11
|
+
"accessKeyId",
|
|
12
|
+
"secretAccessKey",
|
|
13
|
+
"sessionToken",
|
|
14
|
+
"region",
|
|
15
|
+
"profile",
|
|
16
|
+
"roleArn",
|
|
17
|
+
"externalId",
|
|
18
|
+
"roleSessionName",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
for (const [key, value] of Object.entries(props)) {
|
|
22
|
+
if (!validKeys.includes(key)) {
|
|
23
|
+
continue; // Ignore unknown properties
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (value !== undefined && typeof value !== "string" && !isSecret(value)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Invalid AWS configuration in ${context}: Property '${key}' must be a string or Secret, got ${typeof value}. ` +
|
|
29
|
+
"Please ensure all AWS credential properties are strings or Secret objects.",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get global AWS configuration from environment variables.
|
|
37
|
+
* This provides the base layer of AWS credential configuration.
|
|
38
|
+
*/
|
|
39
|
+
export function getGlobalAwsConfig(): AwsClientProps {
|
|
40
|
+
return {
|
|
41
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID
|
|
42
|
+
? alchemy.secret(process.env.AWS_ACCESS_KEY_ID)
|
|
43
|
+
: undefined,
|
|
44
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
45
|
+
? alchemy.secret(process.env.AWS_SECRET_ACCESS_KEY)
|
|
46
|
+
: undefined,
|
|
47
|
+
sessionToken: process.env.AWS_SESSION_TOKEN
|
|
48
|
+
? alchemy.secret(process.env.AWS_SESSION_TOKEN)
|
|
49
|
+
: undefined,
|
|
50
|
+
region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION,
|
|
51
|
+
profile: process.env.AWS_PROFILE,
|
|
52
|
+
roleArn: process.env.AWS_ROLE_ARN,
|
|
53
|
+
externalId: process.env.AWS_EXTERNAL_ID,
|
|
54
|
+
roleSessionName: process.env.AWS_ROLE_SESSION_NAME,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve AWS credentials using three-tier resolution: global → scope → resource.
|
|
60
|
+
*
|
|
61
|
+
* This function implements a comprehensive credential resolution system that allows
|
|
62
|
+
* for flexible AWS credential management across different levels of your application.
|
|
63
|
+
* It enables multi-account and multi-region deployments by providing a consistent
|
|
64
|
+
* way to override credentials at different scopes.
|
|
65
|
+
*
|
|
66
|
+
* The resolution follows this precedence order:
|
|
67
|
+
* 1. Resource-level credentials (highest priority)
|
|
68
|
+
* 2. Scope-level credentials (medium priority)
|
|
69
|
+
* 3. Global environment variables (lowest priority)
|
|
70
|
+
*
|
|
71
|
+
* Supported credential properties include:
|
|
72
|
+
* - `region`: AWS region (e.g., 'us-west-2', 'eu-central-1')
|
|
73
|
+
* - `profile`: AWS profile name from credentials file
|
|
74
|
+
* - `accessKeyId`: AWS access key ID
|
|
75
|
+
* - `secretAccessKey`: AWS secret access key
|
|
76
|
+
* - `sessionToken`: AWS session token for temporary credentials
|
|
77
|
+
* - `roleArn`: AWS role ARN to assume
|
|
78
|
+
* - `roleSessionName`: Session name when assuming a role
|
|
79
|
+
* - `externalId`: External ID when assuming a role
|
|
80
|
+
*
|
|
81
|
+
* @param resourceProps - Resource-level AWS credential properties (optional)
|
|
82
|
+
* @returns Resolved AWS client properties with validation
|
|
83
|
+
*
|
|
84
|
+
* @throws {Error} When scope contains invalid AWS configuration
|
|
85
|
+
* @throws {Error} When resource properties contain invalid AWS configuration
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* // Basic usage with resource-level credentials
|
|
90
|
+
* const credentials = resolveAwsCredentials({
|
|
91
|
+
* region: "us-west-2",
|
|
92
|
+
* profile: "production"
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Usage with scope-level credentials
|
|
99
|
+
* await alchemy.run("my-app", {
|
|
100
|
+
* aws: {
|
|
101
|
+
* region: "eu-west-1",
|
|
102
|
+
* profile: "staging"
|
|
103
|
+
* }
|
|
104
|
+
* }, async () => {
|
|
105
|
+
* // Resources created here will use the scope credentials by default
|
|
106
|
+
* const vpc = await Vpc("main-vpc", {
|
|
107
|
+
* cidrBlock: "10.0.0.0/16"
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* // Resources can override scope credentials
|
|
111
|
+
* const crossRegionSubnet = await Subnet("cross-region-subnet", {
|
|
112
|
+
* vpc,
|
|
113
|
+
* cidrBlock: "10.0.1.0/24",
|
|
114
|
+
* region: "us-east-1" // Override scope region
|
|
115
|
+
* });
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export async function resolveAwsCredentials(
|
|
120
|
+
resourceProps?: AwsClientProps,
|
|
121
|
+
): Promise<AwsClientProps> {
|
|
122
|
+
// 1. Start with global environment variables (lowest priority)
|
|
123
|
+
const globalConfig = getGlobalAwsConfig();
|
|
124
|
+
|
|
125
|
+
// 2. Layer in scope-level credentials (medium priority)
|
|
126
|
+
let scopeConfig: AwsClientProps = {};
|
|
127
|
+
try {
|
|
128
|
+
// Import Scope dynamically to avoid circular dependency
|
|
129
|
+
const { Scope } = await import("../scope.ts");
|
|
130
|
+
const currentScope = Scope.getScope();
|
|
131
|
+
if (currentScope?.providerCredentials?.aws) {
|
|
132
|
+
scopeConfig = currentScope.providerCredentials.aws;
|
|
133
|
+
|
|
134
|
+
// Validate scope-level credentials if provided
|
|
135
|
+
validateAwsClientProps(scopeConfig, "scope");
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
// If we can't access scope (e.g., not running in scope context), just continue
|
|
139
|
+
// with empty scope config unless it's a validation error
|
|
140
|
+
if (
|
|
141
|
+
error instanceof Error &&
|
|
142
|
+
error.message.includes("Invalid AWS configuration")
|
|
143
|
+
) {
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 3. Layer in resource-level credentials (highest priority)
|
|
149
|
+
const resourceConfig = resourceProps || {};
|
|
150
|
+
|
|
151
|
+
// Validate resource-level credentials if provided
|
|
152
|
+
if (resourceProps && Object.keys(resourceProps).length > 0) {
|
|
153
|
+
validateAwsClientProps(resourceProps, "resource properties");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Merge configurations with proper precedence (later properties override earlier ones)
|
|
157
|
+
const resolvedConfig = {
|
|
158
|
+
...globalConfig,
|
|
159
|
+
...scopeConfig,
|
|
160
|
+
...resourceConfig,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Filter out undefined values from the final result
|
|
164
|
+
return Object.fromEntries(
|
|
165
|
+
Object.entries(resolvedConfig).filter(([_, value]) => value !== undefined),
|
|
166
|
+
) as AwsClientProps;
|
|
6
167
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type TimeoutConfig,
|
|
9
9
|
waitForResourceState,
|
|
10
10
|
} from "../../util/timeout.ts";
|
|
11
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
11
12
|
import type { InternetGateway } from "./internet-gateway.ts";
|
|
12
13
|
import { callEC2Api, createEC2Client } from "./utils.ts";
|
|
13
14
|
import type { Vpc } from "./vpc.ts";
|
|
@@ -15,7 +16,7 @@ import type { Vpc } from "./vpc.ts";
|
|
|
15
16
|
/**
|
|
16
17
|
* Properties for creating or updating an Internet Gateway Attachment
|
|
17
18
|
*/
|
|
18
|
-
export interface InternetGatewayAttachmentProps {
|
|
19
|
+
export interface InternetGatewayAttachmentProps extends AwsClientProps {
|
|
19
20
|
/**
|
|
20
21
|
* The Internet Gateway to attach
|
|
21
22
|
*/
|
|
@@ -67,6 +68,9 @@ export interface InternetGatewayAttachment
|
|
|
67
68
|
* allowing traffic to flow between the VPC and the internet when combined with proper
|
|
68
69
|
* routing configuration.
|
|
69
70
|
*
|
|
71
|
+
* Supports AWS credential overrides at the resource level, allowing you to deploy Internet Gateway Attachments
|
|
72
|
+
* to different AWS accounts or regions than the default scope configuration.
|
|
73
|
+
*
|
|
70
74
|
* @example
|
|
71
75
|
* // Basic Internet Gateway attachment for internet connectivity
|
|
72
76
|
* const vpc = await Vpc("main-vpc", {
|
|
@@ -81,6 +85,36 @@ export interface InternetGatewayAttachment
|
|
|
81
85
|
* });
|
|
82
86
|
*
|
|
83
87
|
* @example
|
|
88
|
+
* // Internet Gateway Attachment with AWS credential overrides
|
|
89
|
+
* const crossAccountAttachment = await InternetGatewayAttachment("cross-account-attachment", {
|
|
90
|
+
* internetGateway: igw,
|
|
91
|
+
* vpc: vpc,
|
|
92
|
+
* // Override AWS credentials for this specific resource
|
|
93
|
+
* region: "us-east-1",
|
|
94
|
+
* profile: "production-account",
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Internet Gateway Attachment in different region with role assumption
|
|
99
|
+
* const multiRegionAttachment = await InternetGatewayAttachment("multi-region-attachment", {
|
|
100
|
+
* internetGateway: euIgw,
|
|
101
|
+
* vpc: euVpc,
|
|
102
|
+
* region: "eu-west-1",
|
|
103
|
+
* roleArn: "arn:aws:iam::123456789012:role/CrossRegionRole",
|
|
104
|
+
* roleSessionName: "igw-attachment-deployment",
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Internet Gateway Attachment with explicit credentials
|
|
109
|
+
* const explicitCredsAttachment = await InternetGatewayAttachment("explicit-creds-attachment", {
|
|
110
|
+
* internetGateway: testIgw,
|
|
111
|
+
* vpc: testVpc,
|
|
112
|
+
* accessKeyId: alchemy.secret("AKIAIOSFODNN7EXAMPLE"),
|
|
113
|
+
* secretAccessKey: alchemy.secret("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
|
|
114
|
+
* region: "us-west-2",
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
84
118
|
* // Internet Gateway attachment with custom timeout configuration
|
|
85
119
|
* // for environments with slower AWS API responses
|
|
86
120
|
* const attachment = await InternetGatewayAttachment("slow-env-attachment", {
|
|
@@ -109,7 +143,7 @@ export interface InternetGatewayAttachment
|
|
|
109
143
|
*
|
|
110
144
|
* // Create public subnet and route table for internet access
|
|
111
145
|
* const publicSubnet = await Subnet("public-subnet", {
|
|
112
|
-
* vpc,
|
|
146
|
+
* vpc: vpc,
|
|
113
147
|
* cidrBlock: "10.0.1.0/24",
|
|
114
148
|
* availabilityZone: "us-east-1a"
|
|
115
149
|
* });
|
|
@@ -121,7 +155,27 @@ export interface InternetGatewayAttachment
|
|
|
121
155
|
* const internetRoute = await Route("internet-route", {
|
|
122
156
|
* routeTable: publicRouteTable,
|
|
123
157
|
* destinationCidrBlock: "0.0.0.0/0",
|
|
124
|
-
*
|
|
158
|
+
* target: { internetGateway: igw }
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // Multi-account deployment with scope-level and resource-level overrides
|
|
163
|
+
* await alchemy.run("production", {
|
|
164
|
+
* aws: { region: "us-west-2", profile: "main-account" }
|
|
165
|
+
* }, async () => {
|
|
166
|
+
* // This Internet Gateway Attachment uses scope credentials (main-account, us-west-2)
|
|
167
|
+
* const mainAttachment = await InternetGatewayAttachment("main-attachment", {
|
|
168
|
+
* internetGateway: mainIgw,
|
|
169
|
+
* vpc: mainVpc
|
|
170
|
+
* });
|
|
171
|
+
*
|
|
172
|
+
* // This Internet Gateway Attachment overrides to use different account
|
|
173
|
+
* const crossAccountAttachment = await InternetGatewayAttachment("cross-account-attachment", {
|
|
174
|
+
* internetGateway: crossIgw,
|
|
175
|
+
* vpc: crossVpc,
|
|
176
|
+
* profile: "secondary-account",
|
|
177
|
+
* region: "us-east-1", // Also override region
|
|
178
|
+
* });
|
|
125
179
|
* });
|
|
126
180
|
*/
|
|
127
181
|
export const InternetGatewayAttachment = Resource(
|
|
@@ -131,7 +185,8 @@ export const InternetGatewayAttachment = Resource(
|
|
|
131
185
|
_id: string,
|
|
132
186
|
props: InternetGatewayAttachmentProps,
|
|
133
187
|
): Promise<InternetGatewayAttachment> {
|
|
134
|
-
|
|
188
|
+
// Create EC2 client with credential resolution handled internally
|
|
189
|
+
const client = await createEC2Client(props);
|
|
135
190
|
const timeoutConfig = mergeTimeoutConfig(
|
|
136
191
|
INTERNET_GATEWAY_ATTACHMENT_TIMEOUT,
|
|
137
192
|
props.timeout,
|