alchemy 0.17.2 → 0.19.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 (58) hide show
  1. package/lib/ai/document.d.ts +11 -0
  2. package/lib/ai/document.d.ts.map +1 -1
  3. package/lib/ai/document.js +1 -1
  4. package/lib/ai/document.js.map +1 -1
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +1 -2
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/aws/control/client.d.ts +134 -0
  9. package/lib/aws/control/client.d.ts.map +1 -0
  10. package/lib/aws/control/client.js +232 -0
  11. package/lib/aws/control/client.js.map +1 -0
  12. package/lib/aws/control/error.d.ts +63 -0
  13. package/lib/aws/control/error.d.ts.map +1 -0
  14. package/lib/aws/control/error.js +93 -0
  15. package/lib/aws/control/error.js.map +1 -0
  16. package/lib/aws/control/index.d.ts +6 -0
  17. package/lib/aws/control/index.d.ts.map +1 -0
  18. package/lib/aws/control/index.js +6 -0
  19. package/lib/aws/control/index.js.map +1 -0
  20. package/lib/aws/control/properties.d.ts +1874 -0
  21. package/lib/aws/control/properties.d.ts.map +1 -0
  22. package/lib/aws/control/properties.js +6945 -0
  23. package/lib/aws/control/properties.js.map +1 -0
  24. package/lib/aws/control/proxy.d.ts +7 -0
  25. package/lib/aws/control/proxy.d.ts.map +1 -0
  26. package/lib/aws/control/proxy.js +29 -0
  27. package/lib/aws/control/proxy.js.map +1 -0
  28. package/lib/aws/control/resource.d.ts +110 -0
  29. package/lib/aws/control/resource.d.ts.map +1 -0
  30. package/lib/aws/control/resource.js +192 -0
  31. package/lib/aws/control/resource.js.map +1 -0
  32. package/lib/cloudflare/website.d.ts.map +1 -1
  33. package/lib/cloudflare/website.js +8 -9
  34. package/lib/cloudflare/website.js.map +1 -1
  35. package/lib/destroy.d.ts.map +1 -1
  36. package/lib/destroy.js +2 -2
  37. package/lib/destroy.js.map +1 -1
  38. package/lib/resource.d.ts +7 -1
  39. package/lib/resource.d.ts.map +1 -1
  40. package/lib/resource.js +20 -0
  41. package/lib/resource.js.map +1 -1
  42. package/lib/test/bun.d.ts.map +1 -1
  43. package/lib/test/bun.js +1 -2
  44. package/lib/test/bun.js.map +1 -1
  45. package/package.json +5 -1
  46. package/src/ai/document.ts +1 -1
  47. package/src/alchemy.ts +2 -3
  48. package/src/aws/control/client.ts +450 -0
  49. package/src/aws/control/error.ts +84 -0
  50. package/src/aws/control/index.ts +6 -0
  51. package/src/aws/control/properties.ts +6946 -0
  52. package/src/aws/control/proxy.ts +40 -0
  53. package/src/aws/control/resource.ts +346 -0
  54. package/src/aws/control/types.d.ts +85129 -0
  55. package/src/cloudflare/website.ts +8 -9
  56. package/src/destroy.ts +4 -7
  57. package/src/resource.ts +32 -1
  58. package/src/test/bun.ts +1 -2
