dyno-table 1.0.0-alpha.1 → 1.1.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 +752 -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 +918 -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 +916 -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 +211 -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 +207 -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.cjs → table.cjs} +108 -175
  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.js → table.js} +107 -127
  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,918 @@
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
+ createKeyForPrimaryIndex(key) {
269
+ const keyCondition = {
270
+ [this.indexConfig.partitionKey]: key.pk
271
+ };
272
+ if (this.indexConfig.sortKey) {
273
+ if (key.sk === void 0) {
274
+ throw new Error("Sort key is required for delete operation");
275
+ }
276
+ keyCondition[this.indexConfig.sortKey] = key.sk;
277
+ }
278
+ return keyCondition;
279
+ }
280
+ /**
281
+ * Adds a put operation to the transaction.
282
+ * Use this method when you need to:
283
+ * - Insert new items as part of a transaction
284
+ * - Replace existing items atomically
285
+ * - Ensure items meet certain conditions before insertion
286
+ *
287
+ * The method automatically checks for duplicate items within the transaction
288
+ * to prevent multiple operations on the same item.
289
+ *
290
+ * @example
291
+ * ```typescript
292
+ * // Simple put operation
293
+ * transaction.put('orders', {
294
+ * orderId: '123',
295
+ * status: 'PENDING',
296
+ * amount: 100
297
+ * });
298
+ *
299
+ * // Conditional put operation
300
+ * transaction.put(
301
+ * 'inventory',
302
+ * { productId: 'ABC', quantity: 50 },
303
+ * op => op.attributeNotExists('productId')
304
+ * );
305
+ *
306
+ * // Put with complex condition
307
+ * transaction.put(
308
+ * 'users',
309
+ * { userId: '123', status: 'ACTIVE' },
310
+ * op => op.and([
311
+ * op.attributeNotExists('userId'),
312
+ * op.beginsWith('status', 'ACTIVE')
313
+ * ])
314
+ * );
315
+ * ```
316
+ *
317
+ * @param tableName - The name of the DynamoDB table
318
+ * @param item - The item to put into the table
319
+ * @param condition - Optional condition that must be satisfied
320
+ * @returns The transaction builder for method chaining
321
+ * @throws {Error} If a duplicate item is detected in the transaction
322
+ */
323
+ put(tableName, item, condition) {
324
+ this.checkForDuplicateItem(tableName, item);
325
+ const transactionItem = {
326
+ type: "Put",
327
+ params: {
328
+ tableName,
329
+ item
330
+ }
331
+ };
332
+ if (condition) {
333
+ const { expression, names, values } = prepareExpressionParams(condition);
334
+ transactionItem.params.conditionExpression = expression;
335
+ transactionItem.params.expressionAttributeNames = names;
336
+ transactionItem.params.expressionAttributeValues = values;
337
+ }
338
+ this.items.push(transactionItem);
339
+ return this;
340
+ }
341
+ /**
342
+ * Adds a pre-configured put operation to the transaction.
343
+ * Use this method when you need to:
344
+ * - Reuse put commands from PutBuilder
345
+ * - Add complex put operations with pre-configured parameters
346
+ * - Integrate with existing put command configurations
347
+ *
348
+ * This method is particularly useful when working with PutBuilder
349
+ * to maintain consistency in put operations across your application.
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * // Create a put command with PutBuilder
354
+ * const putCommand = new PutBuilder(executor, newItem, 'users')
355
+ * .condition(op => op.attributeNotExists('userId'))
356
+ * .toDynamoCommand();
357
+ *
358
+ * // Add the command to the transaction
359
+ * transaction.putWithCommand(putCommand);
360
+ * ```
361
+ *
362
+ * @param command - The complete put command configuration
363
+ * @returns The transaction builder for method chaining
364
+ * @throws {Error} If a duplicate item is detected in the transaction
365
+ * @see PutBuilder for creating put commands
366
+ */
367
+ putWithCommand(command) {
368
+ this.checkForDuplicateItem(command.tableName, command.item);
369
+ const transactionItem = {
370
+ type: "Put",
371
+ params: command
372
+ };
373
+ this.items.push(transactionItem);
374
+ return this;
375
+ }
376
+ /**
377
+ * Adds a delete operation to the transaction.
378
+ * Use this method when you need to:
379
+ * - Remove items as part of a transaction
380
+ * - Conditionally delete items
381
+ * - Ensure items exist before deletion
382
+ *
383
+ * The method automatically checks for duplicate items within the transaction
384
+ * to prevent multiple operations on the same item.
385
+ *
386
+ * @example
387
+ * ```typescript
388
+ * // Simple delete operation
389
+ * transaction.delete('orders', {
390
+ * pk: 'ORDER#123',
391
+ * sk: 'METADATA'
392
+ * });
393
+ *
394
+ * // Conditional delete operation
395
+ * transaction.delete(
396
+ * 'users',
397
+ * { pk: 'USER#123' },
398
+ * op => op.eq('status', 'INACTIVE')
399
+ * );
400
+ *
401
+ * // Delete with complex condition
402
+ * transaction.delete(
403
+ * 'products',
404
+ * { pk: 'PROD#ABC' },
405
+ * op => op.and([
406
+ * op.eq('status', 'DRAFT'),
407
+ * op.lt('version', 5)
408
+ * ])
409
+ * );
410
+ * ```
411
+ *
412
+ * @param tableName - The name of the DynamoDB table
413
+ * @param key - The primary key of the item to delete
414
+ * @param condition - Optional condition that must be satisfied
415
+ * @returns The transaction builder for method chaining
416
+ * @throws {Error} If a duplicate item is detected in the transaction
417
+ */
418
+ delete(tableName, key, condition) {
419
+ const keyCondition = this.createKeyForPrimaryIndex(key);
420
+ this.checkForDuplicateItem(tableName, keyCondition);
421
+ const transactionItem = {
422
+ type: "Delete",
423
+ params: {
424
+ tableName,
425
+ key: keyCondition
426
+ }
427
+ };
428
+ if (condition) {
429
+ const { expression, names, values } = prepareExpressionParams(condition);
430
+ transactionItem.params.conditionExpression = expression;
431
+ transactionItem.params.expressionAttributeNames = names;
432
+ transactionItem.params.expressionAttributeValues = values;
433
+ }
434
+ this.items.push(transactionItem);
435
+ return this;
436
+ }
437
+ /**
438
+ * Adds a pre-configured delete operation to the transaction.
439
+ * Use this method when you need to:
440
+ * - Reuse delete commands from DeleteBuilder
441
+ * - Add complex delete operations with pre-configured parameters
442
+ * - Integrate with existing delete command configurations
443
+ *
444
+ * This method is particularly useful when working with DeleteBuilder
445
+ * to maintain consistency in delete operations across your application.
446
+ *
447
+ * @example
448
+ * ```typescript
449
+ * // Create a delete command with DeleteBuilder
450
+ * const deleteCommand = new DeleteBuilder(executor, 'users', { pk: 'USER#123' })
451
+ * .condition(op => op.and([
452
+ * op.attributeExists('pk'),
453
+ * op.eq('status', 'INACTIVE')
454
+ * ]))
455
+ * .toDynamoCommand();
456
+ *
457
+ * // Add the command to the transaction
458
+ * transaction.deleteWithCommand(deleteCommand);
459
+ * ```
460
+ *
461
+ * @param command - The complete delete command configuration
462
+ * @returns The transaction builder for method chaining
463
+ * @throws {Error} If a duplicate item is detected in the transaction
464
+ * @see DeleteBuilder for creating delete commands
465
+ */
466
+ deleteWithCommand(command) {
467
+ let keyForDuplicateCheck;
468
+ let keyForTransaction;
469
+ if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
470
+ keyForTransaction = this.createKeyForPrimaryIndex(command.key);
471
+ keyForDuplicateCheck = keyForTransaction;
472
+ } else {
473
+ keyForTransaction = command.key;
474
+ keyForDuplicateCheck = command.key;
475
+ }
476
+ this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
477
+ const transactionItem = {
478
+ type: "Delete",
479
+ params: {
480
+ ...command,
481
+ key: keyForTransaction
482
+ }
483
+ };
484
+ this.items.push(transactionItem);
485
+ return this;
486
+ }
487
+ /**
488
+ * Adds an update operation to the transaction.
489
+ * Use this method when you need to:
490
+ * - Modify existing items as part of a transaction
491
+ * - Update multiple attributes atomically
492
+ * - Apply conditional updates
493
+ * - Perform complex attribute manipulations
494
+ *
495
+ * The method supports all DynamoDB update expressions:
496
+ * - SET: Modify or add attributes
497
+ * - REMOVE: Delete attributes
498
+ * - ADD: Update numbers and sets
499
+ * - DELETE: Remove elements from a set
500
+ *
501
+ * @example
502
+ * ```typescript
503
+ * // Simple update
504
+ * transaction.update(
505
+ * 'orders',
506
+ * { pk: 'ORDER#123' },
507
+ * 'SET #status = :status',
508
+ * { '#status': 'status' },
509
+ * { ':status': 'PROCESSING' }
510
+ * );
511
+ *
512
+ * // Complex update with multiple operations
513
+ * transaction.update(
514
+ * 'products',
515
+ * { pk: 'PROD#ABC' },
516
+ * 'SET #qty = #qty - :amount, #status = :status REMOVE #oldAttr',
517
+ * { '#qty': 'quantity', '#status': 'status', '#oldAttr': 'deprecated_field' },
518
+ * { ':amount': 1, ':status': 'LOW_STOCK' }
519
+ * );
520
+ *
521
+ * // Conditional update
522
+ * transaction.update(
523
+ * 'users',
524
+ * { pk: 'USER#123' },
525
+ * 'SET #lastLogin = :now',
526
+ * { '#lastLogin': 'lastLoginDate' },
527
+ * { ':now': new Date().toISOString() },
528
+ * op => op.attributeExists('pk')
529
+ * );
530
+ * ```
531
+ *
532
+ * @param tableName - The name of the DynamoDB table
533
+ * @param key - The primary key of the item to update
534
+ * @param updateExpression - The update expression (SET, REMOVE, ADD, DELETE)
535
+ * @param expressionAttributeNames - Map of attribute name placeholders to actual names
536
+ * @param expressionAttributeValues - Map of value placeholders to actual values
537
+ * @param condition - Optional condition that must be satisfied
538
+ * @returns The transaction builder for method chaining
539
+ * @throws {Error} If a duplicate item is detected in the transaction
540
+ */
541
+ update(tableName, key, updateExpression, expressionAttributeNames, expressionAttributeValues, condition) {
542
+ const keyCondition = this.createKeyForPrimaryIndex(key);
543
+ this.checkForDuplicateItem(tableName, keyCondition);
544
+ const transactionItem = {
545
+ type: "Update",
546
+ params: {
547
+ tableName,
548
+ key: keyCondition,
549
+ updateExpression,
550
+ expressionAttributeNames,
551
+ expressionAttributeValues
552
+ }
553
+ };
554
+ if (condition) {
555
+ const { expression, names, values } = prepareExpressionParams(condition);
556
+ transactionItem.params.conditionExpression = expression;
557
+ transactionItem.params.expressionAttributeNames = {
558
+ ...transactionItem.params.expressionAttributeNames,
559
+ ...names
560
+ };
561
+ transactionItem.params.expressionAttributeValues = {
562
+ ...transactionItem.params.expressionAttributeValues,
563
+ ...values
564
+ };
565
+ }
566
+ this.items.push(transactionItem);
567
+ return this;
568
+ }
569
+ /**
570
+ * Adds a pre-configured update operation to the transaction.
571
+ * Use this method when you need to:
572
+ * - Reuse update commands from UpdateBuilder
573
+ * - Add complex update operations with pre-configured parameters
574
+ * - Integrate with existing update command configurations
575
+ *
576
+ * This method is particularly useful when working with UpdateBuilder
577
+ * to maintain consistency in update operations across your application.
578
+ *
579
+ * @example
580
+ * ```typescript
581
+ * // Create an update command with UpdateBuilder
582
+ * const updateCommand = new UpdateBuilder(executor, 'inventory', { pk: 'PROD#ABC' })
583
+ * .set('quantity', ':qty')
584
+ * .set('lastUpdated', ':now')
585
+ * .values({
586
+ * ':qty': 100,
587
+ * ':now': new Date().toISOString()
588
+ * })
589
+ * .condition(op => op.gt('quantity', 0))
590
+ * .toDynamoCommand();
591
+ *
592
+ * // Add the command to the transaction
593
+ * transaction.updateWithCommand(updateCommand);
594
+ * ```
595
+ *
596
+ * @param command - The complete update command configuration
597
+ * @returns The transaction builder for method chaining
598
+ * @throws {Error} If a duplicate item is detected in the transaction
599
+ * @see UpdateBuilder for creating update commands
600
+ */
601
+ updateWithCommand(command) {
602
+ let keyForDuplicateCheck;
603
+ let keyForTransaction;
604
+ if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
605
+ keyForTransaction = this.createKeyForPrimaryIndex(command.key);
606
+ keyForDuplicateCheck = keyForTransaction;
607
+ } else {
608
+ keyForTransaction = command.key;
609
+ keyForDuplicateCheck = command.key;
610
+ }
611
+ this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
612
+ const transactionItem = {
613
+ type: "Update",
614
+ params: {
615
+ ...command,
616
+ key: keyForTransaction
617
+ }
618
+ };
619
+ this.items.push(transactionItem);
620
+ return this;
621
+ }
622
+ /**
623
+ * Adds a condition check operation to the transaction.
624
+ * Use this method when you need to:
625
+ * - Validate item state without modifying it
626
+ * - Ensure data consistency across tables
627
+ * - Implement complex business rules
628
+ * - Verify preconditions for other operations
629
+ *
630
+ * Condition checks are particularly useful for:
631
+ * - Implementing optimistic locking
632
+ * - Ensuring referential integrity
633
+ * - Validating business rules atomically
634
+ *
635
+ * @example
636
+ * ```typescript
637
+ * // Check if order is in correct state
638
+ * transaction.conditionCheck(
639
+ * 'orders',
640
+ * { pk: 'ORDER#123' },
641
+ * op => op.eq('status', 'PENDING')
642
+ * );
643
+ *
644
+ * // Complex condition check
645
+ * transaction.conditionCheck(
646
+ * 'inventory',
647
+ * { pk: 'PROD#ABC' },
648
+ * op => op.and([
649
+ * op.gt('quantity', 0),
650
+ * op.eq('status', 'ACTIVE'),
651
+ * op.attributeExists('lastRestockDate')
652
+ * ])
653
+ * );
654
+ *
655
+ * // Check with multiple attributes
656
+ * transaction.conditionCheck(
657
+ * 'users',
658
+ * { pk: 'USER#123' },
659
+ * op => op.or([
660
+ * op.eq('status', 'PREMIUM'),
661
+ * op.gte('credits', 100)
662
+ * ])
663
+ * );
664
+ * ```
665
+ *
666
+ * @param tableName - The name of the DynamoDB table
667
+ * @param key - The primary key of the item to check
668
+ * @param condition - The condition that must be satisfied
669
+ * @returns The transaction builder for method chaining
670
+ * @throws {Error} If a duplicate item is detected in the transaction
671
+ * @throws {Error} If condition expression generation fails
672
+ */
673
+ conditionCheck(tableName, key, condition) {
674
+ const keyCondition = this.createKeyForPrimaryIndex(key);
675
+ this.checkForDuplicateItem(tableName, keyCondition);
676
+ const { expression, names, values } = prepareExpressionParams(condition);
677
+ if (!expression) {
678
+ throw new Error("Failed to generate condition expression");
679
+ }
680
+ const transactionItem = {
681
+ type: "ConditionCheck",
682
+ params: {
683
+ tableName,
684
+ key: keyCondition,
685
+ conditionExpression: expression,
686
+ expressionAttributeNames: names,
687
+ expressionAttributeValues: values
688
+ }
689
+ };
690
+ this.items.push(transactionItem);
691
+ return this;
692
+ }
693
+ /**
694
+ * Adds a pre-configured condition check operation to the transaction.
695
+ * Use this method when you need to:
696
+ * - Reuse condition checks from ConditionCheckBuilder
697
+ * - Add complex condition checks with pre-configured parameters
698
+ * - Integrate with existing condition check configurations
699
+ *
700
+ * This method is particularly useful when working with ConditionCheckBuilder
701
+ * to maintain consistency in condition checks across your application.
702
+ *
703
+ * @example
704
+ * ```typescript
705
+ * // Create a condition check with ConditionCheckBuilder
706
+ * const checkCommand = new ConditionCheckBuilder('inventory', { pk: 'PROD#ABC' })
707
+ * .condition(op => op.and([
708
+ * op.between('quantity', 10, 100),
709
+ * op.beginsWith('category', 'ELECTRONICS'),
710
+ * op.attributeExists('lastAuditDate')
711
+ * ]))
712
+ * .toDynamoCommand();
713
+ *
714
+ * // Add the command to the transaction
715
+ * transaction.conditionCheckWithCommand(checkCommand);
716
+ * ```
717
+ *
718
+ * @param command - The complete condition check command configuration
719
+ * @returns The transaction builder for method chaining
720
+ * @throws {Error} If a duplicate item is detected in the transaction
721
+ * @see ConditionCheckBuilder for creating condition check commands
722
+ */
723
+ conditionCheckWithCommand(command) {
724
+ let keyForDuplicateCheck;
725
+ let keyForTransaction;
726
+ if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
727
+ keyForTransaction = this.createKeyForPrimaryIndex(command.key);
728
+ keyForDuplicateCheck = keyForTransaction;
729
+ } else {
730
+ keyForTransaction = command.key;
731
+ keyForDuplicateCheck = command.key;
732
+ }
733
+ this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
734
+ const transactionItem = {
735
+ type: "ConditionCheck",
736
+ params: {
737
+ ...command,
738
+ key: keyForTransaction
739
+ }
740
+ };
741
+ this.items.push(transactionItem);
742
+ return this;
743
+ }
744
+ /**
745
+ * Sets options for the transaction execution.
746
+ * Use this method when you need to:
747
+ * - Enable idempotent transactions
748
+ * - Track consumed capacity
749
+ * - Monitor item collection metrics
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * // Enable idempotency and capacity tracking
754
+ * transaction.withOptions({
755
+ * clientRequestToken: 'unique-request-id-123',
756
+ * returnConsumedCapacity: 'TOTAL'
757
+ * });
758
+ *
759
+ * // Track item collection metrics
760
+ * transaction.withOptions({
761
+ * returnItemCollectionMetrics: 'SIZE'
762
+ * });
763
+ * ```
764
+ *
765
+ * Note: ClientRequestToken can be used to make transactions idempotent,
766
+ * ensuring the same transaction is not executed multiple times.
767
+ *
768
+ * @param options - Configuration options for the transaction
769
+ * @returns The transaction builder for method chaining
770
+ */
771
+ withOptions(options) {
772
+ this.options = { ...this.options, ...options };
773
+ return this;
774
+ }
775
+ /**
776
+ * Gets a human-readable representation of the transaction items.
777
+ * Use this method when you need to:
778
+ * - Debug complex transactions
779
+ * - Verify operation parameters
780
+ * - Log transaction details
781
+ * - Troubleshoot condition expressions
782
+ *
783
+ * The method resolves all expression placeholders with their actual values,
784
+ * making it easier to understand the transaction's operations.
785
+ *
786
+ * @example
787
+ * ```typescript
788
+ * // Add multiple operations
789
+ * transaction
790
+ * .put('orders', { orderId: '123', status: 'PENDING' })
791
+ * .update('inventory',
792
+ * { productId: 'ABC' },
793
+ * 'SET quantity = quantity - :amount',
794
+ * undefined,
795
+ * { ':amount': 1 }
796
+ * );
797
+ *
798
+ * // Debug the transaction
799
+ * const debugInfo = transaction.debug();
800
+ * console.log('Transaction operations:', debugInfo);
801
+ * ```
802
+ *
803
+ * @returns An array of readable representations of the transaction items
804
+ */
805
+ debug() {
806
+ return debugTransaction(this.items);
807
+ }
808
+ /**
809
+ * Executes all operations in the transaction atomically.
810
+ * Use this method when you need to:
811
+ * - Perform multiple operations atomically
812
+ * - Ensure all-or-nothing execution
813
+ * - Maintain data consistency across operations
814
+ *
815
+ * The transaction will only succeed if all operations succeed.
816
+ * If any operation fails, the entire transaction is rolled back.
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * try {
821
+ * // Build and execute transaction
822
+ * await transaction
823
+ * .put('orders', newOrder)
824
+ * .update('inventory',
825
+ * { productId: 'ABC' },
826
+ * 'SET quantity = quantity - :qty',
827
+ * undefined,
828
+ * { ':qty': 1 }
829
+ * )
830
+ * .conditionCheck('products',
831
+ * { productId: 'ABC' },
832
+ * op => op.eq('status', 'ACTIVE')
833
+ * )
834
+ * .execute();
835
+ *
836
+ * console.log('Transaction completed successfully');
837
+ * } catch (error) {
838
+ * // Handle transaction failure
839
+ * console.error('Transaction failed:', error);
840
+ * }
841
+ * ```
842
+ *
843
+ * @throws {Error} If no transaction items are specified
844
+ * @throws {Error} If any operation in the transaction fails
845
+ * @returns A promise that resolves when the transaction completes
846
+ */
847
+ async execute() {
848
+ if (this.items.length === 0) {
849
+ throw new Error("No transaction items specified");
850
+ }
851
+ const transactItems = this.items.map((item) => {
852
+ switch (item.type) {
853
+ case "Put":
854
+ return {
855
+ Put: {
856
+ TableName: item.params.tableName,
857
+ Item: item.params.item,
858
+ ConditionExpression: item.params.conditionExpression,
859
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
860
+ ExpressionAttributeValues: item.params.expressionAttributeValues
861
+ }
862
+ };
863
+ case "Delete":
864
+ return {
865
+ Delete: {
866
+ TableName: item.params.tableName,
867
+ Key: item.params.key,
868
+ ConditionExpression: item.params.conditionExpression,
869
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
870
+ ExpressionAttributeValues: item.params.expressionAttributeValues
871
+ }
872
+ };
873
+ case "Update":
874
+ return {
875
+ Update: {
876
+ TableName: item.params.tableName,
877
+ Key: item.params.key,
878
+ UpdateExpression: item.params.updateExpression,
879
+ ConditionExpression: item.params.conditionExpression,
880
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
881
+ ExpressionAttributeValues: item.params.expressionAttributeValues
882
+ }
883
+ };
884
+ case "ConditionCheck":
885
+ return {
886
+ ConditionCheck: {
887
+ TableName: item.params.tableName,
888
+ Key: item.params.key,
889
+ ConditionExpression: item.params.conditionExpression,
890
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
891
+ ExpressionAttributeValues: item.params.expressionAttributeValues
892
+ }
893
+ };
894
+ default: {
895
+ const exhaustiveCheck = item;
896
+ throw new Error(`Unsupported transaction item type: ${String(exhaustiveCheck)}`);
897
+ }
898
+ }
899
+ });
900
+ const params = {
901
+ TransactItems: transactItems,
902
+ ClientRequestToken: this.options.clientRequestToken,
903
+ ReturnConsumedCapacity: this.options.returnConsumedCapacity,
904
+ ReturnItemCollectionMetrics: this.options.returnItemCollectionMetrics
905
+ };
906
+ try {
907
+ await this.executor(params);
908
+ } catch (error) {
909
+ console.log(this.debug());
910
+ console.error("Error executing transaction:", error);
911
+ throw error;
912
+ }
913
+ }
914
+ };
915
+
916
+ exports.TransactionBuilder = TransactionBuilder;
917
+ //# sourceMappingURL=transaction-builder.cjs.map
918
+ //# sourceMappingURL=transaction-builder.cjs.map