dyno-table 0.1.7 → 0.2.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/LICENSE +21 -0
  2. package/README.md +681 -160
  3. package/dist/builder-types-C_PDZhnP.d.ts +118 -0
  4. package/dist/builder-types-DtwbqMeF.d.cts +118 -0
  5. package/dist/builders/condition-check-builder.cjs +394 -0
  6. package/dist/builders/condition-check-builder.cjs.map +1 -0
  7. package/dist/builders/condition-check-builder.d.cts +157 -0
  8. package/dist/builders/condition-check-builder.d.ts +157 -0
  9. package/dist/builders/condition-check-builder.js +392 -0
  10. package/dist/builders/condition-check-builder.js.map +1 -0
  11. package/dist/builders/delete-builder.cjs +405 -0
  12. package/dist/builders/delete-builder.cjs.map +1 -0
  13. package/dist/builders/delete-builder.d.cts +166 -0
  14. package/dist/builders/delete-builder.d.ts +166 -0
  15. package/dist/builders/delete-builder.js +403 -0
  16. package/dist/builders/delete-builder.js.map +1 -0
  17. package/dist/builders/paginator.cjs +199 -0
  18. package/dist/builders/paginator.cjs.map +1 -0
  19. package/dist/builders/paginator.d.cts +179 -0
  20. package/dist/builders/paginator.d.ts +179 -0
  21. package/dist/builders/paginator.js +197 -0
  22. package/dist/builders/paginator.js.map +1 -0
  23. package/dist/builders/put-builder.cjs +476 -0
  24. package/dist/builders/put-builder.cjs.map +1 -0
  25. package/dist/builders/put-builder.d.cts +274 -0
  26. package/dist/builders/put-builder.d.ts +274 -0
  27. package/dist/builders/put-builder.js +474 -0
  28. package/dist/builders/put-builder.js.map +1 -0
  29. package/dist/builders/query-builder.cjs +674 -0
  30. package/dist/builders/query-builder.cjs.map +1 -0
  31. package/dist/builders/query-builder.d.cts +6 -0
  32. package/dist/builders/query-builder.d.ts +6 -0
  33. package/dist/builders/query-builder.js +672 -0
  34. package/dist/builders/query-builder.js.map +1 -0
  35. package/dist/builders/transaction-builder.cjs +894 -0
  36. package/dist/builders/transaction-builder.cjs.map +1 -0
  37. package/dist/builders/transaction-builder.d.cts +511 -0
  38. package/dist/builders/transaction-builder.d.ts +511 -0
  39. package/dist/builders/transaction-builder.js +892 -0
  40. package/dist/builders/transaction-builder.js.map +1 -0
  41. package/dist/builders/update-builder.cjs +627 -0
  42. package/dist/builders/update-builder.cjs.map +1 -0
  43. package/dist/builders/update-builder.d.cts +365 -0
  44. package/dist/builders/update-builder.d.ts +365 -0
  45. package/dist/builders/update-builder.js +625 -0
  46. package/dist/builders/update-builder.js.map +1 -0
  47. package/dist/conditions--ld9a78i.d.ts +331 -0
  48. package/dist/conditions-ChhQWd6z.d.cts +331 -0
  49. package/dist/conditions.cjs +59 -0
  50. package/dist/conditions.cjs.map +1 -0
  51. package/dist/conditions.d.cts +3 -0
  52. package/dist/conditions.d.ts +3 -0
  53. package/dist/conditions.js +43 -0
  54. package/dist/conditions.js.map +1 -0
  55. package/dist/entity.cjs +228 -0
  56. package/dist/entity.cjs.map +1 -0
  57. package/dist/entity.d.cts +149 -0
  58. package/dist/entity.d.ts +149 -0
  59. package/dist/entity.js +224 -0
  60. package/dist/entity.js.map +1 -0
  61. package/dist/query-builder-Csror9Iu.d.ts +507 -0
  62. package/dist/query-builder-D2FM9rsu.d.cts +507 -0
  63. package/dist/standard-schema.cjs +4 -0
  64. package/dist/standard-schema.cjs.map +1 -0
  65. package/dist/standard-schema.d.cts +57 -0
  66. package/dist/standard-schema.d.ts +57 -0
  67. package/dist/standard-schema.js +3 -0
  68. package/dist/standard-schema.js.map +1 -0
  69. package/dist/table-BEhBPy2G.d.cts +364 -0
  70. package/dist/table-BW3cmUqr.d.ts +364 -0
  71. package/dist/{index.js → table.cjs} +330 -354
  72. package/dist/table.cjs.map +1 -0
  73. package/dist/table.d.cts +12 -0
  74. package/dist/table.d.ts +12 -0
  75. package/dist/table.js +3243 -0
  76. package/dist/table.js.map +1 -0
  77. package/dist/types.cjs +4 -0
  78. package/dist/types.cjs.map +1 -0
  79. package/dist/types.d.cts +22 -0
  80. package/dist/types.d.ts +22 -0
  81. package/dist/types.js +3 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/utils/partition-key-template.cjs +19 -0
  84. package/dist/utils/partition-key-template.cjs.map +1 -0
  85. package/dist/utils/partition-key-template.d.cts +32 -0
  86. package/dist/utils/partition-key-template.d.ts +32 -0
  87. package/dist/utils/partition-key-template.js +17 -0
  88. package/dist/utils/partition-key-template.js.map +1 -0
  89. package/dist/utils/sort-key-template.cjs +19 -0
  90. package/dist/utils/sort-key-template.cjs.map +1 -0
  91. package/dist/utils/sort-key-template.d.cts +35 -0
  92. package/dist/utils/sort-key-template.d.ts +35 -0
  93. package/dist/utils/sort-key-template.js +17 -0
  94. package/dist/utils/sort-key-template.js.map +1 -0
  95. package/package.json +97 -22
  96. package/dist/index.d.ts +0 -2923
