dyno-table 2.2.1 → 2.3.0

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