alchemy 0.54.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.
- package/lib/aws/ec2/index.d.ts +10 -0
- package/lib/aws/ec2/index.d.ts.map +1 -0
- package/lib/aws/ec2/index.js +10 -0
- package/lib/aws/ec2/index.js.map +1 -0
- package/lib/aws/ec2/internet-gateway-attachment.d.ts +150 -0
- package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway-attachment.js +248 -0
- package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -0
- package/lib/aws/ec2/internet-gateway.d.ts +132 -0
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.js +311 -0
- package/lib/aws/ec2/internet-gateway.js.map +1 -0
- package/lib/aws/ec2/nat-gateway.d.ts +145 -0
- package/lib/aws/ec2/nat-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/nat-gateway.js +618 -0
- package/lib/aws/ec2/nat-gateway.js.map +1 -0
- package/lib/aws/ec2/route-table-association.d.ts +134 -0
- package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.js +364 -0
- package/lib/aws/ec2/route-table-association.js.map +1 -0
- package/lib/aws/ec2/route-table.d.ts +94 -0
- package/lib/aws/ec2/route-table.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.js +253 -0
- package/lib/aws/ec2/route-table.js.map +1 -0
- package/lib/aws/ec2/route.d.ts +130 -0
- package/lib/aws/ec2/route.d.ts.map +1 -0
- package/lib/aws/ec2/route.js +253 -0
- package/lib/aws/ec2/route.js.map +1 -0
- package/lib/aws/ec2/security-group-rule.d.ts +84 -0
- package/lib/aws/ec2/security-group-rule.d.ts.map +1 -0
- package/lib/aws/ec2/security-group-rule.js +123 -0
- package/lib/aws/ec2/security-group-rule.js.map +1 -0
- package/lib/aws/ec2/security-group.d.ts +84 -0
- package/lib/aws/ec2/security-group.d.ts.map +1 -0
- package/lib/aws/ec2/security-group.js +207 -0
- package/lib/aws/ec2/security-group.js.map +1 -0
- package/lib/aws/ec2/subnet.d.ts +110 -0
- package/lib/aws/ec2/subnet.d.ts.map +1 -0
- package/lib/aws/ec2/subnet.js +336 -0
- package/lib/aws/ec2/subnet.js.map +1 -0
- package/lib/aws/ec2/utils.d.ts +15 -0
- package/lib/aws/ec2/utils.d.ts.map +1 -0
- package/lib/aws/ec2/utils.js +79 -0
- package/lib/aws/ec2/utils.js.map +1 -0
- package/lib/aws/ec2/vpc.d.ts +160 -0
- package/lib/aws/ec2/vpc.d.ts.map +1 -0
- package/lib/aws/ec2/vpc.js +453 -0
- package/lib/aws/ec2/vpc.js.map +1 -0
- package/lib/aws/index.d.ts +7 -0
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +8 -0
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/utils.d.ts +25 -0
- package/lib/aws/utils.d.ts.map +1 -0
- package/lib/aws/utils.js +89 -0
- package/lib/aws/utils.js.map +1 -0
- package/lib/build-date.d.ts +6 -0
- package/lib/build-date.d.ts.map +1 -0
- package/lib/build-date.js +8 -0
- package/lib/build-date.js.map +1 -0
- package/lib/util/params.d.ts +5 -0
- package/lib/util/params.d.ts.map +1 -0
- package/lib/util/params.js +30 -0
- package/lib/util/params.js.map +1 -0
- package/lib/util/timeout.d.ts +31 -0
- package/lib/util/timeout.d.ts.map +1 -0
- package/lib/util/timeout.js +109 -0
- package/lib/util/timeout.js.map +1 -0
- package/lib/util/xml.d.ts +5 -0
- package/lib/util/xml.d.ts.map +1 -0
- package/lib/util/xml.js +8 -0
- package/lib/util/xml.js.map +1 -0
- package/package.json +3 -1
- package/src/aws/ec2/index.ts +9 -0
- package/src/aws/ec2/internet-gateway-attachment.ts +468 -0
- package/src/aws/ec2/internet-gateway.ts +557 -0
- package/src/aws/ec2/nat-gateway.ts +1123 -0
- package/src/aws/ec2/route-table-association.ts +747 -0
- package/src/aws/ec2/route-table.ts +452 -0
- package/src/aws/ec2/route.ts +447 -0
- package/src/aws/ec2/security-group-rule.ts +229 -0
- package/src/aws/ec2/security-group.ts +418 -0
- package/src/aws/ec2/subnet.ts +625 -0
- package/src/aws/ec2/utils.ts +102 -0
- package/src/aws/ec2/vpc.ts +969 -0
- package/src/aws/index.ts +9 -0
- package/src/aws/utils.ts +137 -0
- package/src/build-date.ts +8 -0
- package/src/util/params.ts +33 -0
- package/src/util/timeout.ts +178 -0
- package/src/util/xml.ts +7 -0
package/src/aws/index.ts
CHANGED
|
@@ -9,3 +9,12 @@ export * from "./s3-state-store.ts";
|
|
|
9
9
|
export * from "./ses.ts";
|
|
10
10
|
export * from "./ssm-parameter.ts";
|
|
11
11
|
export * from "./table.ts";
|
|
12
|
+
|
|
13
|
+
// EC2 Resources
|
|
14
|
+
export * from "./ec2/internet-gateway.ts";
|
|
15
|
+
export * from "./ec2/nat-gateway.ts";
|
|
16
|
+
export * from "./ec2/route-table.ts";
|
|
17
|
+
export * from "./ec2/route.ts";
|
|
18
|
+
export * from "./ec2/security-group.ts";
|
|
19
|
+
export * from "./ec2/subnet.ts";
|
|
20
|
+
export * from "./ec2/vpc.ts";
|
package/src/aws/utils.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
|
|
2
|
+
import { loadConfig } from "@smithy/node-config-provider";
|
|
3
|
+
import { AwsClient } from "aws4fetch";
|
|
4
|
+
import { flattenParams } from "../util/params.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get AWS region from configuration
|
|
8
|
+
*/
|
|
9
|
+
export const getRegion = loadConfig({
|
|
10
|
+
environmentVariableSelector: (env: any) =>
|
|
11
|
+
env.AWS_REGION || env.AWS_DEFAULT_REGION,
|
|
12
|
+
configFileSelector: (profile: any) => profile.region,
|
|
13
|
+
default: "us-east-1",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* AWS Service Configuration
|
|
18
|
+
*/
|
|
19
|
+
export interface AwsServiceConfig {
|
|
20
|
+
service: string;
|
|
21
|
+
version: string;
|
|
22
|
+
endpoint: (region: string) => string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* AWS API Caller interface
|
|
27
|
+
*/
|
|
28
|
+
export interface AwsApiCaller<T = any> {
|
|
29
|
+
createClient(): Promise<AwsClient>;
|
|
30
|
+
callApi<R = T>(
|
|
31
|
+
client: AwsClient,
|
|
32
|
+
action: string,
|
|
33
|
+
params?: Record<string, any>,
|
|
34
|
+
): Promise<R>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create an AWS API caller for a specific service
|
|
39
|
+
*/
|
|
40
|
+
export function getAwsApiCaller<T = any>(
|
|
41
|
+
config: AwsServiceConfig,
|
|
42
|
+
responseParser: (xmlText: string) => T,
|
|
43
|
+
): AwsApiCaller<T> {
|
|
44
|
+
return {
|
|
45
|
+
async createClient(): Promise<AwsClient> {
|
|
46
|
+
const credentials = await fromNodeProviderChain()();
|
|
47
|
+
const region = await getRegion();
|
|
48
|
+
|
|
49
|
+
return new AwsClient({
|
|
50
|
+
...credentials,
|
|
51
|
+
service: config.service,
|
|
52
|
+
region,
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async callApi<R = T>(
|
|
57
|
+
client: AwsClient,
|
|
58
|
+
action: string,
|
|
59
|
+
params: Record<string, any> = {},
|
|
60
|
+
): Promise<R> {
|
|
61
|
+
// Try the API call, and retry once with fresh credentials on auth failure
|
|
62
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
63
|
+
try {
|
|
64
|
+
const region = await getRegion();
|
|
65
|
+
const url = config.endpoint(region);
|
|
66
|
+
|
|
67
|
+
const body = new URLSearchParams({
|
|
68
|
+
Action: action,
|
|
69
|
+
Version: config.version,
|
|
70
|
+
...flattenParams(params),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const signedRequest = await client.sign(url, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
77
|
+
},
|
|
78
|
+
body: body.toString(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const response = await fetch(signedRequest);
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const errorText = await response.text();
|
|
85
|
+
|
|
86
|
+
// Parse AWS error code from XML response
|
|
87
|
+
const errorCodeMatch = errorText.match(/<Code>([^<]+)<\/Code>/);
|
|
88
|
+
const errorCode = errorCodeMatch ? errorCodeMatch[1] : null;
|
|
89
|
+
|
|
90
|
+
// If this is an auth failure and we haven't retried yet, create a fresh client and retry
|
|
91
|
+
if (errorCode === "AuthFailure" && attempt === 1) {
|
|
92
|
+
console.log(
|
|
93
|
+
`🔄 Auth failure detected, refreshing credentials and retrying ${action}...`,
|
|
94
|
+
);
|
|
95
|
+
// Create a fresh client with new credentials
|
|
96
|
+
const freshCredentials = await fromNodeProviderChain()();
|
|
97
|
+
const region = await getRegion();
|
|
98
|
+
client = new AwsClient({
|
|
99
|
+
...freshCredentials,
|
|
100
|
+
service: config.service,
|
|
101
|
+
region,
|
|
102
|
+
});
|
|
103
|
+
continue; // Retry with fresh client
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const error = new Error(
|
|
107
|
+
`${config.service.toUpperCase()} API error: ${response.status} ${response.statusText}\n${errorText}`,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Set the error code for the ignore function
|
|
111
|
+
if (errorCode) {
|
|
112
|
+
(error as any).code = errorCode;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const xmlText = await response.text();
|
|
119
|
+
const parsed = responseParser(xmlText);
|
|
120
|
+
return parsed as unknown as R;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
// If it's not an auth failure or we've already retried, throw the error
|
|
123
|
+
if (
|
|
124
|
+
attempt === 2 ||
|
|
125
|
+
!(error as any)?.message?.includes("AuthFailure")
|
|
126
|
+
) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
// Otherwise, continue to retry
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// This should never be reached, but TypeScript needs it
|
|
134
|
+
throw new Error("Unexpected error in API call retry logic");
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten nested parameters for API calls
|
|
3
|
+
*/
|
|
4
|
+
export function flattenParams(
|
|
5
|
+
params: Record<string, any>,
|
|
6
|
+
prefix = "",
|
|
7
|
+
): Record<string, string> {
|
|
8
|
+
const result: Record<string, string> = {};
|
|
9
|
+
|
|
10
|
+
for (const [key, value] of Object.entries(params)) {
|
|
11
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
12
|
+
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
value.forEach((item, index) => {
|
|
19
|
+
if (typeof item === "object") {
|
|
20
|
+
Object.assign(result, flattenParams(item, `${fullKey}.${index + 1}`));
|
|
21
|
+
} else {
|
|
22
|
+
result[`${fullKey}.${index + 1}`] = String(item);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} else if (typeof value === "object") {
|
|
26
|
+
Object.assign(result, flattenParams(value, fullKey));
|
|
27
|
+
} else {
|
|
28
|
+
result[fullKey] = String(value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout configuration interface
|
|
3
|
+
*/
|
|
4
|
+
export interface TimeoutConfig {
|
|
5
|
+
maxAttempts: number;
|
|
6
|
+
delayMs: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resource timeout error class
|
|
11
|
+
*/
|
|
12
|
+
export class ResourceTimeoutError extends Error {
|
|
13
|
+
constructor(
|
|
14
|
+
resourceType: string,
|
|
15
|
+
resourceId: string,
|
|
16
|
+
timeoutMs: number,
|
|
17
|
+
operation: string,
|
|
18
|
+
) {
|
|
19
|
+
super(
|
|
20
|
+
`${resourceType} ${resourceId} did not complete ${operation} within ${timeoutMs}ms`,
|
|
21
|
+
);
|
|
22
|
+
this.name = "ResourceTimeoutError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Validate timeout configuration
|
|
28
|
+
*/
|
|
29
|
+
export function validateTimeoutConfig(config: TimeoutConfig): void {
|
|
30
|
+
if (config.maxAttempts <= 0) {
|
|
31
|
+
throw new Error("maxAttempts must be greater than 0");
|
|
32
|
+
}
|
|
33
|
+
if (config.delayMs <= 0) {
|
|
34
|
+
throw new Error("delayMs must be greater than 0");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Merge default timeout configuration with optional overrides
|
|
40
|
+
*/
|
|
41
|
+
export function mergeTimeoutConfig(
|
|
42
|
+
defaultConfig: TimeoutConfig,
|
|
43
|
+
override?: Partial<TimeoutConfig>,
|
|
44
|
+
): TimeoutConfig {
|
|
45
|
+
if (!override) {
|
|
46
|
+
return defaultConfig;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const merged = {
|
|
50
|
+
maxAttempts: override.maxAttempts ?? defaultConfig.maxAttempts,
|
|
51
|
+
delayMs: override.delayMs ?? defaultConfig.delayMs,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
validateTimeoutConfig(merged);
|
|
55
|
+
return merged;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Wait for a resource to reach a desired state with configurable timeout
|
|
60
|
+
*/
|
|
61
|
+
export async function waitForResourceState<T>(
|
|
62
|
+
checkFunction: () => Promise<T>,
|
|
63
|
+
isReady: (result: T) => boolean,
|
|
64
|
+
config: TimeoutConfig,
|
|
65
|
+
resourceId: string,
|
|
66
|
+
resourceType: string,
|
|
67
|
+
operation = "become ready",
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
validateTimeoutConfig(config);
|
|
70
|
+
const { maxAttempts, delayMs } = config;
|
|
71
|
+
|
|
72
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
73
|
+
try {
|
|
74
|
+
const result = await checkFunction();
|
|
75
|
+
if (isReady(result)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(
|
|
80
|
+
`${resourceType} ${resourceId} not ready, waiting... (attempt ${attempt}/${maxAttempts})`,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (attempt < maxAttempts) {
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.log(
|
|
88
|
+
`Error checking ${resourceType} status (attempt ${attempt}/${maxAttempts}):`,
|
|
89
|
+
error,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Check if this is a non-retryable error (contains "NonRetryableError" in the name)
|
|
93
|
+
if (error instanceof Error && error.name.includes("NonRetryableError")) {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (attempt === maxAttempts) {
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const totalTimeMs = maxAttempts * delayMs;
|
|
105
|
+
throw new ResourceTimeoutError(
|
|
106
|
+
resourceType,
|
|
107
|
+
resourceId,
|
|
108
|
+
totalTimeMs,
|
|
109
|
+
operation,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Generic function to wait for an operation to complete
|
|
114
|
+
* This is the most flexible version that can handle any type of operation
|
|
115
|
+
*/
|
|
116
|
+
export async function waitForOperation<T>(
|
|
117
|
+
checkFunction: () => Promise<T>,
|
|
118
|
+
isComplete: (result: T) => boolean,
|
|
119
|
+
config: TimeoutConfig,
|
|
120
|
+
resourceId: string,
|
|
121
|
+
resourceType: string,
|
|
122
|
+
operation = "complete",
|
|
123
|
+
onError?: (error: any, attempt: number, maxAttempts: number) => boolean, // Return true to continue retrying
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
validateTimeoutConfig(config);
|
|
126
|
+
const { maxAttempts, delayMs } = config;
|
|
127
|
+
|
|
128
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
129
|
+
try {
|
|
130
|
+
const result = await checkFunction();
|
|
131
|
+
|
|
132
|
+
if (isComplete(result)) {
|
|
133
|
+
console.log(`${resourceType} ${resourceId} ${operation} completed`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(
|
|
138
|
+
`${resourceType} ${resourceId} ${operation} in progress... (attempt ${attempt}/${maxAttempts})`,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (attempt < maxAttempts) {
|
|
142
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// Allow custom error handling
|
|
146
|
+
if (onError) {
|
|
147
|
+
const shouldContinue = onError(error, attempt, maxAttempts);
|
|
148
|
+
if (!shouldContinue) {
|
|
149
|
+
// If onError returns false, it means the operation completed successfully
|
|
150
|
+
// (e.g., resource was deleted and not found)
|
|
151
|
+
console.log(
|
|
152
|
+
`${resourceType} ${resourceId} ${operation} completed (via error handling)`,
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
console.log(
|
|
158
|
+
`Error checking ${resourceType} ${operation} status (attempt ${attempt}/${maxAttempts}):`,
|
|
159
|
+
error,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (attempt === maxAttempts) {
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const totalTimeMs = maxAttempts * delayMs;
|
|
172
|
+
throw new ResourceTimeoutError(
|
|
173
|
+
resourceType,
|
|
174
|
+
resourceId,
|
|
175
|
+
totalTimeMs,
|
|
176
|
+
operation,
|
|
177
|
+
);
|
|
178
|
+
}
|