alchemy 0.0.7 → 0.1.1

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 (105) hide show
  1. package/package.json +23 -22
  2. package/src/alchemize.ts +151 -0
  3. package/src/apply.ts +117 -0
  4. package/src/components/agent/agent.ts +45 -0
  5. package/src/components/agent/anthropic.ts +27 -0
  6. package/src/components/agent/index.ts +4 -0
  7. package/src/components/agent/model.ts +22 -0
  8. package/src/components/agent/openai.ts +44 -0
  9. package/src/components/agent/package.ts +126 -0
  10. package/src/components/agent/requirements.ts +111 -0
  11. package/src/components/agent/scrape.ts +47 -0
  12. package/src/components/agent/tsconfig.ts +150 -0
  13. package/src/components/agent/typescript.ts +177 -0
  14. package/src/components/aws/bucket.ts +168 -0
  15. package/src/components/aws/function.ts +302 -0
  16. package/src/components/aws/index.ts +6 -0
  17. package/src/components/aws/policy.ts +245 -0
  18. package/src/components/aws/queue.ts +222 -0
  19. package/src/components/aws/role.ts +245 -0
  20. package/src/components/aws/table.ts +172 -0
  21. package/src/components/esbuild.ts +128 -0
  22. package/src/components/fs.ts +45 -0
  23. package/src/destroy.ts +63 -0
  24. package/src/error.ts +16 -0
  25. package/src/global.ts +72 -0
  26. package/src/index.ts +6 -0
  27. package/src/input.ts +16 -0
  28. package/src/main.ts +34 -0
  29. package/src/output.ts +49 -0
  30. package/src/resource.ts +313 -0
  31. package/src/state.ts +100 -0
  32. package/test/aws/function.test.ts +144 -0
  33. package/test/aws/queue.test.ts +85 -0
  34. package/test/aws/role.test.ts +153 -0
  35. package/test/aws/table.test.ts +73 -0
  36. package/test/esbuild.test.ts +41 -0
  37. package/test/handler.ts +9 -0
  38. package/tsconfig.json +7 -0
  39. package/Column.js +0 -1
  40. package/Create.js +0 -1
  41. package/Delete.js +0 -1
  42. package/Drop.js +0 -1
  43. package/Insert.js +0 -1
  44. package/Makefile +0 -8
  45. package/README.md +0 -0
  46. package/Select.js +0 -1
  47. package/Table.js +0 -1
  48. package/Transaction.js +0 -1
  49. package/Types.js +0 -1
  50. package/Update.js +0 -1
  51. package/alchemy.js +0 -57
  52. package/alchemy.sublime-project +0 -9
  53. package/alchemy.sublime-workspace +0 -2304
  54. package/config/app.js +0 -8
  55. package/config/db.js +0 -20
  56. package/copy.js +0 -1
  57. package/defaultEngine.js +0 -8
  58. package/engine.js +0 -282
  59. package/getInstantiatorFunction.js +0 -28
  60. package/index.js +0 -52
  61. package/jstest.js +0 -3
  62. package/loggerFactory.js +0 -27
  63. package/logs/alchemy.log +0 -46
  64. package/logs/sql.log +0 -41
  65. package/sql/Column.js +0 -158
  66. package/sql/Columnable.js +0 -21
  67. package/sql/Create.js +0 -15
  68. package/sql/Delete.js +0 -34
  69. package/sql/Drop.js +0 -36
  70. package/sql/Insert.js +0 -96
  71. package/sql/SchemaElement.js +0 -11
  72. package/sql/Select.js +0 -192
  73. package/sql/SqlBase.js +0 -134
  74. package/sql/SqlStatement.js +0 -80
  75. package/sql/Table.js +0 -96
  76. package/sql/Transaction.js +0 -45
  77. package/sql/Update.js +0 -105
  78. package/sql/defineElement.js +0 -26
  79. package/sql/defineStatement.js +0 -26
  80. package/sql/types/BaseType.js +0 -43
  81. package/sql/types/BigSerialType.js +0 -5
  82. package/sql/types/IntegerType.js +0 -5
  83. package/sql/types/JsonType.js +0 -5
  84. package/sql/types/SerialType.js +0 -5
  85. package/sql/types/StringType.js +0 -31
  86. package/sql/types/TimestampType.js +0 -5
  87. package/sql/types/UUIDType.js +0 -5
  88. package/sql/types/defineType.js +0 -25
  89. package/sql/types/getType.js +0 -12
  90. package/sql/types/index.js +0 -11
  91. package/sql/where.js +0 -31
  92. package/sql/whereToString.js +0 -29
  93. package/test/ColumnTests.js +0 -80
  94. package/test/CreateTable.js +0 -54
  95. package/test/DeleteTests.js +0 -28
  96. package/test/DropTests.js +0 -23
  97. package/test/GetTypeTests.js +0 -56
  98. package/test/InsertTests.js +0 -86
  99. package/test/SelectExistsTests.js +0 -26
  100. package/test/SelectTests.js +0 -130
  101. package/test/TableTests.js +0 -46
  102. package/test.js +0 -8
  103. package/test.ls +0 -4
  104. package/validate.js +0 -26
  105. package/x.js +0 -5
