dyno-table 2.2.0 → 2.3.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 (106) hide show
  1. package/README.md +200 -1860
  2. package/dist/builders.cjs +55 -0
  3. package/dist/builders.d.cts +4 -0
  4. package/dist/builders.d.ts +4 -0
  5. package/dist/builders.js +2 -0
  6. package/dist/chunk-2EWNZOUK.js +618 -0
  7. package/dist/chunk-2WIBY7PZ.js +46 -0
  8. package/dist/chunk-7UJJ7JXM.cjs +63 -0
  9. package/dist/chunk-DTFJJASK.js +3200 -0
  10. package/dist/chunk-EODPMYPE.js +558 -0
  11. package/dist/chunk-KA3VPIPS.cjs +560 -0
  12. package/dist/chunk-NTA6GDPP.cjs +622 -0
  13. package/dist/chunk-PB7BBCZO.cjs +32 -0
  14. package/dist/chunk-QVRMYGC4.js +29 -0
  15. package/dist/chunk-XYL43FDX.cjs +3217 -0
  16. package/dist/{conditions-CC3NDfUU.d.cts → conditions-CcZL0sR2.d.cts} +1 -1
  17. package/dist/{conditions-DD0bvyHm.d.ts → conditions-D_w7vVYG.d.ts} +1 -1
  18. package/dist/conditions.cjs +67 -62
  19. package/dist/conditions.d.cts +1 -1
  20. package/dist/conditions.d.ts +1 -1
  21. package/dist/conditions.js +1 -48
  22. package/dist/entity.cjs +14 -625
  23. package/dist/entity.d.cts +5 -13
  24. package/dist/entity.d.ts +5 -13
  25. package/dist/entity.js +2 -626
  26. package/dist/index-2cbm07Bi.d.ts +2797 -0
  27. package/dist/index-DlN8G9hd.d.cts +2797 -0
  28. package/dist/index.cjs +111 -4446
  29. package/dist/index.d.cts +3 -11
  30. package/dist/index.d.ts +3 -11
  31. package/dist/index.js +5 -4428
  32. package/dist/standard-schema.cjs +0 -2
  33. package/dist/standard-schema.js +0 -2
  34. package/dist/table.cjs +7 -3782
  35. package/dist/table.d.cts +163 -12
  36. package/dist/table.d.ts +163 -12
  37. package/dist/table.js +3 -3785
  38. package/dist/types.cjs +0 -2
  39. package/dist/types.js +0 -2
  40. package/dist/utils.cjs +10 -30
  41. package/dist/utils.js +1 -31
  42. package/package.json +12 -67
  43. package/dist/batch-builder-BPoHyN_Q.d.cts +0 -398
  44. package/dist/batch-builder-Cdo49C2r.d.ts +0 -398
  45. package/dist/builder-types-BTVhQSHI.d.cts +0 -169
  46. package/dist/builder-types-CzuLR4Th.d.ts +0 -169
  47. package/dist/builders/condition-check-builder.cjs +0 -422
  48. package/dist/builders/condition-check-builder.cjs.map +0 -1
  49. package/dist/builders/condition-check-builder.d.cts +0 -153
  50. package/dist/builders/condition-check-builder.d.ts +0 -153
  51. package/dist/builders/condition-check-builder.js +0 -420
  52. package/dist/builders/condition-check-builder.js.map +0 -1
  53. package/dist/builders/delete-builder.cjs +0 -484
  54. package/dist/builders/delete-builder.cjs.map +0 -1
  55. package/dist/builders/delete-builder.d.cts +0 -211
  56. package/dist/builders/delete-builder.d.ts +0 -211
  57. package/dist/builders/delete-builder.js +0 -482
  58. package/dist/builders/delete-builder.js.map +0 -1
  59. package/dist/builders/paginator.cjs +0 -193
  60. package/dist/builders/paginator.cjs.map +0 -1
  61. package/dist/builders/paginator.d.cts +0 -155
  62. package/dist/builders/paginator.d.ts +0 -155
  63. package/dist/builders/paginator.js +0 -191
  64. package/dist/builders/paginator.js.map +0 -1
  65. package/dist/builders/put-builder.cjs +0 -554
  66. package/dist/builders/put-builder.cjs.map +0 -1
  67. package/dist/builders/put-builder.d.cts +0 -319
  68. package/dist/builders/put-builder.d.ts +0 -319
  69. package/dist/builders/put-builder.js +0 -552
  70. package/dist/builders/put-builder.js.map +0 -1
  71. package/dist/builders/query-builder.cjs +0 -743
  72. package/dist/builders/query-builder.cjs.map +0 -1
  73. package/dist/builders/query-builder.d.cts +0 -6
  74. package/dist/builders/query-builder.d.ts +0 -6
  75. package/dist/builders/query-builder.js +0 -741
  76. package/dist/builders/query-builder.js.map +0 -1
  77. package/dist/builders/transaction-builder.cjs +0 -906
  78. package/dist/builders/transaction-builder.cjs.map +0 -1
  79. package/dist/builders/transaction-builder.d.cts +0 -464
  80. package/dist/builders/transaction-builder.d.ts +0 -464
  81. package/dist/builders/transaction-builder.js +0 -904
  82. package/dist/builders/transaction-builder.js.map +0 -1
  83. package/dist/builders/update-builder.cjs +0 -668
  84. package/dist/builders/update-builder.cjs.map +0 -1
  85. package/dist/builders/update-builder.d.cts +0 -374
  86. package/dist/builders/update-builder.d.ts +0 -374
  87. package/dist/builders/update-builder.js +0 -666
  88. package/dist/builders/update-builder.js.map +0 -1
  89. package/dist/conditions.cjs.map +0 -1
  90. package/dist/conditions.js.map +0 -1
  91. package/dist/entity.cjs.map +0 -1
  92. package/dist/entity.js.map +0 -1
  93. package/dist/index.cjs.map +0 -1
  94. package/dist/index.js.map +0 -1
  95. package/dist/query-builder-CUWdavZw.d.ts +0 -477
  96. package/dist/query-builder-DoZzZz_c.d.cts +0 -477
  97. package/dist/standard-schema.cjs.map +0 -1
  98. package/dist/standard-schema.js.map +0 -1
  99. package/dist/table-CZBMkW2Z.d.ts +0 -276
  100. package/dist/table-f-3wsT7K.d.cts +0 -276
  101. package/dist/table.cjs.map +0 -1
  102. package/dist/table.js.map +0 -1
  103. package/dist/types.cjs.map +0 -1
  104. package/dist/types.js.map +0 -1
  105. package/dist/utils.cjs.map +0 -1
  106. package/dist/utils.js.map +0 -1