@@ -0,0 +1,450 @@
1
+ import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
2
+ import { loadConfig } from "@aws-sdk/node-config-provider";
3
+ import { AwsClient } from "aws4fetch";
4
+
5
+ import {
6
+ AlreadyExistsError,
7
+ CloudControlError,
8
+ ConcurrentOperationError,
9
+ NetworkError,
10
+ NotFoundError,
11
+ RequestError,
12
+ ResourceNotFoundException,
13
+ ThrottlingException,
14
+ UpdateFailedError,
15
+ ValidationException,
16
+ } from "./error.js";
17
+
18
+ /**
19
+ * Status of a Cloud Control API operation
20
+ */
21
+ export type OperationStatus =
22
+ | "PENDING"
23
+ | "IN_PROGRESS"
24
+ | "SUCCESS"
25
+ | "FAILED"
26
+ | "CANCEL_IN_PROGRESS"
27
+ | "CANCEL_COMPLETE";
28
+
29
+ /**
30
+ * Progress event from a Cloud Control API operation
31
+ */
32
+ export interface ProgressEvent {
33
+ /**
34
+ * Error code if the operation failed
35
+ */
36
+ ErrorCode?: string;
37
+
38
+ /**
39
+ * Time when the event occurred
40
+ */
41
+ EventTime?: number;
42
+
43
+ /**
44
+ * Token for hooks associated with the request
45
+ */
46
+ HooksRequestToken?: string;
47
+
48
+ /**
49
+ * Resource identifier
50
+ */
51
+ Identifier: string;
52
+
53
+ /**
54
+ * Operation being performed (CREATE, READ, UPDATE, DELETE)
55
+ */
56
+ Operation?: string;
57
+
58
+ /**
59
+ * Current status of the operation
60
+ */
61
+ OperationStatus: OperationStatus;
62
+
63
+ /**
64
+ * Token that identifies the request
65
+ */
66
+ RequestToken: string;
67
+
68
+ /**
69
+ * JSON string representation of the resource model
70
+ */
71
+ ResourceModel?: string;
72
+
73
+ /**
74
+ * Number of seconds to wait before retrying
75
+ */
76
+ RetryAfter?: number;
77
+
78
+ /**
79
+ * Status message providing details about the operation
80
+ */
81
+ StatusMessage: string;
82
+
83
+ /**
84
+ * Type name of the resource
85
+ */
86
+ TypeName?: string;
87
+ }
88
+
89
+ /**
90
+ * Options for Cloud Control API requests
91
+ */
92
+ export interface CloudControlOptions {
93
+ /**
94
+ * AWS region to use (defaults to us-east-1)
95
+ */
96
+ region?: string;
97
+
98
+ /**
99
+ * AWS access key ID (overrides environment variable)
100
+ */
101
+ accessKeyId?: string;
102
+
103
+ /**
104
+ * AWS secret access key (overrides environment variable)
105
+ */
106
+ secretAccessKey?: string;
107
+
108
+ /**
109
+ * AWS session token for temporary credentials
110
+ */
111
+ sessionToken?: string;
112
+
113
+ /**
114
+ * Initial delay in milliseconds between polling attempts
115
+ */
116
+ initialPollingDelay?: number;
117
+
118
+ /**
119
+ * Maximum delay in milliseconds between polling attempts
120
+ */
121
+ maxPollingDelay?: number;
122
+
123
+ /**
124
+ * Maximum number of retries for retryable errors
125
+ */
126
+ maxRetries?: number;
127
+ }
128
+
129
+ const getRegion = loadConfig({
130
+ environmentVariableSelector: (env) =>
131
+ env.AWS_REGION || env.AWS_DEFAULT_REGION,
132
+ configFileSelector: (profile) => profile.region,
133
+ default: undefined,
134
+ });
135
+
136
+ /**
137
+ * Create a Cloud Control API client
138
+ */
139
+ export async function createCloudControlClient(
140
+ options: CloudControlOptions = {},
141
+ ) {
142
+ const credentials = await fromNodeProviderChain()();
143
+
144
+ const region =
145
+ options.region ?? // 1. favor explicit region passed in
146
+ (await getRegion()) ?? // 2-4. resolve from AWS profile, loadConfig, and environment
147
+ process.env.AWS_REGION ??
148
+ process.env.AWS_DEFAULT_REGION;
149
+
150
+ if (!region) {
151
+ throw new Error(
152
+ "No region found. Please set AWS_REGION or AWS_DEFAULT_REGION in the environment or in your AWS profile.",
153
+ );
154
+ }
155
+
156
+ const client = new AwsClient({
157
+ ...credentials,
158
+ service: "cloudcontrolapi",
159
+ region,
160
+ });
161
+
162
+ return new CloudControlClient(client, {
163
+ ...options,
164
+ region,
165
+ });
166
+ }
167
+
168
+ export class CloudControlClient {
169
+ private region: string;
170
+ private initialPollingDelay: number;
171
+ private maxPollingDelay: number;
172
+ private maxRetries: number;
173
+
174
+ constructor(
175
+ private readonly client: AwsClient,
176
+ options: CloudControlOptions,
177
+ ) {
178
+ this.region = options.region || "us-east-1";
179
+ this.initialPollingDelay = options.initialPollingDelay || 1000;
180
+ this.maxPollingDelay = options.maxPollingDelay || 10000;
181
+ this.maxRetries = options.maxRetries || 3;
182
+ }
183
+
184
+ /**
185
+ * Create a new resource
186
+ */
187
+ public async createResource(
188
+ typeName: string,
189
+ desiredState: Record<string, any>,
190
+ ): Promise<ProgressEvent> {
191
+ return this.sync("CreateResource", {
192
+ TypeName: typeName,
193
+ DesiredState: JSON.stringify(desiredState),
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Get a resource's current state
199
+ */
200
+ public async getResource(
201
+ typeName: string,
202
+ identifier: string,
203
+ ): Promise<Record<string, any> | undefined> {
204
+ try {
205
+ return JSON.parse(
206
+ (
207
+ await this.fetch<{
208
+ ResourceDescription: {
209
+ Properties: string;
210
+ };
211
+ }>("GetResource", {
212
+ TypeName: typeName,
213
+ Identifier: identifier,
214
+ })
215
+ ).ResourceDescription.Properties,
216
+ );
217
+ } catch (error: any) {
218
+ if (error instanceof ResourceNotFoundException) {
219
+ return undefined;
220
+ }
221
+ throw error;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Update a resource
227
+ */
228
+ public async updateResource(
229
+ typeName: string,
230
+ identifier: string,
231
+ patchDocument: Record<string, any>,
232
+ ): Promise<ProgressEvent> {
233
+ return this.sync("UpdateResource", {
234
+ TypeName: typeName,
235
+ Identifier: identifier,
236
+ PatchDocument: JSON.stringify(patchDocument),
237
+ });
238
+ }
239
+
240
+ /**
241
+ * Delete a resource
242
+ */
243
+ public async deleteResource(
244
+ typeName: string,
245
+ identifier: string,
246
+ ): Promise<ProgressEvent> {
247
+ return this.sync("DeleteResource", {
248
+ TypeName: typeName,
249
+ Identifier: identifier,
250
+ });
251
+ }
252
+
253
+ /**
254
+ * List resources of a given type
255
+ */
256
+ public listResources(typeName: string, nextToken?: string) {
257
+ return this.fetch<{
258
+ ResourceDescriptions: Array<{
259
+ Identifier: string;
260
+ Properties: Record<string, any>;
261
+ }>;
262
+ NextToken?: string;
263
+ }>("ListResources", {
264
+ TypeName: typeName,
265
+ NextToken: nextToken,
266
+ });
267
+ }
268
+
269
+ /**
270
+ * Poll an operation until it completes
271
+ */
272
+ public async poll(
273
+ requestToken: string,
274
+ action?: "CreateResource" | "UpdateResource" | "DeleteResource",
275
+ ): Promise<ProgressEvent> {
276
+ let delay = this.initialPollingDelay;
277
+ let logged = false;
278
+
279
+ while (true) {
280
+ try {
281
+ const response = await this.fetch<{
282
+ ProgressEvent: ProgressEvent;
283
+ }>("GetResourceRequestStatus", {
284
+ RequestToken: requestToken,
285
+ });
286
+
287
+ if (response.ProgressEvent.OperationStatus === "SUCCESS") {
288
+ return response.ProgressEvent;
289
+ }
290
+
291
+ if (response.ProgressEvent.OperationStatus === "FAILED") {
292
+ const errorCode = response.ProgressEvent.ErrorCode;
293
+ const progressEvent = response.ProgressEvent;
294
+ if (errorCode === "AlreadyExists") {
295
+ throw new AlreadyExistsError(progressEvent);
296
+ } else if (errorCode === "NotFound") {
297
+ throw new NotFoundError(progressEvent);
298
+ }
299
+ throw new UpdateFailedError(progressEvent);
300
+ }
301
+
302
+ // Use the suggested retry delay if provided
303
+ const waitTime = response.ProgressEvent.RetryAfter
304
+ ? Math.max(100, response.ProgressEvent.RetryAfter * 1000 - Date.now())
305
+ : delay;
306
+
307
+ if (!logged) {
308
+ logged = true;
309
+ console.log(
310
+ `Polling for ${response.ProgressEvent.Identifier} (${response.ProgressEvent.TypeName}) to stabilize`,
311
+ );
312
+ }
313
+
314
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
315
+ delay = Math.min(delay * 2, this.maxPollingDelay);
316
+ } catch (error: any) {
317
+ if (error instanceof NotFoundError && action === "DeleteResource") {
318
+ return error.progressEvent;
319
+ } else if (error instanceof CloudControlError) {
320
+ throw error;
321
+ } else if (error instanceof ValidationException) {
322
+ throw error;
323
+ }
324
+
325
+ // For retryable errors, continue polling
326
+ await new Promise((resolve) =>
327
+ setTimeout(resolve, Math.min(delay * 2, this.maxPollingDelay)),
328
+ );
329
+ delay = Math.min(delay * 2, this.maxPollingDelay);
330
+ }
331
+ }
332
+ }
333
+
334
+ public async sync(
335
+ action: "CreateResource" | "UpdateResource" | "DeleteResource",
336
+ props?: any,
337
+ ): Promise<ProgressEvent> {
338
+ const { ProgressEvent } = await this.fetch<{
339
+ ProgressEvent: ProgressEvent;
340
+ }>(action, props);
341
+
342
+ return this.poll(ProgressEvent.RequestToken, action);
343
+ }
344
+
345
+ public async fetch<T>(
346
+ action: string,
347
+ params?: any,
348
+ options?: {
349
+ maxRetries?: number;
350
+ },
351
+ ): Promise<T> {
352
+ let attempt = 0;
353
+ const maxRetries = options?.maxRetries || this.maxRetries;
354
+
355
+ while (true) {
356
+ try {
357
+ const args = [
358
+ `https://cloudcontrolapi.${this.region}.amazonaws.com/?Action=${action}&Version=2021-09-30`,
359
+ {
360
+ method: "POST",
361
+ headers: {
362
+ "content-type": "application/x-amz-json-1.0",
363
+ "x-amz-target": `CloudApiService.${action}`,
364
+ },
365
+ body: JSON.stringify(params),
366
+ },
367
+ ] as const;
368
+ const signedRequest = await this.client.sign(...args);
369
+ const response = await fetch(signedRequest);
370
+
371
+ if (!response.ok) {
372
+ const data: any = await response.json();
373
+ if (
374
+ data.__type ===
375
+ "com.amazon.cloudapiservice#ResourceNotFoundException"
376
+ ) {
377
+ throw new ResourceNotFoundException(response);
378
+ }
379
+ if (
380
+ data.__type ===
381
+ "com.amazon.cloudapiservice#ConcurrentOperationException"
382
+ ) {
383
+ // Extract request token from message
384
+ const message = data.Message || "";
385
+ const requestTokenMatch = message.match(
386
+ /RequestToken\s+([a-fA-F0-9-]+)/,
387
+ );
388
+ if (!requestTokenMatch) {
389
+ throw new RequestError(response, {
390
+ ...data,
391
+ message: `ConcurrentOperationException without request token: ${message}`,
392
+ });
393
+ }
394
+ throw new ConcurrentOperationError(message, requestTokenMatch[1]);
395
+ }
396
+ if (
397
+ data.__type === "com.amazon.coral.availability#ThrottlingException"
398
+ ) {
399
+ throw new ThrottlingException(response, data);
400
+ }
401
+ if (data.__type === "com.amazon.coral.validate#ValidationException") {
402
+ throw new ValidationException(response, data);
403
+ }
404
+ throw new RequestError(response, data);
405
+ }
406
+
407
+ return (await response.json()) as T;
408
+ } catch (error: any) {
409
+ // Handle other CloudControl errors (don't retry, except ThrottlingException)
410
+ if (
411
+ error instanceof CloudControlError &&
412
+ !(error instanceof ThrottlingException)
413
+ ) {
414
+ throw error;
415
+ }
416
+
417
+ // Handle retryable errors (throttling and network errors)
418
+ if (attempt < maxRetries) {
419
+ // Use exponential backoff with jitter, capped at 3 seconds
420
+ const baseDelay = Math.min(2 ** attempt * 1000, 3000);
421
+ const jitter = Math.random() * 0.1 * baseDelay;
422
+ const retryDelay = baseDelay + jitter;
423
+
424
+ const errorType =
425
+ error instanceof ThrottlingException
426
+ ? "Throttling"
427
+ : "Network error";
428
+ console.log(
429
+ `${errorType} detected, retrying in ${Math.round(retryDelay)}ms (attempt ${attempt + 1}/${maxRetries + 1})`,
430
+ );
431
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
432
+ attempt++;
433
+ continue;
434
+ }
435
+
436
+ // Max retries exceeded
437
+ if (error instanceof ThrottlingException) {
438
+ console.error(
439
+ `Max retries (${maxRetries}) exceeded for throttling exception`,
440
+ );
441
+ throw error;
442
+ }
443
+
444
+ throw new NetworkError(
445
+ error.message || "Network error during Cloud Control API request",
446
+ );
447
+ }
448
+ }
449
+ }
450
+ }
@@ -0,0 +1,84 @@
1
+ import type { ProgressEvent } from "./client.js";
2
+
3
+ /**
4
+ * Error thrown by Cloud Control API operations
5
+ */
6
+ export class CloudControlError extends Error {
7
+ constructor(
8
+ public readonly message: string,
9
+ public readonly response?: Response,
10
+ ) {
11
+ super(message);
12
+ }
13
+ }
14
+ export class NetworkError extends CloudControlError {}
15
+ export class RequestError extends CloudControlError {
16
+ constructor(
17
+ response: Response,
18
+ public readonly data: any,
19
+ ) {
20
+ super(response.statusText, response);
21
+ }
22
+ }
23
+ export class UpdateFailedError extends CloudControlError {
24
+ constructor(public readonly progressEvent: ProgressEvent) {
25
+ super(progressEvent.StatusMessage!);
26
+ }
27
+ }
28
+ export class AlreadyExistsError extends CloudControlError {
29
+ constructor(public readonly progressEvent: ProgressEvent) {
30
+ super(progressEvent.StatusMessage!);
31
+ }
32
+ }
33
+ export class NotFoundError extends CloudControlError {
34
+ constructor(public readonly progressEvent: ProgressEvent) {
35
+ super(progressEvent.StatusMessage!);
36
+ }
37
+ }
38
+ export class ResourceNotFoundException extends CloudControlError {
39
+ constructor(public readonly response: Response) {
40
+ super(response.statusText);
41
+ }
42
+ }
43
+ export class TimeoutError extends CloudControlError {}
44
+
45
+ /**
46
+ * Rate limit exceeded error - should be retried with exponential backoff
47
+ */
48
+ export class ThrottlingException extends CloudControlError {
49
+ constructor(
50
+ public readonly response: Response,
51
+ public readonly data: any,
52
+ ) {
53
+ super(data.message || "Rate exceeded", response);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Model validation failed error - indicates invalid resource properties
59
+ */
60
+ export class ValidationException extends CloudControlError {
61
+ constructor(
62
+ public readonly response: Response,
63
+ public readonly data: any,
64
+ ) {
65
+ super(data.message || "Model validation failed", response);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * A previous Cloud Control API request is still running for the same
71
+ * resource. These are transient and should be retried after a short delay.
72
+ */
73
+ export class ConcurrentOperationError extends CloudControlError {
74
+ /**
75
+ * The request token of the in-flight operation from the AWS error message.
76
+ * (e.g. "Concurrent operation found … with RequestToken 1234…")
77
+ */
78
+ constructor(
79
+ public readonly message: string,
80
+ public readonly requestToken: string,
81
+ ) {
82
+ super(message);
83
+ }
84
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./client.js";
2
+ export * from "./proxy.js";
3
+ export * from "./resource.js";
4
+ import { AWS } from "./proxy.js";
5
+
6
+ export default AWS;