@@ -0,0 +1,222 @@
1
+ import {
2
+ CreateQueueCommand,
3
+ DeleteQueueCommand,
4
+ GetQueueAttributesCommand,
5
+ GetQueueUrlCommand,
6
+ SQSClient,
7
+ } from "@aws-sdk/client-sqs";
8
+ import { Resource } from "../../resource";
9
+
10
+ export interface QueueProps {
11
+ queueName: string;
12
+ fifo?: boolean;
13
+ visibilityTimeout?: number;
14
+ messageRetentionPeriod?: number;
15
+ maximumMessageSize?: number;
16
+ delaySeconds?: number;
17
+ receiveMessageWaitTimeSeconds?: number;
18
+ contentBasedDeduplication?: boolean;
19
+ deduplicationScope?: "messageGroup" | "queue";
20
+ fifoThroughputLimit?: "perQueue" | "perMessageGroupId";
21
+ tags?: Record<string, string>;
22
+ }
23
+
24
+ export interface QueueOutput extends QueueProps {
25
+ id: string; // Same as queueName
26
+ arn: string;
27
+ url: string;
28
+ }
29
+
30
+ export class Queue extends Resource(
31
+ "sqs::Queue",
32
+ async (ctx, props: QueueProps) => {
33
+ const client = new SQSClient({});
34
+ const queueName = props.fifo ? `${props.queueName}.fifo` : props.queueName;
35
+
36
+ if (ctx.event === "delete") {
37
+ try {
38
+ // Get queue URL first
39
+ const urlResponse = await client.send(
40
+ new GetQueueUrlCommand({
41
+ QueueName: queueName,
42
+ }),
43
+ );
44
+
45
+ // Delete the queue
46
+ await client.send(
47
+ new DeleteQueueCommand({
48
+ QueueUrl: urlResponse.QueueUrl,
49
+ }),
50
+ );
51
+
52
+ // Wait for queue to be deleted
53
+ let queueDeleted = false;
54
+ while (!queueDeleted) {
55
+ try {
56
+ await client.send(
57
+ new GetQueueUrlCommand({
58
+ QueueName: queueName,
59
+ }),
60
+ );
61
+ // If we get here, queue still exists
62
+ await new Promise((resolve) => setTimeout(resolve, 1000));
63
+ } catch (error: any) {
64
+ if (error.name === "QueueDoesNotExist") {
65
+ queueDeleted = true;
66
+ } else {
67
+ throw error;
68
+ }
69
+ }
70
+ }
71
+ } catch (error: any) {
72
+ if (error.name !== "QueueDoesNotExist") {
73
+ throw error;
74
+ }
75
+ }
76
+
77
+ return {
78
+ ...props,
79
+ id: props.queueName,
80
+ arn: `arn:aws:sqs:${client.config.region}:${process.env.AWS_ACCOUNT_ID}:${queueName}`,
81
+ url: "",
82
+ };
83
+ } else {
84
+ // Create queue with attributes
85
+ const attributes: Record<string, string> = {};
86
+
87
+ if (props.visibilityTimeout !== undefined) {
88
+ attributes["VisibilityTimeout"] = props.visibilityTimeout.toString();
89
+ }
90
+ if (props.messageRetentionPeriod !== undefined) {
91
+ attributes["MessageRetentionPeriod"] =
92
+ props.messageRetentionPeriod.toString();
93
+ }
94
+ if (props.maximumMessageSize !== undefined) {
95
+ attributes["MaximumMessageSize"] = props.maximumMessageSize.toString();
96
+ }
97
+ if (props.delaySeconds !== undefined) {
98
+ attributes["DelaySeconds"] = props.delaySeconds.toString();
99
+ }
100
+ if (props.receiveMessageWaitTimeSeconds !== undefined) {
101
+ attributes["ReceiveMessageWaitTimeSeconds"] =
102
+ props.receiveMessageWaitTimeSeconds.toString();
103
+ }
104
+
105
+ // FIFO specific attributes
106
+ if (props.fifo) {
107
+ attributes["FifoQueue"] = "true";
108
+ if (props.contentBasedDeduplication) {
109
+ attributes["ContentBasedDeduplication"] = "true";
110
+ }
111
+ if (props.deduplicationScope) {
112
+ attributes["DeduplicationScope"] = props.deduplicationScope;
113
+ }
114
+ if (props.fifoThroughputLimit) {
115
+ attributes["FifoThroughputLimit"] = props.fifoThroughputLimit;
116
+ }
117
+ }
118
+
119
+ // Convert tags to AWS format
120
+ const tags = props.tags
121
+ ? Object.entries(props.tags).reduce(
122
+ (acc, [key, value]) => ({ ...acc, [key]: value }),
123
+ {},
124
+ )
125
+ : undefined;
126
+
127
+ try {
128
+ // Create the queue
129
+ const createResponse = await client.send(
130
+ new CreateQueueCommand({
131
+ QueueName: queueName,
132
+ Attributes: attributes,
133
+ tags,
134
+ }),
135
+ );
136
+
137
+ // Get queue attributes
138
+ const attributesResponse = await client.send(
139
+ new GetQueueAttributesCommand({
140
+ QueueUrl: createResponse.QueueUrl,
141
+ AttributeNames: ["QueueArn"],
142
+ }),
143
+ );
144
+
145
+ return {
146
+ ...props,
147
+ id: props.queueName,
148
+ arn: attributesResponse.Attributes!.QueueArn!,
149
+ url: createResponse.QueueUrl!,
150
+ };
151
+ } catch (error: any) {
152
+ if (error.name === "QueueAlreadyExists") {
153
+ // Get existing queue URL
154
+ const urlResponse = await client.send(
155
+ new GetQueueUrlCommand({
156
+ QueueName: queueName,
157
+ }),
158
+ );
159
+
160
+ // Get queue attributes
161
+ const attributesResponse = await client.send(
162
+ new GetQueueAttributesCommand({
163
+ QueueUrl: urlResponse.QueueUrl,
164
+ AttributeNames: ["QueueArn"],
165
+ }),
166
+ );
167
+
168
+ return {
169
+ ...props,
170
+ id: props.queueName,
171
+ arn: attributesResponse.Attributes!.QueueArn!,
172
+ url: urlResponse.QueueUrl!,
173
+ };
174
+ } else if (error.name === "QueueDeletedRecently") {
175
+ // Queue was recently deleted, wait and retry
176
+ const maxRetries = 3;
177
+ let retryCount = 0;
178
+
179
+ while (retryCount < maxRetries) {
180
+ try {
181
+ // Wait for 60 seconds before retrying
182
+ await new Promise((resolve) => setTimeout(resolve, 61000));
183
+
184
+ // Retry creating the queue
185
+ const createResponse = await client.send(
186
+ new CreateQueueCommand({
187
+ QueueName: queueName,
188
+ Attributes: attributes,
189
+ tags,
190
+ }),
191
+ );
192
+
193
+ // Get queue attributes
194
+ const attributesResponse = await client.send(
195
+ new GetQueueAttributesCommand({
196
+ QueueUrl: createResponse.QueueUrl,
197
+ AttributeNames: ["QueueArn"],
198
+ }),
199
+ );
200
+
201
+ return {
202
+ ...props,
203
+ id: props.queueName,
204
+ arn: attributesResponse.Attributes!.QueueArn!,
205
+ url: createResponse.QueueUrl!,
206
+ };
207
+ } catch (retryError: any) {
208
+ if (
209
+ retryError.name !== "QueueDeletedRecently" ||
210
+ retryCount === maxRetries - 1
211
+ ) {
212
+ throw retryError;
213
+ }
214
+ retryCount++;
215
+ }
216
+ }
217
+ }
218
+ throw error;
219
+ }
220
+ }
221
+ },
222
+ ) {}
@@ -0,0 +1,245 @@
1
+ import {
2
+ CreateRoleCommand,
3
+ DeleteRoleCommand,
4
+ DeleteRolePolicyCommand,
5
+ EntityAlreadyExistsException,
6
+ GetRoleCommand,
7
+ IAMClient,
8
+ NoSuchEntityException,
9
+ PutRolePolicyCommand,
10
+ type Tag,
11
+ TagRoleCommand,
12
+ UpdateAssumeRolePolicyCommand,
13
+ UpdateRoleCommand,
14
+ } from "@aws-sdk/client-iam";
15
+ import { ignore } from "../../error";
16
+ import { type Context, Resource } from "../../resource";
17
+ import type { PolicyDocument } from "./policy";
18
+
19
+ export interface RoleProps {
20
+ roleName: string;
21
+ assumeRolePolicy: PolicyDocument;
22
+ description?: string;
23
+ path?: string;
24
+ maxSessionDuration?: number;
25
+ permissionsBoundary?: string;
26
+ policies?: Array<{
27
+ policyName: string;
28
+ policyDocument: PolicyDocument;
29
+ }>;
30
+ managedPolicyArns?: string[];
31
+ tags?: Record<string, string>;
32
+ }
33
+
34
+ export interface RoleOutput extends RoleProps {
35
+ id: string; // Same as roleName
36
+ arn: string;
37
+ uniqueId: string; // Unique identifier for the role
38
+ roleId: string; // The stable and unique string identifying the role
39
+ createDate: Date;
40
+ }
41
+
42
+ export class Role extends Resource(
43
+ "iam::Role",
44
+ async (ctx: Context<RoleOutput>, props: RoleProps): Promise<RoleOutput> => {
45
+ const client = new IAMClient({});
46
+
47
+ if (ctx.event === "delete") {
48
+ // Delete any inline policies first
49
+ if (props.policies) {
50
+ for (const policy of props.policies) {
51
+ await ignore(NoSuchEntityException.name, () =>
52
+ client.send(
53
+ new DeleteRolePolicyCommand({
54
+ RoleName: props.roleName,
55
+ PolicyName: policy.policyName,
56
+ }),
57
+ ),
58
+ );
59
+ }
60
+ }
61
+
62
+ await ignore(NoSuchEntityException.name, () =>
63
+ client.send(
64
+ new DeleteRoleCommand({
65
+ RoleName: props.roleName,
66
+ }),
67
+ ),
68
+ );
69
+ return {
70
+ ...props,
71
+ id: props.roleName,
72
+ arn: `arn:aws:iam::${process.env.AWS_ACCOUNT_ID}:role/${props.roleName}`,
73
+ uniqueId: "",
74
+ roleId: "",
75
+ createDate: new Date(),
76
+ };
77
+ }
78
+
79
+ const assumeRolePolicyDocument = JSON.stringify(props.assumeRolePolicy);
80
+ let role;
81
+
82
+ try {
83
+ if (ctx.event === "create") {
84
+ // Try to create the role
85
+ await client.send(
86
+ new CreateRoleCommand({
87
+ RoleName: props.roleName,
88
+ AssumeRolePolicyDocument: assumeRolePolicyDocument,
89
+ Description: props.description,
90
+ Path: props.path,
91
+ MaxSessionDuration: props.maxSessionDuration,
92
+ PermissionsBoundary: props.permissionsBoundary,
93
+ Tags: [
94
+ ...Object.entries(props.tags || {}).map(([Key, Value]) => ({
95
+ Key,
96
+ Value,
97
+ })),
98
+ {
99
+ Key: "alchemy_stage",
100
+ Value: ctx.stage,
101
+ },
102
+ {
103
+ Key: "alchemy_resource",
104
+ Value: ctx.resourceID,
105
+ },
106
+ ],
107
+ }),
108
+ );
109
+ }
110
+ } catch (error: any) {
111
+ if (
112
+ error instanceof EntityAlreadyExistsException &&
113
+ ctx.event === "create"
114
+ ) {
115
+ // Check if we were the ones who created it
116
+ const existingRole = await client.send(
117
+ new GetRoleCommand({
118
+ RoleName: props.roleName,
119
+ }),
120
+ );
121
+ const roleTags =
122
+ existingRole.Role?.Tags?.reduce(
123
+ (acc, tag) => {
124
+ acc[tag.Key!] = tag.Value!;
125
+ return acc;
126
+ },
127
+ {} as Record<string, string>,
128
+ ) || {};
129
+
130
+ if (
131
+ roleTags.alchemy_stage !== ctx.stage ||
132
+ roleTags.alchemy_resource !== ctx.resourceID
133
+ ) {
134
+ throw error;
135
+ }
136
+ } else if (error.name !== NoSuchEntityException.name) {
137
+ throw error;
138
+ }
139
+ }
140
+
141
+ // Get or update the role
142
+ role = await client.send(
143
+ new GetRoleCommand({
144
+ RoleName: props.roleName,
145
+ }),
146
+ );
147
+
148
+ // Update assume role policy if it changed
149
+ if (role.Role?.AssumeRolePolicyDocument !== assumeRolePolicyDocument) {
150
+ await client.send(
151
+ new UpdateAssumeRolePolicyCommand({
152
+ RoleName: props.roleName,
153
+ PolicyDocument: assumeRolePolicyDocument,
154
+ }),
155
+ );
156
+ }
157
+
158
+ // Update role description and max session duration if they changed
159
+ if (
160
+ role.Role?.Description !== props.description ||
161
+ role.Role?.MaxSessionDuration !== props.maxSessionDuration
162
+ ) {
163
+ await client.send(
164
+ new UpdateRoleCommand({
165
+ RoleName: props.roleName,
166
+ Description: props.description,
167
+ MaxSessionDuration: props.maxSessionDuration,
168
+ }),
169
+ );
170
+ }
171
+
172
+ // Update tags
173
+ const newTags = {
174
+ ...props.tags,
175
+ alchemy_stage: ctx.stage,
176
+ alchemy_resource: ctx.resourceID,
177
+ };
178
+ const tags: Tag[] = Object.entries(newTags).map(([Key, Value]) => ({
179
+ Key,
180
+ Value,
181
+ }));
182
+ await client.send(
183
+ new TagRoleCommand({
184
+ RoleName: props.roleName,
185
+ Tags: tags,
186
+ }),
187
+ );
188
+
189
+ // Handle policy changes
190
+ const previousPolicies =
191
+ ctx.event === "update" ? ctx.output.policies || [] : [];
192
+ const currentPolicies = props.policies || [];
193
+
194
+ // Delete policies that were removed
195
+ for (const oldPolicy of previousPolicies) {
196
+ if (
197
+ !currentPolicies.some(
198
+ (p: { policyName: string }) => p.policyName === oldPolicy.policyName,
199
+ )
200
+ ) {
201
+ await ignore(NoSuchEntityException.name, () =>
202
+ client.send(
203
+ new DeleteRolePolicyCommand({
204
+ RoleName: props.roleName,
205
+ PolicyName: oldPolicy.policyName,
206
+ }),
207
+ ),
208
+ );
209
+ }
210
+ }
211
+
212
+ // Update or create policies
213
+ for (const policy of currentPolicies) {
214
+ const oldPolicy = previousPolicies.find(
215
+ (p) => p.policyName === policy.policyName,
216
+ );
217
+ if (
218
+ !oldPolicy ||
219
+ JSON.stringify(oldPolicy.policyDocument) !==
220
+ JSON.stringify(policy.policyDocument)
221
+ ) {
222
+ await client.send(
223
+ new PutRolePolicyCommand({
224
+ RoleName: props.roleName,
225
+ PolicyName: policy.policyName,
226
+ PolicyDocument: JSON.stringify(policy.policyDocument),
227
+ }),
228
+ );
229
+ }
230
+ }
231
+
232
+ if (!role?.Role) {
233
+ throw new Error(`Failed to create or update role ${props.roleName}`);
234
+ }
235
+
236
+ return {
237
+ ...props,
238
+ id: props.roleName,
239
+ arn: role.Role.Arn!,
240
+ uniqueId: role.Role.RoleId!,
241
+ roleId: role.Role.RoleId!,
242
+ createDate: role.Role.CreateDate!,
243
+ };
244
+ },
245
+ ) {}
@@ -0,0 +1,172 @@
1
+ import {
2
+ CreateTableCommand,
3
+ DeleteTableCommand,
4
+ DescribeTableCommand,
5
+ DynamoDBClient,
6
+ type KeySchemaElement,
7
+ ResourceInUseException,
8
+ ResourceNotFoundException,
9
+ } from "@aws-sdk/client-dynamodb";
10
+ import { ignore } from "../../error";
11
+ import { type Context, Resource } from "../../resource";
12
+
13
+ export interface TableProps {
14
+ tableName: string;
15
+ partitionKey: {
16
+ name: string;
17
+ type: "S" | "N" | "B";
18
+ };
19
+ sortKey?: {
20
+ name: string;
21
+ type: "S" | "N" | "B";
22
+ };
23
+ billingMode?: "PROVISIONED" | "PAY_PER_REQUEST";
24
+ readCapacity?: number;
25
+ writeCapacity?: number;
26
+ tags?: Record<string, string>;
27
+ }
28
+
29
+ export interface TableOutput extends TableProps {
30
+ id: string; // Same as tableName
31
+ arn: string;
32
+ streamArn?: string;
33
+ tableId: string;
34
+ }
35
+
36
+ export class Table extends Resource(
37
+ "dynamo::Table",
38
+ async (
39
+ ctx: Context<TableOutput>,
40
+ props: TableProps,
41
+ ): Promise<TableOutput> => {
42
+ const client = new DynamoDBClient({});
43
+
44
+ if (ctx.event === "delete") {
45
+ await ignore(ResourceNotFoundException.name, () =>
46
+ client.send(
47
+ new DeleteTableCommand({
48
+ TableName: props.tableName,
49
+ }),
50
+ ),
51
+ );
52
+
53
+ // Wait for table to be deleted
54
+ let tableDeleted = false;
55
+ while (!tableDeleted) {
56
+ try {
57
+ await client.send(
58
+ new DescribeTableCommand({
59
+ TableName: props.tableName,
60
+ }),
61
+ );
62
+ // If we get here, table still exists
63
+ await new Promise((resolve) => setTimeout(resolve, 1000));
64
+ } catch (error) {
65
+ if (error instanceof ResourceNotFoundException) {
66
+ tableDeleted = true;
67
+ } else {
68
+ throw error;
69
+ }
70
+ }
71
+ }
72
+
73
+ return {
74
+ ...props,
75
+ id: props.tableName,
76
+ arn: `arn:aws:dynamodb:${client.config.region}:${process.env.AWS_ACCOUNT_ID}:table/${props.tableName}`,
77
+ tableId: "",
78
+ };
79
+ } else {
80
+ try {
81
+ const attributeDefinitions = [
82
+ {
83
+ AttributeName: props.partitionKey.name,
84
+ AttributeType: props.partitionKey.type,
85
+ },
86
+ ];
87
+
88
+ const keySchema: KeySchemaElement[] = [
89
+ {
90
+ AttributeName: props.partitionKey.name,
91
+ KeyType: "HASH",
92
+ },
93
+ ];
94
+
95
+ if (props.sortKey) {
96
+ attributeDefinitions.push({
97
+ AttributeName: props.sortKey.name,
98
+ AttributeType: props.sortKey.type,
99
+ });
100
+ keySchema.push({
101
+ AttributeName: props.sortKey.name,
102
+ KeyType: "RANGE",
103
+ });
104
+ }
105
+
106
+ await client.send(
107
+ new CreateTableCommand({
108
+ TableName: props.tableName,
109
+ AttributeDefinitions: attributeDefinitions,
110
+ KeySchema: keySchema,
111
+ BillingMode: props.billingMode || "PAY_PER_REQUEST",
112
+ ProvisionedThroughput:
113
+ props.billingMode === "PROVISIONED"
114
+ ? {
115
+ ReadCapacityUnits: props.readCapacity || 5,
116
+ WriteCapacityUnits: props.writeCapacity || 5,
117
+ }
118
+ : undefined,
119
+ Tags: props.tags
120
+ ? Object.entries(props.tags).map(([Key, Value]) => ({
121
+ Key,
122
+ Value,
123
+ }))
124
+ : undefined,
125
+ }),
126
+ );
127
+
128
+ // Wait for table to be active
129
+ let tableActive = false;
130
+ let tableDescription;
131
+ while (!tableActive) {
132
+ const response = await client.send(
133
+ new DescribeTableCommand({
134
+ TableName: props.tableName,
135
+ }),
136
+ );
137
+ tableActive = response.Table?.TableStatus === "ACTIVE";
138
+ if (tableActive) {
139
+ tableDescription = response.Table;
140
+ } else {
141
+ await new Promise((resolve) => setTimeout(resolve, 1000));
142
+ }
143
+ }
144
+
145
+ return {
146
+ ...props,
147
+ id: props.tableName,
148
+ arn: tableDescription!.TableArn!,
149
+ streamArn: tableDescription!.LatestStreamArn,
150
+ tableId: tableDescription!.TableId!,
151
+ };
152
+ } catch (error) {
153
+ if (error instanceof ResourceInUseException) {
154
+ // Table already exists, get its details
155
+ const response = await client.send(
156
+ new DescribeTableCommand({
157
+ TableName: props.tableName,
158
+ }),
159
+ );
160
+ return {
161
+ ...props,
162
+ id: props.tableName,
163
+ arn: response.Table!.TableArn!,
164
+ streamArn: response.Table!.LatestStreamArn,
165
+ tableId: response.Table!.TableId!,
166
+ };
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+ },
172
+ ) {}