@@ -0,0 +1,558 @@
1
+ import { GetBuilder, PutBuilder, QueryBuilder, ScanBuilder, DeleteBuilder, UpdateBuilder, TransactionBuilder, BatchBuilder, ConditionCheckBuilder, buildExpression, generateAttributeName, debugCommand } from './chunk-DTFJJASK.js';
2
+ import { eq, and, beginsWith, between, gte, gt, lte, lt } from './chunk-2WIBY7PZ.js';
3
+
4
+ // src/utils/chunk-array.ts
5
+ function* chunkArray(array, size) {
6
+ if (size <= 0) {
7
+ throw new Error("Chunk size must be greater than 0");
8
+ }
9
+ for (let i = 0; i < array.length; i += size) {
10
+ yield array.slice(i, i + size);
11
+ }
12
+ }
13
+
14
+ // src/table.ts
15
+ var DDB_BATCH_WRITE_LIMIT = 25;
16
+ var DDB_BATCH_GET_LIMIT = 100;
17
+ var Table = class {
18
+ dynamoClient;
19
+ tableName;
20
+ /**
21
+ * The column name of the partitionKey for the Table
22
+ */
23
+ partitionKey;
24
+ /**
25
+ * The column name of the sortKey for the Table
26
+ */
27
+ sortKey;
28
+ /**
29
+ * The Global Secondary Indexes that are configured on this table
30
+ */
31
+ gsis;
32
+ constructor(config) {
33
+ this.dynamoClient = config.client;
34
+ this.tableName = config.tableName;
35
+ this.partitionKey = config.indexes.partitionKey;
36
+ this.sortKey = config.indexes.sortKey;
37
+ this.gsis = config.indexes.gsis || {};
38
+ }
39
+ createKeyForPrimaryIndex(keyCondition) {
40
+ const primaryCondition = { [this.partitionKey]: keyCondition.pk };
41
+ if (this.sortKey) {
42
+ if (!keyCondition.sk) {
43
+ throw new Error("Sort key has not been provided but the Table has a sort key");
44
+ }
45
+ primaryCondition[this.sortKey] = keyCondition.sk;
46
+ }
47
+ return primaryCondition;
48
+ }
49
+ /**
50
+ * Creates a new item in the table, it will fail if the item already exists.
51
+ *
52
+ * By default, this method returns the input values passed to the create operation
53
+ * upon successful creation.
54
+ *
55
+ * You can customise the return behaviour by chaining the `.returnValues()` method:
56
+ *
57
+ * @param item The item to create
58
+ * @returns A PutBuilder instance for chaining additional conditions and executing the create operation
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * // Create with default behavior (returns input values)
63
+ * const result = await table.create({
64
+ * id: 'user-123',
65
+ * name: 'John Doe',
66
+ * email: 'john@example.com'
67
+ * }).execute();
68
+ * console.log(result); // Returns the input object
69
+ *
70
+ * // Create with no return value for better performance
71
+ * await table.create(userData).returnValues('NONE').execute();
72
+ *
73
+ * // Create and get fresh data from dynamodb using a strongly consistent read
74
+ * const freshData = await table.create(userData).returnValues('CONSISTENT').execute();
75
+ *
76
+ * // Create and get previous values (if the item was overwritten)
77
+ * const oldData = await table.create(userData).returnValues('ALL_OLD').execute();
78
+ * ```
79
+ */
80
+ create(item) {
81
+ return this.put(item).condition((op) => op.attributeNotExists(this.partitionKey)).returnValues("INPUT");
82
+ }
83
+ get(keyCondition) {
84
+ const executor = async (params) => {
85
+ try {
86
+ const result = await this.dynamoClient.get({
87
+ TableName: params.tableName,
88
+ Key: this.createKeyForPrimaryIndex(keyCondition),
89
+ ProjectionExpression: params.projectionExpression,
90
+ ExpressionAttributeNames: params.expressionAttributeNames,
91
+ ConsistentRead: params.consistentRead
92
+ });
93
+ return {
94
+ item: result.Item ? result.Item : void 0
95
+ };
96
+ } catch (error) {
97
+ console.error("Error getting item:", error);
98
+ throw error;
99
+ }
100
+ };
101
+ return new GetBuilder(executor, keyCondition, this.tableName);
102
+ }
103
+ /**
104
+ * Updates an item in the table
105
+ *
106
+ * @param item The item to update
107
+ * @returns A PutBuilder instance for chaining conditions and executing the put operation
108
+ */
109
+ put(item) {
110
+ const executor = async (params) => {
111
+ try {
112
+ const result = await this.dynamoClient.put({
113
+ TableName: params.tableName,
114
+ Item: params.item,
115
+ ConditionExpression: params.conditionExpression,
116
+ ExpressionAttributeNames: params.expressionAttributeNames,
117
+ ExpressionAttributeValues: params.expressionAttributeValues,
118
+ // CONSISTENT and INPUT are not valid ReturnValues for DDB, so we set NONE as we are not interested in its
119
+ // response and will be handling these cases separately
120
+ ReturnValues: params.returnValues === "CONSISTENT" || params.returnValues === "INPUT" ? "NONE" : params.returnValues
121
+ });
122
+ if (params.returnValues === "INPUT") {
123
+ return params.item;
124
+ }
125
+ if (params.returnValues === "CONSISTENT") {
126
+ const getResult = await this.dynamoClient.get({
127
+ TableName: params.tableName,
128
+ Key: this.createKeyForPrimaryIndex({
129
+ pk: params.item[this.partitionKey],
130
+ ...this.sortKey && { sk: params.item[this.sortKey] }
131
+ }),
132
+ ConsistentRead: true
133
+ });
134
+ return getResult.Item;
135
+ }
136
+ return result.Attributes;
137
+ } catch (error) {
138
+ console.error("Error creating item:", error);
139
+ throw error;
140
+ }
141
+ };
142
+ return new PutBuilder(executor, item, this.tableName);
143
+ }
144
+ /**
145
+ * Creates a query builder for complex queries
146
+ * If useIndex is called on the returned QueryBuilder, it will use the GSI configuration
147
+ */
148
+ query(keyCondition) {
149
+ const pkAttributeName = this.partitionKey;
150
+ const skAttributeName = this.sortKey;
151
+ let keyConditionExpression = eq(pkAttributeName, keyCondition.pk);
152
+ if (keyCondition.sk) {
153
+ if (!skAttributeName) {
154
+ throw new Error("Sort key is not defined for Index");
155
+ }
156
+ const keyConditionOperator = {
157
+ eq: (value) => eq(skAttributeName, value),
158
+ lt: (value) => lt(skAttributeName, value),
159
+ lte: (value) => lte(skAttributeName, value),
160
+ gt: (value) => gt(skAttributeName, value),
161
+ gte: (value) => gte(skAttributeName, value),
162
+ between: (lower, upper) => between(skAttributeName, lower, upper),
163
+ beginsWith: (value) => beginsWith(skAttributeName, value),
164
+ and: (...conditions) => and(...conditions)
165
+ };
166
+ const skCondition = keyCondition.sk(keyConditionOperator);
167
+ keyConditionExpression = and(eq(pkAttributeName, keyCondition.pk), skCondition);
168
+ }
169
+ const executor = async (originalKeyCondition, options) => {
170
+ let finalKeyCondition = originalKeyCondition;
171
+ if (options.indexName) {
172
+ const gsiName = String(options.indexName);
173
+ const gsi = this.gsis[gsiName];
174
+ if (!gsi) {
175
+ throw new Error(`GSI with name "${gsiName}" does not exist on table "${this.tableName}"`);
176
+ }
177
+ const gsiPkAttributeName = gsi.partitionKey;
178
+ const gsiSkAttributeName = gsi.sortKey;
179
+ let pkValue;
180
+ let skValue;
181
+ let extractedSkCondition;
182
+ if (originalKeyCondition.type === "eq") {
183
+ pkValue = originalKeyCondition.value;
184
+ } else if (originalKeyCondition.type === "and" && originalKeyCondition.conditions) {
185
+ const pkCondition = originalKeyCondition.conditions.find(
186
+ (c) => c.type === "eq" && c.attr === pkAttributeName
187
+ );
188
+ if (pkCondition && pkCondition.type === "eq") {
189
+ pkValue = pkCondition.value;
190
+ }
191
+ const skConditions = originalKeyCondition.conditions.filter((c) => c.attr === skAttributeName);
192
+ if (skConditions.length > 0) {
193
+ if (skConditions.length === 1) {
194
+ extractedSkCondition = skConditions[0];
195
+ if (extractedSkCondition && extractedSkCondition.type === "eq") {
196
+ skValue = extractedSkCondition.value;
197
+ }
198
+ } else if (skConditions.length > 1) {
199
+ extractedSkCondition = and(...skConditions);
200
+ }
201
+ }
202
+ }
203
+ if (!pkValue) {
204
+ throw new Error("Could not extract partition key value from key condition");
205
+ }
206
+ let gsiKeyCondition = eq(gsiPkAttributeName, pkValue);
207
+ if (skValue && gsiSkAttributeName) {
208
+ gsiKeyCondition = and(gsiKeyCondition, eq(gsiSkAttributeName, skValue));
209
+ } else if (extractedSkCondition && gsiSkAttributeName) {
210
+ if (extractedSkCondition.attr === skAttributeName) {
211
+ const updatedSkCondition = {
212
+ ...extractedSkCondition,
213
+ attr: gsiSkAttributeName
214
+ };
215
+ gsiKeyCondition = and(gsiKeyCondition, updatedSkCondition);
216
+ } else {
217
+ gsiKeyCondition = and(gsiKeyCondition, extractedSkCondition);
218
+ }
219
+ }
220
+ finalKeyCondition = gsiKeyCondition;
221
+ }
222
+ const expressionParams = {
223
+ expressionAttributeNames: {},
224
+ expressionAttributeValues: {},
225
+ valueCounter: { count: 0 }
226
+ };
227
+ const keyConditionExpression2 = buildExpression(finalKeyCondition, expressionParams);
228
+ let filterExpression;
229
+ if (options.filter) {
230
+ filterExpression = buildExpression(options.filter, expressionParams);
231
+ }
232
+ const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
233
+ const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
234
+ const { indexName, limit, consistentRead, scanIndexForward, lastEvaluatedKey } = options;
235
+ const params = {
236
+ TableName: this.tableName,
237
+ KeyConditionExpression: keyConditionExpression2,
238
+ FilterExpression: filterExpression,
239
+ ExpressionAttributeNames: expressionAttributeNames,
240
+ ExpressionAttributeValues: expressionAttributeValues,
241
+ IndexName: indexName,
242
+ Limit: limit,
243
+ ConsistentRead: consistentRead,
244
+ ScanIndexForward: scanIndexForward,
245
+ ProjectionExpression: projectionExpression,
246
+ ExclusiveStartKey: lastEvaluatedKey
247
+ };
248
+ try {
249
+ const result = await this.dynamoClient.query(params);
250
+ return {
251
+ items: result.Items,
252
+ lastEvaluatedKey: result.LastEvaluatedKey
253
+ };
254
+ } catch (error) {
255
+ console.log(debugCommand(params));
256
+ console.error("Error querying items:", error);
257
+ throw error;
258
+ }
259
+ };
260
+ return new QueryBuilder(executor, keyConditionExpression);
261
+ }
262
+ /**
263
+ * Creates a scan builder for scanning the entire table
264
+ * Use this when you need to:
265
+ * - Process all items in a table
266
+ * - Apply filters to a large dataset
267
+ * - Use a GSI for scanning
268
+ *
269
+ * @returns A ScanBuilder instance for chaining operations
270
+ */
271
+ scan() {
272
+ const executor = async (options) => {
273
+ const expressionParams = {
274
+ expressionAttributeNames: {},
275
+ expressionAttributeValues: {},
276
+ valueCounter: { count: 0 }
277
+ };
278
+ let filterExpression;
279
+ if (options.filter) {
280
+ filterExpression = buildExpression(options.filter, expressionParams);
281
+ }
282
+ const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
283
+ const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
284
+ const { indexName, limit, consistentRead, lastEvaluatedKey } = options;
285
+ const params = {
286
+ TableName: this.tableName,
287
+ FilterExpression: filterExpression,
288
+ ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
289
+ ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
290
+ IndexName: indexName,
291
+ Limit: limit,
292
+ ConsistentRead: consistentRead,
293
+ ProjectionExpression: projectionExpression,
294
+ ExclusiveStartKey: lastEvaluatedKey
295
+ };
296
+ try {
297
+ const result = await this.dynamoClient.scan(params);
298
+ return {
299
+ items: result.Items,
300
+ lastEvaluatedKey: result.LastEvaluatedKey
301
+ };
302
+ } catch (error) {
303
+ console.log(debugCommand(params));
304
+ console.error("Error scanning items:", error);
305
+ throw error;
306
+ }
307
+ };
308
+ return new ScanBuilder(executor);
309
+ }
310
+ delete(keyCondition) {
311
+ const executor = async (params) => {
312
+ try {
313
+ const result = await this.dynamoClient.delete({
314
+ TableName: params.tableName,
315
+ Key: this.createKeyForPrimaryIndex(keyCondition),
316
+ ConditionExpression: params.conditionExpression,
317
+ ExpressionAttributeNames: params.expressionAttributeNames,
318
+ ExpressionAttributeValues: params.expressionAttributeValues,
319
+ ReturnValues: params.returnValues
320
+ });
321
+ return {
322
+ item: result.Attributes
323
+ };
324
+ } catch (error) {
325
+ console.error("Error deleting item:", error);
326
+ throw error;
327
+ }
328
+ };
329
+ return new DeleteBuilder(executor, this.tableName, keyCondition);
330
+ }
331
+ /**
332
+ * Updates an item in the table
333
+ *
334
+ * @param keyCondition The primary key of the item to update
335
+ * @returns An UpdateBuilder instance for chaining update operations and conditions
336
+ */
337
+ update(keyCondition) {
338
+ const executor = async (params) => {
339
+ try {
340
+ const result = await this.dynamoClient.update({
341
+ TableName: params.tableName,
342
+ Key: this.createKeyForPrimaryIndex(keyCondition),
343
+ UpdateExpression: params.updateExpression,
344
+ ConditionExpression: params.conditionExpression,
345
+ ExpressionAttributeNames: params.expressionAttributeNames,
346
+ ExpressionAttributeValues: params.expressionAttributeValues,
347
+ ReturnValues: params.returnValues
348
+ });
349
+ return {
350
+ item: result.Attributes
351
+ };
352
+ } catch (error) {
353
+ console.error("Error updating item:", error);
354
+ throw error;
355
+ }
356
+ };
357
+ return new UpdateBuilder(executor, this.tableName, keyCondition);
358
+ }
359
+ /**
360
+ * Creates a transaction builder for performing multiple operations atomically
361
+ */
362
+ transactionBuilder() {
363
+ const executor = async (params) => {
364
+ await this.dynamoClient.transactWrite(params);
365
+ };
366
+ return new TransactionBuilder(executor, {
367
+ partitionKey: this.partitionKey,
368
+ sortKey: this.sortKey
369
+ });
370
+ }
371
+ /**
372
+ * Creates a batch builder for performing multiple operations efficiently with optional type inference
373
+ *
374
+ * @example Basic Usage
375
+ * ```typescript
376
+ * const batch = table.batchBuilder();
377
+ *
378
+ * // Add operations
379
+ * userRepo.create(newUser).withBatch(batch);
380
+ * orderRepo.get({ id: 'order-1' }).withBatch(batch);
381
+ *
382
+ * // Execute operations
383
+ * const result = await batch.execute();
384
+ * ```
385
+ *
386
+ * @example Typed Usage
387
+ * ```typescript
388
+ * // Define entity types for the batch
389
+ * const batch = table.batchBuilder<{
390
+ * User: UserEntity;
391
+ * Order: OrderEntity;
392
+ * Product: ProductEntity;
393
+ * }>();
394
+ *
395
+ * // Add operations with type information
396
+ * userRepo.create(newUser).withBatch(batch, 'User');
397
+ * orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
398
+ * productRepo.delete({ id: 'old-product' }).withBatch(batch, 'Product');
399
+ *
400
+ * // Execute and get typed results
401
+ * const result = await batch.execute();
402
+ * const users: UserEntity[] = result.reads.itemsByType.User;
403
+ * const orders: OrderEntity[] = result.reads.itemsByType.Order;
404
+ * ```
405
+ */
406
+ batchBuilder() {
407
+ const batchWriteExecutor = async (operations) => {
408
+ return this.batchWrite(operations);
409
+ };
410
+ const batchGetExecutor = async (keys) => {
411
+ return this.batchGet(keys);
412
+ };
413
+ return new BatchBuilder(batchWriteExecutor, batchGetExecutor, {
414
+ partitionKey: this.partitionKey,
415
+ sortKey: this.sortKey
416
+ });
417
+ }
418
+ /**
419
+ * Executes a transaction using a callback function
420
+ *
421
+ * @param callback A function that receives a transaction context and performs operations on it
422
+ * @param options Optional transaction options
423
+ * @returns A promise that resolves when the transaction is complete
424
+ */
425
+ async transaction(callback, options) {
426
+ const transactionExecutor = async (params) => {
427
+ await this.dynamoClient.transactWrite(params);
428
+ };
429
+ const transaction = new TransactionBuilder(transactionExecutor, {
430
+ partitionKey: this.partitionKey,
431
+ sortKey: this.sortKey
432
+ });
433
+ if (options) {
434
+ transaction.withOptions(options);
435
+ }
436
+ const result = await callback(transaction);
437
+ await transaction.execute();
438
+ return result;
439
+ }
440
+ /**
441
+ * Creates a condition check operation for use in transactions
442
+ *
443
+ * This is useful for when you require a transaction to succeed only when a specific condition is met on a
444
+ * a record within the database that you are not directly updating.
445
+ *
446
+ * For example, you are updating a record and you want to ensure that another record exists and/or has a specific value before proceeding.
447
+ */
448
+ conditionCheck(keyCondition) {
449
+ return new ConditionCheckBuilder(this.tableName, keyCondition);
450
+ }
451
+ /**
452
+ * Performs a batch get operation to retrieve multiple items at once
453
+ *
454
+ * @param keys Array of primary keys to retrieve
455
+ * @returns A promise that resolves to the retrieved items
456
+ */
457
+ async batchGet(keys) {
458
+ const allItems = [];
459
+ const allUnprocessedKeys = [];
460
+ for (const chunk of chunkArray(keys, DDB_BATCH_GET_LIMIT)) {
461
+ const formattedKeys = chunk.map((key) => ({
462
+ [this.partitionKey]: key.pk,
463
+ ...this.sortKey ? { [this.sortKey]: key.sk } : {}
464
+ }));
465
+ const params = {
466
+ RequestItems: {
467
+ [this.tableName]: {
468
+ Keys: formattedKeys
469
+ }
470
+ }
471
+ };
472
+ try {
473
+ const result = await this.dynamoClient.batchGet(params);
474
+ if (result.Responses?.[this.tableName]) {
475
+ allItems.push(...result.Responses[this.tableName]);
476
+ }
477
+ const unprocessedKeysArray = result.UnprocessedKeys?.[this.tableName]?.Keys || [];
478
+ const unprocessedKeys = unprocessedKeysArray.map((key) => ({
479
+ pk: key[this.partitionKey],
480
+ sk: this.sortKey ? key[this.sortKey] : void 0
481
+ }));
482
+ if (unprocessedKeys.length > 0) {
483
+ allUnprocessedKeys.push(...unprocessedKeys);
484
+ }
485
+ } catch (error) {
486
+ console.error("Error in batch get operation:", error);
487
+ throw error;
488
+ }
489
+ }
490
+ return {
491
+ items: allItems,
492
+ unprocessedKeys: allUnprocessedKeys
493
+ };
494
+ }
495
+ /**
496
+ * Performs a batch write operation to put or delete multiple items at once
497
+ *
498
+ * @param operations Array of put or delete operations
499
+ * @returns A promise that resolves to any unprocessed operations
500
+ */
501
+ async batchWrite(operations) {
502
+ const allUnprocessedItems = [];
503
+ for (const chunk of chunkArray(operations, DDB_BATCH_WRITE_LIMIT)) {
504
+ const writeRequests = chunk.map((operation) => {
505
+ if (operation.type === "put") {
506
+ return {
507
+ PutRequest: {
508
+ Item: operation.item
509
+ }
510
+ };
511
+ }
512
+ return {
513
+ DeleteRequest: {
514
+ Key: this.createKeyForPrimaryIndex(operation.key)
515
+ }
516
+ };
517
+ });
518
+ const params = {
519
+ RequestItems: {
520
+ [this.tableName]: writeRequests
521
+ }
522
+ };
523
+ try {
524
+ const result = await this.dynamoClient.batchWrite(params);
525
+ const unprocessedRequestsArray = result.UnprocessedItems?.[this.tableName] || [];
526
+ if (unprocessedRequestsArray.length > 0) {
527
+ const unprocessedItems = unprocessedRequestsArray.map((request) => {
528
+ if (request?.PutRequest?.Item) {
529
+ return {
530
+ type: "put",
531
+ item: request.PutRequest.Item
532
+ };
533
+ }
534
+ if (request?.DeleteRequest?.Key) {
535
+ return {
536
+ type: "delete",
537
+ key: {
538
+ pk: request.DeleteRequest.Key[this.partitionKey],
539
+ sk: this.sortKey ? request.DeleteRequest.Key[this.sortKey] : void 0
540
+ }
541
+ };
542
+ }
543
+ throw new Error("Invalid unprocessed item format returned from DynamoDB");
544
+ });
545
+ allUnprocessedItems.push(...unprocessedItems);
546
+ }
547
+ } catch (error) {
548
+ console.error("Error in batch write operation:", error);
549
+ throw error;
550
+ }
551
+ }
552
+ return {
553
+ unprocessedItems: allUnprocessedItems
554
+ };
555
+ }
556
+ };
557
+
558
+ export { Table };