dyno-table 0.1.6 → 0.1.8

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -17
  3. package/dist/builders/condition-check-builder.cjs +394 -0
  4. package/dist/builders/condition-check-builder.cjs.map +1 -0
  5. package/dist/builders/condition-check-builder.js +392 -0
  6. package/dist/builders/condition-check-builder.js.map +1 -0
  7. package/dist/builders/delete-builder.cjs +422 -0
  8. package/dist/builders/delete-builder.cjs.map +1 -0
  9. package/dist/builders/delete-builder.js +420 -0
  10. package/dist/builders/delete-builder.js.map +1 -0
  11. package/dist/builders/paginator.cjs +199 -0
  12. package/dist/builders/paginator.cjs.map +1 -0
  13. package/dist/builders/paginator.js +197 -0
  14. package/dist/builders/paginator.js.map +1 -0
  15. package/dist/builders/put-builder.cjs +468 -0
  16. package/dist/builders/put-builder.cjs.map +1 -0
  17. package/dist/builders/put-builder.js +466 -0
  18. package/dist/builders/put-builder.js.map +1 -0
  19. package/dist/builders/query-builder.cjs +674 -0
  20. package/dist/builders/query-builder.cjs.map +1 -0
  21. package/dist/builders/query-builder.js +672 -0
  22. package/dist/builders/query-builder.js.map +1 -0
  23. package/dist/builders/transaction-builder.cjs +876 -0
  24. package/dist/builders/transaction-builder.cjs.map +1 -0
  25. package/dist/builders/transaction-builder.js +874 -0
  26. package/dist/builders/transaction-builder.js.map +1 -0
  27. package/dist/builders/update-builder.cjs +662 -0
  28. package/dist/builders/update-builder.cjs.map +1 -0
  29. package/dist/builders/update-builder.js +660 -0
  30. package/dist/builders/update-builder.js.map +1 -0
  31. package/dist/conditions.cjs +59 -0
  32. package/dist/conditions.cjs.map +1 -0
  33. package/dist/conditions.js +43 -0
  34. package/dist/conditions.js.map +1 -0
  35. package/dist/entity.cjs +169 -0
  36. package/dist/entity.cjs.map +1 -0
  37. package/dist/entity.js +165 -0
  38. package/dist/entity.js.map +1 -0
  39. package/dist/index.cjs +3333 -0
  40. package/dist/index.d.cts +2971 -0
  41. package/dist/index.d.ts +1504 -1383
  42. package/dist/index.js +391 -375
  43. package/dist/standard-schema.cjs +4 -0
  44. package/dist/standard-schema.cjs.map +1 -0
  45. package/dist/standard-schema.js +3 -0
  46. package/dist/standard-schema.js.map +1 -0
  47. package/dist/table.cjs +3265 -0
  48. package/dist/table.cjs.map +1 -0
  49. package/dist/table.js +3263 -0
  50. package/dist/table.js.map +1 -0
  51. package/dist/types.cjs +4 -0
  52. package/dist/types.cjs.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/utils/key-template.cjs +19 -0
  56. package/dist/utils/key-template.cjs.map +1 -0
  57. package/dist/utils/key-template.js +17 -0
  58. package/dist/utils/key-template.js.map +1 -0
  59. package/dist/utils/sort-key-template.cjs +19 -0
  60. package/dist/utils/sort-key-template.cjs.map +1 -0
  61. package/dist/utils/sort-key-template.js +17 -0
  62. package/dist/utils/sort-key-template.js.map +1 -0
  63. package/package.json +12 -7
