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