package/dist/table.js ADDED
@@ -0,0 +1,3243 @@
1
+ // src/conditions.ts
2
+ var createComparisonCondition = (type) => (attr, value) => ({
3
+ type,
4
+ attr,
5
+ value
6
+ });
7
+ var eq = createComparisonCondition("eq");
8
+ var ne = createComparisonCondition("ne");
9
+ var lt = createComparisonCondition("lt");
10
+ var lte = createComparisonCondition("lte");
11
+ var gt = createComparisonCondition("gt");
12
+ var gte = createComparisonCondition("gte");
13
+ var between = (attr, lower, upper) => ({
14
+ type: "between",
15
+ attr,
16
+ value: [lower, upper]
17
+ });
18
+ var beginsWith = createComparisonCondition("beginsWith");
19
+ var contains = createComparisonCondition("contains");
20
+ var attributeExists = (attr) => ({
21
+ type: "attributeExists",
22
+ attr
23
+ });
24
+ var attributeNotExists = (attr) => ({
25
+ type: "attributeNotExists",
26
+ attr
27
+ });
28
+ var and = (...conditions) => ({
29
+ type: "and",
30
+ conditions
31
+ });
32
+ var or = (...conditions) => ({
33
+ type: "or",
34
+ conditions
35
+ });
36
+ var not = (condition) => ({
37
+ type: "not",
38
+ condition
39
+ });
40
+
41
+ // src/expression.ts
42
+ var generateAttributeName = (params, attr) => {
43
+ for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
44
+ if (existingAttr === attr) {
45
+ return existingName;
46
+ }
47
+ }
48
+ const attrName = `#${Object.keys(params.expressionAttributeNames).length}`;
49
+ params.expressionAttributeNames[attrName] = attr;
50
+ return attrName;
51
+ };
52
+ var generateValueName = (params, value) => {
53
+ const valueName = `:${params.valueCounter.count++}`;
54
+ params.expressionAttributeValues[valueName] = value;
55
+ return valueName;
56
+ };
57
+ var validateCondition = (condition, requiresAttr = true, requiresValue = true) => {
58
+ if (requiresAttr && !condition.attr) {
59
+ throw new Error(`Attribute is required for ${condition.type} condition`);
60
+ }
61
+ if (requiresValue && condition.value === void 0) {
62
+ throw new Error(`Value is required for ${condition.type} condition`);
63
+ }
64
+ };
65
+ var buildComparisonExpression = (condition, operator, params) => {
66
+ validateCondition(condition);
67
+ if (!condition.attr) {
68
+ throw new Error(`Attribute is required for ${condition.type} condition`);
69
+ }
70
+ const attrName = generateAttributeName(params, condition.attr);
71
+ const valueName = generateValueName(params, condition.value);
72
+ return `${attrName} ${operator} ${valueName}`;
73
+ };
74
+ var buildBetweenExpression = (condition, params) => {
75
+ validateCondition(condition);
76
+ if (!condition.attr) {
77
+ throw new Error(`Attribute is required for ${condition.type} condition`);
78
+ }
79
+ if (!Array.isArray(condition.value) || condition.value.length !== 2) {
80
+ throw new Error("Between condition requires an array of two values");
81
+ }
82
+ const attrName = generateAttributeName(params, condition.attr);
83
+ const lowerName = generateValueName(params, condition.value[0]);
84
+ const upperName = generateValueName(params, condition.value[1]);
85
+ return `${attrName} BETWEEN ${lowerName} AND ${upperName}`;
86
+ };
87
+ var buildFunctionExpression = (functionName, condition, params) => {
88
+ validateCondition(condition);
89
+ if (!condition.attr) {
90
+ throw new Error(`Attribute is required for ${condition.type} condition`);
91
+ }
92
+ const attrName = generateAttributeName(params, condition.attr);
93
+ const valueName = generateValueName(params, condition.value);
94
+ return `${functionName}(${attrName}, ${valueName})`;
95
+ };
96
+ var buildAttributeFunction = (functionName, condition, params) => {
97
+ validateCondition(condition, true, false);
98
+ if (!condition.attr) {
99
+ throw new Error(`Attribute is required for ${condition.type} condition`);
100
+ }
101
+ const attrName = generateAttributeName(params, condition.attr);
102
+ return `${functionName}(${attrName})`;
103
+ };
104
+ var buildLogicalExpression = (operator, conditions, params) => {
105
+ if (!conditions || conditions.length === 0) {
106
+ throw new Error(`At least one condition is required for ${operator} expression`);
107
+ }
108
+ const expressions = conditions.map((c) => buildExpression(c, params));
109
+ return `(${expressions.join(` ${operator} `)})`;
110
+ };
111
+ var buildExpression = (condition, params) => {
112
+ if (!condition) return "";
113
+ try {
114
+ const expressionBuilders = {
115
+ eq: () => buildComparisonExpression(condition, "=", params),
116
+ ne: () => buildComparisonExpression(condition, "<>", params),
117
+ lt: () => buildComparisonExpression(condition, "<", params),
118
+ lte: () => buildComparisonExpression(condition, "<=", params),
119
+ gt: () => buildComparisonExpression(condition, ">", params),
120
+ gte: () => buildComparisonExpression(condition, ">=", params),
121
+ between: () => buildBetweenExpression(condition, params),
122
+ beginsWith: () => buildFunctionExpression("begins_with", condition, params),
123
+ contains: () => buildFunctionExpression("contains", condition, params),
124
+ attributeExists: () => buildAttributeFunction("attribute_exists", condition, params),
125
+ attributeNotExists: () => buildAttributeFunction("attribute_not_exists", condition, params),
126
+ and: () => {
127
+ if (!condition.conditions) {
128
+ throw new Error("Conditions array is required for AND operator");
129
+ }
130
+ return buildLogicalExpression("AND", condition.conditions, params);
131
+ },
132
+ or: () => {
133
+ if (!condition.conditions) {
134
+ throw new Error("Conditions array is required for OR operator");
135
+ }
136
+ return buildLogicalExpression("OR", condition.conditions, params);
137
+ },
138
+ not: () => {
139
+ if (!condition.condition) {
140
+ throw new Error("Condition is required for NOT operator");
141
+ }
142
+ return `NOT (${buildExpression(condition.condition, params)})`;
143
+ }
144
+ };
145
+ const builder = expressionBuilders[condition.type];
146
+ if (!builder) {
147
+ throw new Error(`Unknown condition type: ${condition.type}`);
148
+ }
149
+ return builder();
150
+ } catch (error) {
151
+ if (error instanceof Error) {
152
+ console.error(`Error building expression for condition type ${condition.type}:`, error.message);
153
+ } else {
154
+ console.error(`Error building expression for condition type ${condition.type}:`, error);
155
+ }
156
+ throw error;
157
+ }
158
+ };
159
+ var prepareExpressionParams = (condition) => {
160
+ if (!condition) return {};
161
+ const params = {
162
+ expressionAttributeNames: {},
163
+ expressionAttributeValues: {},
164
+ valueCounter: { count: 0 }
165
+ };
166
+ const expression = buildExpression(condition, params);
167
+ return {
168
+ expression,
169
+ names: Object.keys(params.expressionAttributeNames).length > 0 ? params.expressionAttributeNames : void 0,
170
+ values: Object.keys(params.expressionAttributeValues).length > 0 ? params.expressionAttributeValues : void 0
171
+ };
172
+ };
173
+
174
+ // src/builders/paginator.ts
175
+ var Paginator = class {
176
+ queryBuilder;
177
+ pageSize;
178
+ currentPage = 0;
179
+ lastEvaluatedKey;
180
+ hasMorePages = true;
181
+ totalItemsRetrieved = 0;
182
+ overallLimit;
183
+ constructor(queryBuilder, pageSize) {
184
+ this.queryBuilder = queryBuilder;
185
+ this.pageSize = pageSize;
186
+ this.overallLimit = queryBuilder.getLimit();
187
+ }
188
+ /**
189
+ * Gets the current page number (1-indexed).
190
+ * Use this method when you need to:
191
+ * - Track progress through dinosaur lists
192
+ * - Display habitat inspection status
193
+ * - Monitor security sweep progress
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * const paginator = new QueryBuilder(executor, eq('species', 'Tyrannosaurus'))
198
+ * .paginate(5);
199
+ *
200
+ * await paginator.getNextPage();
201
+ * console.log(`Reviewing T-Rex group ${paginator.getCurrentPage()}`);
202
+ * ```
203
+ *
204
+ * @returns The current page number, starting from 1
205
+ */
206
+ getCurrentPage() {
207
+ return this.currentPage;
208
+ }
209
+ /**
210
+ * Checks if there are more pages of dinosaurs or habitats to process.
211
+ * Use this method when you need to:
212
+ * - Check for more dinosaurs to review
213
+ * - Continue habitat inspections
214
+ * - Process security incidents
215
+ * - Complete feeding schedules
216
+ *
217
+ * This method takes into account both:
218
+ * - DynamoDB's lastEvaluatedKey mechanism
219
+ * - Any overall limit set on the query
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * // Process all security incidents
224
+ * const paginator = new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
225
+ * .sortDescending()
226
+ * .paginate(10);
227
+ *
228
+ * while (paginator.hasNextPage()) {
229
+ * const page = await paginator.getNextPage();
230
+ * for (const incident of page.items) {
231
+ * await processSecurityBreach(incident);
232
+ * }
233
+ * console.log(`Processed incidents page ${page.page}`);
234
+ * }
235
+ * ```
236
+ *
237
+ * @returns true if there are more pages available, false otherwise
238
+ */
239
+ hasNextPage() {
240
+ if (this.overallLimit !== void 0 && this.totalItemsRetrieved >= this.overallLimit) {
241
+ return false;
242
+ }
243
+ return this.hasMorePages;
244
+ }
245
+ /**
246
+ * Retrieves the next page of dinosaurs or habitats from DynamoDB.
247
+ * Use this method when you need to:
248
+ * - Process dinosaur groups systematically
249
+ * - Review habitat inspections in batches
250
+ * - Monitor security incidents in sequence
251
+ * - Schedule feeding rotations
252
+ *
253
+ * This method handles:
254
+ * - Automatic continuation between groups
255
+ * - Respect for park capacity limits
256
+ * - Group size adjustments for safety
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * const paginator = new QueryBuilder(executor, eq('species', 'Velociraptor'))
261
+ * .filter(op => op.eq('status', 'ACTIVE'))
262
+ * .paginate(5);
263
+ *
264
+ * // Check first raptor group
265
+ * const page1 = await paginator.getNextPage();
266
+ * console.log(`Found ${page1.items.length} active raptors`);
267
+ *
268
+ * // Continue inspection if more groups exist
269
+ * if (page1.hasNextPage) {
270
+ * const page2 = await paginator.getNextPage();
271
+ * console.log(`Inspecting raptor group ${page2.page}`);
272
+ *
273
+ * for (const raptor of page2.items) {
274
+ * await performHealthCheck(raptor);
275
+ * }
276
+ * }
277
+ * ```
278
+ *
279
+ * @returns A promise that resolves to a PaginationResult containing:
280
+ * - items: The dinosaurs/habitats for this page
281
+ * - hasNextPage: Whether more groups exist
282
+ * - page: The current group number
283
+ * - lastEvaluatedKey: DynamoDB's continuation token
284
+ */
285
+ async getNextPage() {
286
+ if (!this.hasNextPage()) {
287
+ return {
288
+ items: [],
289
+ hasNextPage: false,
290
+ page: this.currentPage
291
+ };
292
+ }
293
+ let effectivePageSize = this.pageSize;
294
+ if (this.overallLimit !== void 0) {
295
+ const remainingItems = this.overallLimit - this.totalItemsRetrieved;
296
+ if (remainingItems <= 0) {
297
+ return {
298
+ items: [],
299
+ hasNextPage: false,
300
+ page: this.currentPage
301
+ };
302
+ }
303
+ effectivePageSize = Math.min(effectivePageSize, remainingItems);
304
+ }
305
+ const query = this.queryBuilder.clone().limit(effectivePageSize);
306
+ if (this.lastEvaluatedKey) {
307
+ query.startFrom(this.lastEvaluatedKey);
308
+ }
309
+ const result = await query.execute();
310
+ this.currentPage += 1;
311
+ this.lastEvaluatedKey = result.lastEvaluatedKey;
312
+ this.totalItemsRetrieved += result.items.length;
313
+ this.hasMorePages = !!result.lastEvaluatedKey && (this.overallLimit === void 0 || this.totalItemsRetrieved < this.overallLimit);
314
+ return {
315
+ items: result.items,
316
+ lastEvaluatedKey: result.lastEvaluatedKey,
317
+ hasNextPage: this.hasNextPage(),
318
+ page: this.currentPage
319
+ };
320
+ }
321
+ /**
322
+ * Gets all remaining dinosaurs or habitats and combines them into a single array.
323
+ * Use this method when you need to:
324
+ * - Generate complete park inventory
325
+ * - Perform full security audit
326
+ * - Create comprehensive feeding schedule
327
+ * - Run park-wide health checks
328
+ *
329
+ * Note: Use with caution! This method:
330
+ * - Could overwhelm systems with large dinosaur populations
331
+ * - Makes multiple database requests
332
+ * - May cause system strain during peak hours
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * // Get complete carnivore inventory
337
+ * const paginator = new QueryBuilder(executor, eq('diet', 'CARNIVORE'))
338
+ * .filter(op => op.eq('status', 'ACTIVE'))
339
+ * .paginate(10);
340
+ *
341
+ * try {
342
+ * const allCarnivores = await paginator.getAllPages();
343
+ * console.log(`Park contains ${allCarnivores.length} active carnivores`);
344
+ *
345
+ * // Calculate total threat level
346
+ * const totalThreat = allCarnivores.reduce(
347
+ * (sum, dino) => sum + dino.stats.threatLevel,
348
+ * 0
349
+ * );
350
+ * console.log(`Total threat level: ${totalThreat}`);
351
+ * } catch (error) {
352
+ * console.error('Failed to complete carnivore census:', error);
353
+ * }
354
+ * ```
355
+ *
356
+ * @returns A promise that resolves to an array containing all remaining items
357
+ */
358
+ async getAllPages() {
359
+ const allItems = [];
360
+ while (this.hasNextPage()) {
361
+ const result = await this.getNextPage();
362
+ allItems.push(...result.items);
363
+ }
364
+ return allItems;
365
+ }
366
+ };
367
+
368
+ // src/builders/filter-builder.ts
369
+ var FilterBuilder = class {
370
+ options = {};
371
+ selectedFields = /* @__PURE__ */ new Set();
372
+ /**
373
+ * Sets the maximum number of items to return.
374
+ * Use this method when you need to:
375
+ * - Limit the number of dinosaurs returned
376
+ * - Control the size of habitat reports
377
+ * - Implement manual pagination of security logs
378
+ *
379
+ * Note: This limit applies to the items that match the key condition
380
+ * before any filter expressions are applied.
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * // Get first 10 dinosaurs
385
+ * const result = await builder
386
+ * .limit(10)
387
+ * .execute();
388
+ * ```
389
+ *
390
+ * @param limit - Maximum number of items to return
391
+ * @returns The builder instance for method chaining
392
+ */
393
+ limit(limit) {
394
+ this.options.limit = limit;
395
+ return this;
396
+ }
397
+ /**
398
+ * Gets the current limit set on the operation.
399
+ * This is used internally by the paginator to manage result sets.
400
+ *
401
+ * @returns The current limit or undefined if no limit is set
402
+ */
403
+ getLimit() {
404
+ return this.options.limit;
405
+ }
406
+ /**
407
+ * Specifies a Global Secondary Index (GSI) to use for the operation.
408
+ * Use this method when you need to:
409
+ * - Find dinosaurs by species or status
410
+ * - Search habitats by security level
411
+ * - Find incidents by date
412
+ * - List feeding schedules by time
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * // Find all dinosaurs of a specific species
417
+ * builder
418
+ * .useIndex('species-status-index')
419
+ * .filter(op => op.eq('status', 'ACTIVE'));
420
+ *
421
+ * // Search high-security habitats
422
+ * builder
423
+ * .useIndex('security-level-index')
424
+ * .filter(op =>
425
+ * op.and([
426
+ * op.gt('securityLevel', 8),
427
+ * op.eq('status', 'OPERATIONAL')
428
+ * ])
429
+ * );
430
+ * ```
431
+ *
432
+ * @param indexName - The name of the GSI to use (type-safe based on table configuration)
433
+ * @returns The builder instance for method chaining
434
+ */
435
+ useIndex(indexName) {
436
+ this.options.indexName = indexName;
437
+ return this;
438
+ }
439
+ /**
440
+ * Sets whether to use strongly consistent reads for the operation.
441
+ * Use this method when you need to:
442
+ * - Get real-time dinosaur status updates
443
+ * - Monitor critical security systems
444
+ * - Track immediate habitat changes
445
+ * - Verify containment protocols
446
+ *
447
+ * Note:
448
+ * - Consistent reads are not available on GSIs
449
+ * - Consistent reads consume twice the throughput
450
+ * - Default is eventually consistent reads
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * // Check immediate dinosaur status
455
+ * const result = await builder
456
+ * .filter(op => op.eq('status', 'ACTIVE'))
457
+ * .consistentRead()
458
+ * .execute();
459
+ *
460
+ * // Monitor security breaches
461
+ * const result = await builder
462
+ * .useIndex('primary-index')
463
+ * .consistentRead(isEmergencyMode)
464
+ * .execute();
465
+ * ```
466
+ *
467
+ * @param consistentRead - Whether to use consistent reads (defaults to true)
468
+ * @returns The builder instance for method chaining
469
+ */
470
+ consistentRead(consistentRead = true) {
471
+ this.options.consistentRead = consistentRead;
472
+ return this;
473
+ }
474
+ /**
475
+ * Adds a filter expression to refine the operation results.
476
+ * Use this method when you need to:
477
+ * - Filter dinosaurs by behavior patterns
478
+ * - Find habitats with specific conditions
479
+ * - Search for security incidents
480
+ * - Monitor feeding patterns
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * // Find aggressive carnivores
485
+ * builder.filter(op =>
486
+ * op.and([
487
+ * op.eq('diet', 'CARNIVORE'),
488
+ * op.gt('aggressionLevel', 7),
489
+ * op.eq('status', 'ACTIVE')
490
+ * ])
491
+ * );
492
+ *
493
+ * // Search suitable breeding habitats
494
+ * builder.filter(op =>
495
+ * op.and([
496
+ * op.between('temperature', 25, 30),
497
+ * op.lt('currentOccupants', 3),
498
+ * op.eq('quarantineStatus', 'CLEAR')
499
+ * ])
500
+ * );
501
+ * ```
502
+ *
503
+ * @param condition - Either a Condition object or a callback function that builds the condition
504
+ * @returns The builder instance for method chaining
505
+ */
506
+ filter(condition) {
507
+ if (typeof condition === "function") {
508
+ const conditionOperator = {
509
+ eq,
510
+ ne,
511
+ lt,
512
+ lte,
513
+ gt,
514
+ gte,
515
+ between,
516
+ beginsWith,
517
+ contains,
518
+ attributeExists,
519
+ attributeNotExists,
520
+ and,
521
+ or,
522
+ not
523
+ };
524
+ this.options.filter = condition(conditionOperator);
525
+ } else {
526
+ this.options.filter = condition;
527
+ }
528
+ return this;
529
+ }
530
+ /**
531
+ * Specifies which attributes to return in the results.
532
+ * Use this method when you need to:
533
+ * - Get specific dinosaur attributes
534
+ * - Retrieve habitat statistics
535
+ * - Monitor security metrics
536
+ * - Optimize response size
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * // Get basic dinosaur info
541
+ * builder.select([
542
+ * 'species',
543
+ * 'status',
544
+ * 'stats.health',
545
+ * 'stats.aggressionLevel'
546
+ * ]);
547
+ *
548
+ * // Monitor habitat conditions
549
+ * builder
550
+ * .select('securityStatus')
551
+ * .select([
552
+ * 'currentOccupants',
553
+ * 'temperature',
554
+ * 'lastInspectionDate'
555
+ * ]);
556
+ * ```
557
+ *
558
+ * @param fields - A single field name or an array of field names to return
559
+ * @returns The builder instance for method chaining
560
+ */
561
+ select(fields) {
562
+ if (typeof fields === "string") {
563
+ this.selectedFields.add(fields);
564
+ } else if (Array.isArray(fields)) {
565
+ for (const field of fields) {
566
+ this.selectedFields.add(field);
567
+ }
568
+ }
569
+ this.options.projection = Array.from(this.selectedFields);
570
+ return this;
571
+ }
572
+ /**
573
+ * Creates a paginator that handles DynamoDB pagination automatically.
574
+ * The paginator handles:
575
+ * - Tracking the last evaluated key
576
+ * - Managing page boundaries
577
+ * - Respecting overall query limits
578
+ *
579
+ * @example
580
+ * ```typescript
581
+ * // Create a paginator for dinosaur records
582
+ * const paginator = builder
583
+ * .filter(op => op.eq('status', 'ACTIVE'))
584
+ * .paginate(10);
585
+ *
586
+ * // Process pages of dinosaur results
587
+ * while (paginator.hasNextPage()) {
588
+ * const page = await paginator.getNextPage();
589
+ * console.log(`Processing page ${page.page}, count: ${page.items.length}`);
590
+ * // Process dinosaur data
591
+ * }
592
+ * ```
593
+ *
594
+ * @param pageSize - The number of items to return per page
595
+ * @returns A Paginator instance that manages the pagination state
596
+ * @see Paginator for more pagination control options
597
+ */
598
+ paginate(pageSize) {
599
+ return new Paginator(this, pageSize);
600
+ }
601
+ /**
602
+ * Sets the starting point using a previous lastEvaluatedKey.
603
+ * Use this method when you need to:
604
+ * - Implement manual dinosaur list pagination
605
+ * - Resume habitat inspection reviews
606
+ * - Continue security incident analysis
607
+ * - Store operation position between sessions
608
+ *
609
+ * Note: This method is typically used for manual pagination.
610
+ * For automatic pagination, use the paginate() method instead.
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * // First batch of dinosaurs
615
+ * const result1 = await builder
616
+ * .filter(op => op.eq('status', 'ACTIVE'))
617
+ * .limit(5)
618
+ * .execute();
619
+ *
620
+ * if (result1.lastEvaluatedKey) {
621
+ * // Continue listing dinosaurs
622
+ * const result2 = await builder
623
+ * .filter(op => op.eq('status', 'ACTIVE'))
624
+ * .startFrom(result1.lastEvaluatedKey)
625
+ * .limit(5)
626
+ * .execute();
627
+ *
628
+ * console.log('Additional dinosaurs:', result2.items);
629
+ * }
630
+ * ```
631
+ *
632
+ * @param lastEvaluatedKey - The exclusive start key from a previous result
633
+ * @returns The builder instance for method chaining
634
+ */
635
+ startFrom(lastEvaluatedKey) {
636
+ this.options.lastEvaluatedKey = lastEvaluatedKey;
637
+ return this;
638
+ }
639
+ };
640
+
641
+ // src/builders/query-builder.ts
642
+ var QueryBuilder = class _QueryBuilder extends FilterBuilder {
643
+ keyCondition;
644
+ options = {};
645
+ executor;
646
+ constructor(executor, keyCondition) {
647
+ super();
648
+ this.executor = executor;
649
+ this.keyCondition = keyCondition;
650
+ }
651
+ /**
652
+ * Sets the maximum number of items to return from the query.
653
+ *
654
+ * Note: This is the default behavior if no sort order is specified.
655
+ *
656
+ * @example
657
+ * ```typescript
658
+ * // Get orders in chronological order
659
+ * const result = await new QueryBuilder(executor, eq('userId', '123'))
660
+ * .sortAscending()
661
+ * .execute();
662
+ *
663
+ * // Get events from oldest to newest
664
+ * const result = await new QueryBuilder(executor, eq('entityId', 'order-123'))
665
+ * .useIndex('entity-timestamp-index')
666
+ * .sortAscending()
667
+ * .execute();
668
+ * ```
669
+ *
670
+ * @returns The builder instance for method chaining
671
+ */
672
+ /**
673
+ * Sets the query to return items in ascending order by sort key.
674
+ *
675
+ * @example
676
+ * ```typescript
677
+ * // List dinosaurs by age
678
+ * const result = await new QueryBuilder(executor, eq('species', 'Velociraptor'))
679
+ * .useIndex('age-index')
680
+ * .sortAscending()
681
+ * .execute();
682
+ *
683
+ * // View incidents chronologically
684
+ * const result = await new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
685
+ * .useIndex('date-index')
686
+ * .sortAscending()
687
+ * .execute();
688
+ * ```
689
+ *
690
+ * @returns The builder instance for method chaining
691
+ */
692
+ sortAscending() {
693
+ this.options.scanIndexForward = true;
694
+ return this;
695
+ }
696
+ /**
697
+ * Sets the query to return items in descending order by sort key.
698
+ *
699
+ * @example
700
+ * ```typescript
701
+ * // Get most recent security incidents
702
+ * const result = await new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
703
+ * .useIndex('date-index')
704
+ * .sortDescending()
705
+ * .limit(10)
706
+ * .execute();
707
+ *
708
+ * // Check latest dinosaur activities
709
+ * const result = await new QueryBuilder(executor, eq('species', 'Velociraptor'))
710
+ * .useIndex('activity-time-index')
711
+ * .filter(op => op.eq('status', 'ACTIVE'))
712
+ * .sortDescending()
713
+ * .execute();
714
+ * ```
715
+ *
716
+ * @returns The builder instance for method chaining
717
+ */
718
+ sortDescending() {
719
+ this.options.scanIndexForward = false;
720
+ return this;
721
+ }
722
+ /**
723
+ * Creates a deep clone of this QueryBuilder instance.
724
+ *
725
+ * This is particularly useful when:
726
+ * - Implementing pagination (used internally by paginate())
727
+ * - Creating query templates
728
+ * - Running multiple variations of a query
729
+ *
730
+ * @example
731
+ * ```typescript
732
+ * // Create base dinosaur query
733
+ * const baseQuery = new QueryBuilder(executor, eq('species', 'Velociraptor'))
734
+ * .useIndex('status-index')
735
+ * .select(['id', 'status', 'location']);
736
+ *
737
+ * // Check active dinosaurs
738
+ * const activeRaptors = baseQuery.clone()
739
+ * .filter(op => op.eq('status', 'HUNTING'))
740
+ * .execute();
741
+ *
742
+ * // Check contained dinosaurs
743
+ * const containedRaptors = baseQuery.clone()
744
+ * .filter(op => op.eq('status', 'CONTAINED'))
745
+ * .execute();
746
+ *
747
+ * // Check sedated dinosaurs
748
+ * const sedatedRaptors = baseQuery.clone()
749
+ * .filter(op => op.eq('status', 'SEDATED'))
750
+ * .execute();
751
+ * ```
752
+ *
753
+ * @returns A new QueryBuilder instance with the same configuration
754
+ */
755
+ clone() {
756
+ const clone = new _QueryBuilder(this.executor, this.keyCondition);
757
+ clone.options = { ...this.options };
758
+ clone.selectedFields = new Set(this.selectedFields);
759
+ return clone;
760
+ }
761
+ /**
762
+ * Executes the query against DynamoDB.
763
+ *
764
+ * The method returns both the matched items and, if there are more results,
765
+ * a lastEvaluatedKey that can be used with startFrom() to continue the query.
766
+ *
767
+ * @example
768
+ * ```typescript
769
+ * try {
770
+ * // Find active carnivores in specific habitat
771
+ * const result = await new QueryBuilder(executor, eq('habitatId', 'PADDOCK-A'))
772
+ * .useIndex('species-status-index')
773
+ * .filter(op =>
774
+ * op.and([
775
+ * op.eq('diet', 'CARNIVORE'),
776
+ * op.eq('status', 'ACTIVE'),
777
+ * op.gt('aggressionLevel', 7)
778
+ * ])
779
+ * )
780
+ * .sortDescending()
781
+ * .limit(5)
782
+ * .execute();
783
+ *
784
+ * console.log(`Found ${result.items.length} dangerous dinosaurs`);
785
+ *
786
+ * if (result.lastEvaluatedKey) {
787
+ * console.log('Additional threats detected');
788
+ * }
789
+ * } catch (error) {
790
+ * console.error('Security scan failed:', error);
791
+ * }
792
+ * ```
793
+ *
794
+ * @returns A promise that resolves to an object containing:
795
+ * - items: Array of items matching the query
796
+ * - lastEvaluatedKey: Token for continuing the query, if more items exist
797
+ */
798
+ async execute() {
799
+ return this.executor(this.keyCondition, this.options);
800
+ }
801
+ };
802
+
803
+ // src/utils/debug-expression.ts
804
+ function debugCommand(command) {
805
+ const result = {};
806
+ function replaceAliases(expressionString) {
807
+ if (!expressionString) {
808
+ return expressionString;
809
+ }
810
+ let replacedString = expressionString;
811
+ for (const alias in command.expressionAttributeNames) {
812
+ const attributeName = command.expressionAttributeNames[alias];
813
+ const regex = new RegExp(alias, "g");
814
+ replacedString = replacedString.replace(regex, attributeName);
815
+ }
816
+ for (const alias in command.expressionAttributeValues) {
817
+ let attributeValue = command.expressionAttributeValues[alias];
818
+ if (attributeValue instanceof Set) {
819
+ const array = Array.from(attributeValue);
820
+ attributeValue = `Set(${array.length}){${array.map((v) => JSON.stringify(v)).join(", ")}}`;
821
+ } else {
822
+ attributeValue = JSON.stringify(attributeValue);
823
+ }
824
+ const regex = new RegExp(alias, "g");
825
+ replacedString = replacedString.replace(regex, attributeValue);
826
+ }
827
+ return replacedString;
828
+ }
829
+ if (command.updateExpression) {
830
+ result.updateExpression = replaceAliases(command.updateExpression);
831
+ }
832
+ if (command.conditionExpression) {
833
+ result.conditionExpression = replaceAliases(command.conditionExpression);
834
+ }
835
+ if (command.filterExpression) {
836
+ result.filterExpression = replaceAliases(command.filterExpression);
837
+ }
838
+ if (command.keyConditionExpression) {
839
+ result.keyConditionExpression = replaceAliases(command.keyConditionExpression);
840
+ }
841
+ if (command.projectionExpression) {
842
+ result.projectionExpression = replaceAliases(command.projectionExpression);
843
+ }
844
+ return {
845
+ raw: command,
846
+ readable: result
847
+ };
848
+ }
849
+
850
+ // src/builders/put-builder.ts
851
+ var PutBuilder = class {
852
+ item;
853
+ options;
854
+ executor;
855
+ tableName;
856
+ constructor(executor, item, tableName) {
857
+ this.executor = executor;
858
+ this.item = item;
859
+ this.tableName = tableName;
860
+ this.options = {
861
+ returnValues: "NONE"
862
+ };
863
+ }
864
+ set(valuesOrPath, value) {
865
+ if (typeof valuesOrPath === "object") {
866
+ Object.assign(this.item, valuesOrPath);
867
+ } else {
868
+ this.item[valuesOrPath] = value;
869
+ }
870
+ return this;
871
+ }
872
+ /**
873
+ * Adds a condition that must be satisfied for the put operation to succeed.
874
+ * Use this method when you need to:
875
+ * - Prevent overwriting existing items (optimistic locking)
876
+ * - Ensure items meet certain criteria before replacement
877
+ * - Implement complex business rules for item updates
878
+ *
879
+ * @example
880
+ * ```ts
881
+ * // Ensure item doesn't exist (insert only)
882
+ * builder.condition(op => op.attributeNotExists('id'))
883
+ *
884
+ * // Complex condition with version check
885
+ * builder.condition(op =>
886
+ * op.and([
887
+ * op.attributeExists('id'),
888
+ * op.eq('version', currentVersion),
889
+ * op.eq('status', 'ACTIVE')
890
+ * ])
891
+ * )
892
+ * ```
893
+ *
894
+ * @param condition - Either a Condition object or a callback function that builds the condition
895
+ * @returns The builder instance for method chaining
896
+ */
897
+ /**
898
+ * Adds a condition that must be satisfied for the put operation to succeed.
899
+ *
900
+ * @example
901
+ * ```typescript
902
+ * // Ensure unique dinosaur ID
903
+ * builder.condition(op =>
904
+ * op.attributeNotExists('id')
905
+ * );
906
+ *
907
+ * // Verify habitat requirements
908
+ * builder.condition(op =>
909
+ * op.and([
910
+ * op.eq('securityStatus', 'READY'),
911
+ * op.attributeExists('lastInspection'),
912
+ * op.gt('securityLevel', 5)
913
+ * ])
914
+ * );
915
+ *
916
+ * // Check breeding facility conditions
917
+ * builder.condition(op =>
918
+ * op.and([
919
+ * op.between('temperature', 25, 30),
920
+ * op.between('humidity', 60, 80),
921
+ * op.eq('quarantineStatus', 'CLEAR')
922
+ * ])
923
+ * );
924
+ * ```
925
+ *
926
+ * @param condition - Either a Condition object or a callback function that builds the condition
927
+ * @returns The builder instance for method chaining
928
+ */
929
+ condition(condition) {
930
+ if (typeof condition === "function") {
931
+ const conditionOperator = {
932
+ eq,
933
+ ne,
934
+ lt,
935
+ lte,
936
+ gt,
937
+ gte,
938
+ between,
939
+ beginsWith,
940
+ contains,
941
+ attributeExists,
942
+ attributeNotExists,
943
+ and,
944
+ or,
945
+ not
946
+ };
947
+ this.options.condition = condition(conditionOperator);
948
+ } else {
949
+ this.options.condition = condition;
950
+ }
951
+ return this;
952
+ }
953
+ /**
954
+ * Sets whether to return the item's previous values (if it existed).
955
+ *
956
+ * @options
957
+ * - NONE: No return value
958
+ * - ALL_OLD: Returns the item's previous state if it existed, no read capacity units are consumed
959
+ * - CONSISTENT: (default) Performs a GET operation after the put to retrieve the item's new state
960
+ *
961
+ * @example
962
+ * ```ts
963
+ * // Get previous dinosaur state
964
+ * const result = await builder
965
+ * .returnValues('ALL_OLD')
966
+ * .execute();
967
+ *
968
+ * if (result) {
969
+ * console.log('Previous profile:', {
970
+ * species: result.species,
971
+ * status: result.status,
972
+ * stats: {
973
+ * health: result.stats.health,
974
+ * threatLevel: result.stats.threatLevel
975
+ * }
976
+ * });
977
+ * }
978
+ * ```
979
+ *
980
+ * @param returnValues - Use 'ALL_OLD' to return previous values if the item was overwritten, or 'NONE' (default).
981
+ * @returns The builder instance for method chaining
982
+ */
983
+ returnValues(returnValues) {
984
+ this.options.returnValues = returnValues;
985
+ return this;
986
+ }
987
+ /**
988
+ * Generate the DynamoDB command parameters
989
+ */
990
+ toDynamoCommand() {
991
+ const { expression, names, values } = prepareExpressionParams(this.options.condition);
992
+ return {
993
+ tableName: this.tableName,
994
+ item: this.item,
995
+ conditionExpression: expression,
996
+ expressionAttributeNames: names,
997
+ expressionAttributeValues: values,
998
+ returnValues: this.options.returnValues
999
+ };
1000
+ }
1001
+ /**
1002
+ * Adds this put operation to a transaction.
1003
+ * Use this method when you need to:
1004
+ * - Transfer dinosaurs between habitats
1005
+ * - Initialize new breeding programs
1006
+ * - Update multiple facility records
1007
+ *
1008
+ * @example
1009
+ * ```ts
1010
+ * const transaction = new TransactionBuilder();
1011
+ *
1012
+ * // Add dinosaur to new habitat
1013
+ * new PutBuilder(executor, {
1014
+ * id: 'TREX-002',
1015
+ * location: 'PADDOCK-B',
1016
+ * status: 'ACTIVE',
1017
+ * transferDate: new Date().toISOString()
1018
+ * }, 'dinosaurs')
1019
+ * .withTransaction(transaction);
1020
+ *
1021
+ * // Update habitat records
1022
+ * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-B' })
1023
+ * .add('occupants', 1)
1024
+ * .set('lastTransfer', new Date().toISOString())
1025
+ * .withTransaction(transaction);
1026
+ *
1027
+ * // Execute transfer atomically
1028
+ * await transaction.execute();
1029
+ * ```
1030
+ *
1031
+ * @param transaction - The transaction builder to add this operation to
1032
+ * @returns The builder instance for method chaining
1033
+ */
1034
+ withTransaction(transaction) {
1035
+ const command = this.toDynamoCommand();
1036
+ transaction.putWithCommand(command);
1037
+ return this;
1038
+ }
1039
+ /**
1040
+ * Executes the put operation against DynamoDB.
1041
+ *
1042
+ * @example
1043
+ * ```ts
1044
+ * try {
1045
+ * // Put with condition and return old values
1046
+ * const result = await new PutBuilder(executor, newItem, 'myTable')
1047
+ * .condition(op => op.eq('version', 1))
1048
+ * .returnValues('ALL_OLD')
1049
+ * .execute();
1050
+ *
1051
+ * console.log('Put successful, old item:', result);
1052
+ * } catch (error) {
1053
+ * // Handle condition check failure or other errors
1054
+ * console.error('Put failed:', error);
1055
+ * }
1056
+ * ```
1057
+ *
1058
+ * @returns A promise that resolves to the operation result (type depends on returnValues setting)
1059
+ * @throws Will throw an error if the condition check fails or other DynamoDB errors occur
1060
+ */
1061
+ async execute() {
1062
+ const params = this.toDynamoCommand();
1063
+ return this.executor(params);
1064
+ }
1065
+ /**
1066
+ * Gets a human-readable representation of the put command
1067
+ * with all expression placeholders replaced by their actual values.
1068
+ *
1069
+ * @example
1070
+ * ```ts
1071
+ * const debugInfo = new PutBuilder(executor, {
1072
+ * id: 'RAPTOR-003',
1073
+ * species: 'Velociraptor',
1074
+ * status: 'QUARANTINE',
1075
+ * stats: {
1076
+ * health: 100,
1077
+ * aggressionLevel: 7,
1078
+ * age: 2
1079
+ * }
1080
+ * }, 'dinosaurs')
1081
+ * .condition(op =>
1082
+ * op.and([
1083
+ * op.attributeNotExists('id'),
1084
+ * op.eq('quarantineStatus', 'READY'),
1085
+ * op.gt('securityLevel', 8)
1086
+ * ])
1087
+ * )
1088
+ * .debug();
1089
+ *
1090
+ * console.log('Dinosaur transfer command:', debugInfo);
1091
+ * ```
1092
+ *
1093
+ * @returns A readable representation of the put command with resolved expressions
1094
+ */
1095
+ debug() {
1096
+ const command = this.toDynamoCommand();
1097
+ return debugCommand(command);
1098
+ }
1099
+ };
1100
+
1101
+ // src/builders/delete-builder.ts
1102
+ var DeleteBuilder = class {
1103
+ options = {
1104
+ returnValues: "ALL_OLD"
1105
+ };
1106
+ executor;
1107
+ tableName;
1108
+ key;
1109
+ constructor(executor, tableName, key) {
1110
+ this.executor = executor;
1111
+ this.tableName = tableName;
1112
+ this.key = key;
1113
+ }
1114
+ /**
1115
+ * Adds a condition that must be satisfied for the delete operation to succeed.
1116
+ *
1117
+ * @example
1118
+ * ```typescript
1119
+ * // Ensure dinosaur can be safely removed
1120
+ * builder.condition(op =>
1121
+ * op.and([
1122
+ * op.eq('status', 'SEDATED'),
1123
+ * op.eq('location', 'MEDICAL_BAY'),
1124
+ * op.attributeExists('lastCheckup')
1125
+ * ])
1126
+ * );
1127
+ *
1128
+ * // Verify habitat is empty
1129
+ * builder.condition(op =>
1130
+ * op.and([
1131
+ * op.eq('occupants', 0),
1132
+ * op.eq('maintenanceStatus', 'COMPLETE'),
1133
+ * op.not(op.attributeExists('activeAlerts'))
1134
+ * ])
1135
+ * );
1136
+ * ```
1137
+ *
1138
+ * @param condition - Either a Condition object or a callback function that builds the condition
1139
+ * @returns The builder instance for method chaining
1140
+ */
1141
+ condition(condition) {
1142
+ if (typeof condition === "function") {
1143
+ const conditionOperator = {
1144
+ eq,
1145
+ ne,
1146
+ lt,
1147
+ lte,
1148
+ gt,
1149
+ gte,
1150
+ between,
1151
+ beginsWith,
1152
+ contains,
1153
+ attributeExists,
1154
+ attributeNotExists,
1155
+ and,
1156
+ or,
1157
+ not
1158
+ };
1159
+ this.options.condition = condition(conditionOperator);
1160
+ } else {
1161
+ this.options.condition = condition;
1162
+ }
1163
+ return this;
1164
+ }
1165
+ /**
1166
+ * Sets whether to return the item's attribute values before deletion.
1167
+ *
1168
+ * @example
1169
+ * ```ts
1170
+ * // Archive dinosaur data before removal
1171
+ * const result = await builder
1172
+ * .returnValues('ALL_OLD')
1173
+ * .execute();
1174
+ *
1175
+ * if (result.item) {
1176
+ * console.log('Removed dinosaur data:', {
1177
+ * species: result.item.species,
1178
+ * age: result.item.age,
1179
+ * lastLocation: result.item.location
1180
+ * });
1181
+ * }
1182
+ * ```
1183
+ *
1184
+ * @param returnValues - Use 'ALL_OLD' to return all attributes of the deleted item
1185
+ * @returns The builder instance for method chaining
1186
+ */
1187
+ returnValues(returnValues) {
1188
+ this.options.returnValues = returnValues;
1189
+ return this;
1190
+ }
1191
+ /**
1192
+ * Generate the DynamoDB command parameters
1193
+ */
1194
+ toDynamoCommand() {
1195
+ const { expression, names, values } = prepareExpressionParams(this.options.condition);
1196
+ return {
1197
+ tableName: this.tableName,
1198
+ key: this.key,
1199
+ conditionExpression: expression,
1200
+ expressionAttributeNames: names,
1201
+ expressionAttributeValues: values,
1202
+ returnValues: this.options.returnValues
1203
+ };
1204
+ }
1205
+ /**
1206
+ * Adds this delete operation to a transaction.
1207
+ *
1208
+ * @example
1209
+ * ```ts
1210
+ * const transaction = new TransactionBuilder();
1211
+ *
1212
+ * // Remove dinosaur from old habitat
1213
+ * new DeleteBuilder(executor, 'dinosaurs', { id: 'RAPTOR-001' })
1214
+ * .condition(op => op.eq('status', 'SEDATED'))
1215
+ * .withTransaction(transaction);
1216
+ *
1217
+ * // Update old habitat occupancy
1218
+ * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-A' })
1219
+ * .add('occupants', -1)
1220
+ * .withTransaction(transaction);
1221
+ *
1222
+ * // Execute transfer atomically
1223
+ * await transaction.execute();
1224
+ * ```
1225
+ *
1226
+ * @param transaction - The transaction builder to add this operation to
1227
+ */
1228
+ withTransaction(transaction) {
1229
+ const command = this.toDynamoCommand();
1230
+ transaction.deleteWithCommand(command);
1231
+ }
1232
+ /**
1233
+ * Executes the delete operation against DynamoDB.
1234
+ *
1235
+ * @example
1236
+ * ```ts
1237
+ * // Delete with condition and retrieve old values
1238
+ * const result = await new DeleteBuilder(executor, 'myTable', { id: '123' })
1239
+ * .condition(op => op.eq('status', 'INACTIVE'))
1240
+ * .returnValues('ALL_OLD')
1241
+ * .execute();
1242
+ *
1243
+ * if (result.item) {
1244
+ * console.log('Deleted item:', result.item);
1245
+ * }
1246
+ * ```
1247
+ *
1248
+ * @returns A promise that resolves to an object containing the deleted item's attributes (if returnValues is 'ALL_OLD')
1249
+ */
1250
+ async execute() {
1251
+ const params = this.toDynamoCommand();
1252
+ return this.executor(params);
1253
+ }
1254
+ /**
1255
+ * Gets a human-readable representation of the delete command
1256
+ * with all expression placeholders replaced by their actual values.
1257
+ *
1258
+ * @example
1259
+ * ```ts
1260
+ * const debugInfo = new DeleteBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
1261
+ * .condition(op => op.and([
1262
+ * op.eq('status', 'SEDATED'),
1263
+ * op.eq('location', 'MEDICAL_BAY'),
1264
+ * op.gt('sedationLevel', 8)
1265
+ * op.eq('version', 1),
1266
+ * op.attributeExists('status')
1267
+ * ]))
1268
+ * .debug();
1269
+ *
1270
+ * console.log('Delete command:', debugInfo);
1271
+ * ```
1272
+ *
1273
+ * @returns A readable representation of the delete command with resolved expressions
1274
+ */
1275
+ debug() {
1276
+ const command = this.toDynamoCommand();
1277
+ return debugCommand(command);
1278
+ }
1279
+ };
1280
+
1281
+ // src/builders/update-builder.ts
1282
+ var UpdateBuilder = class {
1283
+ updates = [];
1284
+ options = {
1285
+ returnValues: "ALL_NEW"
1286
+ };
1287
+ executor;
1288
+ tableName;
1289
+ key;
1290
+ constructor(executor, tableName, key) {
1291
+ this.executor = executor;
1292
+ this.tableName = tableName;
1293
+ this.key = key;
1294
+ }
1295
+ set(valuesOrPath, value) {
1296
+ if (typeof valuesOrPath === "object") {
1297
+ for (const [key, value2] of Object.entries(valuesOrPath)) {
1298
+ this.updates.push({
1299
+ type: "SET",
1300
+ path: key,
1301
+ value: value2
1302
+ });
1303
+ }
1304
+ } else {
1305
+ this.updates.push({
1306
+ type: "SET",
1307
+ path: valuesOrPath,
1308
+ value
1309
+ });
1310
+ }
1311
+ return this;
1312
+ }
1313
+ /**
1314
+ * Removes an attribute from the item.
1315
+ *
1316
+ * @example
1317
+ * ```typescript
1318
+ * // Remove simple attributes
1319
+ * builder
1320
+ * .remove('temporaryTag')
1321
+ * .remove('previousLocation');
1322
+ *
1323
+ * // Remove nested attributes
1324
+ * builder
1325
+ * .remove('metadata.testData')
1326
+ * .remove('stats.experimentalMetrics');
1327
+ * ```
1328
+ *
1329
+ * @param path - The path to the attribute to remove
1330
+ * @returns The builder instance for method chaining
1331
+ */
1332
+ remove(path) {
1333
+ this.updates.push({
1334
+ type: "REMOVE",
1335
+ path
1336
+ });
1337
+ return this;
1338
+ }
1339
+ /**
1340
+ * Adds a value to a number attribute or adds elements to a set.
1341
+ *
1342
+ * @example
1343
+ * ```typescript
1344
+ * // Increment counters
1345
+ * builder
1346
+ * .add('escapeAttempts', 1)
1347
+ * .add('feedingCount', 1);
1348
+ *
1349
+ * // Add to sets
1350
+ * builder
1351
+ * .add('knownBehaviors', new Set(['PACK_HUNTING', 'AMBUSH_TACTICS']))
1352
+ * .add('visitedZones', new Set(['ZONE_A', 'ZONE_B']));
1353
+ * ```
1354
+ *
1355
+ * @param path - The path to the attribute to update
1356
+ * @param value - The value to add (number or set)
1357
+ * @returns The builder instance for method chaining
1358
+ */
1359
+ add(path, value) {
1360
+ this.updates.push({
1361
+ type: "ADD",
1362
+ path,
1363
+ value
1364
+ });
1365
+ return this;
1366
+ }
1367
+ /**
1368
+ * Removes elements from a set attribute.
1369
+ *
1370
+ * @example
1371
+ * ```typescript
1372
+ * // Remove from sets using arrays
1373
+ * builder.deleteElementsFromSet(
1374
+ * 'allowedHabitats',
1375
+ * ['JUNGLE', 'COASTAL']
1376
+ * );
1377
+ *
1378
+ * // Remove from sets using Set DynamoItems
1379
+ * builder.deleteElementsFromSet(
1380
+ * 'knownBehaviors',
1381
+ * new Set(['NOCTURNAL', 'TERRITORIAL'])
1382
+ * );
1383
+ *
1384
+ * // Remove from nested sets
1385
+ * builder.deleteElementsFromSet(
1386
+ * 'stats.compatibleSpecies',
1387
+ * ['VELOCIRAPTOR', 'DILOPHOSAURUS']
1388
+ * );
1389
+ * ```
1390
+ *
1391
+ * @param path - The path to the set attribute
1392
+ * @param value - Elements to remove (array or Set)
1393
+ * @returns The builder instance for method chaining
1394
+ */
1395
+ deleteElementsFromSet(path, value) {
1396
+ let valuesToDelete;
1397
+ if (Array.isArray(value)) {
1398
+ valuesToDelete = new Set(value);
1399
+ } else {
1400
+ valuesToDelete = value;
1401
+ }
1402
+ this.updates.push({
1403
+ type: "DELETE",
1404
+ path,
1405
+ value: valuesToDelete
1406
+ });
1407
+ return this;
1408
+ }
1409
+ /**
1410
+ * Adds a condition that must be satisfied for the update to succeed.
1411
+ *
1412
+ * @example
1413
+ * ```typescript
1414
+ * // Simple condition
1415
+ * builder.condition(op =>
1416
+ * op.eq('status', 'ACTIVE')
1417
+ * );
1418
+ *
1419
+ * // Health check condition
1420
+ * builder.condition(op =>
1421
+ * op.and([
1422
+ * op.gt('health', 50),
1423
+ * op.eq('status', 'HUNTING')
1424
+ * ])
1425
+ * );
1426
+ *
1427
+ * // Complex security condition
1428
+ * builder.condition(op =>
1429
+ * op.and([
1430
+ * op.attributeExists('securitySystem'),
1431
+ * op.eq('containmentStatus', 'SECURE'),
1432
+ * op.lt('aggressionLevel', 8)
1433
+ * ])
1434
+ * );
1435
+ *
1436
+ * // Version check (optimistic locking)
1437
+ * builder.condition(op =>
1438
+ * op.eq('version', currentVersion)
1439
+ * );
1440
+ * ```
1441
+ *
1442
+ * @param condition - Either a Condition DynamoItem or a callback function that builds the condition
1443
+ * @returns The builder instance for method chaining
1444
+ */
1445
+ condition(condition) {
1446
+ if (typeof condition === "function") {
1447
+ const conditionOperator = {
1448
+ eq,
1449
+ ne,
1450
+ lt,
1451
+ lte,
1452
+ gt,
1453
+ gte,
1454
+ between,
1455
+ beginsWith,
1456
+ contains,
1457
+ attributeExists,
1458
+ attributeNotExists,
1459
+ and,
1460
+ or,
1461
+ not
1462
+ };
1463
+ this.options.condition = condition(conditionOperator);
1464
+ } else {
1465
+ this.options.condition = condition;
1466
+ }
1467
+ return this;
1468
+ }
1469
+ /**
1470
+ * Sets which item attributes to include in the response.
1471
+ *
1472
+ * Available options:
1473
+ * - ALL_NEW: All attributes after the update
1474
+ * - UPDATED_NEW: Only updated attributes, new values
1475
+ * - ALL_OLD: All attributes before the update
1476
+ * - UPDATED_OLD: Only updated attributes, old values
1477
+ * - NONE: No attributes returned (default)
1478
+ *
1479
+ * @example
1480
+ * ```typescript
1481
+ * // Get complete updated dinosaur
1482
+ * const result = await builder
1483
+ * .set('status', 'SLEEPING')
1484
+ * .returnValues('ALL_NEW')
1485
+ * .execute();
1486
+ *
1487
+ * // Track specific attribute changes
1488
+ * const result = await builder
1489
+ * .set({
1490
+ * 'stats.health': 100,
1491
+ * 'stats.energy': 95
1492
+ * })
1493
+ * .returnValues('UPDATED_OLD')
1494
+ * .execute();
1495
+ *
1496
+ * if (result.item) {
1497
+ * console.log('Previous health:', result.item.stats?.health);
1498
+ * }
1499
+ * ```
1500
+ *
1501
+ * @param returnValues - Which attributes to return in the response
1502
+ * @returns The builder instance for method chaining
1503
+ */
1504
+ returnValues(returnValues) {
1505
+ this.options.returnValues = returnValues;
1506
+ return this;
1507
+ }
1508
+ /**
1509
+ * Generate the DynamoDB command parameters
1510
+ */
1511
+ toDynamoCommand() {
1512
+ if (this.updates.length === 0) {
1513
+ throw new Error("No update actions specified");
1514
+ }
1515
+ const expressionParams = {
1516
+ expressionAttributeNames: {},
1517
+ expressionAttributeValues: {},
1518
+ valueCounter: { count: 0 }
1519
+ };
1520
+ let updateExpression = "";
1521
+ const setUpdates = [];
1522
+ const removeUpdates = [];
1523
+ const addUpdates = [];
1524
+ const deleteUpdates = [];
1525
+ for (const update of this.updates) {
1526
+ switch (update.type) {
1527
+ case "SET":
1528
+ setUpdates.push(update);
1529
+ break;
1530
+ case "REMOVE":
1531
+ removeUpdates.push(update);
1532
+ break;
1533
+ case "ADD":
1534
+ addUpdates.push(update);
1535
+ break;
1536
+ case "DELETE":
1537
+ deleteUpdates.push(update);
1538
+ break;
1539
+ }
1540
+ }
1541
+ if (setUpdates.length > 0) {
1542
+ updateExpression += "SET ";
1543
+ updateExpression += setUpdates.map((update) => {
1544
+ const attrName = generateAttributeName(expressionParams, update.path);
1545
+ const valueName = generateValueName(expressionParams, update.value);
1546
+ expressionParams.expressionAttributeValues[valueName] = update.value;
1547
+ return `${attrName} = ${valueName}`;
1548
+ }).join(", ");
1549
+ }
1550
+ if (removeUpdates.length > 0) {
1551
+ if (updateExpression) {
1552
+ updateExpression += " ";
1553
+ }
1554
+ updateExpression += "REMOVE ";
1555
+ updateExpression += removeUpdates.map((update) => {
1556
+ return generateAttributeName(expressionParams, update.path);
1557
+ }).join(", ");
1558
+ }
1559
+ if (addUpdates.length > 0) {
1560
+ if (updateExpression) {
1561
+ updateExpression += " ";
1562
+ }
1563
+ updateExpression += "ADD ";
1564
+ updateExpression += addUpdates.map((update) => {
1565
+ const attrName = generateAttributeName(expressionParams, update.path);
1566
+ const valueName = generateValueName(expressionParams, update.value);
1567
+ return `${attrName} ${valueName}`;
1568
+ }).join(", ");
1569
+ }
1570
+ if (deleteUpdates.length > 0) {
1571
+ if (updateExpression) {
1572
+ updateExpression += " ";
1573
+ }
1574
+ updateExpression += "DELETE ";
1575
+ updateExpression += deleteUpdates.map((update) => {
1576
+ const attrName = generateAttributeName(expressionParams, update.path);
1577
+ const valueName = generateValueName(expressionParams, update.value);
1578
+ return `${attrName} ${valueName}`;
1579
+ }).join(", ");
1580
+ }
1581
+ let conditionExpression;
1582
+ if (this.options.condition) {
1583
+ conditionExpression = buildExpression(this.options.condition, expressionParams);
1584
+ }
1585
+ const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
1586
+ return {
1587
+ tableName: this.tableName,
1588
+ key: this.key,
1589
+ updateExpression,
1590
+ conditionExpression,
1591
+ expressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
1592
+ expressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
1593
+ returnValues: this.options.returnValues
1594
+ };
1595
+ }
1596
+ /**
1597
+ * Adds this update operation to a transaction.
1598
+ *
1599
+ * @example
1600
+ * ```typescript
1601
+ * const transaction = new TransactionBuilder(executor);
1602
+ *
1603
+ * // Update dinosaur status and habitat occupancy atomically
1604
+ * new UpdateBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
1605
+ * .set('location', 'PADDOCK_A')
1606
+ * .set('status', 'CONTAINED')
1607
+ * .withTransaction(transaction);
1608
+ *
1609
+ * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-A' })
1610
+ * .add('occupants', 1)
1611
+ * .set('lastOccupied', new Date().toISOString())
1612
+ * .withTransaction(transaction);
1613
+ *
1614
+ * // Execute all operations atomically
1615
+ * await transaction.execute();
1616
+ * ```
1617
+ *
1618
+ * @param transaction - The transaction builder to add this operation to
1619
+ * @returns The builder instance for method chaining
1620
+ */
1621
+ withTransaction(transaction) {
1622
+ const command = this.toDynamoCommand();
1623
+ transaction.updateWithCommand(command);
1624
+ }
1625
+ /**
1626
+ * Gets a human-readable representation of the update command.
1627
+ *
1628
+ * @example
1629
+ * ```typescript
1630
+ * // Create complex update
1631
+ * const builder = new UpdateBuilder(executor, 'dinosaurs', { id: 'RAPTOR-001' })
1632
+ * .set({
1633
+ * status: 'HUNTING',
1634
+ * 'stats.health': 95,
1635
+ * 'behavior.lastObserved': new Date().toISOString()
1636
+ * })
1637
+ * .add('huntingSuccesses', 1)
1638
+ * .condition(op => op.gt('health', 50));
1639
+ *
1640
+ * // Debug the update
1641
+ * const debugInfo = builder.debug();
1642
+ * console.log('Update operation:', debugInfo);
1643
+ * ```
1644
+ *
1645
+ * @returns A readable representation of the update command with resolved expressions
1646
+ */
1647
+ debug() {
1648
+ const command = this.toDynamoCommand();
1649
+ return debugCommand(command);
1650
+ }
1651
+ /**
1652
+ * Executes the update operation against DynamoDB.
1653
+ *
1654
+ * @example
1655
+ * ```typescript
1656
+ * try {
1657
+ * // Update dinosaur status with conditions
1658
+ * const result = await new UpdateBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
1659
+ * .set({
1660
+ * status: 'FEEDING',
1661
+ * lastMeal: new Date().toISOString(),
1662
+ * 'stats.hunger': 0
1663
+ * })
1664
+ * .add('feedingCount', 1)
1665
+ * .condition(op =>
1666
+ * op.and([
1667
+ * op.gt('stats.hunger', 80),
1668
+ * op.eq('status', 'HUNTING')
1669
+ * ])
1670
+ * )
1671
+ * .returnValues('ALL_NEW')
1672
+ * .execute();
1673
+ *
1674
+ * if (result.item) {
1675
+ * console.log('Updated dinosaur:', result.item);
1676
+ * }
1677
+ * } catch (error) {
1678
+ * // Handle condition check failure
1679
+ * console.error('Failed to update dinosaur:', error);
1680
+ * // Check if dinosaur wasn't hungry enough
1681
+ * if (error.name === 'ConditionalCheckFailedException') {
1682
+ * console.log('Dinosaur not ready for feeding');
1683
+ * }
1684
+ * }
1685
+ * ```
1686
+ *
1687
+ * @returns A promise that resolves to an DynamoItem containing the updated item (if returnValues is set)
1688
+ * @throws {ConditionalCheckFailedException} If the condition check fails
1689
+ * @throws {Error} If the update operation fails for other reasons
1690
+ */
1691
+ async execute() {
1692
+ const params = this.toDynamoCommand();
1693
+ return this.executor(params);
1694
+ }
1695
+ };
1696
+
1697
+ // src/utils/debug-transaction.ts
1698
+ function debugTransactionItem(item) {
1699
+ const result = {
1700
+ type: item.type,
1701
+ tableName: item.params.tableName
1702
+ };
1703
+ if ("key" in item.params) {
1704
+ result.key = item.params.key;
1705
+ }
1706
+ if (item.type === "Put") {
1707
+ result.item = item.params.item;
1708
+ }
1709
+ switch (item.type) {
1710
+ case "Put":
1711
+ case "Delete":
1712
+ case "ConditionCheck":
1713
+ result.readable = debugCommand(item.params).readable;
1714
+ break;
1715
+ case "Update":
1716
+ result.readable = debugCommand(item.params).readable;
1717
+ break;
1718
+ }
1719
+ return result;
1720
+ }
1721
+ function debugTransaction(items) {
1722
+ return items.map((item) => debugTransactionItem(item));
1723
+ }
1724
+
1725
+ // src/builders/transaction-builder.ts
1726
+ var TransactionBuilder = class {
1727
+ items = [];
1728
+ options = {};
1729
+ indexConfig;
1730
+ executor;
1731
+ constructor(executor, indexConfig) {
1732
+ this.executor = executor;
1733
+ this.indexConfig = indexConfig;
1734
+ }
1735
+ /**
1736
+ * Checks if an item with the same primary key already exists in the transaction
1737
+ * @private
1738
+ */
1739
+ checkForDuplicateItem(tableName, newItem) {
1740
+ const pkName = this.indexConfig.partitionKey;
1741
+ const skName = this.indexConfig.sortKey ?? "";
1742
+ const pkValue = newItem[pkName];
1743
+ const skValue = skName ? newItem[skName] : void 0;
1744
+ if (!pkValue) {
1745
+ throw new Error(`Primary key value for '${pkName}' is missing`);
1746
+ }
1747
+ const duplicateItem = this.items.find((item) => {
1748
+ let itemKey;
1749
+ let itemTableName;
1750
+ switch (item.type) {
1751
+ case "Put":
1752
+ itemTableName = item.params.tableName;
1753
+ itemKey = item.params.item;
1754
+ break;
1755
+ case "Update":
1756
+ case "Delete":
1757
+ case "ConditionCheck":
1758
+ itemTableName = item.params.tableName;
1759
+ itemKey = item.params.key;
1760
+ break;
1761
+ }
1762
+ if (itemTableName === tableName && itemKey) {
1763
+ const itemPkValue = itemKey[pkName];
1764
+ const itemSkValue = skName ? itemKey[skName] : void 0;
1765
+ if (itemPkValue === pkValue) {
1766
+ if (skValue === void 0 && itemSkValue === void 0) {
1767
+ return true;
1768
+ }
1769
+ if (skValue !== void 0 && itemSkValue !== void 0 && skValue === itemSkValue) {
1770
+ return true;
1771
+ }
1772
+ }
1773
+ }
1774
+ return false;
1775
+ });
1776
+ if (duplicateItem) {
1777
+ throw new Error(
1778
+ `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.`
1779
+ );
1780
+ }
1781
+ }
1782
+ createKeyForPrimaryIndex(key) {
1783
+ const keyCondition = {
1784
+ [this.indexConfig.partitionKey]: key.pk
1785
+ };
1786
+ if (this.indexConfig.sortKey) {
1787
+ if (key.sk === void 0) {
1788
+ throw new Error("Sort key is required for delete operation");
1789
+ }
1790
+ keyCondition[this.indexConfig.sortKey] = key.sk;
1791
+ }
1792
+ return keyCondition;
1793
+ }
1794
+ /**
1795
+ * Adds a put operation to the transaction.
1796
+ * Use this method when you need to:
1797
+ * - Insert new items as part of a transaction
1798
+ * - Replace existing items atomically
1799
+ * - Ensure items meet certain conditions before insertion
1800
+ *
1801
+ * The method automatically checks for duplicate items within the transaction
1802
+ * to prevent multiple operations on the same item.
1803
+ *
1804
+ * @example
1805
+ * ```typescript
1806
+ * // Simple put operation
1807
+ * transaction.put('orders', {
1808
+ * orderId: '123',
1809
+ * status: 'PENDING',
1810
+ * amount: 100
1811
+ * });
1812
+ *
1813
+ * // Conditional put operation
1814
+ * transaction.put(
1815
+ * 'inventory',
1816
+ * { productId: 'ABC', quantity: 50 },
1817
+ * op => op.attributeNotExists('productId')
1818
+ * );
1819
+ *
1820
+ * // Put with complex condition
1821
+ * transaction.put(
1822
+ * 'users',
1823
+ * { userId: '123', status: 'ACTIVE' },
1824
+ * op => op.and([
1825
+ * op.attributeNotExists('userId'),
1826
+ * op.beginsWith('status', 'ACTIVE')
1827
+ * ])
1828
+ * );
1829
+ * ```
1830
+ *
1831
+ * @param tableName - The name of the DynamoDB table
1832
+ * @param item - The item to put into the table
1833
+ * @param condition - Optional condition that must be satisfied
1834
+ * @returns The transaction builder for method chaining
1835
+ * @throws {Error} If a duplicate item is detected in the transaction
1836
+ */
1837
+ put(tableName, item, condition) {
1838
+ this.checkForDuplicateItem(tableName, item);
1839
+ const transactionItem = {
1840
+ type: "Put",
1841
+ params: {
1842
+ tableName,
1843
+ item
1844
+ }
1845
+ };
1846
+ if (condition) {
1847
+ const { expression, names, values } = prepareExpressionParams(condition);
1848
+ transactionItem.params.conditionExpression = expression;
1849
+ transactionItem.params.expressionAttributeNames = names;
1850
+ transactionItem.params.expressionAttributeValues = values;
1851
+ }
1852
+ this.items.push(transactionItem);
1853
+ return this;
1854
+ }
1855
+ /**
1856
+ * Adds a pre-configured put operation to the transaction.
1857
+ * Use this method when you need to:
1858
+ * - Reuse put commands from PutBuilder
1859
+ * - Add complex put operations with pre-configured parameters
1860
+ * - Integrate with existing put command configurations
1861
+ *
1862
+ * This method is particularly useful when working with PutBuilder
1863
+ * to maintain consistency in put operations across your application.
1864
+ *
1865
+ * @example
1866
+ * ```typescript
1867
+ * // Create a put command with PutBuilder
1868
+ * const putCommand = new PutBuilder(executor, newItem, 'users')
1869
+ * .condition(op => op.attributeNotExists('userId'))
1870
+ * .toDynamoCommand();
1871
+ *
1872
+ * // Add the command to the transaction
1873
+ * transaction.putWithCommand(putCommand);
1874
+ * ```
1875
+ *
1876
+ * @param command - The complete put command configuration
1877
+ * @returns The transaction builder for method chaining
1878
+ * @throws {Error} If a duplicate item is detected in the transaction
1879
+ * @see PutBuilder for creating put commands
1880
+ */
1881
+ putWithCommand(command) {
1882
+ this.checkForDuplicateItem(command.tableName, command.item);
1883
+ const transactionItem = {
1884
+ type: "Put",
1885
+ params: command
1886
+ };
1887
+ this.items.push(transactionItem);
1888
+ return this;
1889
+ }
1890
+ /**
1891
+ * Adds a delete operation to the transaction.
1892
+ * Use this method when you need to:
1893
+ * - Remove items as part of a transaction
1894
+ * - Conditionally delete items
1895
+ * - Ensure items exist before deletion
1896
+ *
1897
+ * The method automatically checks for duplicate items within the transaction
1898
+ * to prevent multiple operations on the same item.
1899
+ *
1900
+ * @example
1901
+ * ```typescript
1902
+ * // Simple delete operation
1903
+ * transaction.delete('orders', {
1904
+ * pk: 'ORDER#123',
1905
+ * sk: 'METADATA'
1906
+ * });
1907
+ *
1908
+ * // Conditional delete operation
1909
+ * transaction.delete(
1910
+ * 'users',
1911
+ * { pk: 'USER#123' },
1912
+ * op => op.eq('status', 'INACTIVE')
1913
+ * );
1914
+ *
1915
+ * // Delete with complex condition
1916
+ * transaction.delete(
1917
+ * 'products',
1918
+ * { pk: 'PROD#ABC' },
1919
+ * op => op.and([
1920
+ * op.eq('status', 'DRAFT'),
1921
+ * op.lt('version', 5)
1922
+ * ])
1923
+ * );
1924
+ * ```
1925
+ *
1926
+ * @param tableName - The name of the DynamoDB table
1927
+ * @param key - The primary key of the item to delete
1928
+ * @param condition - Optional condition that must be satisfied
1929
+ * @returns The transaction builder for method chaining
1930
+ * @throws {Error} If a duplicate item is detected in the transaction
1931
+ */
1932
+ delete(tableName, key, condition) {
1933
+ const keyCondition = this.createKeyForPrimaryIndex(key);
1934
+ this.checkForDuplicateItem(tableName, keyCondition);
1935
+ const transactionItem = {
1936
+ type: "Delete",
1937
+ params: {
1938
+ tableName,
1939
+ key: keyCondition
1940
+ }
1941
+ };
1942
+ if (condition) {
1943
+ const { expression, names, values } = prepareExpressionParams(condition);
1944
+ transactionItem.params.conditionExpression = expression;
1945
+ transactionItem.params.expressionAttributeNames = names;
1946
+ transactionItem.params.expressionAttributeValues = values;
1947
+ }
1948
+ this.items.push(transactionItem);
1949
+ return this;
1950
+ }
1951
+ /**
1952
+ * Adds a pre-configured delete operation to the transaction.
1953
+ * Use this method when you need to:
1954
+ * - Reuse delete commands from DeleteBuilder
1955
+ * - Add complex delete operations with pre-configured parameters
1956
+ * - Integrate with existing delete command configurations
1957
+ *
1958
+ * This method is particularly useful when working with DeleteBuilder
1959
+ * to maintain consistency in delete operations across your application.
1960
+ *
1961
+ * @example
1962
+ * ```typescript
1963
+ * // Create a delete command with DeleteBuilder
1964
+ * const deleteCommand = new DeleteBuilder(executor, 'users', { pk: 'USER#123' })
1965
+ * .condition(op => op.and([
1966
+ * op.attributeExists('pk'),
1967
+ * op.eq('status', 'INACTIVE')
1968
+ * ]))
1969
+ * .toDynamoCommand();
1970
+ *
1971
+ * // Add the command to the transaction
1972
+ * transaction.deleteWithCommand(deleteCommand);
1973
+ * ```
1974
+ *
1975
+ * @param command - The complete delete command configuration
1976
+ * @returns The transaction builder for method chaining
1977
+ * @throws {Error} If a duplicate item is detected in the transaction
1978
+ * @see DeleteBuilder for creating delete commands
1979
+ */
1980
+ deleteWithCommand(command) {
1981
+ const keyCondition = this.createKeyForPrimaryIndex(command.key);
1982
+ this.checkForDuplicateItem(command.tableName, keyCondition);
1983
+ const transactionItem = {
1984
+ type: "Delete",
1985
+ params: {
1986
+ ...command,
1987
+ key: keyCondition
1988
+ }
1989
+ };
1990
+ this.items.push(transactionItem);
1991
+ return this;
1992
+ }
1993
+ /**
1994
+ * Adds an update operation to the transaction.
1995
+ * Use this method when you need to:
1996
+ * - Modify existing items as part of a transaction
1997
+ * - Update multiple attributes atomically
1998
+ * - Apply conditional updates
1999
+ * - Perform complex attribute manipulations
2000
+ *
2001
+ * The method supports all DynamoDB update expressions:
2002
+ * - SET: Modify or add attributes
2003
+ * - REMOVE: Delete attributes
2004
+ * - ADD: Update numbers and sets
2005
+ * - DELETE: Remove elements from a set
2006
+ *
2007
+ * @example
2008
+ * ```typescript
2009
+ * // Simple update
2010
+ * transaction.update(
2011
+ * 'orders',
2012
+ * { pk: 'ORDER#123' },
2013
+ * 'SET #status = :status',
2014
+ * { '#status': 'status' },
2015
+ * { ':status': 'PROCESSING' }
2016
+ * );
2017
+ *
2018
+ * // Complex update with multiple operations
2019
+ * transaction.update(
2020
+ * 'products',
2021
+ * { pk: 'PROD#ABC' },
2022
+ * 'SET #qty = #qty - :amount, #status = :status REMOVE #oldAttr',
2023
+ * { '#qty': 'quantity', '#status': 'status', '#oldAttr': 'deprecated_field' },
2024
+ * { ':amount': 1, ':status': 'LOW_STOCK' }
2025
+ * );
2026
+ *
2027
+ * // Conditional update
2028
+ * transaction.update(
2029
+ * 'users',
2030
+ * { pk: 'USER#123' },
2031
+ * 'SET #lastLogin = :now',
2032
+ * { '#lastLogin': 'lastLoginDate' },
2033
+ * { ':now': new Date().toISOString() },
2034
+ * op => op.attributeExists('pk')
2035
+ * );
2036
+ * ```
2037
+ *
2038
+ * @param tableName - The name of the DynamoDB table
2039
+ * @param key - The primary key of the item to update
2040
+ * @param updateExpression - The update expression (SET, REMOVE, ADD, DELETE)
2041
+ * @param expressionAttributeNames - Map of attribute name placeholders to actual names
2042
+ * @param expressionAttributeValues - Map of value placeholders to actual values
2043
+ * @param condition - Optional condition that must be satisfied
2044
+ * @returns The transaction builder for method chaining
2045
+ * @throws {Error} If a duplicate item is detected in the transaction
2046
+ */
2047
+ update(tableName, key, updateExpression, expressionAttributeNames, expressionAttributeValues, condition) {
2048
+ const keyCondition = this.createKeyForPrimaryIndex(key);
2049
+ this.checkForDuplicateItem(tableName, keyCondition);
2050
+ const transactionItem = {
2051
+ type: "Update",
2052
+ params: {
2053
+ tableName,
2054
+ key: keyCondition,
2055
+ updateExpression,
2056
+ expressionAttributeNames,
2057
+ expressionAttributeValues
2058
+ }
2059
+ };
2060
+ if (condition) {
2061
+ const { expression, names, values } = prepareExpressionParams(condition);
2062
+ transactionItem.params.conditionExpression = expression;
2063
+ transactionItem.params.expressionAttributeNames = {
2064
+ ...transactionItem.params.expressionAttributeNames,
2065
+ ...names
2066
+ };
2067
+ transactionItem.params.expressionAttributeValues = {
2068
+ ...transactionItem.params.expressionAttributeValues,
2069
+ ...values
2070
+ };
2071
+ }
2072
+ this.items.push(transactionItem);
2073
+ return this;
2074
+ }
2075
+ /**
2076
+ * Adds a pre-configured update operation to the transaction.
2077
+ * Use this method when you need to:
2078
+ * - Reuse update commands from UpdateBuilder
2079
+ * - Add complex update operations with pre-configured parameters
2080
+ * - Integrate with existing update command configurations
2081
+ *
2082
+ * This method is particularly useful when working with UpdateBuilder
2083
+ * to maintain consistency in update operations across your application.
2084
+ *
2085
+ * @example
2086
+ * ```typescript
2087
+ * // Create an update command with UpdateBuilder
2088
+ * const updateCommand = new UpdateBuilder(executor, 'inventory', { pk: 'PROD#ABC' })
2089
+ * .set('quantity', ':qty')
2090
+ * .set('lastUpdated', ':now')
2091
+ * .values({
2092
+ * ':qty': 100,
2093
+ * ':now': new Date().toISOString()
2094
+ * })
2095
+ * .condition(op => op.gt('quantity', 0))
2096
+ * .toDynamoCommand();
2097
+ *
2098
+ * // Add the command to the transaction
2099
+ * transaction.updateWithCommand(updateCommand);
2100
+ * ```
2101
+ *
2102
+ * @param command - The complete update command configuration
2103
+ * @returns The transaction builder for method chaining
2104
+ * @throws {Error} If a duplicate item is detected in the transaction
2105
+ * @see UpdateBuilder for creating update commands
2106
+ */
2107
+ updateWithCommand(command) {
2108
+ const keyCondition = this.createKeyForPrimaryIndex(command.key);
2109
+ this.checkForDuplicateItem(command.tableName, keyCondition);
2110
+ const transactionItem = {
2111
+ type: "Update",
2112
+ params: {
2113
+ ...command,
2114
+ key: keyCondition
2115
+ }
2116
+ };
2117
+ this.items.push(transactionItem);
2118
+ return this;
2119
+ }
2120
+ /**
2121
+ * Adds a condition check operation to the transaction.
2122
+ * Use this method when you need to:
2123
+ * - Validate item state without modifying it
2124
+ * - Ensure data consistency across tables
2125
+ * - Implement complex business rules
2126
+ * - Verify preconditions for other operations
2127
+ *
2128
+ * Condition checks are particularly useful for:
2129
+ * - Implementing optimistic locking
2130
+ * - Ensuring referential integrity
2131
+ * - Validating business rules atomically
2132
+ *
2133
+ * @example
2134
+ * ```typescript
2135
+ * // Check if order is in correct state
2136
+ * transaction.conditionCheck(
2137
+ * 'orders',
2138
+ * { pk: 'ORDER#123' },
2139
+ * op => op.eq('status', 'PENDING')
2140
+ * );
2141
+ *
2142
+ * // Complex condition check
2143
+ * transaction.conditionCheck(
2144
+ * 'inventory',
2145
+ * { pk: 'PROD#ABC' },
2146
+ * op => op.and([
2147
+ * op.gt('quantity', 0),
2148
+ * op.eq('status', 'ACTIVE'),
2149
+ * op.attributeExists('lastRestockDate')
2150
+ * ])
2151
+ * );
2152
+ *
2153
+ * // Check with multiple attributes
2154
+ * transaction.conditionCheck(
2155
+ * 'users',
2156
+ * { pk: 'USER#123' },
2157
+ * op => op.or([
2158
+ * op.eq('status', 'PREMIUM'),
2159
+ * op.gte('credits', 100)
2160
+ * ])
2161
+ * );
2162
+ * ```
2163
+ *
2164
+ * @param tableName - The name of the DynamoDB table
2165
+ * @param key - The primary key of the item to check
2166
+ * @param condition - The condition that must be satisfied
2167
+ * @returns The transaction builder for method chaining
2168
+ * @throws {Error} If a duplicate item is detected in the transaction
2169
+ * @throws {Error} If condition expression generation fails
2170
+ */
2171
+ conditionCheck(tableName, key, condition) {
2172
+ const keyCondition = this.createKeyForPrimaryIndex(key);
2173
+ this.checkForDuplicateItem(tableName, keyCondition);
2174
+ const { expression, names, values } = prepareExpressionParams(condition);
2175
+ if (!expression) {
2176
+ throw new Error("Failed to generate condition expression");
2177
+ }
2178
+ const transactionItem = {
2179
+ type: "ConditionCheck",
2180
+ params: {
2181
+ tableName,
2182
+ key: keyCondition,
2183
+ conditionExpression: expression,
2184
+ expressionAttributeNames: names,
2185
+ expressionAttributeValues: values
2186
+ }
2187
+ };
2188
+ this.items.push(transactionItem);
2189
+ return this;
2190
+ }
2191
+ /**
2192
+ * Adds a pre-configured condition check operation to the transaction.
2193
+ * Use this method when you need to:
2194
+ * - Reuse condition checks from ConditionCheckBuilder
2195
+ * - Add complex condition checks with pre-configured parameters
2196
+ * - Integrate with existing condition check configurations
2197
+ *
2198
+ * This method is particularly useful when working with ConditionCheckBuilder
2199
+ * to maintain consistency in condition checks across your application.
2200
+ *
2201
+ * @example
2202
+ * ```typescript
2203
+ * // Create a condition check with ConditionCheckBuilder
2204
+ * const checkCommand = new ConditionCheckBuilder('inventory', { pk: 'PROD#ABC' })
2205
+ * .condition(op => op.and([
2206
+ * op.between('quantity', 10, 100),
2207
+ * op.beginsWith('category', 'ELECTRONICS'),
2208
+ * op.attributeExists('lastAuditDate')
2209
+ * ]))
2210
+ * .toDynamoCommand();
2211
+ *
2212
+ * // Add the command to the transaction
2213
+ * transaction.conditionCheckWithCommand(checkCommand);
2214
+ * ```
2215
+ *
2216
+ * @param command - The complete condition check command configuration
2217
+ * @returns The transaction builder for method chaining
2218
+ * @throws {Error} If a duplicate item is detected in the transaction
2219
+ * @see ConditionCheckBuilder for creating condition check commands
2220
+ */
2221
+ conditionCheckWithCommand(command) {
2222
+ const keyCondition = this.createKeyForPrimaryIndex(command.key);
2223
+ this.checkForDuplicateItem(command.tableName, keyCondition);
2224
+ const transactionItem = {
2225
+ type: "ConditionCheck",
2226
+ params: {
2227
+ ...command,
2228
+ key: keyCondition
2229
+ }
2230
+ };
2231
+ this.items.push(transactionItem);
2232
+ return this;
2233
+ }
2234
+ /**
2235
+ * Sets options for the transaction execution.
2236
+ * Use this method when you need to:
2237
+ * - Enable idempotent transactions
2238
+ * - Track consumed capacity
2239
+ * - Monitor item collection metrics
2240
+ *
2241
+ * @example
2242
+ * ```typescript
2243
+ * // Enable idempotency and capacity tracking
2244
+ * transaction.withOptions({
2245
+ * clientRequestToken: 'unique-request-id-123',
2246
+ * returnConsumedCapacity: 'TOTAL'
2247
+ * });
2248
+ *
2249
+ * // Track item collection metrics
2250
+ * transaction.withOptions({
2251
+ * returnItemCollectionMetrics: 'SIZE'
2252
+ * });
2253
+ * ```
2254
+ *
2255
+ * Note: ClientRequestToken can be used to make transactions idempotent,
2256
+ * ensuring the same transaction is not executed multiple times.
2257
+ *
2258
+ * @param options - Configuration options for the transaction
2259
+ * @returns The transaction builder for method chaining
2260
+ */
2261
+ withOptions(options) {
2262
+ this.options = { ...this.options, ...options };
2263
+ return this;
2264
+ }
2265
+ /**
2266
+ * Gets a human-readable representation of the transaction items.
2267
+ * Use this method when you need to:
2268
+ * - Debug complex transactions
2269
+ * - Verify operation parameters
2270
+ * - Log transaction details
2271
+ * - Troubleshoot condition expressions
2272
+ *
2273
+ * The method resolves all expression placeholders with their actual values,
2274
+ * making it easier to understand the transaction's operations.
2275
+ *
2276
+ * @example
2277
+ * ```typescript
2278
+ * // Add multiple operations
2279
+ * transaction
2280
+ * .put('orders', { orderId: '123', status: 'PENDING' })
2281
+ * .update('inventory',
2282
+ * { productId: 'ABC' },
2283
+ * 'SET quantity = quantity - :amount',
2284
+ * undefined,
2285
+ * { ':amount': 1 }
2286
+ * );
2287
+ *
2288
+ * // Debug the transaction
2289
+ * const debugInfo = transaction.debug();
2290
+ * console.log('Transaction operations:', debugInfo);
2291
+ * ```
2292
+ *
2293
+ * @returns An array of readable representations of the transaction items
2294
+ */
2295
+ debug() {
2296
+ return debugTransaction(this.items);
2297
+ }
2298
+ /**
2299
+ * Executes all operations in the transaction atomically.
2300
+ * Use this method when you need to:
2301
+ * - Perform multiple operations atomically
2302
+ * - Ensure all-or-nothing execution
2303
+ * - Maintain data consistency across operations
2304
+ *
2305
+ * The transaction will only succeed if all operations succeed.
2306
+ * If any operation fails, the entire transaction is rolled back.
2307
+ *
2308
+ * @example
2309
+ * ```typescript
2310
+ * try {
2311
+ * // Build and execute transaction
2312
+ * await transaction
2313
+ * .put('orders', newOrder)
2314
+ * .update('inventory',
2315
+ * { productId: 'ABC' },
2316
+ * 'SET quantity = quantity - :qty',
2317
+ * undefined,
2318
+ * { ':qty': 1 }
2319
+ * )
2320
+ * .conditionCheck('products',
2321
+ * { productId: 'ABC' },
2322
+ * op => op.eq('status', 'ACTIVE')
2323
+ * )
2324
+ * .execute();
2325
+ *
2326
+ * console.log('Transaction completed successfully');
2327
+ * } catch (error) {
2328
+ * // Handle transaction failure
2329
+ * console.error('Transaction failed:', error);
2330
+ * }
2331
+ * ```
2332
+ *
2333
+ * @throws {Error} If no transaction items are specified
2334
+ * @throws {Error} If any operation in the transaction fails
2335
+ * @returns A promise that resolves when the transaction completes
2336
+ */
2337
+ async execute() {
2338
+ if (this.items.length === 0) {
2339
+ throw new Error("No transaction items specified");
2340
+ }
2341
+ const transactItems = this.items.map((item) => {
2342
+ switch (item.type) {
2343
+ case "Put":
2344
+ return {
2345
+ Put: {
2346
+ TableName: item.params.tableName,
2347
+ Item: item.params.item,
2348
+ ConditionExpression: item.params.conditionExpression,
2349
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
2350
+ ExpressionAttributeValues: item.params.expressionAttributeValues
2351
+ }
2352
+ };
2353
+ case "Delete":
2354
+ return {
2355
+ Delete: {
2356
+ TableName: item.params.tableName,
2357
+ Key: item.params.key,
2358
+ ConditionExpression: item.params.conditionExpression,
2359
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
2360
+ ExpressionAttributeValues: item.params.expressionAttributeValues
2361
+ }
2362
+ };
2363
+ case "Update":
2364
+ return {
2365
+ Update: {
2366
+ TableName: item.params.tableName,
2367
+ Key: item.params.key,
2368
+ UpdateExpression: item.params.updateExpression,
2369
+ ConditionExpression: item.params.conditionExpression,
2370
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
2371
+ ExpressionAttributeValues: item.params.expressionAttributeValues
2372
+ }
2373
+ };
2374
+ case "ConditionCheck":
2375
+ return {
2376
+ ConditionCheck: {
2377
+ TableName: item.params.tableName,
2378
+ Key: item.params.key,
2379
+ ConditionExpression: item.params.conditionExpression,
2380
+ ExpressionAttributeNames: item.params.expressionAttributeNames,
2381
+ ExpressionAttributeValues: item.params.expressionAttributeValues
2382
+ }
2383
+ };
2384
+ default: {
2385
+ const exhaustiveCheck = item;
2386
+ throw new Error(`Unsupported transaction item type: ${String(exhaustiveCheck)}`);
2387
+ }
2388
+ }
2389
+ });
2390
+ const params = {
2391
+ TransactItems: transactItems,
2392
+ ClientRequestToken: this.options.clientRequestToken,
2393
+ ReturnConsumedCapacity: this.options.returnConsumedCapacity,
2394
+ ReturnItemCollectionMetrics: this.options.returnItemCollectionMetrics
2395
+ };
2396
+ try {
2397
+ await this.executor(params);
2398
+ } catch (error) {
2399
+ console.log(this.debug());
2400
+ console.error("Error executing transaction:", error);
2401
+ throw error;
2402
+ }
2403
+ }
2404
+ };
2405
+
2406
+ // src/utils/chunk-array.ts
2407
+ function* chunkArray(array, size) {
2408
+ if (size <= 0) {
2409
+ throw new Error("Chunk size must be greater than 0");
2410
+ }
2411
+ for (let i = 0; i < array.length; i += size) {
2412
+ yield array.slice(i, i + size);
2413
+ }
2414
+ }
2415
+
2416
+ // src/builders/condition-check-builder.ts
2417
+ var ConditionCheckBuilder = class {
2418
+ key;
2419
+ tableName;
2420
+ conditionExpression;
2421
+ constructor(tableName, key) {
2422
+ this.tableName = tableName;
2423
+ this.key = key;
2424
+ }
2425
+ /**
2426
+ * Adds a condition that must be satisfied for the check to succeed.
2427
+ * Use this method when you need to:
2428
+ * - Validate complex item states
2429
+ * - Check multiple attributes together
2430
+ * - Ensure safety conditions are met
2431
+ *
2432
+ * @example
2433
+ * ```typescript
2434
+ * // Check dinosaur health and behavior
2435
+ * builder.condition(op =>
2436
+ * op.and([
2437
+ * op.gt('stats.health', 50),
2438
+ * op.not(op.eq('status', 'SEDATED')),
2439
+ * op.lt('aggressionLevel', 8)
2440
+ * ])
2441
+ * );
2442
+ *
2443
+ * // Verify habitat conditions
2444
+ * builder.condition(op =>
2445
+ * op.and([
2446
+ * op.eq('powerStatus', 'ONLINE'),
2447
+ * op.between('temperature', 20, 30),
2448
+ * op.attributeExists('lastMaintenance')
2449
+ * ])
2450
+ * );
2451
+ *
2452
+ * // Check breeding conditions
2453
+ * builder.condition(op =>
2454
+ * op.and([
2455
+ * op.eq('species', 'VELOCIRAPTOR'),
2456
+ * op.gte('age', 3),
2457
+ * op.eq('geneticPurity', 100)
2458
+ * ])
2459
+ * );
2460
+ * ```
2461
+ *
2462
+ * @param condition - Either a Condition DynamoItem or a callback function that builds the condition
2463
+ * @returns The builder instance for method chaining
2464
+ */
2465
+ condition(condition) {
2466
+ if (typeof condition === "function") {
2467
+ const conditionOperator = {
2468
+ eq,
2469
+ ne,
2470
+ lt,
2471
+ lte,
2472
+ gt,
2473
+ gte,
2474
+ between,
2475
+ beginsWith,
2476
+ contains,
2477
+ attributeExists,
2478
+ attributeNotExists,
2479
+ and,
2480
+ or,
2481
+ not
2482
+ };
2483
+ this.conditionExpression = condition(conditionOperator);
2484
+ } else {
2485
+ this.conditionExpression = condition;
2486
+ }
2487
+ return this;
2488
+ }
2489
+ /**
2490
+ * Generates the DynamoDB command parameters for direct execution.
2491
+ * Use this method when you want to:
2492
+ * - Execute the condition check as a standalone operation
2493
+ * - Get the raw DynamoDB command for custom execution
2494
+ * - Inspect the generated command parameters
2495
+ *
2496
+ * @example
2497
+ * ```ts
2498
+ * const command = new ConditionCheckBuilder('myTable', { id: '123' })
2499
+ * .condition(op => op.attributeExists('status'))
2500
+ * .toDynamoCommand();
2501
+ * // Use command with DynamoDB client
2502
+ * ```
2503
+ *
2504
+ * @throws {Error} If no condition has been set
2505
+ * @returns The DynamoDB command parameters
2506
+ */
2507
+ toDynamoCommand() {
2508
+ if (!this.conditionExpression) {
2509
+ throw new Error("Condition is required for condition check operations");
2510
+ }
2511
+ const { expression, names, values } = prepareExpressionParams(this.conditionExpression);
2512
+ if (!expression) {
2513
+ throw new Error("Failed to generate condition expression");
2514
+ }
2515
+ return {
2516
+ tableName: this.tableName,
2517
+ key: this.key,
2518
+ conditionExpression: expression,
2519
+ expressionAttributeNames: names,
2520
+ expressionAttributeValues: values
2521
+ };
2522
+ }
2523
+ /**
2524
+ * Adds this condition check operation to a transaction.
2525
+ * Use this method when you need to:
2526
+ * - Verify habitat safety before transfers
2527
+ * - Ensure proper feeding conditions
2528
+ * - Validate security protocols
2529
+ *
2530
+ * @example
2531
+ * ```ts
2532
+ * const transaction = new TransactionBuilder();
2533
+ * new ConditionCheckBuilder('habitats', { id: 'PADDOCK-B' })
2534
+ * .condition(op => op.and([
2535
+ * op.eq('securityStatus', 'ACTIVE'),
2536
+ * op.lt('currentOccupants', 3),
2537
+ * op.eq('habitatType', 'CARNIVORE')
2538
+ * ]))
2539
+ * .withTransaction(transaction);
2540
+ * // Add dinosaur transfer operations
2541
+ * ```
2542
+ *
2543
+ * @param transaction - The transaction builder to add this operation to
2544
+ * @throws {Error} If no condition has been set
2545
+ * @returns The builder instance for method chaining
2546
+ */
2547
+ withTransaction(transaction) {
2548
+ if (!this.conditionExpression) {
2549
+ throw new Error("Condition is required for condition check operations");
2550
+ }
2551
+ const command = this.toDynamoCommand();
2552
+ transaction.conditionCheckWithCommand(command);
2553
+ return this;
2554
+ }
2555
+ /**
2556
+ * Gets a human-readable representation of the condition check command
2557
+ * with all expression placeholders replaced by their actual values.
2558
+ * Use this method when you need to:
2559
+ * - Debug complex condition expressions
2560
+ * - Verify condition parameters
2561
+ * - Log safety checks
2562
+ * - Troubleshoot condition failures
2563
+ *
2564
+ * @example
2565
+ * ```ts
2566
+ * const debugInfo = new ConditionCheckBuilder('dinosaurs', { id: 'TREX-001' })
2567
+ * .condition(op => op.and([
2568
+ * op.between('stats.health', 50, 100),
2569
+ * op.not(op.eq('status', 'SEDATED')),
2570
+ * op.attributeExists('lastFeedingTime')
2571
+ * op.eq('version', 1)
2572
+ * ]))
2573
+ * .debug();
2574
+ * console.log(debugInfo);
2575
+ * ```
2576
+ *
2577
+ * @returns A readable representation of the condition check command with resolved expressions
2578
+ */
2579
+ debug() {
2580
+ const command = this.toDynamoCommand();
2581
+ return debugCommand(command);
2582
+ }
2583
+ };
2584
+
2585
+ // src/builders/get-builder.ts
2586
+ var GetBuilder = class {
2587
+ /**
2588
+ * Creates a new GetBuilder instance.
2589
+ *
2590
+ * @param executor - Function that executes the get operation
2591
+ * @param key - Primary key of the item to retrieve
2592
+ * @param tableName - Name of the DynamoDB table
2593
+ */
2594
+ constructor(executor, key, tableName) {
2595
+ this.executor = executor;
2596
+ this.params = {
2597
+ tableName,
2598
+ key
2599
+ };
2600
+ }
2601
+ params;
2602
+ options = {};
2603
+ selectedFields = /* @__PURE__ */ new Set();
2604
+ /**
2605
+ * Specifies which attributes to return in the get results.
2606
+ * Use this method when you need to:
2607
+ * - Reduce data transfer by selecting specific dinosaur attributes
2608
+ * - Optimize response size for dinosaur records
2609
+ * - Focus on relevant dinosaur characteristics only
2610
+ *
2611
+ * @example
2612
+ * ```typescript
2613
+ * // Select single attribute
2614
+ * builder.select('species')
2615
+ *
2616
+ * // Select multiple attributes
2617
+ * builder.select(['id', 'species', 'diet'])
2618
+ *
2619
+ * // Chain multiple select calls
2620
+ * builder
2621
+ * .select('id')
2622
+ * .select(['species', 'diet'])
2623
+ * ```
2624
+ *
2625
+ * @param fields - A single field name or an array of field names to return
2626
+ * @returns The builder instance for method chaining
2627
+ */
2628
+ select(fields) {
2629
+ if (typeof fields === "string") {
2630
+ this.selectedFields.add(fields);
2631
+ } else if (Array.isArray(fields)) {
2632
+ for (const field of fields) {
2633
+ this.selectedFields.add(field);
2634
+ }
2635
+ }
2636
+ this.options.projection = Array.from(this.selectedFields);
2637
+ return this;
2638
+ }
2639
+ /**
2640
+ * Sets whether to use strongly consistent reads for the get operation.
2641
+ * Use this method when you need:
2642
+ * - The most up-to-date dinosaur data
2643
+ * - To ensure you're reading the latest dinosaur status
2644
+ * - Critical safety information about dangerous species
2645
+ *
2646
+ * Note: Consistent reads consume twice the throughput
2647
+ *
2648
+ * @example
2649
+ * ```typescript
2650
+ * // Get the latest T-Rex data
2651
+ * const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
2652
+ * .consistentRead()
2653
+ * .execute();
2654
+ * ```
2655
+ *
2656
+ * @param consistentRead - Whether to use consistent reads (defaults to true)
2657
+ * @returns The builder instance for method chaining
2658
+ */
2659
+ consistentRead(consistentRead = true) {
2660
+ this.params.consistentRead = consistentRead;
2661
+ return this;
2662
+ }
2663
+ /**
2664
+ * Executes the get operation against DynamoDB.
2665
+ *
2666
+ * @example
2667
+ * ```typescript
2668
+ * try {
2669
+ * const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
2670
+ * .select(['species', 'name', 'diet'])
2671
+ * .consistentRead()
2672
+ * .execute();
2673
+ *
2674
+ * if (result.item) {
2675
+ * console.log('Dinosaur found:', result.item);
2676
+ * } else {
2677
+ * console.log('Dinosaur not found');
2678
+ * }
2679
+ * } catch (error) {
2680
+ * console.error('Error getting dinosaur:', error);
2681
+ * }
2682
+ * ```
2683
+ *
2684
+ * @returns A promise that resolves to an object containing:
2685
+ * - item: The retrieved dinosaur or undefined if not found
2686
+ */
2687
+ async execute() {
2688
+ const expressionParams = {
2689
+ expressionAttributeNames: {},
2690
+ expressionAttributeValues: {},
2691
+ valueCounter: { count: 0 }
2692
+ };
2693
+ const projectionExpression = Array.from(this.selectedFields).map((p) => generateAttributeName(expressionParams, p)).join(", ");
2694
+ const { expressionAttributeNames } = expressionParams;
2695
+ return this.executor({
2696
+ ...this.params,
2697
+ projectionExpression: projectionExpression.length > 0 ? projectionExpression : void 0,
2698
+ expressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0
2699
+ });
2700
+ }
2701
+ };
2702
+
2703
+ // src/builders/scan-builder.ts
2704
+ var ScanBuilder = class _ScanBuilder extends FilterBuilder {
2705
+ executor;
2706
+ constructor(executor) {
2707
+ super();
2708
+ this.executor = executor;
2709
+ }
2710
+ /**
2711
+ * Creates a deep clone of this ScanBuilder instance.
2712
+ * Use this method when you need to:
2713
+ * - Create scan templates
2714
+ * - Run multiple variations of a scan
2715
+ * - Implement pagination (used internally by paginate())
2716
+ *
2717
+ * @returns A new ScanBuilder instance with the same configuration
2718
+ */
2719
+ clone() {
2720
+ const clone = new _ScanBuilder(this.executor);
2721
+ clone.options = { ...this.options };
2722
+ clone.selectedFields = new Set(this.selectedFields);
2723
+ return clone;
2724
+ }
2725
+ /**
2726
+ * Executes the scan against DynamoDB.
2727
+ * Use this method when you need to:
2728
+ * - Search across the entire table
2729
+ * - Find items matching specific criteria
2730
+ * - Perform full table analysis
2731
+ * - Generate reports across all data
2732
+ *
2733
+ * The method returns both the matched items and, if there are more results,
2734
+ * a lastEvaluatedKey that can be used with startFrom() to continue the scan.
2735
+ *
2736
+ * @example
2737
+ * ```typescript
2738
+ * try {
2739
+ * // Find all dinosaurs with high aggression levels
2740
+ * const result = await new ScanBuilder(executor)
2741
+ * .filter(op =>
2742
+ * op.and([
2743
+ * op.eq('status', 'ACTIVE'),
2744
+ * op.gt('aggressionLevel', 7)
2745
+ * ])
2746
+ * )
2747
+ * .limit(20)
2748
+ * .execute();
2749
+ *
2750
+ * console.log(`Found ${result.items.length} potentially dangerous dinosaurs`);
2751
+ *
2752
+ * if (result.lastEvaluatedKey) {
2753
+ * console.log('More results available');
2754
+ * }
2755
+ * } catch (error) {
2756
+ * console.error('Security scan failed:', error);
2757
+ * }
2758
+ * ```
2759
+ *
2760
+ * @returns A promise that resolves to an object containing:
2761
+ * - items: Array of items matching the scan criteria
2762
+ * - lastEvaluatedKey: Token for continuing the scan, if more items exist
2763
+ */
2764
+ async execute() {
2765
+ return this.executor(this.options);
2766
+ }
2767
+ };
2768
+
2769
+ // src/table.ts
2770
+ var DDB_BATCH_WRITE_LIMIT = 25;
2771
+ var DDB_BATCH_GET_LIMIT = 100;
2772
+ var Table = class {
2773
+ dynamoClient;
2774
+ tableName;
2775
+ /**
2776
+ * The column name of the partitionKey for the Table
2777
+ */
2778
+ partitionKey;
2779
+ /**
2780
+ * The column name of the sortKey for the Table
2781
+ */
2782
+ sortKey;
2783
+ /**
2784
+ * The Global Secondary Indexes that are configured on this table
2785
+ */
2786
+ gsis;
2787
+ constructor(config) {
2788
+ this.dynamoClient = config.client;
2789
+ this.tableName = config.tableName;
2790
+ this.partitionKey = config.indexes.partitionKey;
2791
+ this.sortKey = config.indexes.sortKey;
2792
+ this.gsis = config.indexes.gsis || {};
2793
+ }
2794
+ createKeyForPrimaryIndex(keyCondition) {
2795
+ const primaryCondition = { [this.partitionKey]: keyCondition.pk };
2796
+ if (this.sortKey) {
2797
+ if (!keyCondition.sk) {
2798
+ throw new Error("Sort key has not been provided but the Table has a sort key");
2799
+ }
2800
+ primaryCondition[this.sortKey] = keyCondition.sk;
2801
+ }
2802
+ return primaryCondition;
2803
+ }
2804
+ /**
2805
+ * Creates a new item in the table, it will fail if the item already exists
2806
+ *
2807
+ * @param item The item to create
2808
+ * @returns A PutBuilder instance for chaining conditions and executing the put operation
2809
+ */
2810
+ create(item) {
2811
+ return this.put(item).condition((op) => op.attributeNotExists(this.partitionKey));
2812
+ }
2813
+ get(keyCondition) {
2814
+ const executor = async (params) => {
2815
+ try {
2816
+ const result = await this.dynamoClient.get({
2817
+ TableName: params.tableName,
2818
+ Key: this.createKeyForPrimaryIndex(keyCondition),
2819
+ ProjectionExpression: params.projectionExpression,
2820
+ ExpressionAttributeNames: params.expressionAttributeNames,
2821
+ ConsistentRead: params.consistentRead
2822
+ });
2823
+ return {
2824
+ item: result.Item ? result.Item : void 0
2825
+ };
2826
+ } catch (error) {
2827
+ console.error("Error getting item:", error);
2828
+ throw error;
2829
+ }
2830
+ };
2831
+ return new GetBuilder(executor, keyCondition, this.tableName);
2832
+ }
2833
+ /**
2834
+ * Updates an item in the table
2835
+ *
2836
+ * @param item The item to update
2837
+ * @returns A PutBuilder instance for chaining conditions and executing the put operation
2838
+ */
2839
+ put(item) {
2840
+ const executor = async (params) => {
2841
+ try {
2842
+ const result = await this.dynamoClient.put({
2843
+ TableName: params.tableName,
2844
+ Item: params.item,
2845
+ ConditionExpression: params.conditionExpression,
2846
+ ExpressionAttributeNames: params.expressionAttributeNames,
2847
+ ExpressionAttributeValues: params.expressionAttributeValues,
2848
+ // CONSISTENT is not a valid ReturnValue for DDB, so we set NONE as we are not interested in its
2849
+ // response and will be reloading the item from the DB through a get instead
2850
+ ReturnValues: params.returnValues === "CONSISTENT" ? "NONE" : params.returnValues
2851
+ });
2852
+ if (params.returnValues === "CONSISTENT") {
2853
+ const getResult = await this.dynamoClient.get({
2854
+ TableName: params.tableName,
2855
+ Key: this.createKeyForPrimaryIndex({
2856
+ pk: params.item[this.partitionKey],
2857
+ ...this.sortKey && { sk: params.item[this.sortKey] }
2858
+ }),
2859
+ ConsistentRead: true
2860
+ });
2861
+ return getResult.Item;
2862
+ }
2863
+ return result.Attributes;
2864
+ } catch (error) {
2865
+ console.error("Error creating item:", error);
2866
+ throw error;
2867
+ }
2868
+ };
2869
+ return new PutBuilder(executor, item, this.tableName);
2870
+ }
2871
+ /**
2872
+ * Creates a query builder for complex queries
2873
+ * If useIndex is called on the returned QueryBuilder, it will use the GSI configuration
2874
+ */
2875
+ query(keyCondition) {
2876
+ const pkAttributeName = this.partitionKey;
2877
+ const skAttributeName = this.sortKey;
2878
+ let keyConditionExpression = eq(pkAttributeName, keyCondition.pk);
2879
+ if (keyCondition.sk) {
2880
+ if (!skAttributeName) {
2881
+ throw new Error("Sort key is not defined for Index");
2882
+ }
2883
+ const keyConditionOperator = {
2884
+ eq: (value) => eq(skAttributeName, value),
2885
+ lt: (value) => lt(skAttributeName, value),
2886
+ lte: (value) => lte(skAttributeName, value),
2887
+ gt: (value) => gt(skAttributeName, value),
2888
+ gte: (value) => gte(skAttributeName, value),
2889
+ between: (lower, upper) => between(skAttributeName, lower, upper),
2890
+ beginsWith: (value) => beginsWith(skAttributeName, value),
2891
+ and: (...conditions) => and(...conditions)
2892
+ };
2893
+ const skCondition = keyCondition.sk(keyConditionOperator);
2894
+ keyConditionExpression = and(eq(pkAttributeName, keyCondition.pk), skCondition);
2895
+ }
2896
+ const executor = async (originalKeyCondition, options) => {
2897
+ let finalKeyCondition = originalKeyCondition;
2898
+ if (options.indexName) {
2899
+ const gsiName = String(options.indexName);
2900
+ const gsi = this.gsis[gsiName];
2901
+ if (!gsi) {
2902
+ throw new Error(`GSI with name "${gsiName}" does not exist on table "${this.tableName}"`);
2903
+ }
2904
+ const gsiPkAttributeName = gsi.partitionKey;
2905
+ const gsiSkAttributeName = gsi.sortKey;
2906
+ let pkValue;
2907
+ let skValue;
2908
+ let extractedSkCondition;
2909
+ if (originalKeyCondition.type === "eq") {
2910
+ pkValue = originalKeyCondition.value;
2911
+ } else if (originalKeyCondition.type === "and" && originalKeyCondition.conditions) {
2912
+ const pkCondition = originalKeyCondition.conditions.find(
2913
+ (c) => c.type === "eq" && c.attr === pkAttributeName
2914
+ );
2915
+ if (pkCondition && pkCondition.type === "eq") {
2916
+ pkValue = pkCondition.value;
2917
+ }
2918
+ const skConditions = originalKeyCondition.conditions.filter((c) => c.attr === skAttributeName);
2919
+ if (skConditions.length > 0) {
2920
+ if (skConditions.length === 1) {
2921
+ extractedSkCondition = skConditions[0];
2922
+ if (extractedSkCondition && extractedSkCondition.type === "eq") {
2923
+ skValue = extractedSkCondition.value;
2924
+ }
2925
+ } else if (skConditions.length > 1) {
2926
+ extractedSkCondition = and(...skConditions);
2927
+ }
2928
+ }
2929
+ }
2930
+ if (!pkValue) {
2931
+ throw new Error("Could not extract partition key value from key condition");
2932
+ }
2933
+ let gsiKeyCondition = eq(gsiPkAttributeName, pkValue);
2934
+ if (skValue && gsiSkAttributeName) {
2935
+ gsiKeyCondition = and(gsiKeyCondition, eq(gsiSkAttributeName, skValue));
2936
+ } else if (extractedSkCondition && gsiSkAttributeName) {
2937
+ if (extractedSkCondition.attr === skAttributeName) {
2938
+ const updatedSkCondition = {
2939
+ ...extractedSkCondition,
2940
+ attr: gsiSkAttributeName
2941
+ };
2942
+ gsiKeyCondition = and(gsiKeyCondition, updatedSkCondition);
2943
+ } else {
2944
+ gsiKeyCondition = and(gsiKeyCondition, extractedSkCondition);
2945
+ }
2946
+ }
2947
+ finalKeyCondition = gsiKeyCondition;
2948
+ }
2949
+ const expressionParams = {
2950
+ expressionAttributeNames: {},
2951
+ expressionAttributeValues: {},
2952
+ valueCounter: { count: 0 }
2953
+ };
2954
+ const keyConditionExpression2 = buildExpression(finalKeyCondition, expressionParams);
2955
+ let filterExpression;
2956
+ if (options.filter) {
2957
+ filterExpression = buildExpression(options.filter, expressionParams);
2958
+ }
2959
+ const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
2960
+ const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
2961
+ const { indexName, limit, consistentRead, scanIndexForward, lastEvaluatedKey } = options;
2962
+ const params = {
2963
+ TableName: this.tableName,
2964
+ KeyConditionExpression: keyConditionExpression2,
2965
+ FilterExpression: filterExpression,
2966
+ ExpressionAttributeNames: expressionAttributeNames,
2967
+ ExpressionAttributeValues: expressionAttributeValues,
2968
+ IndexName: indexName,
2969
+ Limit: limit,
2970
+ ConsistentRead: consistentRead,
2971
+ ScanIndexForward: scanIndexForward,
2972
+ ProjectionExpression: projectionExpression,
2973
+ ExclusiveStartKey: lastEvaluatedKey
2974
+ };
2975
+ try {
2976
+ const result = await this.dynamoClient.query(params);
2977
+ return {
2978
+ items: result.Items,
2979
+ lastEvaluatedKey: result.LastEvaluatedKey
2980
+ };
2981
+ } catch (error) {
2982
+ console.log(debugCommand(params));
2983
+ console.error("Error querying items:", error);
2984
+ throw error;
2985
+ }
2986
+ };
2987
+ return new QueryBuilder(executor, keyConditionExpression);
2988
+ }
2989
+ /**
2990
+ * Creates a scan builder for scanning the entire table
2991
+ * Use this when you need to:
2992
+ * - Process all items in a table
2993
+ * - Apply filters to a large dataset
2994
+ * - Use a GSI for scanning
2995
+ *
2996
+ * @returns A ScanBuilder instance for chaining operations
2997
+ */
2998
+ scan() {
2999
+ const executor = async (options) => {
3000
+ const expressionParams = {
3001
+ expressionAttributeNames: {},
3002
+ expressionAttributeValues: {},
3003
+ valueCounter: { count: 0 }
3004
+ };
3005
+ let filterExpression;
3006
+ if (options.filter) {
3007
+ filterExpression = buildExpression(options.filter, expressionParams);
3008
+ }
3009
+ const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
3010
+ const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
3011
+ const { indexName, limit, consistentRead, lastEvaluatedKey } = options;
3012
+ const params = {
3013
+ TableName: this.tableName,
3014
+ FilterExpression: filterExpression,
3015
+ ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
3016
+ ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
3017
+ IndexName: indexName,
3018
+ Limit: limit,
3019
+ ConsistentRead: consistentRead,
3020
+ ProjectionExpression: projectionExpression,
3021
+ ExclusiveStartKey: lastEvaluatedKey
3022
+ };
3023
+ try {
3024
+ const result = await this.dynamoClient.scan(params);
3025
+ return {
3026
+ items: result.Items,
3027
+ lastEvaluatedKey: result.LastEvaluatedKey
3028
+ };
3029
+ } catch (error) {
3030
+ console.log(debugCommand(params));
3031
+ console.error("Error scanning items:", error);
3032
+ throw error;
3033
+ }
3034
+ };
3035
+ return new ScanBuilder(executor);
3036
+ }
3037
+ delete(keyCondition) {
3038
+ const executor = async (params) => {
3039
+ try {
3040
+ const result = await this.dynamoClient.delete({
3041
+ TableName: params.tableName,
3042
+ Key: this.createKeyForPrimaryIndex(keyCondition),
3043
+ ConditionExpression: params.conditionExpression,
3044
+ ExpressionAttributeNames: params.expressionAttributeNames,
3045
+ ExpressionAttributeValues: params.expressionAttributeValues,
3046
+ ReturnValues: params.returnValues
3047
+ });
3048
+ return {
3049
+ item: result.Attributes
3050
+ };
3051
+ } catch (error) {
3052
+ console.error("Error deleting item:", error);
3053
+ throw error;
3054
+ }
3055
+ };
3056
+ return new DeleteBuilder(executor, this.tableName, keyCondition);
3057
+ }
3058
+ /**
3059
+ * Updates an item in the table
3060
+ *
3061
+ * @param keyCondition The primary key of the item to update
3062
+ * @returns An UpdateBuilder instance for chaining update operations and conditions
3063
+ */
3064
+ update(keyCondition) {
3065
+ const executor = async (params) => {
3066
+ try {
3067
+ const result = await this.dynamoClient.update({
3068
+ TableName: params.tableName,
3069
+ Key: this.createKeyForPrimaryIndex(keyCondition),
3070
+ UpdateExpression: params.updateExpression,
3071
+ ConditionExpression: params.conditionExpression,
3072
+ ExpressionAttributeNames: params.expressionAttributeNames,
3073
+ ExpressionAttributeValues: params.expressionAttributeValues,
3074
+ ReturnValues: params.returnValues
3075
+ });
3076
+ return {
3077
+ item: result.Attributes
3078
+ };
3079
+ } catch (error) {
3080
+ console.error("Error updating item:", error);
3081
+ throw error;
3082
+ }
3083
+ };
3084
+ return new UpdateBuilder(executor, this.tableName, keyCondition);
3085
+ }
3086
+ /**
3087
+ * Creates a transaction builder for performing multiple operations atomically
3088
+ */
3089
+ transactionBuilder() {
3090
+ const executor = async (params) => {
3091
+ await this.dynamoClient.transactWrite(params);
3092
+ };
3093
+ return new TransactionBuilder(executor, {
3094
+ partitionKey: this.partitionKey,
3095
+ sortKey: this.sortKey
3096
+ });
3097
+ }
3098
+ /**
3099
+ * Executes a transaction using a callback function
3100
+ *
3101
+ * @param callback A function that receives a transaction context and performs operations on it
3102
+ * @param options Optional transaction options
3103
+ * @returns A promise that resolves when the transaction is complete
3104
+ */
3105
+ transaction(callback, options) {
3106
+ const executor = async () => {
3107
+ const transactionExecutor = async (params) => {
3108
+ await this.dynamoClient.transactWrite(params);
3109
+ };
3110
+ const transaction = new TransactionBuilder(transactionExecutor, {
3111
+ partitionKey: this.partitionKey,
3112
+ sortKey: this.sortKey
3113
+ });
3114
+ if (options) {
3115
+ transaction.withOptions(options);
3116
+ }
3117
+ const result = await callback(transaction);
3118
+ await transaction.execute();
3119
+ return result;
3120
+ };
3121
+ return executor();
3122
+ }
3123
+ /**
3124
+ * Creates a condition check operation for use in transactions
3125
+ *
3126
+ * This is useful for when you require a transaction to succeed only when a specific condition is met on a
3127
+ * a record within the database that you are not directly updating.
3128
+ *
3129
+ * For example, you are updating a record and you want to ensure that another record exists and/or has a specific value before proceeding.
3130
+ */
3131
+ conditionCheck(keyCondition) {
3132
+ return new ConditionCheckBuilder(this.tableName, keyCondition);
3133
+ }
3134
+ /**
3135
+ * Performs a batch get operation to retrieve multiple items at once
3136
+ *
3137
+ * @param keys Array of primary keys to retrieve
3138
+ * @returns A promise that resolves to the retrieved items
3139
+ */
3140
+ async batchGet(keys) {
3141
+ const allItems = [];
3142
+ const allUnprocessedKeys = [];
3143
+ for (const chunk of chunkArray(keys, DDB_BATCH_GET_LIMIT)) {
3144
+ const formattedKeys = chunk.map((key) => ({
3145
+ [this.partitionKey]: key.pk,
3146
+ ...this.sortKey ? { [this.sortKey]: key.sk } : {}
3147
+ }));
3148
+ const params = {
3149
+ RequestItems: {
3150
+ [this.tableName]: {
3151
+ Keys: formattedKeys
3152
+ }
3153
+ }
3154
+ };
3155
+ try {
3156
+ const result = await this.dynamoClient.batchGet(params);
3157
+ if (result.Responses?.[this.tableName]) {
3158
+ allItems.push(...result.Responses[this.tableName]);
3159
+ }
3160
+ const unprocessedKeysArray = result.UnprocessedKeys?.[this.tableName]?.Keys || [];
3161
+ const unprocessedKeys = unprocessedKeysArray.map((key) => ({
3162
+ pk: key[this.partitionKey],
3163
+ sk: this.sortKey ? key[this.sortKey] : void 0
3164
+ }));
3165
+ if (unprocessedKeys.length > 0) {
3166
+ allUnprocessedKeys.push(...unprocessedKeys);
3167
+ }
3168
+ } catch (error) {
3169
+ console.error("Error in batch get operation:", error);
3170
+ throw error;
3171
+ }
3172
+ }
3173
+ return {
3174
+ items: allItems,
3175
+ unprocessedKeys: allUnprocessedKeys
3176
+ };
3177
+ }
3178
+ /**
3179
+ * Performs a batch write operation to put or delete multiple items at once
3180
+ *
3181
+ * @param operations Array of put or delete operations
3182
+ * @returns A promise that resolves to any unprocessed operations
3183
+ */
3184
+ async batchWrite(operations) {
3185
+ const allUnprocessedItems = [];
3186
+ for (const chunk of chunkArray(operations, DDB_BATCH_WRITE_LIMIT)) {
3187
+ const writeRequests = chunk.map((operation) => {
3188
+ if (operation.type === "put") {
3189
+ return {
3190
+ PutRequest: {
3191
+ Item: operation.item
3192
+ }
3193
+ };
3194
+ }
3195
+ return {
3196
+ DeleteRequest: {
3197
+ Key: this.createKeyForPrimaryIndex(operation.key)
3198
+ }
3199
+ };
3200
+ });
3201
+ const params = {
3202
+ RequestItems: {
3203
+ [this.tableName]: writeRequests
3204
+ }
3205
+ };
3206
+ try {
3207
+ const result = await this.dynamoClient.batchWrite(params);
3208
+ const unprocessedRequestsArray = result.UnprocessedItems?.[this.tableName] || [];
3209
+ if (unprocessedRequestsArray.length > 0) {
3210
+ const unprocessedItems = unprocessedRequestsArray.map((request) => {
3211
+ if (request?.PutRequest?.Item) {
3212
+ return {
3213
+ type: "put",
3214
+ item: request.PutRequest.Item
3215
+ };
3216
+ }
3217
+ if (request?.DeleteRequest?.Key) {
3218
+ return {
3219
+ type: "delete",
3220
+ key: {
3221
+ pk: request.DeleteRequest.Key[this.partitionKey],
3222
+ sk: this.sortKey ? request.DeleteRequest.Key[this.sortKey] : void 0
3223
+ }
3224
+ };
3225
+ }
3226
+ throw new Error("Invalid unprocessed item format returned from DynamoDB");
3227
+ });
3228
+ allUnprocessedItems.push(...unprocessedItems);
3229
+ }
3230
+ } catch (error) {
3231
+ console.error("Error in batch write operation:", error);
3232
+ throw error;
3233
+ }
3234
+ }
3235
+ return {
3236
+ unprocessedItems: allUnprocessedItems
3237
+ };
3238
+ }
3239
+ };
3240
+
3241
+ export { Table };
3242
+ //# sourceMappingURL=table.js.map
3243
+ //# sourceMappingURL=table.js.map