@@ -0,0 +1,876 @@
1
+ 'use strict';
2
+
3
+ // src/expression.ts
4
+ var generateAttributeName = (params, attr) => {
5
+ for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
6
+ if (existingAttr === attr) {
7
+ return existingName;
8
+ }
9
+ }
10
+ const attrName = `#${Object.keys(params.expressionAttributeNames).length}`;
11
+ params.expressionAttributeNames[attrName] = attr;
12
+ return attrName;
13
+ };
14
+ var generateValueName = (params, value) => {
15
+ const valueName = `:${params.valueCounter.count++}`;
16
+ params.expressionAttributeValues[valueName] = value;
17
+ return valueName;
18
+ };
19
+ var validateCondition = (condition, requiresAttr = true, requiresValue = true) => {
20
+ if (requiresAttr && !condition.attr) {
21
+ throw new Error(`Attribute is required for ${condition.type} condition`);
22
+ }
23
+ if (requiresValue && condition.value === void 0) {
24
+ throw new Error(`Value is required for ${condition.type} condition`);
25
+ }
26
+ };
27
+ var buildComparisonExpression = (condition, operator, params) => {
28
+ validateCondition(condition);
29
+ if (!condition.attr) {
30
+ throw new Error(`Attribute is required for ${condition.type} condition`);
31
+ }
32
+ const attrName = generateAttributeName(params, condition.attr);
33
+ const valueName = generateValueName(params, condition.value);
34
+ return `${attrName} ${operator} ${valueName}`;
35
+ };
36
+ var buildBetweenExpression = (condition, params) => {
37
+ validateCondition(condition);
38
+ if (!condition.attr) {
39
+ throw new Error(`Attribute is required for ${condition.type} condition`);
40
+ }
41
+ if (!Array.isArray(condition.value) || condition.value.length !== 2) {
42
+ throw new Error("Between condition requires an array of two values");
43
+ }
44
+ const attrName = generateAttributeName(params, condition.attr);
45
+ const lowerName = generateValueName(params, condition.value[0]);
46
+ const upperName = generateValueName(params, condition.value[1]);
47
+ return `${attrName} BETWEEN ${lowerName} AND ${upperName}`;
48
+ };
49
+ var buildFunctionExpression = (functionName, condition, params) => {
50
+ validateCondition(condition);
51
+ if (!condition.attr) {
52
+ throw new Error(`Attribute is required for ${condition.type} condition`);
53
+ }
54
+ const attrName = generateAttributeName(params, condition.attr);
55
+ const valueName = generateValueName(params, condition.value);
56
+ return `${functionName}(${attrName}, ${valueName})`;
57
+ };
58
+ var buildAttributeFunction = (functionName, condition, params) => {
59
+ validateCondition(condition, true, false);
60
+ if (!condition.attr) {
61
+ throw new Error(`Attribute is required for ${condition.type} condition`);
62
+ }
63
+ const attrName = generateAttributeName(params, condition.attr);
64
+ return `${functionName}(${attrName})`;
65
+ };
66
+ var buildLogicalExpression = (operator, conditions, params) => {
67
+ if (!conditions || conditions.length === 0) {
68
+ throw new Error(`At least one condition is required for ${operator} expression`);
69
+ }
70
+ const expressions = conditions.map((c) => buildExpression(c, params));
71
+ return `(${expressions.join(` ${operator} `)})`;
72
+ };
73
+ var buildExpression = (condition, params) => {
74
+ if (!condition) return "";
75
+ try {
76
+ const expressionBuilders = {
77
+ eq: () => buildComparisonExpression(condition, "=", params),
78
+ ne: () => buildComparisonExpression(condition, "<>", params),
79
+ lt: () => buildComparisonExpression(condition, "<", params),
80
+ lte: () => buildComparisonExpression(condition, "<=", params),
81
+ gt: () => buildComparisonExpression(condition, ">", params),
82
+ gte: () => buildComparisonExpression(condition, ">=", params),
83
+ between: () => buildBetweenExpression(condition, params),
84
+ beginsWith: () => buildFunctionExpression("begins_with", condition, params),
85
+ contains: () => buildFunctionExpression("contains", condition, params),
86
+ attributeExists: () => buildAttributeFunction("attribute_exists", condition, params),
87
+ attributeNotExists: () => buildAttributeFunction("attribute_not_exists", condition, params),
88
+ and: () => {
89
+ if (!condition.conditions) {
90
+ throw new Error("Conditions array is required for AND operator");
91
+ }
92
+ return buildLogicalExpression("AND", condition.conditions, params);
93
+ },
94
+ or: () => {
95
+ if (!condition.conditions) {
96
+ throw new Error("Conditions array is required for OR operator");
97
+ }
98
+ return buildLogicalExpression("OR", condition.conditions, params);
99
+ },
100
+ not: () => {
101
+ if (!condition.condition) {
102
+ throw new Error("Condition is required for NOT operator");
103
+ }
104
+ return `NOT (${buildExpression(condition.condition, params)})`;
105
+ }
106
+ };
107
+ const builder = expressionBuilders[condition.type];
108
+ if (!builder) {
109
+ throw new Error(`Unknown condition type: ${condition.type}`);
110
+ }
111
+ return builder();
112
+ } catch (error) {
113
+ if (error instanceof Error) {
114
+ console.error(`Error building expression for condition type ${condition.type}:`, error.message);
115
+ } else {
116
+ console.error(`Error building expression for condition type ${condition.type}:`, error);
117
+ }
118
+ throw error;
119
+ }
120
+ };
121
+ var prepareExpressionParams = (condition) => {
122
+ if (!condition) return {};
123
+ const params = {
124
+ expressionAttributeNames: {},
125
+ expressionAttributeValues: {},
126
+ valueCounter: { count: 0 }
127
+ };
128
+ const expression = buildExpression(condition, params);
129
+ return {
130
+ expression,
131
+ names: Object.keys(params.expressionAttributeNames).length > 0 ? params.expressionAttributeNames : void 0,
132
+ values: Object.keys(params.expressionAttributeValues).length > 0 ? params.expressionAttributeValues : void 0
133
+ };
134
+ };
135
+
136
+ // src/utils/debug-expression.ts
137
+ function debugCommand(command) {
138
+ const result = {};
139
+ function replaceAliases(expressionString) {
140
+ if (!expressionString) {
141
+ return expressionString;
142
+ }
143
+ let replacedString = expressionString;
144
+ for (const alias in command.expressionAttributeNames) {
145
+ const attributeName = command.expressionAttributeNames[alias];
146
+ const regex = new RegExp(alias, "g");
147
+ replacedString = replacedString.replace(regex, attributeName);
148
+ }
149
+ for (const alias in command.expressionAttributeValues) {
150
+ let attributeValue = command.expressionAttributeValues[alias];
151
+ if (attributeValue instanceof Set) {
152
+ const array = Array.from(attributeValue);
153
+ attributeValue = `Set(${array.length}){${array.map((v) => JSON.stringify(v)).join(", ")}}`;
154
+ } else {
155
+ attributeValue = JSON.stringify(attributeValue);
156
+ }
157
+ const regex = new RegExp(alias, "g");
158
+ replacedString = replacedString.replace(regex, attributeValue);
159
+ }
160
+ return replacedString;
161
+ }
162
+ if (command.updateExpression) {
163
+ result.updateExpression = replaceAliases(command.updateExpression);
164
+ }
165
+ if (command.conditionExpression) {
166
+ result.conditionExpression = replaceAliases(command.conditionExpression);
167
+ }
168
+ if (command.filterExpression) {
169
+ result.filterExpression = replaceAliases(command.filterExpression);
170
+ }
171
+ if (command.keyConditionExpression) {
172
+ result.keyConditionExpression = replaceAliases(command.keyConditionExpression);
173
+ }
174
+ if (command.projectionExpression) {
175
+ result.projectionExpression = replaceAliases(command.projectionExpression);
176
+ }
177
+ return {
178
+ raw: command,
179
+ readable: result
180
+ };
181
+ }
182
+
183
+ // src/utils/debug-transaction.ts
184
+ function debugTransactionItem(item) {
185
+ const result = {
186
+ type: item.type,
187
+ tableName: item.params.tableName
188
+ };
189
+ if ("key" in item.params) {
190
+ result.key = item.params.key;
191
+ }
192
+ if (item.type === "Put") {
193
+ result.item = item.params.item;
194
+ }
195
+ switch (item.type) {
196
+ case "Put":
197
+ case "Delete":
198
+ case "ConditionCheck":
199
+ result.readable = debugCommand(item.params).readable;
200
+ break;
201
+ case "Update":
202
+ result.readable = debugCommand(item.params).readable;
203
+ break;
204
+ }
205
+ return result;
206
+ }
207
+ function debugTransaction(items) {
208
+ return items.map((item) => debugTransactionItem(item));
209
+ }
210
+
211
+ // src/builders/transaction-builder.ts
212
+ var TransactionBuilder = class {
213
+ items = [];
214
+ options = {};
215
+ indexConfig;
216
+ executor;
217
+ constructor(executor, indexConfig) {
218
+ this.executor = executor;
219
+ this.indexConfig = indexConfig;
220
+ }
221
+ /**
222
+ * Checks if an item with the same primary key already exists in the transaction
223
+ * @private
224
+ */
225
+ checkForDuplicateItem(tableName, newItem) {
226
+ const pkName = this.indexConfig.partitionKey;
227
+ const skName = this.indexConfig.sortKey || "";
228
+ const pkValue = newItem[pkName];
229
+ const skValue = skName ? newItem[skName] : void 0;
230
+ if (!pkValue) {
231
+ throw new Error(`Primary key value for '${pkName}' is missing`);
232
+ }
233
+ const duplicateItem = this.items.find((item) => {
234
+ let itemKey;
235
+ let itemTableName;
236
+ switch (item.type) {
237
+ case "Put":
238
+ itemTableName = item.params.tableName;
239
+ itemKey = item.params.item;
240
+ break;
241
+ case "Update":
242
+ case "Delete":
243
+ case "ConditionCheck":
244
+ itemTableName = item.params.tableName;
245
+ itemKey = item.params.key;
246
+ break;
247
+ }
248
+ if (itemTableName === tableName && itemKey) {
249
+ const itemPkValue = itemKey[pkName];
250
+ const itemSkValue = skName ? itemKey[skName] : void 0;
251
+ if (itemPkValue === pkValue) {
252
+ if (skValue === void 0 && itemSkValue === void 0) {
253
+ return true;
254
+ }
255
+ if (skValue !== void 0 && itemSkValue !== void 0 && skValue === itemSkValue) {
256
+ return true;
257
+ }
258
+ }
259
+ }
260
+ return false;
261
+ });
262
+ if (duplicateItem) {
263
+ throw new Error(
264
+ `Duplicate item detected in transaction: Table=${tableName}, ${pkName}=${String(pkValue)}, ${skName}=${skValue !== void 0 ? String(skValue) : "undefined"}. DynamoDB transactions do not allow multiple operations on the same item.`
265
+ );
266
+ }
267
+ }
268
+ /**
269
+ * Adds a put operation to the transaction.
270
+ * Use this method when you need to:
271
+ * - Insert new items as part of a transaction
272
+ * - Replace existing items atomically
273
+ * - Ensure items meet certain conditions before insertion
274
+ *
275
+ * The method automatically checks for duplicate items within the transaction
276
+ * to prevent multiple operations on the same item.
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * // Simple put operation
281
+ * transaction.put('orders', {
282
+ * orderId: '123',
283
+ * status: 'PENDING',
284
+ * amount: 100
285
+ * });
286
+ *
287
+ * // Conditional put operation
288
+ * transaction.put(
289
+ * 'inventory',
290
+ * { productId: 'ABC', quantity: 50 },
291
+ * op => op.attributeNotExists('productId')
292
+ * );
293
+ *
294
+ * // Put with complex condition
295
+ * transaction.put(
296
+ * 'users',
297
+ * { userId: '123', status: 'ACTIVE' },
298
+ * op => op.and([
299
+ * op.attributeNotExists('userId'),
300
+ * op.beginsWith('status', 'ACTIVE')
301
+ * ])
302
+ * );
303
+ * ```
304
+ *
305
+ * @param tableName - The name of the DynamoDB table
306
+ * @param item - The item to put into the table
307
+ * @param condition - Optional condition that must be satisfied
308
+ * @returns The transaction builder for method chaining
309
+ * @throws {Error} If a duplicate item is detected in the transaction
310
+ */
311
+ put(tableName, item, condition) {
312
+ this.checkForDuplicateItem(tableName, item);
313
+ const transactionItem = {
314
+ type: "Put",
315
+ params: {
316
+ tableName,
317
+ item
318
+ }
319
+ };
320
+ if (condition) {
321
+ const { expression, names, values } = prepareExpressionParams(condition);
322
+ transactionItem.params.conditionExpression = expression;
323
+ transactionItem.params.expressionAttributeNames = names;
324
+ transactionItem.params.expressionAttributeValues = values;
325
+ }
326
+ this.items.push(transactionItem);
327
+ return this;
328
+ }
329
+ /**
330
+ * Adds a pre-configured put operation to the transaction.
331
+ * Use this method when you need to:
332
+ * - Reuse put commands from PutBuilder
333
+ * - Add complex put operations with pre-configured parameters
334
+ * - Integrate with existing put command configurations
335
+ *
336
+ * This method is particularly useful when working with PutBuilder
337
+ * to maintain consistency in put operations across your application.
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * // Create a put command with PutBuilder
342
+ * const putCommand = new PutBuilder(executor, newItem, 'users')
343
+ * .condition(op => op.attributeNotExists('userId'))
344
+ * .toDynamoCommand();
345
+ *
346
+ * // Add the command to the transaction
347
+ * transaction.putWithCommand(putCommand);
348
+ * ```
349
+ *
350
+ * @param command - The complete put command configuration
351
+ * @returns The transaction builder for method chaining
352
+ * @throws {Error} If a duplicate item is detected in the transaction
353
+ * @see PutBuilder for creating put commands
354
+ */
355
+ putWithCommand(command) {
356
+ this.checkForDuplicateItem(command.tableName, command.item);
357
+ const transactionItem = {
358
+ type: "Put",
359
+ params: command
360
+ };
361
+ this.items.push(transactionItem);
362
+ return this;
363
+ }
364
+ /**
365
+ * Adds a delete operation to the transaction.
366
+ * Use this method when you need to:
367
+ * - Remove items as part of a transaction
368
+ * - Conditionally delete items
369
+ * - Ensure items exist before deletion
370
+ *
371
+ * The method automatically checks for duplicate items within the transaction
372
+ * to prevent multiple operations on the same item.
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * // Simple delete operation
377
+ * transaction.delete('orders', {
378
+ * pk: 'ORDER#123',
379
+ * sk: 'METADATA'
380
+ * });
381
+ *
382
+ * // Conditional delete operation
383
+ * transaction.delete(
384
+ * 'users',
385
+ * { pk: 'USER#123' },
386
+ * op => op.eq('status', 'INACTIVE')
387
+ * );
388
+ *
389
+ * // Delete with complex condition
390
+ * transaction.delete(
391
+ * 'products',
392
+ * { pk: 'PROD#ABC' },
393
+ * op => op.and([
394
+ * op.eq('status', 'DRAFT'),
395
+ * op.lt('version', 5)
396
+ * ])
397
+ * );
398
+ * ```
399
+ *
400
+ * @param tableName - The name of the DynamoDB table
401
+ * @param key - The primary key of the item to delete
402
+ * @param condition - Optional condition that must be satisfied
403
+ * @returns The transaction builder for method chaining
404
+ * @throws {Error} If a duplicate item is detected in the transaction
405
+ */
406
+ delete(tableName, key, condition) {
407
+ this.checkForDuplicateItem(tableName, key);
408
+ const transactionItem = {
409
+ type: "Delete",
410
+ params: {
411
+ tableName,
412
+ key: {
413
+ pk: key.pk,
414
+ sk: key.sk
415
+ }
416
+ }
417
+ };
418
+ if (condition) {
419
+ const { expression, names, values } = prepareExpressionParams(condition);
420
+ transactionItem.params.conditionExpression = expression;
421
+ transactionItem.params.expressionAttributeNames = names;
422
+ transactionItem.params.expressionAttributeValues = values;
423
+ }
424
+ this.items.push(transactionItem);
425
+ return this;
426
+ }
427
+ /**
428
+ * Adds a pre-configured delete operation to the transaction.
429
+ * Use this method when you need to:
430
+ * - Reuse delete commands from DeleteBuilder
431
+ * - Add complex delete operations with pre-configured parameters
432
+ * - Integrate with existing delete command configurations
433
+ *
434
+ * This method is particularly useful when working with DeleteBuilder
435
+ * to maintain consistency in delete operations across your application.
436
+ *
437
+ * @example
438
+ * ```typescript
439
+ * // Create a delete command with DeleteBuilder
440
+ * const deleteCommand = new DeleteBuilder(executor, 'users', { pk: 'USER#123' })
441
+ * .condition(op => op.and([
442
+ * op.attributeExists('pk'),
443
+ * op.eq('status', 'INACTIVE')
444
+ * ]))
445
+ * .toDynamoCommand();
446
+ *
447
+ * // Add the command to the transaction
448
+ * transaction.deleteWithCommand(deleteCommand);
449
+ * ```
450
+ *
451
+ * @param command - The complete delete command configuration
452
+ * @returns The transaction builder for method chaining
453
+ * @throws {Error} If a duplicate item is detected in the transaction
454
+ * @see DeleteBuilder for creating delete commands
455
+ */
456
+ deleteWithCommand(command) {
457
+ this.checkForDuplicateItem(command.tableName, command.key);
458
+ const transactionItem = {
459
+ type: "Delete",
460
+ params: command
461
+ };
462
+ this.items.push(transactionItem);
463
+ return this;
464
+ }
465
+ /**
466
+ * Adds an update operation to the transaction.
467
+ * Use this method when you need to:
468
+ * - Modify existing items as part of a transaction
469
+ * - Update multiple attributes atomically
470
+ * - Apply conditional updates
471
+ * - Perform complex attribute manipulations
472
+ *
473
+ * The method supports all DynamoDB update expressions:
474
+ * - SET: Modify or add attributes
475
+ * - REMOVE: Delete attributes
476
+ * - ADD: Update numbers and sets
477
+ * - DELETE: Remove elements from a set
478
+ *
479
+ * @example
480
+ * ```typescript
481
+ * // Simple update
482
+ * transaction.update(
483
+ * 'orders',
484
+ * { pk: 'ORDER#123' },
485
+ * 'SET #status = :status',
486
+ * { '#status': 'status' },
487
+ * { ':status': 'PROCESSING' }
488
+ * );
489
+ *
490
+ * // Complex update with multiple operations
491
+ * transaction.update(
492
+ * 'products',
493
+ * { pk: 'PROD#ABC' },
494
+ * 'SET #qty = #qty - :amount, #status = :status REMOVE #oldAttr',
495
+ * { '#qty': 'quantity', '#status': 'status', '#oldAttr': 'deprecated_field' },
496
+ * { ':amount': 1, ':status': 'LOW_STOCK' }
497
+ * );
498
+ *
499
+ * // Conditional update
500
+ * transaction.update(
501
+ * 'users',
502
+ * { pk: 'USER#123' },
503
+ * 'SET #lastLogin = :now',
504
+ * { '#lastLogin': 'lastLoginDate' },
505
+ * { ':now': new Date().toISOString() },
506
+ * op => op.attributeExists('pk')
507
+ * );
508
+ * ```
509
+ *
510
+ * @param tableName - The name of the DynamoDB table
511
+ * @param key - The primary key of the item to update
512
+ * @param updateExpression - The update expression (SET, REMOVE, ADD, DELETE)
513
+ * @param expressionAttributeNames - Map of attribute name placeholders to actual names
514
+ * @param expressionAttributeValues - Map of value placeholders to actual values
515
+ * @param condition - Optional condition that must be satisfied
516
+ * @returns The transaction builder for method chaining
517
+ * @throws {Error} If a duplicate item is detected in the transaction
518
+ */
519
+ update(tableName, key, updateExpression, expressionAttributeNames, expressionAttributeValues, condition) {
520
+ this.checkForDuplicateItem(tableName, key);
521
+ const transactionItem = {
522
+ type: "Update",
523
+ params: {
524
+ tableName,
525
+ key: {
526
+ pk: key.pk,
527
+ sk: key.sk
528
+ },
529
+ updateExpression,
530
+ expressionAttributeNames,
531
+ expressionAttributeValues
532
+ }
533
+ };
534
+ if (condition) {
535
+ const { expression, names, values } = prepareExpressionParams(condition);
536
+ transactionItem.params.conditionExpression = expression;
537
+ transactionItem.params.expressionAttributeNames = {
538
+ ...transactionItem.params.expressionAttributeNames,
539
+ ...names
540
+ };
541
+ transactionItem.params.expressionAttributeValues = {
542
+ ...transactionItem.params.expressionAttributeValues,
543
+ ...values
544
+ };
545
+ }
546
+ this.items.push(transactionItem);
547
+ return this;
548
+ }
549
+ /**
550
+ * Adds a pre-configured update operation to the transaction.
551
+ * Use this method when you need to:
552
+ * - Reuse update commands from UpdateBuilder
553
+ * - Add complex update operations with pre-configured parameters
554
+ * - Integrate with existing update command configurations
555
+ *
556
+ * This method is particularly useful when working with UpdateBuilder
557
+ * to maintain consistency in update operations across your application.
558
+ *
559
+ * @example
560
+ * ```typescript
561
+ * // Create an update command with UpdateBuilder
562
+ * const updateCommand = new UpdateBuilder(executor, 'inventory', { pk: 'PROD#ABC' })
563
+ * .set('quantity', ':qty')
564
+ * .set('lastUpdated', ':now')
565
+ * .values({
566
+ * ':qty': 100,
567
+ * ':now': new Date().toISOString()
568
+ * })
569
+ * .condition(op => op.gt('quantity', 0))
570
+ * .toDynamoCommand();
571
+ *
572
+ * // Add the command to the transaction
573
+ * transaction.updateWithCommand(updateCommand);
574
+ * ```
575
+ *
576
+ * @param command - The complete update command configuration
577
+ * @returns The transaction builder for method chaining
578
+ * @throws {Error} If a duplicate item is detected in the transaction
579
+ * @see UpdateBuilder for creating update commands
580
+ */
581
+ updateWithCommand(command) {
582
+ this.checkForDuplicateItem(command.tableName, command.key);
583
+ const transactionItem = {
584
+ type: "Update",
585
+ params: command
586
+ };
587
+ this.items.push(transactionItem);
588
+ return this;
589
+ }
590
+ /**
591
+ * Adds a condition check operation to the transaction.
592
+ * Use this method when you need to:
593
+ * - Validate item state without modifying it
594
+ * - Ensure data consistency across tables
595
+ * - Implement complex business rules
596
+ * - Verify preconditions for other operations
597
+ *
598
+ * Condition checks are particularly useful for:
599
+ * - Implementing optimistic locking
600
+ * - Ensuring referential integrity
601
+ * - Validating business rules atomically
602
+ *
603
+ * @example
604
+ * ```typescript
605
+ * // Check if order is in correct state
606
+ * transaction.conditionCheck(
607
+ * 'orders',
608
+ * { pk: 'ORDER#123' },
609
+ * op => op.eq('status', 'PENDING')
610
+ * );
611
+ *
612
+ * // Complex condition check
613
+ * transaction.conditionCheck(
614
+ * 'inventory',
615
+ * { pk: 'PROD#ABC' },
616
+ * op => op.and([
617
+ * op.gt('quantity', 0),
618
+ * op.eq('status', 'ACTIVE'),
619
+ * op.attributeExists('lastRestockDate')
620
+ * ])
621
+ * );
622
+ *
623
+ * // Check with multiple attributes
624
+ * transaction.conditionCheck(
625
+ * 'users',
626
+ * { pk: 'USER#123' },
627
+ * op => op.or([
628
+ * op.eq('status', 'PREMIUM'),
629
+ * op.gte('credits', 100)
630
+ * ])
631
+ * );
632
+ * ```
633
+ *
634
+ * @param tableName - The name of the DynamoDB table
635
+ * @param key - The primary key of the item to check
636
+ * @param condition - The condition that must be satisfied
637
+ * @returns The transaction builder for method chaining
638
+ * @throws {Error} If a duplicate item is detected in the transaction
639
+ * @throws {Error} If condition expression generation fails
640
+ */
641
+ conditionCheck(tableName, key, condition) {
642
+ this.checkForDuplicateItem(tableName, key);
643
+ const { expression, names, values } = prepareExpressionParams(condition);
644
+ if (!expression) {
645
+ throw new Error("Failed to generate condition expression");
646
+ }
647
+ const transactionItem = {
648
+ type: "ConditionCheck",
649
+ params: {
650
+ tableName,
651
+ key: {
652
+ pk: key.pk,
653
+ sk: key.sk
654
+ },
655
+ conditionExpression: expression,
656
+ expressionAttributeNames: names,
657
+ expressionAttributeValues: values
658
+ }
659
+ };
660
+ this.items.push(transactionItem);
661
+ return this;
662
+ }
663
+ /**
664
+ * Adds a pre-configured condition check operation to the transaction.
665
+ * Use this method when you need to:
666
+ * - Reuse condition checks from ConditionCheckBuilder
667
+ * - Add complex condition checks with pre-configured parameters
668
+ * - Integrate with existing condition check configurations
669
+ *
670
+ * This method is particularly useful when working with ConditionCheckBuilder
671
+ * to maintain consistency in condition checks across your application.
672
+ *
673
+ * @example
674
+ * ```typescript
675
+ * // Create a condition check with ConditionCheckBuilder
676
+ * const checkCommand = new ConditionCheckBuilder('inventory', { pk: 'PROD#ABC' })
677
+ * .condition(op => op.and([
678
+ * op.between('quantity', 10, 100),
679
+ * op.beginsWith('category', 'ELECTRONICS'),
680
+ * op.attributeExists('lastAuditDate')
681
+ * ]))
682
+ * .toDynamoCommand();
683
+ *
684
+ * // Add the command to the transaction
685
+ * transaction.conditionCheckWithCommand(checkCommand);
686
+ * ```
687
+ *
688
+ * @param command - The complete condition check command configuration
689
+ * @returns The transaction builder for method chaining
690
+ * @throws {Error} If a duplicate item is detected in the transaction
691
+ * @see ConditionCheckBuilder for creating condition check commands
692
+ */
693
+ conditionCheckWithCommand(command) {
694
+ this.checkForDuplicateItem(command.tableName, command.key);
695
+ const transactionItem = {
696
+ type: "ConditionCheck",
697
+ params: command
698
+ };
699
+ this.items.push(transactionItem);
700
+ return this;
701
+ }
702
+ /**
703
+ * Sets options for the transaction execution.
704
+ * Use this method when you need to:
705
+ * - Enable idempotent transactions
706
+ * - Track consumed capacity
707
+ * - Monitor item collection metrics
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * // Enable idempotency and capacity tracking
712
+ * transaction.withOptions({
713
+ * clientRequestToken: 'unique-request-id-123',
714
+ * returnConsumedCapacity: 'TOTAL'
715
+ * });
716
+ *
717
+ * // Track item collection metrics
718
+ * transaction.withOptions({
719
+ * returnItemCollectionMetrics: 'SIZE'
720
+ * });
721
+ * ```
722
+ *
723
+ * Note: ClientRequestToken can be used to make transactions idempotent,
724
+ * ensuring the same transaction is not executed multiple times.
725
+ *
726
+ * @param options - Configuration options for the transaction
727
+ * @returns The transaction builder for method chaining
728
+ */
729
+ withOptions(options) {
730
+ this.options = { ...this.options, ...options };
731
+ return this;
732
+ }
733
+ /**
734
+ * Gets a human-readable representation of the transaction items.
735
+ * Use this method when you need to:
736
+ * - Debug complex transactions
737
+ * - Verify operation parameters
738
+ * - Log transaction details
739
+ * - Troubleshoot condition expressions
740
+ *
741
+ * The method resolves all expression placeholders with their actual values,
742
+ * making it easier to understand the transaction's operations.
743
+ *
744
+ * @example
745
+ * ```typescript
746
+ * // Add multiple operations
747
+ * transaction
748
+ * .put('orders', { orderId: '123', status: 'PENDING' })
749
+ * .update('inventory',
750
+ * { productId: 'ABC' },
751
+ * 'SET quantity = quantity - :amount',
752
+ * undefined,
753
+ * { ':amount': 1 }
754
+ * );
755
+ *
756
+ * // Debug the transaction
757
+ * const debugInfo = transaction.debug();
758
+ * console.log('Transaction operations:', debugInfo);
759
+ * ```
760
+ *
761
+ * @returns An array of readable representations of the transaction items
762
+ */
763
+ debug() {
764
+ return debugTransaction(this.items);
765
+ }
766
+ /**
767
+ * Executes all operations in the transaction atomically.
768
+ * Use this method when you need to:
769
+ * - Perform multiple operations atomically
770
+ * - Ensure all-or-nothing execution
771
+ * - Maintain data consistency across operations
772
+ *
773
+ * The transaction will only succeed if all operations succeed.
774
+ * If any operation fails, the entire transaction is rolled back.
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * try {
779
+ * // Build and execute transaction
780
+ * await transaction
781
+ * .put('orders', newOrder)
782
+ * .update('inventory',
783
+ * { productId: 'ABC' },
784
+ * 'SET quantity = quantity - :qty',
785
+ * undefined,
786
+ * { ':qty': 1 }
787
+ * )
788
+ * .conditionCheck('products',
789
+ * { productId: 'ABC' },
790
+ * op => op.eq('status', 'ACTIVE')
791
+ * )
792
+ * .execute();
793
+ *
794
+ * console.log('Transaction completed successfully');
795
+ * } catch (error) {
796
+ * // Handle transaction failure
797
+ * console.error('Transaction failed:', error);
798
+ * }
799
+ * ```
800
+ *
801
+ * @throws {Error} If no transaction items are specified
802
+ * @throws {Error} If any operation in the transaction fails
803
+ * @returns A promise that resolves when the transaction completes
804
+ */
805
+ async execute() {
806
+ if (this.items.length === 0) {
807
+ throw new Error("No transaction items specified");
808
+ }
809
+ const transactItems = this.items.map((item) => {
810
+ switch (item.type) {
811
+ case "Put":
812
+ return {
813
+ Put: {
814
+ TableName: item.params.tableName,
815
+ Item: item.params.item,
816
+ ConditionExpression: item.params.conditionExpression,
817
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
818
+ ExpressionAttributeValues: item.params.expressionAttributeValues
819
+ }
820
+ };
821
+ case "Delete":
822
+ return {
823
+ Delete: {
824
+ TableName: item.params.tableName,
825
+ Key: item.params.key,
826
+ ConditionExpression: item.params.conditionExpression,
827
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
828
+ ExpressionAttributeValues: item.params.expressionAttributeValues
829
+ }
830
+ };
831
+ case "Update":
832
+ return {
833
+ Update: {
834
+ TableName: item.params.tableName,
835
+ Key: item.params.key,
836
+ UpdateExpression: item.params.updateExpression,
837
+ ConditionExpression: item.params.conditionExpression,
838
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
839
+ ExpressionAttributeValues: item.params.expressionAttributeValues
840
+ }
841
+ };
842
+ case "ConditionCheck":
843
+ return {
844
+ ConditionCheck: {
845
+ TableName: item.params.tableName,
846
+ Key: item.params.key,
847
+ ConditionExpression: item.params.conditionExpression,
848
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
849
+ ExpressionAttributeValues: item.params.expressionAttributeValues
850
+ }
851
+ };
852
+ default: {
853
+ const exhaustiveCheck = item;
854
+ throw new Error(`Unsupported transaction item type: ${String(exhaustiveCheck)}`);
855
+ }
856
+ }
857
+ });
858
+ const params = {
859
+ TransactItems: transactItems,
860
+ ClientRequestToken: this.options.clientRequestToken,
861
+ ReturnConsumedCapacity: this.options.returnConsumedCapacity,
862
+ ReturnItemCollectionMetrics: this.options.returnItemCollectionMetrics
863
+ };
864
+ try {
865
+ await this.executor(params);
866
+ } catch (error) {
867
+ console.log(this.debug());
868
+ console.error("Error executing transaction:", error);
869
+ throw error;
870
+ }
871
+ }
872
+ };
873
+
874
+ exports.TransactionBuilder = TransactionBuilder;
875
+ //# sourceMappingURL=transaction-builder.cjs.map
876
+ //# sourceMappingURL=transaction-builder.cjs.map