dyno-table 2.2.1 → 2.3.1

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 +187 -1865
  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
package/dist/index.cjs CHANGED
@@ -1,4471 +1,122 @@
1
1
  'use strict';
2
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
- };
3
+ var chunkKA3VPIPS_cjs = require('./chunk-KA3VPIPS.cjs');
4
+ var chunkNTA6GDPP_cjs = require('./chunk-NTA6GDPP.cjs');
5
+ var chunkPB7BBCZO_cjs = require('./chunk-PB7BBCZO.cjs');
6
+ var chunkXYL43FDX_cjs = require('./chunk-XYL43FDX.cjs');
7
+ var chunk7UJJ7JXM_cjs = require('./chunk-7UJJ7JXM.cjs');
279
8
 
280
- // src/conditions.ts
281
- var createComparisonCondition = (type) => (attr, value) => ({
282
- type,
283
- attr,
284
- value
9
+
10
+
11
+ Object.defineProperty(exports, "Table", {
12
+ enumerable: true,
13
+ get: function () { return chunkKA3VPIPS_cjs.Table; }
285
14
  });
286
- var eq = createComparisonCondition("eq");
287
- var ne = createComparisonCondition("ne");
288
- var lt = createComparisonCondition("lt");
289
- var lte = createComparisonCondition("lte");
290
- var gt = createComparisonCondition("gt");
291
- var gte = createComparisonCondition("gte");
292
- var between = (attr, lower, upper) => ({
293
- type: "between",
294
- attr,
295
- value: [lower, upper]
15
+ Object.defineProperty(exports, "createIndex", {
16
+ enumerable: true,
17
+ get: function () { return chunkNTA6GDPP_cjs.createIndex; }
296
18
  });
297
- var inArray = (attr, values) => ({
298
- type: "in",
299
- attr,
300
- value: values
19
+ Object.defineProperty(exports, "createQueries", {
20
+ enumerable: true,
21
+ get: function () { return chunkNTA6GDPP_cjs.createQueries; }
301
22
  });
302
- var beginsWith = createComparisonCondition("beginsWith");
303
- var contains = createComparisonCondition("contains");
304
- var attributeExists = (attr) => ({
305
- type: "attributeExists",
306
- attr
23
+ Object.defineProperty(exports, "defineEntity", {
24
+ enumerable: true,
25
+ get: function () { return chunkNTA6GDPP_cjs.defineEntity; }
307
26
  });
308
- var attributeNotExists = (attr) => ({
309
- type: "attributeNotExists",
310
- attr
27
+ Object.defineProperty(exports, "partitionKey", {
28
+ enumerable: true,
29
+ get: function () { return chunkPB7BBCZO_cjs.partitionKey; }
311
30
  });
312
- var and = (...conditions) => ({
313
- type: "and",
314
- conditions
31
+ Object.defineProperty(exports, "sortKey", {
32
+ enumerable: true,
33
+ get: function () { return chunkPB7BBCZO_cjs.sortKey; }
315
34
  });
316
- var or = (...conditions) => ({
317
- type: "or",
318
- conditions
35
+ Object.defineProperty(exports, "BatchBuilder", {
36
+ enumerable: true,
37
+ get: function () { return chunkXYL43FDX_cjs.BatchBuilder; }
319
38
  });
320
- var not = (condition) => ({
321
- type: "not",
322
- condition
39
+ Object.defineProperty(exports, "BatchError", {
40
+ enumerable: true,
41
+ get: function () { return chunkXYL43FDX_cjs.BatchError; }
42
+ });
43
+ Object.defineProperty(exports, "DeleteBuilder", {
44
+ enumerable: true,
45
+ get: function () { return chunkXYL43FDX_cjs.DeleteBuilder; }
46
+ });
47
+ Object.defineProperty(exports, "PutBuilder", {
48
+ enumerable: true,
49
+ get: function () { return chunkXYL43FDX_cjs.PutBuilder; }
50
+ });
51
+ Object.defineProperty(exports, "QueryBuilder", {
52
+ enumerable: true,
53
+ get: function () { return chunkXYL43FDX_cjs.QueryBuilder; }
54
+ });
55
+ Object.defineProperty(exports, "TransactionBuilder", {
56
+ enumerable: true,
57
+ get: function () { return chunkXYL43FDX_cjs.TransactionBuilder; }
58
+ });
59
+ Object.defineProperty(exports, "UpdateBuilder", {
60
+ enumerable: true,
61
+ get: function () { return chunkXYL43FDX_cjs.UpdateBuilder; }
62
+ });
63
+ Object.defineProperty(exports, "and", {
64
+ enumerable: true,
65
+ get: function () { return chunk7UJJ7JXM_cjs.and; }
66
+ });
67
+ Object.defineProperty(exports, "attributeExists", {
68
+ enumerable: true,
69
+ get: function () { return chunk7UJJ7JXM_cjs.attributeExists; }
70
+ });
71
+ Object.defineProperty(exports, "attributeNotExists", {
72
+ enumerable: true,
73
+ get: function () { return chunk7UJJ7JXM_cjs.attributeNotExists; }
74
+ });
75
+ Object.defineProperty(exports, "beginsWith", {
76
+ enumerable: true,
77
+ get: function () { return chunk7UJJ7JXM_cjs.beginsWith; }
78
+ });
79
+ Object.defineProperty(exports, "between", {
80
+ enumerable: true,
81
+ get: function () { return chunk7UJJ7JXM_cjs.between; }
82
+ });
83
+ Object.defineProperty(exports, "contains", {
84
+ enumerable: true,
85
+ get: function () { return chunk7UJJ7JXM_cjs.contains; }
86
+ });
87
+ Object.defineProperty(exports, "eq", {
88
+ enumerable: true,
89
+ get: function () { return chunk7UJJ7JXM_cjs.eq; }
90
+ });
91
+ Object.defineProperty(exports, "gt", {
92
+ enumerable: true,
93
+ get: function () { return chunk7UJJ7JXM_cjs.gt; }
94
+ });
95
+ Object.defineProperty(exports, "gte", {
96
+ enumerable: true,
97
+ get: function () { return chunk7UJJ7JXM_cjs.gte; }
98
+ });
99
+ Object.defineProperty(exports, "inArray", {
100
+ enumerable: true,
101
+ get: function () { return chunk7UJJ7JXM_cjs.inArray; }
102
+ });
103
+ Object.defineProperty(exports, "lt", {
104
+ enumerable: true,
105
+ get: function () { return chunk7UJJ7JXM_cjs.lt; }
106
+ });
107
+ Object.defineProperty(exports, "lte", {
108
+ enumerable: true,
109
+ get: function () { return chunk7UJJ7JXM_cjs.lte; }
110
+ });
111
+ Object.defineProperty(exports, "ne", {
112
+ enumerable: true,
113
+ get: function () { return chunk7UJJ7JXM_cjs.ne; }
114
+ });
115
+ Object.defineProperty(exports, "not", {
116
+ enumerable: true,
117
+ get: function () { return chunk7UJJ7JXM_cjs.not; }
118
+ });
119
+ Object.defineProperty(exports, "or", {
120
+ enumerable: true,
121
+ get: function () { return chunk7UJJ7JXM_cjs.or; }
323
122
  });
324
-
325
- // src/expression.ts
326
- var generateAttributeName = (params, attr) => {
327
- if (attr.includes(".")) {
328
- const pathSegments = attr.split(".");
329
- const segmentNames = [];
330
- for (const segment of pathSegments) {
331
- let segmentName;
332
- for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
333
- if (existingAttr === segment) {
334
- segmentName = existingName;
335
- break;
336
- }
337
- }
338
- if (!segmentName) {
339
- segmentName = `#${Object.keys(params.expressionAttributeNames).length}`;
340
- params.expressionAttributeNames[segmentName] = segment;
341
- }
342
- segmentNames.push(segmentName);
343
- }
344
- return segmentNames.join(".");
345
- }
346
- for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
347
- if (existingAttr === attr) {
348
- return existingName;
349
- }
350
- }
351
- const attrName = `#${Object.keys(params.expressionAttributeNames).length}`;
352
- params.expressionAttributeNames[attrName] = attr;
353
- return attrName;
354
- };
355
- var generateValueName = (params, value) => {
356
- const valueName = `:${params.valueCounter.count++}`;
357
- params.expressionAttributeValues[valueName] = value;
358
- return valueName;
359
- };
360
- var validateCondition = (condition, requiresAttr = true, requiresValue = true) => {
361
- if (requiresAttr && !condition.attr) {
362
- throw new Error(`Attribute is required for ${condition.type} condition`);
363
- }
364
- if (requiresValue && condition.value === void 0) {
365
- throw new Error(`Value is required for ${condition.type} condition`);
366
- }
367
- };
368
- var buildComparisonExpression = (condition, operator, params) => {
369
- validateCondition(condition);
370
- if (!condition.attr) {
371
- throw new Error(`Attribute is required for ${condition.type} condition`);
372
- }
373
- const attrName = generateAttributeName(params, condition.attr);
374
- const valueName = generateValueName(params, condition.value);
375
- return `${attrName} ${operator} ${valueName}`;
376
- };
377
- var buildBetweenExpression = (condition, params) => {
378
- validateCondition(condition);
379
- if (!condition.attr) {
380
- throw new Error(`Attribute is required for ${condition.type} condition`);
381
- }
382
- if (!Array.isArray(condition.value) || condition.value.length !== 2) {
383
- throw new Error("Between condition requires an array of two values");
384
- }
385
- const attrName = generateAttributeName(params, condition.attr);
386
- const lowerName = generateValueName(params, condition.value[0]);
387
- const upperName = generateValueName(params, condition.value[1]);
388
- return `${attrName} BETWEEN ${lowerName} AND ${upperName}`;
389
- };
390
- var buildInExpression = (condition, params) => {
391
- validateCondition(condition);
392
- if (!condition.attr) {
393
- throw new Error(`Attribute is required for ${condition.type} condition`);
394
- }
395
- if (!Array.isArray(condition.value) || condition.value.length === 0) {
396
- throw new Error("In condition requires a non-empty array of values");
397
- }
398
- if (condition.value.length > 100) {
399
- throw new Error("In condition supports a maximum of 100 values");
400
- }
401
- const attrName = generateAttributeName(params, condition.attr);
402
- const valueNames = condition.value.map((value) => generateValueName(params, value));
403
- return `${attrName} IN (${valueNames.join(", ")})`;
404
- };
405
- var buildFunctionExpression = (functionName, condition, params) => {
406
- validateCondition(condition);
407
- if (!condition.attr) {
408
- throw new Error(`Attribute is required for ${condition.type} condition`);
409
- }
410
- const attrName = generateAttributeName(params, condition.attr);
411
- const valueName = generateValueName(params, condition.value);
412
- return `${functionName}(${attrName}, ${valueName})`;
413
- };
414
- var buildAttributeFunction = (functionName, condition, params) => {
415
- validateCondition(condition, true, false);
416
- if (!condition.attr) {
417
- throw new Error(`Attribute is required for ${condition.type} condition`);
418
- }
419
- const attrName = generateAttributeName(params, condition.attr);
420
- return `${functionName}(${attrName})`;
421
- };
422
- var buildLogicalExpression = (operator, conditions, params) => {
423
- if (!conditions || conditions.length === 0) {
424
- throw new Error(`At least one condition is required for ${operator} expression`);
425
- }
426
- const expressions = conditions.map((c) => buildExpression(c, params));
427
- return `(${expressions.join(` ${operator} `)})`;
428
- };
429
- var buildExpression = (condition, params) => {
430
- if (!condition) return "";
431
- try {
432
- const expressionBuilders = {
433
- eq: () => buildComparisonExpression(condition, "=", params),
434
- ne: () => buildComparisonExpression(condition, "<>", params),
435
- lt: () => buildComparisonExpression(condition, "<", params),
436
- lte: () => buildComparisonExpression(condition, "<=", params),
437
- gt: () => buildComparisonExpression(condition, ">", params),
438
- gte: () => buildComparisonExpression(condition, ">=", params),
439
- between: () => buildBetweenExpression(condition, params),
440
- in: () => buildInExpression(condition, params),
441
- beginsWith: () => buildFunctionExpression("begins_with", condition, params),
442
- contains: () => buildFunctionExpression("contains", condition, params),
443
- attributeExists: () => buildAttributeFunction("attribute_exists", condition, params),
444
- attributeNotExists: () => buildAttributeFunction("attribute_not_exists", condition, params),
445
- and: () => {
446
- if (!condition.conditions) {
447
- throw new Error("Conditions array is required for AND operator");
448
- }
449
- return buildLogicalExpression("AND", condition.conditions, params);
450
- },
451
- or: () => {
452
- if (!condition.conditions) {
453
- throw new Error("Conditions array is required for OR operator");
454
- }
455
- return buildLogicalExpression("OR", condition.conditions, params);
456
- },
457
- not: () => {
458
- if (!condition.condition) {
459
- throw new Error("Condition is required for NOT operator");
460
- }
461
- return `NOT (${buildExpression(condition.condition, params)})`;
462
- }
463
- };
464
- const builder = expressionBuilders[condition.type];
465
- if (!builder) {
466
- throw new Error(`Unknown condition type: ${condition.type}`);
467
- }
468
- return builder();
469
- } catch (error) {
470
- if (error instanceof Error) {
471
- console.error(`Error building expression for condition type ${condition.type}:`, error.message);
472
- } else {
473
- console.error(`Error building expression for condition type ${condition.type}:`, error);
474
- }
475
- throw error;
476
- }
477
- };
478
- var prepareExpressionParams = (condition) => {
479
- if (!condition) return {};
480
- const params = {
481
- expressionAttributeNames: {},
482
- expressionAttributeValues: {},
483
- valueCounter: { count: 0 }
484
- };
485
- const expression = buildExpression(condition, params);
486
- return {
487
- expression,
488
- names: Object.keys(params.expressionAttributeNames).length > 0 ? params.expressionAttributeNames : void 0,
489
- values: Object.keys(params.expressionAttributeValues).length > 0 ? params.expressionAttributeValues : void 0
490
- };
491
- };
492
-
493
- // src/utils/debug-expression.ts
494
- function debugCommand(command) {
495
- const result = {};
496
- function replaceAliases(expressionString) {
497
- if (!expressionString) {
498
- return expressionString;
499
- }
500
- let replacedString = expressionString;
501
- for (const alias in command.expressionAttributeNames) {
502
- const attributeName = command.expressionAttributeNames[alias];
503
- const regex = new RegExp(alias, "g");
504
- replacedString = replacedString.replace(regex, attributeName);
505
- }
506
- for (const alias in command.expressionAttributeValues) {
507
- let attributeValue = command.expressionAttributeValues[alias];
508
- if (attributeValue instanceof Set) {
509
- const array = Array.from(attributeValue);
510
- attributeValue = `Set(${array.length}){${array.map((v) => JSON.stringify(v)).join(", ")}}`;
511
- } else {
512
- attributeValue = JSON.stringify(attributeValue);
513
- }
514
- const regex = new RegExp(alias, "g");
515
- replacedString = replacedString.replace(regex, attributeValue);
516
- }
517
- return replacedString;
518
- }
519
- if (command.updateExpression) {
520
- result.updateExpression = replaceAliases(command.updateExpression);
521
- }
522
- if (command.conditionExpression) {
523
- result.conditionExpression = replaceAliases(command.conditionExpression);
524
- }
525
- if (command.filterExpression) {
526
- result.filterExpression = replaceAliases(command.filterExpression);
527
- }
528
- if (command.keyConditionExpression) {
529
- result.keyConditionExpression = replaceAliases(command.keyConditionExpression);
530
- }
531
- if (command.projectionExpression) {
532
- result.projectionExpression = replaceAliases(command.projectionExpression);
533
- }
534
- return {
535
- raw: command,
536
- readable: result
537
- };
538
- }
539
-
540
- // src/builders/delete-builder.ts
541
- var DeleteBuilder = class {
542
- options = {
543
- returnValues: "ALL_OLD"
544
- };
545
- executor;
546
- tableName;
547
- key;
548
- constructor(executor, tableName, key) {
549
- this.executor = executor;
550
- this.tableName = tableName;
551
- this.key = key;
552
- }
553
- /**
554
- * Adds a condition that must be satisfied for the delete operation to succeed.
555
- *
556
- * @example
557
- * ```typescript
558
- * // Ensure dinosaur can be safely removed
559
- * builder.condition(op =>
560
- * op.and([
561
- * op.eq('status', 'SEDATED'),
562
- * op.eq('location', 'MEDICAL_BAY'),
563
- * op.attributeExists('lastCheckup')
564
- * ])
565
- * );
566
- *
567
- * // Verify habitat is empty
568
- * builder.condition(op =>
569
- * op.and([
570
- * op.eq('occupants', 0),
571
- * op.eq('maintenanceStatus', 'COMPLETE'),
572
- * op.not(op.attributeExists('activeAlerts'))
573
- * ])
574
- * );
575
- * ```
576
- *
577
- * @param condition - Either a Condition object or a callback function that builds the condition
578
- * @returns The builder instance for method chaining
579
- */
580
- condition(condition) {
581
- if (typeof condition === "function") {
582
- const conditionOperator = {
583
- eq,
584
- ne,
585
- lt,
586
- lte,
587
- gt,
588
- gte,
589
- between,
590
- inArray,
591
- beginsWith,
592
- contains,
593
- attributeExists,
594
- attributeNotExists,
595
- and,
596
- or,
597
- not
598
- };
599
- this.options.condition = condition(conditionOperator);
600
- } else {
601
- this.options.condition = condition;
602
- }
603
- return this;
604
- }
605
- /**
606
- * Sets whether to return the item's attribute values before deletion.
607
- *
608
- * @example
609
- * ```ts
610
- * // Archive dinosaur data before removal
611
- * const result = await builder
612
- * .returnValues('ALL_OLD')
613
- * .execute();
614
- *
615
- * if (result.item) {
616
- * console.log('Removed dinosaur data:', {
617
- * species: result.item.species,
618
- * age: result.item.age,
619
- * lastLocation: result.item.location
620
- * });
621
- * }
622
- * ```
623
- *
624
- * @param returnValues - Use 'ALL_OLD' to return all attributes of the deleted item
625
- * @returns The builder instance for method chaining
626
- */
627
- returnValues(returnValues) {
628
- this.options.returnValues = returnValues;
629
- return this;
630
- }
631
- /**
632
- * Generate the DynamoDB command parameters
633
- */
634
- toDynamoCommand() {
635
- const { expression, names, values } = prepareExpressionParams(this.options.condition);
636
- return {
637
- tableName: this.tableName,
638
- key: this.key,
639
- conditionExpression: expression,
640
- expressionAttributeNames: names,
641
- expressionAttributeValues: values,
642
- returnValues: this.options.returnValues
643
- };
644
- }
645
- /**
646
- * Adds this delete operation to a transaction.
647
- *
648
- * @example
649
- * ```ts
650
- * const transaction = new TransactionBuilder();
651
- *
652
- * // Remove dinosaur from old habitat
653
- * new DeleteBuilder(executor, 'dinosaurs', { id: 'RAPTOR-001' })
654
- * .condition(op => op.eq('status', 'SEDATED'))
655
- * .withTransaction(transaction);
656
- *
657
- * // Update old habitat occupancy
658
- * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-A' })
659
- * .add('occupants', -1)
660
- * .withTransaction(transaction);
661
- *
662
- * // Execute transfer atomically
663
- * await transaction.execute();
664
- * ```
665
- *
666
- * @param transaction - The transaction builder to add this operation to
667
- */
668
- withTransaction(transaction) {
669
- const command = this.toDynamoCommand();
670
- transaction.deleteWithCommand(command);
671
- }
672
- /**
673
- * Adds this delete operation to a batch with optional entity type information.
674
- *
675
- * @example Basic Usage
676
- * ```ts
677
- * const batch = table.batchBuilder();
678
- *
679
- * // Remove multiple dinosaurs in batch
680
- * dinosaurRepo.delete({ id: 'old-dino-1' }).withBatch(batch);
681
- * dinosaurRepo.delete({ id: 'old-dino-2' }).withBatch(batch);
682
- * dinosaurRepo.delete({ id: 'old-dino-3' }).withBatch(batch);
683
- *
684
- * // Execute all deletions efficiently
685
- * await batch.execute();
686
- * ```
687
- *
688
- * @example Typed Usage
689
- * ```ts
690
- * const batch = table.batchBuilder<{
691
- * User: UserEntity;
692
- * Order: OrderEntity;
693
- * }>();
694
- *
695
- * // Add operations with type information
696
- * userRepo.delete({ id: 'user-1' }).withBatch(batch, 'User');
697
- * orderRepo.delete({ id: 'order-1' }).withBatch(batch, 'Order');
698
- *
699
- * // Execute batch operations
700
- * await batch.execute();
701
- * ```
702
- *
703
- * @param batch - The batch builder to add this operation to
704
- * @param entityType - Optional entity type key for type tracking
705
- */
706
- withBatch(batch, entityType) {
707
- const command = this.toDynamoCommand();
708
- batch.deleteWithCommand(command, entityType);
709
- }
710
- /**
711
- * Executes the delete operation against DynamoDB.
712
- *
713
- * @example
714
- * ```ts
715
- * // Delete with condition and retrieve old values
716
- * const result = await new DeleteBuilder(executor, 'myTable', { id: '123' })
717
- * .condition(op => op.eq('status', 'INACTIVE'))
718
- * .returnValues('ALL_OLD')
719
- * .execute();
720
- *
721
- * if (result.item) {
722
- * console.log('Deleted item:', result.item);
723
- * }
724
- * ```
725
- *
726
- * @returns A promise that resolves to an object containing the deleted item's attributes (if returnValues is 'ALL_OLD')
727
- */
728
- async execute() {
729
- const params = this.toDynamoCommand();
730
- return this.executor(params);
731
- }
732
- /**
733
- * Gets a human-readable representation of the delete command
734
- * with all expression placeholders replaced by their actual values.
735
- *
736
- * @example
737
- * ```ts
738
- * const debugInfo = new DeleteBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
739
- * .condition(op => op.and([
740
- * op.eq('status', 'SEDATED'),
741
- * op.eq('location', 'MEDICAL_BAY'),
742
- * op.gt('sedationLevel', 8)
743
- * op.eq('version', 1),
744
- * op.attributeExists('status')
745
- * ]))
746
- * .debug();
747
- *
748
- * console.log('Delete command:', debugInfo);
749
- * ```
750
- *
751
- * @returns A readable representation of the delete command with resolved expressions
752
- */
753
- debug() {
754
- const command = this.toDynamoCommand();
755
- return debugCommand(command);
756
- }
757
- };
758
-
759
- // src/builders/put-builder.ts
760
- var PutBuilder = class {
761
- item;
762
- options;
763
- executor;
764
- tableName;
765
- constructor(executor, item, tableName) {
766
- this.executor = executor;
767
- this.item = item;
768
- this.tableName = tableName;
769
- this.options = {
770
- returnValues: "NONE"
771
- };
772
- }
773
- set(valuesOrPath, value) {
774
- if (typeof valuesOrPath === "object") {
775
- Object.assign(this.item, valuesOrPath);
776
- } else {
777
- this.item[valuesOrPath] = value;
778
- }
779
- return this;
780
- }
781
- /**
782
- * Adds a condition that must be satisfied for the put operation to succeed.
783
- *
784
- * @example
785
- * ```ts
786
- * // Ensure item doesn't exist (insert only)
787
- * builder.condition(op => op.attributeNotExists('id'))
788
- *
789
- * // Complex condition with version check
790
- * builder.condition(op =>
791
- * op.and([
792
- * op.attributeExists('id'),
793
- * op.eq('version', currentVersion),
794
- * op.eq('status', 'ACTIVE')
795
- * ])
796
- * )
797
- * ```
798
- *
799
- * @param condition - Either a Condition object or a callback function that builds the condition
800
- * @returns The builder instance for method chaining
801
- */
802
- /**
803
- * Adds a condition that must be satisfied for the put operation to succeed.
804
- *
805
- * @example
806
- * ```typescript
807
- * // Ensure unique dinosaur ID
808
- * builder.condition(op =>
809
- * op.attributeNotExists('id')
810
- * );
811
- *
812
- * // Verify habitat requirements
813
- * builder.condition(op =>
814
- * op.and([
815
- * op.eq('securityStatus', 'READY'),
816
- * op.attributeExists('lastInspection'),
817
- * op.gt('securityLevel', 5)
818
- * ])
819
- * );
820
- *
821
- * // Check breeding facility conditions
822
- * builder.condition(op =>
823
- * op.and([
824
- * op.between('temperature', 25, 30),
825
- * op.between('humidity', 60, 80),
826
- * op.eq('quarantineStatus', 'CLEAR')
827
- * ])
828
- * );
829
- * ```
830
- *
831
- * @param condition - Either a Condition object or a callback function that builds the condition
832
- * @returns The builder instance for method chaining
833
- */
834
- condition(condition) {
835
- if (typeof condition === "function") {
836
- const conditionOperator = {
837
- eq,
838
- ne,
839
- lt,
840
- lte,
841
- gt,
842
- gte,
843
- between,
844
- inArray,
845
- beginsWith,
846
- contains,
847
- attributeExists,
848
- attributeNotExists,
849
- and,
850
- or,
851
- not
852
- };
853
- this.options.condition = condition(conditionOperator);
854
- } else {
855
- this.options.condition = condition;
856
- }
857
- return this;
858
- }
859
- /**
860
- * Sets whether to return the item's previous values (if it existed).
861
- *
862
- * @options
863
- * - NONE: No return value
864
- * - ALL_OLD: Returns the item's previous state if it existed, no read capacity units are consumed
865
- * - CONSISTENT: Performs a GET operation after the put to retrieve the item's new state
866
- * - INPUT: Returns the input values that were passed to the operation
867
- *
868
- * @example
869
- * ```ts
870
- * // Get previous dinosaur state
871
- * const result = await builder
872
- * .returnValues('ALL_OLD')
873
- * .execute();
874
- *
875
- * if (result) {
876
- * console.log('Previous profile:', {
877
- * species: result.species,
878
- * status: result.status,
879
- * stats: {
880
- * health: result.stats.health,
881
- * threatLevel: result.stats.threatLevel
882
- * }
883
- * });
884
- * }
885
- *
886
- * // Return input values for create operations
887
- * const createResult = await builder
888
- * .returnValues('INPUT')
889
- * .execute();
890
- * ```
891
- *
892
- * @param returnValues - Use 'ALL_OLD' to return previous values, 'INPUT' to return input values, 'CONSISTENT' for fresh data, or 'NONE' (default).
893
- * @returns The builder instance for method chaining
894
- */
895
- returnValues(returnValues) {
896
- this.options.returnValues = returnValues;
897
- return this;
898
- }
899
- /**
900
- * Generate the DynamoDB command parameters
901
- */
902
- toDynamoCommand() {
903
- const { expression, names, values } = prepareExpressionParams(this.options.condition);
904
- return {
905
- tableName: this.tableName,
906
- item: this.item,
907
- conditionExpression: expression,
908
- expressionAttributeNames: names,
909
- expressionAttributeValues: values,
910
- returnValues: this.options.returnValues
911
- };
912
- }
913
- /**
914
- * Adds this put operation to a transaction.
915
- *
916
- * @example
917
- * ```ts
918
- * const transaction = new TransactionBuilder();
919
- *
920
- * // Add dinosaur to new habitat
921
- * new PutBuilder(executor, {
922
- * id: 'TREX-002',
923
- * location: 'PADDOCK-B',
924
- * status: 'ACTIVE',
925
- * transferDate: new Date().toISOString()
926
- * }, 'dinosaurs')
927
- * .withTransaction(transaction);
928
- *
929
- * // Update habitat records
930
- * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-B' })
931
- * .add('occupants', 1)
932
- * .set('lastTransfer', new Date().toISOString())
933
- * .withTransaction(transaction);
934
- *
935
- * // Execute transfer atomically
936
- * await transaction.execute();
937
- * ```
938
- *
939
- * @param transaction - The transaction builder to add this operation to
940
- * @returns The builder instance for method chaining
941
- */
942
- withTransaction(transaction) {
943
- const command = this.toDynamoCommand();
944
- transaction.putWithCommand(command);
945
- return this;
946
- }
947
- /**
948
- * Adds this put operation to a batch with optional entity type information.
949
- *
950
- * @example Basic Usage
951
- * ```ts
952
- * const batch = table.batchBuilder();
953
- *
954
- * // Add multiple dinosaurs to batch
955
- * dinosaurRepo.create(newDino1).withBatch(batch);
956
- * dinosaurRepo.create(newDino2).withBatch(batch);
957
- * dinosaurRepo.create(newDino3).withBatch(batch);
958
- *
959
- * // Execute all operations efficiently
960
- * await batch.execute();
961
- * ```
962
- *
963
- * @example Typed Usage
964
- * ```ts
965
- * const batch = table.batchBuilder<{
966
- * User: UserEntity;
967
- * Order: OrderEntity;
968
- * }>();
969
- *
970
- * // Add operations with type information
971
- * userRepo.create(newUser).withBatch(batch, 'User');
972
- * orderRepo.create(newOrder).withBatch(batch, 'Order');
973
- *
974
- * // Execute and get typed results
975
- * const result = await batch.execute();
976
- * const users: UserEntity[] = result.reads.itemsByType.User;
977
- * ```
978
- *
979
- * @param batch - The batch builder to add this operation to
980
- * @param entityType - Optional entity type key for type tracking
981
- */
982
- withBatch(batch, entityType) {
983
- const command = this.toDynamoCommand();
984
- batch.putWithCommand(command, entityType);
985
- }
986
- /**
987
- * Executes the put operation against DynamoDB.
988
- *
989
- * @example
990
- * ```ts
991
- * try {
992
- * // Put with condition and return old values
993
- * const result = await new PutBuilder(executor, newItem, 'myTable')
994
- * .condition(op => op.eq('version', 1))
995
- * .returnValues('ALL_OLD')
996
- * .execute();
997
- *
998
- * console.log('Put successful, old item:', result);
999
- * } catch (error) {
1000
- * // Handle condition check failure or other errors
1001
- * console.error('Put failed:', error);
1002
- * }
1003
- * ```
1004
- *
1005
- * @returns A promise that resolves to the operation result (type depends on returnValues setting)
1006
- * @throws Will throw an error if the condition check fails or other DynamoDB errors occur
1007
- */
1008
- async execute() {
1009
- const params = this.toDynamoCommand();
1010
- return this.executor(params);
1011
- }
1012
- /**
1013
- * Gets a human-readable representation of the put command
1014
- * with all expression placeholders replaced by their actual values.
1015
- *
1016
- * @example
1017
- * ```ts
1018
- * const debugInfo = new PutBuilder(executor, {
1019
- * id: 'RAPTOR-003',
1020
- * species: 'Velociraptor',
1021
- * status: 'QUARANTINE',
1022
- * stats: {
1023
- * health: 100,
1024
- * aggressionLevel: 7,
1025
- * age: 2
1026
- * }
1027
- * }, 'dinosaurs')
1028
- * .condition(op =>
1029
- * op.and([
1030
- * op.attributeNotExists('id'),
1031
- * op.eq('quarantineStatus', 'READY'),
1032
- * op.gt('securityLevel', 8)
1033
- * ])
1034
- * )
1035
- * .debug();
1036
- *
1037
- * console.log('Dinosaur transfer command:', debugInfo);
1038
- * ```
1039
- *
1040
- * @returns A readable representation of the put command with resolved expressions
1041
- */
1042
- debug() {
1043
- const command = this.toDynamoCommand();
1044
- return debugCommand(command);
1045
- }
1046
- };
1047
-
1048
- // src/builders/paginator.ts
1049
- var Paginator = class {
1050
- queryBuilder;
1051
- pageSize;
1052
- currentPage = 0;
1053
- lastEvaluatedKey;
1054
- hasMorePages = true;
1055
- totalItemsRetrieved = 0;
1056
- overallLimit;
1057
- constructor(queryBuilder, pageSize) {
1058
- this.queryBuilder = queryBuilder;
1059
- this.pageSize = pageSize;
1060
- this.overallLimit = queryBuilder.getLimit();
1061
- }
1062
- /**
1063
- * Gets the current page number (1-indexed).
1064
- *
1065
- * @example
1066
- * ```ts
1067
- * const paginator = new QueryBuilder(executor, eq('species', 'Tyrannosaurus'))
1068
- * .paginate(5);
1069
- *
1070
- * await paginator.getNextPage();
1071
- * console.log(`Reviewing T-Rex group ${paginator.getCurrentPage()}`);
1072
- * ```
1073
- *
1074
- * @returns The current page number, starting from 1
1075
- */
1076
- getCurrentPage() {
1077
- return this.currentPage;
1078
- }
1079
- /**
1080
- * Checks if there are more pages of dinosaurs or habitats to process.
1081
- *
1082
- * This method takes into account both:
1083
- * - DynamoDB's lastEvaluatedKey mechanism
1084
- * - Any overall limit set on the query
1085
- *
1086
- * @example
1087
- * ```ts
1088
- * // Process all security incidents
1089
- * const paginator = new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
1090
- * .sortDescending()
1091
- * .paginate(10);
1092
- *
1093
- * while (paginator.hasNextPage()) {
1094
- * const page = await paginator.getNextPage();
1095
- * for (const incident of page.items) {
1096
- * await processSecurityBreach(incident);
1097
- * }
1098
- * console.log(`Processed incidents page ${page.page}`);
1099
- * }
1100
- * ```
1101
- *
1102
- * @returns true if there are more pages available, false otherwise
1103
- */
1104
- hasNextPage() {
1105
- if (this.overallLimit !== void 0 && this.totalItemsRetrieved >= this.overallLimit) {
1106
- return false;
1107
- }
1108
- return this.hasMorePages;
1109
- }
1110
- /**
1111
- * Retrieves the next page of dinosaurs or habitats from DynamoDB.
1112
- *
1113
- * This method handles:
1114
- * - Automatic continuation between groups
1115
- * - Respect for park capacity limits
1116
- * - Group size adjustments for safety
1117
- *
1118
- * @example
1119
- * ```ts
1120
- * const paginator = new QueryBuilder(executor, eq('species', 'Velociraptor'))
1121
- * .filter(op => op.eq('status', 'ACTIVE'))
1122
- * .paginate(5);
1123
- *
1124
- * // Check first raptor group
1125
- * const page1 = await paginator.getNextPage();
1126
- * console.log(`Found ${page1.items.length} active raptors`);
1127
- *
1128
- * // Continue inspection if more groups exist
1129
- * if (page1.hasNextPage) {
1130
- * const page2 = await paginator.getNextPage();
1131
- * console.log(`Inspecting raptor group ${page2.page}`);
1132
- *
1133
- * for (const raptor of page2.items) {
1134
- * await performHealthCheck(raptor);
1135
- * }
1136
- * }
1137
- * ```
1138
- *
1139
- * @returns A promise that resolves to a PaginationResult containing:
1140
- * - items: The dinosaurs/habitats for this page
1141
- * - hasNextPage: Whether more groups exist
1142
- * - page: The current group number
1143
- * - lastEvaluatedKey: DynamoDB's continuation token
1144
- */
1145
- async getNextPage() {
1146
- if (!this.hasNextPage()) {
1147
- return {
1148
- items: [],
1149
- hasNextPage: false,
1150
- page: this.currentPage
1151
- };
1152
- }
1153
- let effectivePageSize = this.pageSize;
1154
- if (this.overallLimit !== void 0) {
1155
- const remainingItems = this.overallLimit - this.totalItemsRetrieved;
1156
- if (remainingItems <= 0) {
1157
- return {
1158
- items: [],
1159
- hasNextPage: false,
1160
- page: this.currentPage
1161
- };
1162
- }
1163
- if (effectivePageSize !== void 0) {
1164
- effectivePageSize = Math.min(effectivePageSize, remainingItems);
1165
- } else {
1166
- effectivePageSize = remainingItems;
1167
- }
1168
- }
1169
- const query = this.queryBuilder.clone();
1170
- if (effectivePageSize !== void 0) {
1171
- query.limit(effectivePageSize);
1172
- }
1173
- if (this.lastEvaluatedKey) {
1174
- query.startFrom(this.lastEvaluatedKey);
1175
- }
1176
- const generator = await query.execute();
1177
- const items = [];
1178
- let itemCount = 0;
1179
- for await (const item of generator) {
1180
- if (effectivePageSize !== void 0 && itemCount >= effectivePageSize) {
1181
- break;
1182
- }
1183
- items.push(item);
1184
- itemCount++;
1185
- }
1186
- const lastEvaluatedKey = generator.getLastEvaluatedKey();
1187
- const result = { items, lastEvaluatedKey };
1188
- this.currentPage += 1;
1189
- this.lastEvaluatedKey = result.lastEvaluatedKey;
1190
- this.totalItemsRetrieved += result.items.length;
1191
- this.hasMorePages = !!result.lastEvaluatedKey && (this.overallLimit === void 0 || this.totalItemsRetrieved < this.overallLimit);
1192
- return {
1193
- items: result.items,
1194
- lastEvaluatedKey: result.lastEvaluatedKey,
1195
- hasNextPage: this.hasNextPage(),
1196
- page: this.currentPage
1197
- };
1198
- }
1199
- /**
1200
- * Gets all remaining dinosaurs or habitats and combines them into a single array.
1201
- *
1202
- * @example
1203
- * ```ts
1204
- * // Get complete carnivore inventory
1205
- * const paginator = new QueryBuilder(executor, eq('diet', 'CARNIVORE'))
1206
- * .filter(op => op.eq('status', 'ACTIVE'))
1207
- * .paginate(10);
1208
- *
1209
- * try {
1210
- * const allCarnivores = await paginator.getAllPages();
1211
- * console.log(`Park contains ${allCarnivores.length} active carnivores`);
1212
- *
1213
- * // Calculate total threat level
1214
- * const totalThreat = allCarnivores.reduce(
1215
- * (sum, dino) => sum + dino.stats.threatLevel,
1216
- * 0
1217
- * );
1218
- * console.log(`Total threat level: ${totalThreat}`);
1219
- * } catch (error) {
1220
- * console.error('Failed to complete carnivore census:', error);
1221
- * }
1222
- * ```
1223
- *
1224
- * @returns A promise that resolves to an array containing all remaining items
1225
- */
1226
- async getAllPages() {
1227
- const allItems = [];
1228
- while (this.hasNextPage()) {
1229
- const result = await this.getNextPage();
1230
- allItems.push(...result.items);
1231
- }
1232
- return allItems;
1233
- }
1234
- };
1235
-
1236
- // src/builders/filter-builder.ts
1237
- var FilterBuilder = class {
1238
- options = {};
1239
- selectedFields = /* @__PURE__ */ new Set();
1240
- /**
1241
- * Sets the maximum number of items to return.
1242
- *
1243
- * Note: This limit applies to the items that match the key condition
1244
- * before any filter expressions are applied.
1245
- *
1246
- * @example
1247
- * ```typescript
1248
- * // Get first 10 dinosaurs
1249
- * const result = await builder
1250
- * .limit(10)
1251
- * .execute();
1252
- * ```
1253
- *
1254
- * @param limit - Maximum number of items to return
1255
- * @returns The builder instance for method chaining
1256
- */
1257
- limit(limit) {
1258
- this.options.limit = limit;
1259
- return this;
1260
- }
1261
- /**
1262
- * Gets the current limit set on the operation.
1263
- * This is used internally by the paginator to manage result sets.
1264
- *
1265
- * @returns The current limit or undefined if no limit is set
1266
- */
1267
- getLimit() {
1268
- return this.options.limit;
1269
- }
1270
- /**
1271
- * Specifies a Global Secondary Index (GSI) to use for the operation.
1272
- *
1273
- * @example
1274
- * ```typescript
1275
- * // Find all dinosaurs of a specific species
1276
- * builder
1277
- * .useIndex('species-status-index')
1278
- * .filter(op => op.eq('status', 'ACTIVE'));
1279
- *
1280
- * // Search high-security habitats
1281
- * builder
1282
- * .useIndex('security-level-index')
1283
- * .filter(op =>
1284
- * op.and([
1285
- * op.gt('securityLevel', 8),
1286
- * op.eq('status', 'OPERATIONAL')
1287
- * ])
1288
- * );
1289
- * ```
1290
- *
1291
- * @param indexName - The name of the GSI to use (type-safe based on table configuration)
1292
- * @returns The builder instance for method chaining
1293
- */
1294
- useIndex(indexName) {
1295
- this.options.indexName = indexName;
1296
- return this;
1297
- }
1298
- /**
1299
- * Sets whether to use strongly consistent reads for the operation.
1300
- *
1301
- * Note:
1302
- * - Consistent reads are not available on GSIs
1303
- * - Consistent reads consume twice the throughput
1304
- * - Default is eventually consistent reads
1305
- *
1306
- * @example
1307
- * ```typescript
1308
- * // Check immediate dinosaur status
1309
- * const result = await builder
1310
- * .filter(op => op.eq('status', 'ACTIVE'))
1311
- * .consistentRead()
1312
- * .execute();
1313
- *
1314
- * // Monitor security breaches
1315
- * const result = await builder
1316
- * .useIndex('primary-index')
1317
- * .consistentRead(isEmergencyMode)
1318
- * .execute();
1319
- * ```
1320
- *
1321
- * @param consistentRead - Whether to use consistent reads (defaults to true)
1322
- * @returns The builder instance for method chaining
1323
- */
1324
- consistentRead(consistentRead = true) {
1325
- this.options.consistentRead = consistentRead;
1326
- return this;
1327
- }
1328
- /**
1329
- * Adds a filter expression to refine the operation results.
1330
- *
1331
- * @example
1332
- * ```typescript
1333
- * // Find aggressive carnivores
1334
- * builder.filter(op =>
1335
- * op.and([
1336
- * op.eq('diet', 'CARNIVORE'),
1337
- * op.gt('aggressionLevel', 7),
1338
- * op.eq('status', 'ACTIVE')
1339
- * ])
1340
- * );
1341
- *
1342
- * // Search suitable breeding habitats
1343
- * builder.filter(op =>
1344
- * op.and([
1345
- * op.between('temperature', 25, 30),
1346
- * op.lt('currentOccupants', 3),
1347
- * op.eq('quarantineStatus', 'CLEAR')
1348
- * ])
1349
- * );
1350
- * ```
1351
- *
1352
- * @param condition - Either a Condition object or a callback function that builds the condition
1353
- * @returns The builder instance for method chaining
1354
- */
1355
- filter(condition) {
1356
- const newCondition = typeof condition === "function" ? condition(this.getConditionOperator()) : condition;
1357
- if (this.options.filter) {
1358
- if (this.options.filter.type === "and" && this.options.filter.conditions) {
1359
- if (newCondition.type === "and" && newCondition.conditions) {
1360
- this.options.filter = {
1361
- type: "and",
1362
- conditions: [...this.options.filter.conditions, ...newCondition.conditions]
1363
- };
1364
- } else {
1365
- this.options.filter = {
1366
- type: "and",
1367
- conditions: [...this.options.filter.conditions, newCondition]
1368
- };
1369
- }
1370
- } else {
1371
- if (newCondition.type === "and" && newCondition.conditions) {
1372
- this.options.filter = {
1373
- type: "and",
1374
- conditions: [this.options.filter, ...newCondition.conditions]
1375
- };
1376
- } else {
1377
- this.options.filter = and(this.options.filter, newCondition);
1378
- }
1379
- }
1380
- } else {
1381
- this.options.filter = newCondition;
1382
- }
1383
- return this;
1384
- }
1385
- getConditionOperator() {
1386
- return {
1387
- eq,
1388
- ne,
1389
- lt,
1390
- lte,
1391
- gt,
1392
- gte,
1393
- between,
1394
- inArray,
1395
- beginsWith,
1396
- contains,
1397
- attributeExists,
1398
- attributeNotExists,
1399
- and,
1400
- or,
1401
- not
1402
- };
1403
- }
1404
- /**
1405
- * Specifies which attributes to return in the results.
1406
- *
1407
- * @example
1408
- * ```typescript
1409
- * // Get basic dinosaur info
1410
- * builder.select([
1411
- * 'species',
1412
- * 'status',
1413
- * 'stats.health',
1414
- * 'stats.aggressionLevel'
1415
- * ]);
1416
- *
1417
- * // Monitor habitat conditions
1418
- * builder
1419
- * .select('securityStatus')
1420
- * .select([
1421
- * 'currentOccupants',
1422
- * 'temperature',
1423
- * 'lastInspectionDate'
1424
- * ]);
1425
- * ```
1426
- *
1427
- * @param fields - A single field name or an array of field names to return
1428
- * @returns The builder instance for method chaining
1429
- */
1430
- select(fields) {
1431
- if (typeof fields === "string") {
1432
- this.selectedFields.add(fields);
1433
- } else if (Array.isArray(fields)) {
1434
- for (const field of fields) {
1435
- this.selectedFields.add(field);
1436
- }
1437
- }
1438
- this.options.projection = Array.from(this.selectedFields);
1439
- return this;
1440
- }
1441
- /**
1442
- * Creates a paginator that handles DynamoDB pagination automatically.
1443
- * The paginator handles:
1444
- * - Tracking the last evaluated key
1445
- * - Managing page boundaries
1446
- * - Respecting overall query limits
1447
- *
1448
- * @example
1449
- * ```typescript
1450
- * // Create a paginator for dinosaur records with specific page size
1451
- * const paginator = builder
1452
- * .filter(op => op.eq('status', 'ACTIVE'))
1453
- * .paginate(10);
1454
- *
1455
- * // Create a paginator with automatic DynamoDB paging (no page size limit)
1456
- * const autoPaginator = builder
1457
- * .filter(op => op.eq('status', 'ACTIVE'))
1458
- * .paginate();
1459
- *
1460
- * // Process pages of dinosaur results
1461
- * while (paginator.hasNextPage()) {
1462
- * const page = await paginator.getNextPage();
1463
- * console.log(`Processing page ${page.page}, count: ${page.items.length}`);
1464
- * // Process dinosaur data
1465
- * }
1466
- * ```
1467
- *
1468
- * @param pageSize - The number of items to return per page. If not provided, DynamoDB will automatically determine page sizes.
1469
- * @returns A Paginator instance that manages the pagination state
1470
- * @see Paginator for more pagination control options
1471
- */
1472
- paginate(pageSize) {
1473
- return new Paginator(this, pageSize);
1474
- }
1475
- /**
1476
- * Sets the starting point using a previous lastEvaluatedKey.
1477
- *
1478
- * Note: This method is typically used for manual pagination.
1479
- * For automatic pagination, use the paginate() method instead.
1480
- *
1481
- * @example
1482
- * ```typescript
1483
- * // First batch of dinosaurs
1484
- * const result1 = await builder
1485
- * .filter(op => op.eq('status', 'ACTIVE'))
1486
- * .limit(5)
1487
- * .execute();
1488
- *
1489
- * const lastKey = result1.getLastEvaluatedKey();
1490
- * if (lastKey) {
1491
- * // Continue listing dinosaurs
1492
- * const result2 = await builder
1493
- * .filter(op => op.eq('status', 'ACTIVE'))
1494
- * .startFrom(lastKey)
1495
- * .limit(5)
1496
- * .execute();
1497
- *
1498
- * const items = await result2.toArray();
1499
- * console.log('Additional dinosaurs:', items);
1500
- * }
1501
- * ```
1502
- *
1503
- * @param lastEvaluatedKey - The exclusive start key from a previous result
1504
- * @returns The builder instance for method chaining
1505
- */
1506
- startFrom(lastEvaluatedKey) {
1507
- this.options.lastEvaluatedKey = lastEvaluatedKey;
1508
- return this;
1509
- }
1510
- };
1511
-
1512
- // src/builders/result-iterator.ts
1513
- var ResultIterator = class {
1514
- constructor(queryBuilder, directExecutor) {
1515
- this.queryBuilder = queryBuilder;
1516
- this.directExecutor = directExecutor;
1517
- this.overallLimit = queryBuilder.getLimit();
1518
- }
1519
- lastEvaluatedKey;
1520
- itemsYielded = 0;
1521
- overallLimit;
1522
- /**
1523
- * Async iterator with automatic pagination
1524
- */
1525
- async *[Symbol.asyncIterator]() {
1526
- let hasMorePages = true;
1527
- while (hasMorePages) {
1528
- const result = await this.directExecutor();
1529
- for (const item of result.items) {
1530
- if (this.overallLimit !== void 0 && this.itemsYielded >= this.overallLimit) {
1531
- return;
1532
- }
1533
- yield item;
1534
- this.itemsYielded++;
1535
- }
1536
- if (result.lastEvaluatedKey !== null && result.lastEvaluatedKey !== void 0) {
1537
- this.lastEvaluatedKey = result.lastEvaluatedKey;
1538
- this.queryBuilder.startFrom(result.lastEvaluatedKey);
1539
- } else if (result.lastEvaluatedKey === null) {
1540
- if (this.lastEvaluatedKey === void 0) {
1541
- this.lastEvaluatedKey = null;
1542
- }
1543
- }
1544
- hasMorePages = !!result.lastEvaluatedKey && (this.overallLimit === void 0 || this.itemsYielded < this.overallLimit);
1545
- }
1546
- }
1547
- /**
1548
- * Convert to array (loads all pages).
1549
- *
1550
- * ```ts
1551
- * const result = await table.query({ pk: "foo" }).execute();
1552
- * const allItemsFromDynamo = await result.toArray();
1553
- * ```
1554
- *
1555
- * Note: This will load all pages into memory. For large datasets, consider using async iteration instead.
1556
- *```ts
1557
- * const result = await table.query({ pk: "foo" }).execute();
1558
- * for await (const item of result) {
1559
- * // Process each item
1560
- * }
1561
- * ```
1562
- */
1563
- async toArray() {
1564
- const items = [];
1565
- for await (const item of this) {
1566
- items.push(item);
1567
- }
1568
- return items;
1569
- }
1570
- /**
1571
- * Get the last evaluated key
1572
- */
1573
- getLastEvaluatedKey() {
1574
- return this.lastEvaluatedKey === null ? void 0 : this.lastEvaluatedKey;
1575
- }
1576
- };
1577
-
1578
- // src/builders/query-builder.ts
1579
- var QueryBuilder = class _QueryBuilder extends FilterBuilder {
1580
- keyCondition;
1581
- options = {};
1582
- executor;
1583
- constructor(executor, keyCondition) {
1584
- super();
1585
- this.executor = executor;
1586
- this.keyCondition = keyCondition;
1587
- }
1588
- /**
1589
- * Sets the maximum number of items to return from the query.
1590
- *
1591
- * Note: This is the default behavior if no sort order is specified.
1592
- *
1593
- * @example
1594
- * ```typescript
1595
- * // Get orders in chronological order
1596
- * const result = await new QueryBuilder(executor, eq('userId', '123'))
1597
- * .sortAscending()
1598
- * .execute();
1599
- *
1600
- * // Get events from oldest to newest
1601
- * const result = await new QueryBuilder(executor, eq('entityId', 'order-123'))
1602
- * .useIndex('entity-timestamp-index')
1603
- * .sortAscending()
1604
- * .execute();
1605
- * ```
1606
- *
1607
- * @returns The builder instance for method chaining
1608
- */
1609
- /**
1610
- * Sets the query to return items in ascending order by sort key.
1611
- *
1612
- * @example
1613
- * ```typescript
1614
- * // List dinosaurs by age
1615
- * const result = await new QueryBuilder(executor, eq('species', 'Velociraptor'))
1616
- * .useIndex('age-index')
1617
- * .sortAscending()
1618
- * .execute();
1619
- *
1620
- * // View incidents chronologically
1621
- * const result = await new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
1622
- * .useIndex('date-index')
1623
- * .sortAscending()
1624
- * .execute();
1625
- * ```
1626
- *
1627
- * @returns The builder instance for method chaining
1628
- */
1629
- sortAscending() {
1630
- this.options.scanIndexForward = true;
1631
- return this;
1632
- }
1633
- /**
1634
- * Sets the query to return items in descending order by sort key.
1635
- *
1636
- * @example
1637
- * ```typescript
1638
- * // Get most recent security incidents
1639
- * const result = await new QueryBuilder(executor, eq('type', 'SECURITY_BREACH'))
1640
- * .useIndex('date-index')
1641
- * .sortDescending()
1642
- * .limit(10)
1643
- * .execute();
1644
- *
1645
- * // Check latest dinosaur activities
1646
- * const result = await new QueryBuilder(executor, eq('species', 'Velociraptor'))
1647
- * .useIndex('activity-time-index')
1648
- * .filter(op => op.eq('status', 'ACTIVE'))
1649
- * .sortDescending()
1650
- * .execute();
1651
- * ```
1652
- *
1653
- * @returns The builder instance for method chaining
1654
- */
1655
- sortDescending() {
1656
- this.options.scanIndexForward = false;
1657
- return this;
1658
- }
1659
- /**
1660
- * Creates a deep clone of this QueryBuilder instance.
1661
- *
1662
- * This is particularly useful when:
1663
- * - Implementing pagination (used internally by paginate())
1664
- * - Creating query templates
1665
- * - Running multiple variations of a query
1666
- *
1667
- * @example
1668
- * ```typescript
1669
- * // Create base dinosaur query
1670
- * const baseQuery = new QueryBuilder(executor, eq('species', 'Velociraptor'))
1671
- * .useIndex('status-index')
1672
- * .select(['id', 'status', 'location']);
1673
- *
1674
- * // Check active dinosaurs
1675
- * const activeRaptors = baseQuery.clone()
1676
- * .filter(op => op.eq('status', 'HUNTING'))
1677
- * .execute();
1678
- *
1679
- * // Check contained dinosaurs
1680
- * const containedRaptors = baseQuery.clone()
1681
- * .filter(op => op.eq('status', 'CONTAINED'))
1682
- * .execute();
1683
- *
1684
- * // Check sedated dinosaurs
1685
- * const sedatedRaptors = baseQuery.clone()
1686
- * .filter(op => op.eq('status', 'SEDATED'))
1687
- * .execute();
1688
- * ```
1689
- *
1690
- * @returns A new QueryBuilder instance with the same configuration
1691
- */
1692
- clone() {
1693
- const clone = new _QueryBuilder(this.executor, this.keyCondition);
1694
- clone.options = {
1695
- ...this.options,
1696
- filter: this.deepCloneFilter(this.options.filter)
1697
- };
1698
- clone.selectedFields = new Set(this.selectedFields);
1699
- return clone;
1700
- }
1701
- deepCloneFilter(filter) {
1702
- if (!filter) return filter;
1703
- if (filter.type === "and" || filter.type === "or") {
1704
- return {
1705
- ...filter,
1706
- conditions: filter.conditions?.map((condition) => this.deepCloneFilter(condition)).filter((c) => c !== void 0)
1707
- };
1708
- }
1709
- return { ...filter };
1710
- }
1711
- /**
1712
- * Executes the query against DynamoDB and returns a generator that behaves like an array.
1713
- *
1714
- * The generator automatically handles pagination and provides array-like methods
1715
- * for processing results efficiently without loading everything into memory at once.
1716
- *
1717
- * @example
1718
- * ```typescript
1719
- * try {
1720
- * // Find active carnivores with automatic pagination
1721
- * const results = await new QueryBuilder(executor, eq('habitatId', 'PADDOCK-A'))
1722
- * .useIndex('species-status-index')
1723
- * .filter(op =>
1724
- * op.and([
1725
- * op.eq('diet', 'CARNIVORE'),
1726
- * op.eq('status', 'ACTIVE'),
1727
- * op.gt('aggressionLevel', 7)
1728
- * ])
1729
- * )
1730
- * .sortDescending()
1731
- * .execute();
1732
- *
1733
- * // Use like an array with automatic pagination
1734
- * for await (const dinosaur of results) {
1735
- * console.log(`Processing ${dinosaur.name}`);
1736
- * }
1737
- *
1738
- * // Or convert to array and use array methods
1739
- * const allItems = await results.toArray();
1740
- * const dangerousOnes = allItems.filter(dino => dino.aggressionLevel > 9);
1741
- * const totalCount = allItems.length;
1742
- * } catch (error) {
1743
- * console.error('Security scan failed:', error);
1744
- * }
1745
- * ```
1746
- *
1747
- * @returns A promise that resolves to a ResultGenerator that behaves like an array
1748
- */
1749
- async execute() {
1750
- const directExecutor = () => this.executor(this.keyCondition, this.options);
1751
- return new ResultIterator(this, directExecutor);
1752
- }
1753
- };
1754
-
1755
- // src/utils/debug-transaction.ts
1756
- function debugTransactionItem(item) {
1757
- const result = {
1758
- type: item.type,
1759
- tableName: item.params.tableName
1760
- };
1761
- if ("key" in item.params) {
1762
- result.key = item.params.key;
1763
- }
1764
- if (item.type === "Put") {
1765
- result.item = item.params.item;
1766
- }
1767
- switch (item.type) {
1768
- case "Put":
1769
- case "Delete":
1770
- case "ConditionCheck":
1771
- result.readable = debugCommand(item.params).readable;
1772
- break;
1773
- case "Update":
1774
- result.readable = debugCommand(item.params).readable;
1775
- break;
1776
- }
1777
- return result;
1778
- }
1779
- function debugTransaction(items) {
1780
- return items.map((item) => debugTransactionItem(item));
1781
- }
1782
-
1783
- // src/builders/transaction-builder.ts
1784
- var TransactionBuilder = class {
1785
- items = [];
1786
- options = {};
1787
- indexConfig;
1788
- executor;
1789
- constructor(executor, indexConfig) {
1790
- this.executor = executor;
1791
- this.indexConfig = indexConfig;
1792
- }
1793
- /**
1794
- * Checks if an item with the same primary key already exists in the transaction
1795
- * @private
1796
- */
1797
- checkForDuplicateItem(tableName, newItem) {
1798
- const pkName = this.indexConfig.partitionKey;
1799
- const skName = this.indexConfig.sortKey ?? "";
1800
- const pkValue = newItem[pkName];
1801
- const skValue = skName ? newItem[skName] : void 0;
1802
- if (!pkValue) {
1803
- throw new Error(`Primary key value for '${pkName}' is missing`);
1804
- }
1805
- const duplicateItem = this.items.find((item) => {
1806
- let itemKey;
1807
- let itemTableName;
1808
- switch (item.type) {
1809
- case "Put":
1810
- itemTableName = item.params.tableName;
1811
- itemKey = item.params.item;
1812
- break;
1813
- case "Update":
1814
- case "Delete":
1815
- case "ConditionCheck":
1816
- itemTableName = item.params.tableName;
1817
- itemKey = item.params.key;
1818
- break;
1819
- }
1820
- if (itemTableName === tableName && itemKey) {
1821
- const itemPkValue = itemKey[pkName];
1822
- const itemSkValue = skName ? itemKey[skName] : void 0;
1823
- if (itemPkValue === pkValue) {
1824
- if (skValue === void 0 && itemSkValue === void 0) {
1825
- return true;
1826
- }
1827
- if (skValue !== void 0 && itemSkValue !== void 0 && skValue === itemSkValue) {
1828
- return true;
1829
- }
1830
- }
1831
- }
1832
- return false;
1833
- });
1834
- if (duplicateItem) {
1835
- throw new Error(
1836
- `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.`
1837
- );
1838
- }
1839
- }
1840
- createKeyForPrimaryIndex(key) {
1841
- const keyCondition = {
1842
- [this.indexConfig.partitionKey]: key.pk
1843
- };
1844
- if (this.indexConfig.sortKey) {
1845
- if (key.sk === void 0) {
1846
- throw new Error("Sort key is required for delete operation");
1847
- }
1848
- keyCondition[this.indexConfig.sortKey] = key.sk;
1849
- }
1850
- return keyCondition;
1851
- }
1852
- /**
1853
- * Adds a put operation to the transaction.
1854
- *
1855
- * The method automatically checks for duplicate items within the transaction
1856
- * to prevent multiple operations on the same item.
1857
- *
1858
- * @example
1859
- * ```typescript
1860
- * // Simple put operation
1861
- * transaction.put('orders', {
1862
- * orderId: '123',
1863
- * status: 'PENDING',
1864
- * amount: 100
1865
- * });
1866
- *
1867
- * // Conditional put operation
1868
- * transaction.put(
1869
- * 'inventory',
1870
- * { productId: 'ABC', quantity: 50 },
1871
- * op => op.attributeNotExists('productId')
1872
- * );
1873
- *
1874
- * // Put with complex condition
1875
- * transaction.put(
1876
- * 'users',
1877
- * { userId: '123', status: 'ACTIVE' },
1878
- * op => op.and([
1879
- * op.attributeNotExists('userId'),
1880
- * op.beginsWith('status', 'ACTIVE')
1881
- * ])
1882
- * );
1883
- * ```
1884
- *
1885
- * @param tableName - The name of the DynamoDB table
1886
- * @param item - The item to put into the table
1887
- * @param condition - Optional condition that must be satisfied
1888
- * @returns The transaction builder for method chaining
1889
- * @throws {Error} If a duplicate item is detected in the transaction
1890
- */
1891
- put(tableName, item, condition) {
1892
- this.checkForDuplicateItem(tableName, item);
1893
- const transactionItem = {
1894
- type: "Put",
1895
- params: {
1896
- tableName,
1897
- item
1898
- }
1899
- };
1900
- if (condition) {
1901
- const { expression, names, values } = prepareExpressionParams(condition);
1902
- transactionItem.params.conditionExpression = expression;
1903
- transactionItem.params.expressionAttributeNames = names;
1904
- transactionItem.params.expressionAttributeValues = values;
1905
- }
1906
- this.items.push(transactionItem);
1907
- return this;
1908
- }
1909
- /**
1910
- * Adds a pre-configured put operation to the transaction.
1911
- *
1912
- * This method is particularly useful when working with PutBuilder
1913
- * to maintain consistency in put operations across your application.
1914
- *
1915
- * @example
1916
- * ```typescript
1917
- * // Create a put command with PutBuilder
1918
- * const putCommand = new PutBuilder(executor, newItem, 'users')
1919
- * .condition(op => op.attributeNotExists('userId'))
1920
- * .toDynamoCommand();
1921
- *
1922
- * // Add the command to the transaction
1923
- * transaction.putWithCommand(putCommand);
1924
- * ```
1925
- *
1926
- * @param command - The complete put command configuration
1927
- * @returns The transaction builder for method chaining
1928
- * @throws {Error} If a duplicate item is detected in the transaction
1929
- * @see PutBuilder for creating put commands
1930
- */
1931
- putWithCommand(command) {
1932
- this.checkForDuplicateItem(command.tableName, command.item);
1933
- const transactionItem = {
1934
- type: "Put",
1935
- params: command
1936
- };
1937
- this.items.push(transactionItem);
1938
- return this;
1939
- }
1940
- /**
1941
- * Adds a delete operation to the transaction.
1942
- *
1943
- * The method automatically checks for duplicate items within the transaction
1944
- * to prevent multiple operations on the same item.
1945
- *
1946
- * @example
1947
- * ```typescript
1948
- * // Simple delete operation
1949
- * transaction.delete('orders', {
1950
- * pk: 'ORDER#123',
1951
- * sk: 'METADATA'
1952
- * });
1953
- *
1954
- * // Conditional delete operation
1955
- * transaction.delete(
1956
- * 'users',
1957
- * { pk: 'USER#123' },
1958
- * op => op.eq('status', 'INACTIVE')
1959
- * );
1960
- *
1961
- * // Delete with complex condition
1962
- * transaction.delete(
1963
- * 'products',
1964
- * { pk: 'PROD#ABC' },
1965
- * op => op.and([
1966
- * op.eq('status', 'DRAFT'),
1967
- * op.lt('version', 5)
1968
- * ])
1969
- * );
1970
- * ```
1971
- *
1972
- * @param tableName - The name of the DynamoDB table
1973
- * @param key - The primary key of the item to delete
1974
- * @param condition - Optional condition that must be satisfied
1975
- * @returns The transaction builder for method chaining
1976
- * @throws {Error} If a duplicate item is detected in the transaction
1977
- */
1978
- delete(tableName, key, condition) {
1979
- const keyCondition = this.createKeyForPrimaryIndex(key);
1980
- this.checkForDuplicateItem(tableName, keyCondition);
1981
- const transactionItem = {
1982
- type: "Delete",
1983
- params: {
1984
- tableName,
1985
- key: keyCondition
1986
- }
1987
- };
1988
- if (condition) {
1989
- const { expression, names, values } = prepareExpressionParams(condition);
1990
- transactionItem.params.conditionExpression = expression;
1991
- transactionItem.params.expressionAttributeNames = names;
1992
- transactionItem.params.expressionAttributeValues = values;
1993
- }
1994
- this.items.push(transactionItem);
1995
- return this;
1996
- }
1997
- /**
1998
- * Adds a pre-configured delete operation to the transaction.
1999
- *
2000
- * This method is particularly useful when working with DeleteBuilder
2001
- * to maintain consistency in delete operations across your application.
2002
- *
2003
- * @example
2004
- * ```typescript
2005
- * // Create a delete command with DeleteBuilder
2006
- * const deleteCommand = new DeleteBuilder(executor, 'users', { pk: 'USER#123' })
2007
- * .condition(op => op.and([
2008
- * op.attributeExists('pk'),
2009
- * op.eq('status', 'INACTIVE')
2010
- * ]))
2011
- * .toDynamoCommand();
2012
- *
2013
- * // Add the command to the transaction
2014
- * transaction.deleteWithCommand(deleteCommand);
2015
- * ```
2016
- *
2017
- * @param command - The complete delete command configuration
2018
- * @returns The transaction builder for method chaining
2019
- * @throws {Error} If a duplicate item is detected in the transaction
2020
- * @see DeleteBuilder for creating delete commands
2021
- */
2022
- deleteWithCommand(command) {
2023
- let keyForDuplicateCheck;
2024
- let keyForTransaction;
2025
- if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
2026
- keyForTransaction = this.createKeyForPrimaryIndex(command.key);
2027
- keyForDuplicateCheck = keyForTransaction;
2028
- } else {
2029
- keyForTransaction = command.key;
2030
- keyForDuplicateCheck = command.key;
2031
- }
2032
- this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
2033
- const transactionItem = {
2034
- type: "Delete",
2035
- params: {
2036
- ...command,
2037
- key: keyForTransaction
2038
- }
2039
- };
2040
- this.items.push(transactionItem);
2041
- return this;
2042
- }
2043
- /**
2044
- * Adds an update operation to the transaction.
2045
- *
2046
- * The method supports all DynamoDB update expressions:
2047
- * - SET: Modify or add attributes
2048
- * - REMOVE: Delete attributes
2049
- * - ADD: Update numbers and sets
2050
- * - DELETE: Remove elements from a set
2051
- *
2052
- * @example
2053
- * ```typescript
2054
- * // Simple update
2055
- * transaction.update(
2056
- * 'orders',
2057
- * { pk: 'ORDER#123' },
2058
- * 'SET #status = :status',
2059
- * { '#status': 'status' },
2060
- * { ':status': 'PROCESSING' }
2061
- * );
2062
- *
2063
- * // Complex update with multiple operations
2064
- * transaction.update(
2065
- * 'products',
2066
- * { pk: 'PROD#ABC' },
2067
- * 'SET #qty = #qty - :amount, #status = :status REMOVE #oldAttr',
2068
- * { '#qty': 'quantity', '#status': 'status', '#oldAttr': 'deprecated_field' },
2069
- * { ':amount': 1, ':status': 'LOW_STOCK' }
2070
- * );
2071
- *
2072
- * // Conditional update
2073
- * transaction.update(
2074
- * 'users',
2075
- * { pk: 'USER#123' },
2076
- * 'SET #lastLogin = :now',
2077
- * { '#lastLogin': 'lastLoginDate' },
2078
- * { ':now': new Date().toISOString() },
2079
- * op => op.attributeExists('pk')
2080
- * );
2081
- * ```
2082
- *
2083
- * @param tableName - The name of the DynamoDB table
2084
- * @param key - The primary key of the item to update
2085
- * @param updateExpression - The update expression (SET, REMOVE, ADD, DELETE)
2086
- * @param expressionAttributeNames - Map of attribute name placeholders to actual names
2087
- * @param expressionAttributeValues - Map of value placeholders to actual values
2088
- * @param condition - Optional condition that must be satisfied
2089
- * @returns The transaction builder for method chaining
2090
- * @throws {Error} If a duplicate item is detected in the transaction
2091
- */
2092
- update(tableName, key, updateExpression, expressionAttributeNames, expressionAttributeValues, condition) {
2093
- const keyCondition = this.createKeyForPrimaryIndex(key);
2094
- this.checkForDuplicateItem(tableName, keyCondition);
2095
- const transactionItem = {
2096
- type: "Update",
2097
- params: {
2098
- tableName,
2099
- key: keyCondition,
2100
- updateExpression,
2101
- expressionAttributeNames,
2102
- expressionAttributeValues
2103
- }
2104
- };
2105
- if (condition) {
2106
- const { expression, names, values } = prepareExpressionParams(condition);
2107
- transactionItem.params.conditionExpression = expression;
2108
- transactionItem.params.expressionAttributeNames = {
2109
- ...transactionItem.params.expressionAttributeNames,
2110
- ...names
2111
- };
2112
- transactionItem.params.expressionAttributeValues = {
2113
- ...transactionItem.params.expressionAttributeValues,
2114
- ...values
2115
- };
2116
- }
2117
- this.items.push(transactionItem);
2118
- return this;
2119
- }
2120
- /**
2121
- * Adds a pre-configured update operation to the transaction.
2122
- *
2123
- * This method is particularly useful when working with UpdateBuilder
2124
- * to maintain consistency in update operations across your application.
2125
- *
2126
- * @example
2127
- * ```typescript
2128
- * // Create an update command with UpdateBuilder
2129
- * const updateCommand = new UpdateBuilder(executor, 'inventory', { pk: 'PROD#ABC' })
2130
- * .set('quantity', ':qty')
2131
- * .set('lastUpdated', ':now')
2132
- * .values({
2133
- * ':qty': 100,
2134
- * ':now': new Date().toISOString()
2135
- * })
2136
- * .condition(op => op.gt('quantity', 0))
2137
- * .toDynamoCommand();
2138
- *
2139
- * // Add the command to the transaction
2140
- * transaction.updateWithCommand(updateCommand);
2141
- * ```
2142
- *
2143
- * @param command - The complete update command configuration
2144
- * @returns The transaction builder for method chaining
2145
- * @throws {Error} If a duplicate item is detected in the transaction
2146
- * @see UpdateBuilder for creating update commands
2147
- */
2148
- updateWithCommand(command) {
2149
- let keyForDuplicateCheck;
2150
- let keyForTransaction;
2151
- if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
2152
- keyForTransaction = this.createKeyForPrimaryIndex(command.key);
2153
- keyForDuplicateCheck = keyForTransaction;
2154
- } else {
2155
- keyForTransaction = command.key;
2156
- keyForDuplicateCheck = command.key;
2157
- }
2158
- this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
2159
- const transactionItem = {
2160
- type: "Update",
2161
- params: {
2162
- ...command,
2163
- key: keyForTransaction
2164
- }
2165
- };
2166
- this.items.push(transactionItem);
2167
- return this;
2168
- }
2169
- /**
2170
- * Adds a condition check operation to the transaction.
2171
- *
2172
- * Condition checks are particularly useful for:
2173
- * - Implementing optimistic locking
2174
- * - Ensuring referential integrity
2175
- * - Validating business rules atomically
2176
- *
2177
- * @example
2178
- * ```typescript
2179
- * // Check if order is in correct state
2180
- * transaction.conditionCheck(
2181
- * 'orders',
2182
- * { pk: 'ORDER#123' },
2183
- * op => op.eq('status', 'PENDING')
2184
- * );
2185
- *
2186
- * // Complex condition check
2187
- * transaction.conditionCheck(
2188
- * 'inventory',
2189
- * { pk: 'PROD#ABC' },
2190
- * op => op.and([
2191
- * op.gt('quantity', 0),
2192
- * op.eq('status', 'ACTIVE'),
2193
- * op.attributeExists('lastRestockDate')
2194
- * ])
2195
- * );
2196
- *
2197
- * // Check with multiple attributes
2198
- * transaction.conditionCheck(
2199
- * 'users',
2200
- * { pk: 'USER#123' },
2201
- * op => op.or([
2202
- * op.eq('status', 'PREMIUM'),
2203
- * op.gte('credits', 100)
2204
- * ])
2205
- * );
2206
- * ```
2207
- *
2208
- * @param tableName - The name of the DynamoDB table
2209
- * @param key - The primary key of the item to check
2210
- * @param condition - The condition that must be satisfied
2211
- * @returns The transaction builder for method chaining
2212
- * @throws {Error} If a duplicate item is detected in the transaction
2213
- * @throws {Error} If condition expression generation fails
2214
- */
2215
- conditionCheck(tableName, key, condition) {
2216
- const keyCondition = this.createKeyForPrimaryIndex(key);
2217
- this.checkForDuplicateItem(tableName, keyCondition);
2218
- const { expression, names, values } = prepareExpressionParams(condition);
2219
- if (!expression) {
2220
- throw new Error("Failed to generate condition expression");
2221
- }
2222
- const transactionItem = {
2223
- type: "ConditionCheck",
2224
- params: {
2225
- tableName,
2226
- key: keyCondition,
2227
- conditionExpression: expression,
2228
- expressionAttributeNames: names,
2229
- expressionAttributeValues: values
2230
- }
2231
- };
2232
- this.items.push(transactionItem);
2233
- return this;
2234
- }
2235
- /**
2236
- * Adds a pre-configured condition check operation to the transaction.
2237
- *
2238
- * This method is particularly useful when working with ConditionCheckBuilder
2239
- * to maintain consistency in condition checks across your application.
2240
- *
2241
- * @example
2242
- * ```typescript
2243
- * // Create a condition check with ConditionCheckBuilder
2244
- * const checkCommand = new ConditionCheckBuilder('inventory', { pk: 'PROD#ABC' })
2245
- * .condition(op => op.and([
2246
- * op.between('quantity', 10, 100),
2247
- * op.beginsWith('category', 'ELECTRONICS'),
2248
- * op.attributeExists('lastAuditDate')
2249
- * ]))
2250
- * .toDynamoCommand();
2251
- *
2252
- * // Add the command to the transaction
2253
- * transaction.conditionCheckWithCommand(checkCommand);
2254
- * ```
2255
- *
2256
- * @param command - The complete condition check command configuration
2257
- * @returns The transaction builder for method chaining
2258
- * @throws {Error} If a duplicate item is detected in the transaction
2259
- * @see ConditionCheckBuilder for creating condition check commands
2260
- */
2261
- conditionCheckWithCommand(command) {
2262
- let keyForDuplicateCheck;
2263
- let keyForTransaction;
2264
- if (typeof command.key === "object" && command.key !== null && "pk" in command.key) {
2265
- keyForTransaction = this.createKeyForPrimaryIndex(command.key);
2266
- keyForDuplicateCheck = keyForTransaction;
2267
- } else {
2268
- keyForTransaction = command.key;
2269
- keyForDuplicateCheck = command.key;
2270
- }
2271
- this.checkForDuplicateItem(command.tableName, keyForDuplicateCheck);
2272
- const transactionItem = {
2273
- type: "ConditionCheck",
2274
- params: {
2275
- ...command,
2276
- key: keyForTransaction
2277
- }
2278
- };
2279
- this.items.push(transactionItem);
2280
- return this;
2281
- }
2282
- /**
2283
- * Sets options for the transaction execution.
2284
- *
2285
- * @example
2286
- * ```typescript
2287
- * // Enable idempotency and capacity tracking
2288
- * transaction.withOptions({
2289
- * clientRequestToken: 'unique-request-id-123',
2290
- * returnConsumedCapacity: 'TOTAL'
2291
- * });
2292
- *
2293
- * // Track item collection metrics
2294
- * transaction.withOptions({
2295
- * returnItemCollectionMetrics: 'SIZE'
2296
- * });
2297
- * ```
2298
- *
2299
- * Note: ClientRequestToken can be used to make transactions idempotent,
2300
- * ensuring the same transaction is not executed multiple times.
2301
- *
2302
- * @param options - Configuration options for the transaction
2303
- * @returns The transaction builder for method chaining
2304
- */
2305
- withOptions(options) {
2306
- this.options = { ...this.options, ...options };
2307
- return this;
2308
- }
2309
- /**
2310
- * Gets a human-readable representation of the transaction items.
2311
- *
2312
- * The method resolves all expression placeholders with their actual values,
2313
- * making it easier to understand the transaction's operations.
2314
- *
2315
- * @example
2316
- * ```typescript
2317
- * // Add multiple operations
2318
- * transaction
2319
- * .put('orders', { orderId: '123', status: 'PENDING' })
2320
- * .update('inventory',
2321
- * { productId: 'ABC' },
2322
- * 'SET quantity = quantity - :amount',
2323
- * undefined,
2324
- * { ':amount': 1 }
2325
- * );
2326
- *
2327
- * // Debug the transaction
2328
- * const debugInfo = transaction.debug();
2329
- * console.log('Transaction operations:', debugInfo);
2330
- * ```
2331
- *
2332
- * @returns An array of readable representations of the transaction items
2333
- */
2334
- debug() {
2335
- return debugTransaction(this.items);
2336
- }
2337
- /**
2338
- * Executes all operations in the transaction atomically.
2339
- *
2340
- * The transaction will only succeed if all operations succeed.
2341
- * If any operation fails, the entire transaction is rolled back.
2342
- *
2343
- * @example
2344
- * ```typescript
2345
- * try {
2346
- * // Build and execute transaction
2347
- * await transaction
2348
- * .put('orders', newOrder)
2349
- * .update('inventory',
2350
- * { productId: 'ABC' },
2351
- * 'SET quantity = quantity - :qty',
2352
- * undefined,
2353
- * { ':qty': 1 }
2354
- * )
2355
- * .conditionCheck('products',
2356
- * { productId: 'ABC' },
2357
- * op => op.eq('status', 'ACTIVE')
2358
- * )
2359
- * .execute();
2360
- *
2361
- * console.log('Transaction completed successfully');
2362
- * } catch (error) {
2363
- * // Handle transaction failure
2364
- * console.error('Transaction failed:', error);
2365
- * }
2366
- * ```
2367
- *
2368
- * @throws {Error} If no transaction items are specified
2369
- * @throws {Error} If any operation in the transaction fails
2370
- * @returns A promise that resolves when the transaction completes
2371
- */
2372
- async execute() {
2373
- if (this.items.length === 0) {
2374
- throw new Error("No transaction items specified");
2375
- }
2376
- const transactItems = this.items.map((item) => {
2377
- switch (item.type) {
2378
- case "Put":
2379
- return {
2380
- Put: {
2381
- TableName: item.params.tableName,
2382
- Item: item.params.item,
2383
- ConditionExpression: item.params.conditionExpression,
2384
- ExpressionAttributeNames: item.params.expressionAttributeNames,
2385
- ExpressionAttributeValues: item.params.expressionAttributeValues
2386
- }
2387
- };
2388
- case "Delete":
2389
- return {
2390
- Delete: {
2391
- TableName: item.params.tableName,
2392
- Key: item.params.key,
2393
- ConditionExpression: item.params.conditionExpression,
2394
- ExpressionAttributeNames: item.params.expressionAttributeNames,
2395
- ExpressionAttributeValues: item.params.expressionAttributeValues
2396
- }
2397
- };
2398
- case "Update":
2399
- return {
2400
- Update: {
2401
- TableName: item.params.tableName,
2402
- Key: item.params.key,
2403
- UpdateExpression: item.params.updateExpression,
2404
- ConditionExpression: item.params.conditionExpression,
2405
- ExpressionAttributeNames: item.params.expressionAttributeNames,
2406
- ExpressionAttributeValues: item.params.expressionAttributeValues
2407
- }
2408
- };
2409
- case "ConditionCheck":
2410
- return {
2411
- ConditionCheck: {
2412
- TableName: item.params.tableName,
2413
- Key: item.params.key,
2414
- ConditionExpression: item.params.conditionExpression,
2415
- ExpressionAttributeNames: item.params.expressionAttributeNames,
2416
- ExpressionAttributeValues: item.params.expressionAttributeValues
2417
- }
2418
- };
2419
- default: {
2420
- const exhaustiveCheck = item;
2421
- throw new Error(`Unsupported transaction item type: ${String(exhaustiveCheck)}`);
2422
- }
2423
- }
2424
- });
2425
- const params = {
2426
- TransactItems: transactItems,
2427
- ClientRequestToken: this.options.clientRequestToken,
2428
- ReturnConsumedCapacity: this.options.returnConsumedCapacity,
2429
- ReturnItemCollectionMetrics: this.options.returnItemCollectionMetrics
2430
- };
2431
- try {
2432
- await this.executor(params);
2433
- } catch (error) {
2434
- console.log(this.debug());
2435
- console.error("Error executing transaction:", error);
2436
- throw error;
2437
- }
2438
- }
2439
- };
2440
-
2441
- // src/builders/update-builder.ts
2442
- var UpdateBuilder = class {
2443
- updates = [];
2444
- options = {
2445
- returnValues: "ALL_NEW"
2446
- };
2447
- executor;
2448
- tableName;
2449
- key;
2450
- constructor(executor, tableName, key) {
2451
- this.executor = executor;
2452
- this.tableName = tableName;
2453
- this.key = key;
2454
- }
2455
- set(valuesOrPath, value) {
2456
- if (typeof valuesOrPath === "object") {
2457
- for (const [key, value2] of Object.entries(valuesOrPath)) {
2458
- this.updates.push({
2459
- type: "SET",
2460
- path: key,
2461
- value: value2
2462
- });
2463
- }
2464
- } else {
2465
- this.updates.push({
2466
- type: "SET",
2467
- path: valuesOrPath,
2468
- value
2469
- });
2470
- }
2471
- return this;
2472
- }
2473
- /**
2474
- * Removes an attribute from the item.
2475
- *
2476
- * @example
2477
- * ```typescript
2478
- * // Remove simple attributes
2479
- * builder
2480
- * .remove('temporaryTag')
2481
- * .remove('previousLocation');
2482
- *
2483
- * // Remove nested attributes
2484
- * builder
2485
- * .remove('metadata.testData')
2486
- * .remove('stats.experimentalMetrics');
2487
- * ```
2488
- *
2489
- * @param path - The path to the attribute to remove
2490
- * @returns The builder instance for method chaining
2491
- */
2492
- remove(path) {
2493
- this.updates.push({
2494
- type: "REMOVE",
2495
- path
2496
- });
2497
- return this;
2498
- }
2499
- /**
2500
- * Adds a value to a number attribute or adds elements to a set.
2501
- *
2502
- * @example
2503
- * ```typescript
2504
- * // Increment counters
2505
- * builder
2506
- * .add('escapeAttempts', 1)
2507
- * .add('feedingCount', 1);
2508
- *
2509
- * // Add to sets
2510
- * builder
2511
- * .add('knownBehaviors', new Set(['PACK_HUNTING', 'AMBUSH_TACTICS']))
2512
- * .add('visitedZones', new Set(['ZONE_A', 'ZONE_B']));
2513
- * ```
2514
- *
2515
- * @param path - The path to the attribute to update
2516
- * @param value - The value to add (number or set)
2517
- * @returns The builder instance for method chaining
2518
- */
2519
- add(path, value) {
2520
- this.updates.push({
2521
- type: "ADD",
2522
- path,
2523
- value
2524
- });
2525
- return this;
2526
- }
2527
- /**
2528
- * Removes elements from a set attribute.
2529
- *
2530
- * @example
2531
- * ```typescript
2532
- * // Remove from sets using arrays
2533
- * builder.deleteElementsFromSet(
2534
- * 'allowedHabitats',
2535
- * ['JUNGLE', 'COASTAL']
2536
- * );
2537
- *
2538
- * // Remove from sets using Set DynamoItems
2539
- * builder.deleteElementsFromSet(
2540
- * 'knownBehaviors',
2541
- * new Set(['NOCTURNAL', 'TERRITORIAL'])
2542
- * );
2543
- *
2544
- * // Remove from nested sets
2545
- * builder.deleteElementsFromSet(
2546
- * 'stats.compatibleSpecies',
2547
- * ['VELOCIRAPTOR', 'DILOPHOSAURUS']
2548
- * );
2549
- * ```
2550
- *
2551
- * @param path - The path to the set attribute
2552
- * @param value - Elements to remove (array or Set)
2553
- * @returns The builder instance for method chaining
2554
- */
2555
- deleteElementsFromSet(path, value) {
2556
- let valuesToDelete;
2557
- if (Array.isArray(value)) {
2558
- valuesToDelete = new Set(value);
2559
- } else {
2560
- valuesToDelete = value;
2561
- }
2562
- this.updates.push({
2563
- type: "DELETE",
2564
- path,
2565
- value: valuesToDelete
2566
- });
2567
- return this;
2568
- }
2569
- /**
2570
- * Adds a condition that must be satisfied for the update to succeed.
2571
- *
2572
- * @example
2573
- * ```typescript
2574
- * // Simple condition
2575
- * builder.condition(op =>
2576
- * op.eq('status', 'ACTIVE')
2577
- * );
2578
- *
2579
- * // Health check condition
2580
- * builder.condition(op =>
2581
- * op.and([
2582
- * op.gt('health', 50),
2583
- * op.eq('status', 'HUNTING')
2584
- * ])
2585
- * );
2586
- *
2587
- * // Complex security condition
2588
- * builder.condition(op =>
2589
- * op.and([
2590
- * op.attributeExists('securitySystem'),
2591
- * op.eq('containmentStatus', 'SECURE'),
2592
- * op.lt('aggressionLevel', 8)
2593
- * ])
2594
- * );
2595
- *
2596
- * // Version check (optimistic locking)
2597
- * builder.condition(op =>
2598
- * op.eq('version', currentVersion)
2599
- * );
2600
- * ```
2601
- *
2602
- * @param condition - Either a Condition DynamoItem or a callback function that builds the condition
2603
- * @returns The builder instance for method chaining
2604
- */
2605
- condition(condition) {
2606
- if (typeof condition === "function") {
2607
- const conditionOperator = {
2608
- eq,
2609
- ne,
2610
- lt,
2611
- lte,
2612
- gt,
2613
- gte,
2614
- between,
2615
- inArray,
2616
- beginsWith,
2617
- contains,
2618
- attributeExists,
2619
- attributeNotExists,
2620
- and,
2621
- or,
2622
- not
2623
- };
2624
- this.options.condition = condition(conditionOperator);
2625
- } else {
2626
- this.options.condition = condition;
2627
- }
2628
- return this;
2629
- }
2630
- /**
2631
- * Sets which item attributes to include in the response.
2632
- *
2633
- * Available options:
2634
- * - ALL_NEW: All attributes after the update (default)
2635
- * - UPDATED_NEW: Only updated attributes, new values
2636
- * - ALL_OLD: All attributes before the update
2637
- * - UPDATED_OLD: Only updated attributes, old values
2638
- * - NONE: No attributes returned
2639
- *
2640
- * @example
2641
- * ```typescript
2642
- * // Get complete updated dinosaur
2643
- * const result = await builder
2644
- * .set('status', 'SLEEPING')
2645
- * .returnValues('ALL_NEW')
2646
- * .execute();
2647
- *
2648
- * // Track specific attribute changes
2649
- * const result = await builder
2650
- * .set({
2651
- * 'stats.health': 100,
2652
- * 'stats.energy': 95
2653
- * })
2654
- * .returnValues('UPDATED_OLD')
2655
- * .execute();
2656
- *
2657
- * if (result.item) {
2658
- * console.log('Previous health:', result.item.stats?.health);
2659
- * }
2660
- * ```
2661
- *
2662
- * @param returnValues - Which attributes to return in the response
2663
- * @returns The builder instance for method chaining
2664
- */
2665
- returnValues(returnValues) {
2666
- this.options.returnValues = returnValues;
2667
- return this;
2668
- }
2669
- /**
2670
- * Generate the DynamoDB command parameters
2671
- */
2672
- toDynamoCommand() {
2673
- if (this.updates.length === 0) {
2674
- throw new Error("No update actions specified");
2675
- }
2676
- const expressionParams = {
2677
- expressionAttributeNames: {},
2678
- expressionAttributeValues: {},
2679
- valueCounter: { count: 0 }
2680
- };
2681
- let updateExpression = "";
2682
- const setUpdates = [];
2683
- const removeUpdates = [];
2684
- const addUpdates = [];
2685
- const deleteUpdates = [];
2686
- for (const update of this.updates) {
2687
- switch (update.type) {
2688
- case "SET":
2689
- setUpdates.push(update);
2690
- break;
2691
- case "REMOVE":
2692
- removeUpdates.push(update);
2693
- break;
2694
- case "ADD":
2695
- addUpdates.push(update);
2696
- break;
2697
- case "DELETE":
2698
- deleteUpdates.push(update);
2699
- break;
2700
- }
2701
- }
2702
- if (setUpdates.length > 0) {
2703
- updateExpression += "SET ";
2704
- updateExpression += setUpdates.map((update) => {
2705
- const attrName = generateAttributeName(expressionParams, update.path);
2706
- const valueName = generateValueName(expressionParams, update.value);
2707
- expressionParams.expressionAttributeValues[valueName] = update.value;
2708
- return `${attrName} = ${valueName}`;
2709
- }).join(", ");
2710
- }
2711
- if (removeUpdates.length > 0) {
2712
- if (updateExpression) {
2713
- updateExpression += " ";
2714
- }
2715
- updateExpression += "REMOVE ";
2716
- updateExpression += removeUpdates.map((update) => {
2717
- return generateAttributeName(expressionParams, update.path);
2718
- }).join(", ");
2719
- }
2720
- if (addUpdates.length > 0) {
2721
- if (updateExpression) {
2722
- updateExpression += " ";
2723
- }
2724
- updateExpression += "ADD ";
2725
- updateExpression += addUpdates.map((update) => {
2726
- const attrName = generateAttributeName(expressionParams, update.path);
2727
- const valueName = generateValueName(expressionParams, update.value);
2728
- return `${attrName} ${valueName}`;
2729
- }).join(", ");
2730
- }
2731
- if (deleteUpdates.length > 0) {
2732
- if (updateExpression) {
2733
- updateExpression += " ";
2734
- }
2735
- updateExpression += "DELETE ";
2736
- updateExpression += deleteUpdates.map((update) => {
2737
- const attrName = generateAttributeName(expressionParams, update.path);
2738
- const valueName = generateValueName(expressionParams, update.value);
2739
- return `${attrName} ${valueName}`;
2740
- }).join(", ");
2741
- }
2742
- let conditionExpression;
2743
- if (this.options.condition) {
2744
- conditionExpression = buildExpression(this.options.condition, expressionParams);
2745
- }
2746
- const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
2747
- return {
2748
- tableName: this.tableName,
2749
- key: this.key,
2750
- updateExpression,
2751
- conditionExpression,
2752
- expressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
2753
- expressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
2754
- returnValues: this.options.returnValues
2755
- };
2756
- }
2757
- /**
2758
- * Adds this update operation to a transaction.
2759
- *
2760
- * @example
2761
- * ```typescript
2762
- * const transaction = new TransactionBuilder(executor);
2763
- *
2764
- * // Update dinosaur status and habitat occupancy atomically
2765
- * new UpdateBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
2766
- * .set('location', 'PADDOCK_A')
2767
- * .set('status', 'CONTAINED')
2768
- * .withTransaction(transaction);
2769
- *
2770
- * new UpdateBuilder(executor, 'habitats', { id: 'PADDOCK-A' })
2771
- * .add('occupants', 1)
2772
- * .set('lastOccupied', new Date().toISOString())
2773
- * .withTransaction(transaction);
2774
- *
2775
- * // Execute all operations atomically
2776
- * await transaction.execute();
2777
- * ```
2778
- *
2779
- * @param transaction - The transaction builder to add this operation to
2780
- * @returns The builder instance for method chaining
2781
- */
2782
- withTransaction(transaction) {
2783
- const command = this.toDynamoCommand();
2784
- transaction.updateWithCommand(command);
2785
- }
2786
- /**
2787
- * Gets a human-readable representation of the update command.
2788
- *
2789
- * @example
2790
- * ```typescript
2791
- * // Create complex update
2792
- * const builder = new UpdateBuilder(executor, 'dinosaurs', { id: 'RAPTOR-001' })
2793
- * .set({
2794
- * status: 'HUNTING',
2795
- * 'stats.health': 95,
2796
- * 'behavior.lastObserved': new Date().toISOString()
2797
- * })
2798
- * .add('huntingSuccesses', 1)
2799
- * .condition(op => op.gt('health', 50));
2800
- *
2801
- * // Debug the update
2802
- * const debugInfo = builder.debug();
2803
- * console.log('Update operation:', debugInfo);
2804
- * ```
2805
- *
2806
- * @returns A readable representation of the update command with resolved expressions
2807
- */
2808
- debug() {
2809
- const command = this.toDynamoCommand();
2810
- return debugCommand(command);
2811
- }
2812
- /**
2813
- * Executes the update operation against DynamoDB.
2814
- *
2815
- * @example
2816
- * ```typescript
2817
- * try {
2818
- * // Update dinosaur status with conditions
2819
- * const result = await new UpdateBuilder(executor, 'dinosaurs', { id: 'TREX-001' })
2820
- * .set({
2821
- * status: 'FEEDING',
2822
- * lastMeal: new Date().toISOString(),
2823
- * 'stats.hunger': 0
2824
- * })
2825
- * .add('feedingCount', 1)
2826
- * .condition(op =>
2827
- * op.and([
2828
- * op.gt('stats.hunger', 80),
2829
- * op.eq('status', 'HUNTING')
2830
- * ])
2831
- * )
2832
- * .returnValues('ALL_NEW')
2833
- * .execute();
2834
- *
2835
- * if (result.item) {
2836
- * console.log('Updated dinosaur:', result.item);
2837
- * }
2838
- * } catch (error) {
2839
- * // Handle condition check failure
2840
- * console.error('Failed to update dinosaur:', error);
2841
- * // Check if dinosaur wasn't hungry enough
2842
- * if (error.name === 'ConditionalCheckFailedException') {
2843
- * console.log('Dinosaur not ready for feeding');
2844
- * }
2845
- * }
2846
- * ```
2847
- *
2848
- * @returns A promise that resolves to an DynamoItem containing the updated item (if returnValues is set)
2849
- * @throws {ConditionalCheckFailedException} If the condition check fails
2850
- * @throws {Error} If the update operation fails for other reasons
2851
- */
2852
- async execute() {
2853
- const params = this.toDynamoCommand();
2854
- return this.executor(params);
2855
- }
2856
- };
2857
-
2858
- // src/builders/entity-aware-builders.ts
2859
- function createEntityAwareBuilder(builder, entityName) {
2860
- return new Proxy(builder, {
2861
- get(target, prop, receiver) {
2862
- if (prop === "entityName") {
2863
- return entityName;
2864
- }
2865
- if (prop === "withBatch" && typeof target[prop] === "function") {
2866
- return (batch, entityType) => {
2867
- const typeToUse = entityType ?? entityName;
2868
- const fn = target[prop];
2869
- return fn.call(target, batch, typeToUse);
2870
- };
2871
- }
2872
- return Reflect.get(target, prop, receiver);
2873
- }
2874
- });
2875
- }
2876
- function createEntityAwarePutBuilder(builder, entityName) {
2877
- return createEntityAwareBuilder(builder, entityName);
2878
- }
2879
- function createEntityAwareGetBuilder(builder, entityName) {
2880
- return createEntityAwareBuilder(builder, entityName);
2881
- }
2882
- function createEntityAwareDeleteBuilder(builder, entityName) {
2883
- return createEntityAwareBuilder(builder, entityName);
2884
- }
2885
- var EntityAwareUpdateBuilder = class {
2886
- forceRebuildIndexes = [];
2887
- entityName;
2888
- builder;
2889
- entityConfig;
2890
- updateDataApplied = false;
2891
- constructor(builder, entityName) {
2892
- this.builder = builder;
2893
- this.entityName = entityName;
2894
- }
2895
- /**
2896
- * Configure entity-specific logic for automatic timestamp generation and index updates
2897
- */
2898
- configureEntityLogic(config) {
2899
- this.entityConfig = config;
2900
- }
2901
- /**
2902
- * Forces a rebuild of one or more readonly indexes during the update operation.
2903
- *
2904
- * By default, readonly indexes are not updated during entity updates to prevent
2905
- * errors when required index attributes are missing. This method allows you to
2906
- * override that behavior and force specific indexes to be rebuilt.
2907
- *
2908
- * @example
2909
- * ```typescript
2910
- * // Force rebuild a single readonly index
2911
- * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
2912
- * .forceIndexRebuild('gsi1')
2913
- * .execute();
2914
- *
2915
- * // Force rebuild multiple readonly indexes
2916
- * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
2917
- * .forceIndexRebuild(['gsi1', 'gsi2'])
2918
- * .execute();
2919
- *
2920
- * // Chain with other update operations
2921
- * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
2922
- * .set('lastUpdated', new Date().toISOString())
2923
- * .forceIndexRebuild('gsi1')
2924
- * .condition(op => op.eq('status', 'INACTIVE'))
2925
- * .execute();
2926
- * ```
2927
- *
2928
- * @param indexes - A single index name or array of index names to force rebuild
2929
- * @returns The builder instance for method chaining
2930
- */
2931
- forceIndexRebuild(indexes) {
2932
- if (Array.isArray(indexes)) {
2933
- this.forceRebuildIndexes = [...this.forceRebuildIndexes, ...indexes];
2934
- } else {
2935
- this.forceRebuildIndexes.push(indexes);
2936
- }
2937
- return this;
2938
- }
2939
- /**
2940
- * Gets the list of indexes that should be force rebuilt.
2941
- * This is used internally by entity update logic.
2942
- *
2943
- * @returns Array of index names to force rebuild
2944
- */
2945
- getForceRebuildIndexes() {
2946
- return [...this.forceRebuildIndexes];
2947
- }
2948
- /**
2949
- * Apply entity-specific update data (timestamps and index updates)
2950
- * This is called automatically when needed
2951
- */
2952
- applyEntityUpdates() {
2953
- if (!this.entityConfig || this.updateDataApplied) return;
2954
- const timestamps = this.entityConfig.generateTimestamps();
2955
- const updatedItem = { ...this.entityConfig.key, ...this.entityConfig.data, ...timestamps };
2956
- const indexUpdates = this.entityConfig.buildIndexUpdates(
2957
- this.entityConfig.key,
2958
- updatedItem,
2959
- this.entityConfig.table,
2960
- this.entityConfig.indexes,
2961
- this.forceRebuildIndexes
2962
- );
2963
- this.builder.set({ ...this.entityConfig.data, ...timestamps, ...indexUpdates });
2964
- this.updateDataApplied = true;
2965
- }
2966
- set(valuesOrPath, value) {
2967
- if (typeof valuesOrPath === "object") {
2968
- this.builder.set(valuesOrPath);
2969
- } else {
2970
- if (value === void 0) {
2971
- throw new Error("Value is required when setting a single path");
2972
- }
2973
- this.builder.set(valuesOrPath, value);
2974
- }
2975
- return this;
2976
- }
2977
- remove(path) {
2978
- this.builder.remove(path);
2979
- return this;
2980
- }
2981
- add(path, value) {
2982
- this.builder.add(path, value);
2983
- return this;
2984
- }
2985
- deleteElementsFromSet(path, value) {
2986
- this.builder.deleteElementsFromSet(path, value);
2987
- return this;
2988
- }
2989
- condition(condition) {
2990
- this.builder.condition(condition);
2991
- return this;
2992
- }
2993
- returnValues(returnValues) {
2994
- this.builder.returnValues(returnValues);
2995
- return this;
2996
- }
2997
- toDynamoCommand() {
2998
- return this.builder.toDynamoCommand();
2999
- }
3000
- withTransaction(transaction) {
3001
- this.applyEntityUpdates();
3002
- this.builder.withTransaction(transaction);
3003
- }
3004
- debug() {
3005
- return this.builder.debug();
3006
- }
3007
- async execute() {
3008
- this.updateDataApplied = false;
3009
- this.applyEntityUpdates();
3010
- return this.builder.execute();
3011
- }
3012
- };
3013
- function createEntityAwareUpdateBuilder(builder, entityName) {
3014
- return new EntityAwareUpdateBuilder(builder, entityName);
3015
- }
3016
-
3017
- // src/entity/ddb-indexing.ts
3018
- var IndexBuilder = class {
3019
- /**
3020
- * Creates a new IndexBuilder instance
3021
- *
3022
- * @param table - The DynamoDB table instance
3023
- * @param indexes - The index definitions
3024
- */
3025
- constructor(table, indexes = {}) {
3026
- this.table = table;
3027
- this.indexes = indexes;
3028
- }
3029
- /**
3030
- * Build index attributes for item creation
3031
- *
3032
- * @param item - The item to generate indexes for
3033
- * @param options - Options for building indexes
3034
- * @returns Record of GSI attribute names to their values
3035
- */
3036
- buildForCreate(item, options = {}) {
3037
- const attributes = {};
3038
- for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3039
- if (options.excludeReadOnly && indexDef.isReadOnly) {
3040
- continue;
3041
- }
3042
- const key = indexDef.generateKey(item);
3043
- const gsiConfig = this.table.gsis[indexName];
3044
- if (!gsiConfig) {
3045
- throw new Error(`GSI configuration not found for index: ${indexName}`);
3046
- }
3047
- if (key.pk) {
3048
- attributes[gsiConfig.partitionKey] = key.pk;
3049
- }
3050
- if (key.sk && gsiConfig.sortKey) {
3051
- attributes[gsiConfig.sortKey] = key.sk;
3052
- }
3053
- }
3054
- return attributes;
3055
- }
3056
- /**
3057
- * Build index attributes for item updates
3058
- *
3059
- * @param currentData - The current data before update
3060
- * @param updates - The update data
3061
- * @param options - Options for building indexes
3062
- * @returns Record of GSI attribute names to their updated values
3063
- */
3064
- buildForUpdate(currentData, updates, options = {}) {
3065
- const attributes = {};
3066
- const updatedItem = { ...currentData, ...updates };
3067
- if (options.forceRebuildIndexes && options.forceRebuildIndexes.length > 0) {
3068
- const invalidIndexes = options.forceRebuildIndexes.filter((indexName) => !this.indexes[indexName]);
3069
- if (invalidIndexes.length > 0) {
3070
- throw new Error(
3071
- `Cannot force rebuild unknown indexes: ${invalidIndexes.join(", ")}. Available indexes: ${Object.keys(this.indexes).join(", ")}`
3072
- );
3073
- }
3074
- }
3075
- for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3076
- const isForced = options.forceRebuildIndexes?.includes(indexName);
3077
- if (indexDef.isReadOnly && !isForced) {
3078
- continue;
3079
- }
3080
- if (!isForced) {
3081
- let shouldUpdateIndex = false;
3082
- try {
3083
- const currentKey = indexDef.generateKey(currentData);
3084
- const updatedKey = indexDef.generateKey(updatedItem);
3085
- if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
3086
- shouldUpdateIndex = true;
3087
- }
3088
- } catch {
3089
- shouldUpdateIndex = true;
3090
- }
3091
- if (!shouldUpdateIndex) {
3092
- continue;
3093
- }
3094
- }
3095
- let key;
3096
- try {
3097
- key = indexDef.generateKey(updatedItem);
3098
- } catch (error) {
3099
- if (error instanceof Error) {
3100
- throw new Error(`Missing attributes: ${error.message}`);
3101
- }
3102
- throw error;
3103
- }
3104
- if (this.hasUndefinedValues(key)) {
3105
- throw new Error(
3106
- `Missing attributes: Cannot update entity: insufficient data to regenerate index "${indexName}". All attributes required by the index must be provided in the update operation, or the index must be marked as readOnly.`
3107
- );
3108
- }
3109
- const gsiConfig = this.table.gsis[indexName];
3110
- if (!gsiConfig) {
3111
- throw new Error(`GSI configuration not found for index: ${indexName}`);
3112
- }
3113
- if (key.pk) {
3114
- attributes[gsiConfig.partitionKey] = key.pk;
3115
- }
3116
- if (key.sk && gsiConfig.sortKey) {
3117
- attributes[gsiConfig.sortKey] = key.sk;
3118
- }
3119
- }
3120
- return attributes;
3121
- }
3122
- /**
3123
- * Check if a key has undefined values
3124
- *
3125
- * @param key - The index key to check
3126
- * @returns True if the key contains undefined values, false otherwise
3127
- */
3128
- hasUndefinedValues(key) {
3129
- return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
3130
- }
3131
- };
3132
-
3133
- // src/entity/index-utils.ts
3134
- function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
3135
- if (!indexes) {
3136
- return {};
3137
- }
3138
- const indexBuilder = new IndexBuilder(table, indexes);
3139
- return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
3140
- }
3141
- function buildIndexUpdates(currentData, updates, table, indexes, forceRebuildIndexes) {
3142
- if (!indexes) {
3143
- return {};
3144
- }
3145
- const indexBuilder = new IndexBuilder(table, indexes);
3146
- return indexBuilder.buildForUpdate(currentData, updates, { forceRebuildIndexes });
3147
- }
3148
-
3149
- // src/entity/entity.ts
3150
- function defineEntity(config) {
3151
- const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
3152
- const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
3153
- return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
3154
- };
3155
- const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
3156
- const wrappedMethod = (...args) => {
3157
- prepareFn();
3158
- return originalMethod.call(context, ...args);
3159
- };
3160
- Object.setPrototypeOf(wrappedMethod, originalMethod);
3161
- const propertyNames = Object.getOwnPropertyNames(originalMethod);
3162
- for (let i = 0; i < propertyNames.length; i++) {
3163
- const prop = propertyNames[i];
3164
- if (prop !== "length" && prop !== "name" && prop !== "prototype") {
3165
- const descriptor = Object.getOwnPropertyDescriptor(originalMethod, prop);
3166
- if (descriptor && descriptor.writable !== false && !descriptor.get) {
3167
- wrappedMethod[prop] = originalMethod[prop];
3168
- }
3169
- }
3170
- }
3171
- return wrappedMethod;
3172
- };
3173
- const generateTimestamps = (timestampsToGenerate, data) => {
3174
- if (!config.settings?.timestamps) return {};
3175
- const timestamps = {};
3176
- const now = /* @__PURE__ */ new Date();
3177
- const unixTime = Math.floor(Date.now() / 1e3);
3178
- const { createdAt, updatedAt } = config.settings.timestamps;
3179
- if (createdAt && timestampsToGenerate.includes("createdAt") && !data.createdAt) {
3180
- const name = createdAt.attributeName ?? "createdAt";
3181
- timestamps[name] = createdAt.format === "UNIX" ? unixTime : now.toISOString();
3182
- }
3183
- if (updatedAt && timestampsToGenerate.includes("updatedAt") && !data.updatedAt) {
3184
- const name = updatedAt.attributeName ?? "updatedAt";
3185
- timestamps[name] = updatedAt.format === "UNIX" ? unixTime : now.toISOString();
3186
- }
3187
- return timestamps;
3188
- };
3189
- return {
3190
- name: config.name,
3191
- createRepository: (table) => {
3192
- const repository = {
3193
- create: (data) => {
3194
- const builder = table.create({});
3195
- const prepareValidatedItemAsync = async () => {
3196
- const validatedData = await config.schema["~standard"].validate(data);
3197
- if ("issues" in validatedData && validatedData.issues) {
3198
- throw new Error(`Validation failed: ${validatedData.issues.map((i) => i.message).join(", ")}`);
3199
- }
3200
- const dataForKeyGeneration = {
3201
- ...validatedData.value,
3202
- ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3203
- };
3204
- const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3205
- const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3206
- const validatedItem = {
3207
- ...dataForKeyGeneration,
3208
- [entityTypeAttributeName]: config.name,
3209
- [table.partitionKey]: primaryKey.pk,
3210
- ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
3211
- ...indexes
3212
- };
3213
- Object.assign(builder, { item: validatedItem });
3214
- return validatedItem;
3215
- };
3216
- const prepareValidatedItemSync = () => {
3217
- const validationResult = config.schema["~standard"].validate(data);
3218
- if (validationResult instanceof Promise) {
3219
- throw new Error(
3220
- "Async validation is not supported in withBatch or withTransaction. The schema must support synchronous validation for compatibility."
3221
- );
3222
- }
3223
- if ("issues" in validationResult && validationResult.issues) {
3224
- throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(", ")}`);
3225
- }
3226
- const dataForKeyGeneration = {
3227
- ...validationResult.value,
3228
- ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3229
- };
3230
- const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3231
- const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3232
- const validatedItem = {
3233
- ...dataForKeyGeneration,
3234
- [entityTypeAttributeName]: config.name,
3235
- [table.partitionKey]: primaryKey.pk,
3236
- ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
3237
- ...indexes
3238
- };
3239
- Object.assign(builder, { item: validatedItem });
3240
- return validatedItem;
3241
- };
3242
- const originalExecute = builder.execute;
3243
- builder.execute = async () => {
3244
- await prepareValidatedItemAsync();
3245
- return await originalExecute.call(builder);
3246
- };
3247
- const originalWithTransaction = builder.withTransaction;
3248
- if (originalWithTransaction) {
3249
- builder.withTransaction = wrapMethodWithPreparation(
3250
- originalWithTransaction,
3251
- prepareValidatedItemSync,
3252
- builder
3253
- );
3254
- }
3255
- const originalWithBatch = builder.withBatch;
3256
- if (originalWithBatch) {
3257
- builder.withBatch = wrapMethodWithPreparation(originalWithBatch, prepareValidatedItemSync, builder);
3258
- }
3259
- return createEntityAwarePutBuilder(builder, config.name);
3260
- },
3261
- upsert: (data) => {
3262
- const builder = table.put({});
3263
- const prepareValidatedItemAsync = async () => {
3264
- const validatedData = await config.schema["~standard"].validate(data);
3265
- if ("issues" in validatedData && validatedData.issues) {
3266
- throw new Error(`Validation failed: ${validatedData.issues.map((i) => i.message).join(", ")}`);
3267
- }
3268
- const dataForKeyGeneration = {
3269
- ...validatedData.value,
3270
- ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3271
- };
3272
- const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3273
- const indexes = buildIndexes2(dataForKeyGeneration, table, false);
3274
- const validatedItem = {
3275
- [table.partitionKey]: primaryKey.pk,
3276
- ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
3277
- ...dataForKeyGeneration,
3278
- [entityTypeAttributeName]: config.name,
3279
- ...indexes
3280
- };
3281
- Object.assign(builder, { item: validatedItem });
3282
- return validatedItem;
3283
- };
3284
- const prepareValidatedItemSync = () => {
3285
- const validationResult = config.schema["~standard"].validate(data);
3286
- if (validationResult instanceof Promise) {
3287
- throw new Error(
3288
- "Async validation is not supported in withTransaction or withBatch. Use execute() instead."
3289
- );
3290
- }
3291
- if ("issues" in validationResult && validationResult.issues) {
3292
- throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(", ")}`);
3293
- }
3294
- const dataForKeyGeneration = {
3295
- ...validationResult.value,
3296
- ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3297
- };
3298
- const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3299
- const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3300
- const validatedItem = {
3301
- [table.partitionKey]: primaryKey.pk,
3302
- ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
3303
- ...dataForKeyGeneration,
3304
- [entityTypeAttributeName]: config.name,
3305
- ...indexes
3306
- };
3307
- Object.assign(builder, { item: validatedItem });
3308
- return validatedItem;
3309
- };
3310
- const originalExecute = builder.execute;
3311
- builder.execute = async () => {
3312
- await prepareValidatedItemAsync();
3313
- const result = await originalExecute.call(builder);
3314
- if (!result) {
3315
- throw new Error("Failed to upsert item");
3316
- }
3317
- return result;
3318
- };
3319
- const originalWithTransaction = builder.withTransaction;
3320
- if (originalWithTransaction) {
3321
- builder.withTransaction = wrapMethodWithPreparation(
3322
- originalWithTransaction,
3323
- prepareValidatedItemSync,
3324
- builder
3325
- );
3326
- }
3327
- const originalWithBatch = builder.withBatch;
3328
- if (originalWithBatch) {
3329
- builder.withBatch = wrapMethodWithPreparation(originalWithBatch, prepareValidatedItemSync, builder);
3330
- }
3331
- return createEntityAwarePutBuilder(builder, config.name);
3332
- },
3333
- get: (key) => {
3334
- return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
3335
- },
3336
- update: (key, data) => {
3337
- const primaryKeyObj = config.primaryKey.generateKey(key);
3338
- const builder = table.update(primaryKeyObj);
3339
- builder.condition(eq(entityTypeAttributeName, config.name));
3340
- const entityAwareBuilder = createEntityAwareUpdateBuilder(builder, config.name);
3341
- entityAwareBuilder.configureEntityLogic({
3342
- data,
3343
- key,
3344
- table,
3345
- indexes: config.indexes,
3346
- generateTimestamps: () => generateTimestamps(["updatedAt"], data),
3347
- buildIndexUpdates
3348
- });
3349
- return entityAwareBuilder;
3350
- },
3351
- delete: (key) => {
3352
- const builder = table.delete(config.primaryKey.generateKey(key));
3353
- builder.condition(eq(entityTypeAttributeName, config.name));
3354
- return createEntityAwareDeleteBuilder(builder, config.name);
3355
- },
3356
- query: Object.entries(config.queries || {}).reduce((acc, [key, inputCallback]) => {
3357
- acc[key] = (input) => {
3358
- const queryEntity = {
3359
- scan: repository.scan,
3360
- get: (key2) => createEntityAwareGetBuilder(table.get(key2), config.name),
3361
- query: (keyCondition) => {
3362
- return table.query(keyCondition);
3363
- }
3364
- };
3365
- const queryBuilderCallback = inputCallback(input);
3366
- const builder = queryBuilderCallback(queryEntity);
3367
- if (builder && typeof builder === "object" && "filter" in builder && typeof builder.filter === "function") {
3368
- builder.filter(eq(entityTypeAttributeName, config.name));
3369
- }
3370
- if (builder && typeof builder === "object" && "execute" in builder) {
3371
- const originalExecute = builder.execute;
3372
- builder.execute = async () => {
3373
- const queryFn = config.queries[key];
3374
- if (queryFn && typeof queryFn === "function") {
3375
- const schema = queryFn.schema;
3376
- if (schema?.["~standard"]?.validate && typeof schema["~standard"].validate === "function") {
3377
- const validationResult = schema["~standard"].validate(input);
3378
- if ("issues" in validationResult && validationResult.issues) {
3379
- throw new Error(
3380
- `Validation failed: ${validationResult.issues.map((issue) => issue.message).join(", ")}`
3381
- );
3382
- }
3383
- }
3384
- }
3385
- const result = await originalExecute.call(builder);
3386
- if (!result) {
3387
- throw new Error("Failed to execute query");
3388
- }
3389
- return result;
3390
- };
3391
- }
3392
- return builder;
3393
- };
3394
- return acc;
3395
- }, {}),
3396
- scan: () => {
3397
- const builder = table.scan();
3398
- builder.filter(eq(entityTypeAttributeName, config.name));
3399
- return builder;
3400
- }
3401
- };
3402
- return repository;
3403
- }
3404
- };
3405
- }
3406
- function createQueries() {
3407
- return {
3408
- input: (schema) => ({
3409
- query: (handler) => {
3410
- const queryFn = (input) => (entity) => handler({ input, entity });
3411
- queryFn.schema = schema;
3412
- return queryFn;
3413
- }
3414
- })
3415
- };
3416
- }
3417
- function createIndex() {
3418
- return {
3419
- input: (schema) => {
3420
- const createIndexBuilder = (isReadOnly = false) => ({
3421
- partitionKey: (pkFn) => ({
3422
- sortKey: (skFn) => {
3423
- const index = {
3424
- name: "custom",
3425
- partitionKey: "pk",
3426
- sortKey: "sk",
3427
- isReadOnly,
3428
- generateKey: (item) => {
3429
- const data = schema["~standard"].validate(item);
3430
- if ("issues" in data && data.issues) {
3431
- throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
3432
- }
3433
- const validData = "value" in data ? data.value : item;
3434
- return { pk: pkFn(validData), sk: skFn(validData) };
3435
- }
3436
- };
3437
- return Object.assign(index, {
3438
- readOnly: (value = false) => ({
3439
- ...index,
3440
- isReadOnly: value
3441
- })
3442
- });
3443
- },
3444
- withoutSortKey: () => {
3445
- const index = {
3446
- name: "custom",
3447
- partitionKey: "pk",
3448
- isReadOnly,
3449
- generateKey: (item) => {
3450
- const data = schema["~standard"].validate(item);
3451
- if ("issues" in data && data.issues) {
3452
- throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
3453
- }
3454
- const validData = "value" in data ? data.value : item;
3455
- return { pk: pkFn(validData) };
3456
- }
3457
- };
3458
- return Object.assign(index, {
3459
- readOnly: (value = true) => ({
3460
- ...index,
3461
- isReadOnly: value
3462
- })
3463
- });
3464
- }
3465
- }),
3466
- readOnly: (value = true) => createIndexBuilder(value)
3467
- });
3468
- return createIndexBuilder(false);
3469
- }
3470
- };
3471
- }
3472
-
3473
- // src/builders/condition-check-builder.ts
3474
- var ConditionCheckBuilder = class {
3475
- key;
3476
- tableName;
3477
- conditionExpression;
3478
- constructor(tableName, key) {
3479
- this.tableName = tableName;
3480
- this.key = key;
3481
- }
3482
- /**
3483
- * Adds a condition that must be satisfied for the check to succeed.
3484
- *
3485
- * @example
3486
- * ```typescript
3487
- * // Check dinosaur health and behavior
3488
- * builder.condition(op =>
3489
- * op.and([
3490
- * op.gt('stats.health', 50),
3491
- * op.not(op.eq('status', 'SEDATED')),
3492
- * op.lt('aggressionLevel', 8)
3493
- * ])
3494
- * );
3495
- *
3496
- * // Verify habitat conditions
3497
- * builder.condition(op =>
3498
- * op.and([
3499
- * op.eq('powerStatus', 'ONLINE'),
3500
- * op.between('temperature', 20, 30),
3501
- * op.attributeExists('lastMaintenance')
3502
- * ])
3503
- * );
3504
- *
3505
- * // Check breeding conditions
3506
- * builder.condition(op =>
3507
- * op.and([
3508
- * op.eq('species', 'VELOCIRAPTOR'),
3509
- * op.gte('age', 3),
3510
- * op.eq('geneticPurity', 100)
3511
- * ])
3512
- * );
3513
- * ```
3514
- *
3515
- * @param condition - Either a Condition DynamoItem or a callback function that builds the condition
3516
- * @returns The builder instance for method chaining
3517
- */
3518
- condition(condition) {
3519
- if (typeof condition === "function") {
3520
- const conditionOperator = {
3521
- eq,
3522
- ne,
3523
- lt,
3524
- lte,
3525
- gt,
3526
- gte,
3527
- between,
3528
- inArray,
3529
- beginsWith,
3530
- contains,
3531
- attributeExists,
3532
- attributeNotExists,
3533
- and,
3534
- or,
3535
- not
3536
- };
3537
- this.conditionExpression = condition(conditionOperator);
3538
- } else {
3539
- this.conditionExpression = condition;
3540
- }
3541
- return this;
3542
- }
3543
- /**
3544
- * Generates the DynamoDB command parameters for direct execution.
3545
- * Use this method when you want to:
3546
- * - Execute the condition check as a standalone operation
3547
- * - Get the raw DynamoDB command for custom execution
3548
- * - Inspect the generated command parameters
3549
- *
3550
- * @example
3551
- * ```ts
3552
- * const command = new ConditionCheckBuilder('myTable', { id: '123' })
3553
- * .condition(op => op.attributeExists('status'))
3554
- * .toDynamoCommand();
3555
- * // Use command with DynamoDB client
3556
- * ```
3557
- *
3558
- * @throws {Error} If no condition has been set
3559
- * @returns The DynamoDB command parameters
3560
- */
3561
- toDynamoCommand() {
3562
- if (!this.conditionExpression) {
3563
- throw new Error("Condition is required for condition check operations");
3564
- }
3565
- const { expression, names, values } = prepareExpressionParams(this.conditionExpression);
3566
- if (!expression) {
3567
- throw new Error("Failed to generate condition expression");
3568
- }
3569
- return {
3570
- tableName: this.tableName,
3571
- key: this.key,
3572
- conditionExpression: expression,
3573
- expressionAttributeNames: names,
3574
- expressionAttributeValues: values
3575
- };
3576
- }
3577
- /**
3578
- * Adds this condition check operation to a transaction.
3579
- *
3580
- * @example
3581
- * ```ts
3582
- * const transaction = new TransactionBuilder();
3583
- * new ConditionCheckBuilder('habitats', { id: 'PADDOCK-B' })
3584
- * .condition(op => op.and([
3585
- * op.eq('securityStatus', 'ACTIVE'),
3586
- * op.lt('currentOccupants', 3),
3587
- * op.eq('habitatType', 'CARNIVORE')
3588
- * ]))
3589
- * .withTransaction(transaction);
3590
- * // Add dinosaur transfer operations
3591
- * ```
3592
- *
3593
- * @param transaction - The transaction builder to add this operation to
3594
- * @throws {Error} If no condition has been set
3595
- * @returns The builder instance for method chaining
3596
- */
3597
- withTransaction(transaction) {
3598
- if (!this.conditionExpression) {
3599
- throw new Error("Condition is required for condition check operations");
3600
- }
3601
- const command = this.toDynamoCommand();
3602
- transaction.conditionCheckWithCommand(command);
3603
- return this;
3604
- }
3605
- /**
3606
- * Gets a human-readable representation of the condition check command
3607
- * with all expression placeholders replaced by their actual values.
3608
- *
3609
- * @example
3610
- * ```ts
3611
- * const debugInfo = new ConditionCheckBuilder('dinosaurs', { id: 'TREX-001' })
3612
- * .condition(op => op.and([
3613
- * op.between('stats.health', 50, 100),
3614
- * op.not(op.eq('status', 'SEDATED')),
3615
- * op.attributeExists('lastFeedingTime')
3616
- * op.eq('version', 1)
3617
- * ]))
3618
- * .debug();
3619
- * console.log(debugInfo);
3620
- * ```
3621
- *
3622
- * @returns A readable representation of the condition check command with resolved expressions
3623
- */
3624
- debug() {
3625
- const command = this.toDynamoCommand();
3626
- return debugCommand(command);
3627
- }
3628
- };
3629
-
3630
- // src/builders/get-builder.ts
3631
- var GetBuilder = class {
3632
- /**
3633
- * Creates a new GetBuilder instance.
3634
- *
3635
- * @param executor - Function that executes the get operation
3636
- * @param key - Primary key of the item to retrieve
3637
- * @param tableName - Name of the DynamoDB table
3638
- */
3639
- constructor(executor, key, tableName) {
3640
- this.executor = executor;
3641
- this.params = {
3642
- tableName,
3643
- key
3644
- };
3645
- }
3646
- params;
3647
- options = {};
3648
- selectedFields = /* @__PURE__ */ new Set();
3649
- /**
3650
- * Specifies which attributes to return in the get results.
3651
- *
3652
- * @example
3653
- * ```typescript
3654
- * // Select single attribute
3655
- * builder.select('species')
3656
- *
3657
- * // Select multiple attributes
3658
- * builder.select(['id', 'species', 'diet'])
3659
- *
3660
- * // Chain multiple select calls
3661
- * builder
3662
- * .select('id')
3663
- * .select(['species', 'diet'])
3664
- * ```
3665
- *
3666
- * @param fields - A single field name or an array of field names to return
3667
- * @returns The builder instance for method chaining
3668
- */
3669
- select(fields) {
3670
- if (typeof fields === "string") {
3671
- this.selectedFields.add(fields);
3672
- } else if (Array.isArray(fields)) {
3673
- for (const field of fields) {
3674
- this.selectedFields.add(field);
3675
- }
3676
- }
3677
- this.options.projection = Array.from(this.selectedFields);
3678
- return this;
3679
- }
3680
- /**
3681
- * Sets whether to use strongly consistent reads for the get operation.
3682
- * Use this method when you need:
3683
- * - The most up-to-date dinosaur data
3684
- * - To ensure you're reading the latest dinosaur status
3685
- * - Critical safety information about dangerous species
3686
- *
3687
- * Note: Consistent reads consume twice the throughput
3688
- *
3689
- * @example
3690
- * ```typescript
3691
- * // Get the latest T-Rex data
3692
- * const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
3693
- * .consistentRead()
3694
- * .execute();
3695
- * ```
3696
- *
3697
- * @param consistentRead - Whether to use consistent reads (defaults to true)
3698
- * @returns The builder instance for method chaining
3699
- */
3700
- consistentRead(consistentRead = true) {
3701
- this.params.consistentRead = consistentRead;
3702
- return this;
3703
- }
3704
- /**
3705
- * Adds this get operation to a batch with optional entity type information.
3706
- *
3707
- * @example Basic Usage
3708
- * ```ts
3709
- * const batch = table.batchBuilder();
3710
- *
3711
- * // Add multiple get operations to batch
3712
- * dinosaurRepo.get({ id: 'dino-1' }).withBatch(batch);
3713
- * dinosaurRepo.get({ id: 'dino-2' }).withBatch(batch);
3714
- * dinosaurRepo.get({ id: 'dino-3' }).withBatch(batch);
3715
- *
3716
- * // Execute all gets efficiently
3717
- * const results = await batch.execute();
3718
- * ```
3719
- *
3720
- * @example Typed Usage
3721
- * ```ts
3722
- * const batch = table.batchBuilder<{
3723
- * User: UserEntity;
3724
- * Order: OrderEntity;
3725
- * }>();
3726
- *
3727
- * // Add operations with type information
3728
- * userRepo.get({ id: 'user-1' }).withBatch(batch, 'User');
3729
- * orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
3730
- *
3731
- * // Execute and get typed results
3732
- * const result = await batch.execute();
3733
- * const users: UserEntity[] = result.reads.itemsByType.User;
3734
- * const orders: OrderEntity[] = result.reads.itemsByType.Order;
3735
- * ```
3736
- *
3737
- * @param batch - The batch builder to add this operation to
3738
- * @param entityType - Optional entity type key for type tracking
3739
- */
3740
- withBatch(batch, entityType) {
3741
- const command = this.toDynamoCommand();
3742
- batch.getWithCommand(command, entityType);
3743
- }
3744
- /**
3745
- * Converts the builder configuration to a DynamoDB command
3746
- */
3747
- toDynamoCommand() {
3748
- const expressionParams = {
3749
- expressionAttributeNames: {}};
3750
- const projectionExpression = Array.from(this.selectedFields).map((p) => generateAttributeName(expressionParams, p)).join(", ");
3751
- const { expressionAttributeNames } = expressionParams;
3752
- return {
3753
- ...this.params,
3754
- projectionExpression: projectionExpression.length > 0 ? projectionExpression : void 0,
3755
- expressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0
3756
- };
3757
- }
3758
- /**
3759
- * Executes the get operation against DynamoDB.
3760
- *
3761
- * @example
3762
- * ```typescript
3763
- * try {
3764
- * const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
3765
- * .select(['species', 'name', 'diet'])
3766
- * .consistentRead()
3767
- * .execute();
3768
- *
3769
- * if (result.item) {
3770
- * console.log('Dinosaur found:', result.item);
3771
- * } else {
3772
- * console.log('Dinosaur not found');
3773
- * }
3774
- * } catch (error) {
3775
- * console.error('Error getting dinosaur:', error);
3776
- * }
3777
- * ```
3778
- *
3779
- * @returns A promise that resolves to an object containing:
3780
- * - item: The retrieved dinosaur or undefined if not found
3781
- */
3782
- async execute() {
3783
- const command = this.toDynamoCommand();
3784
- return this.executor(command);
3785
- }
3786
- };
3787
-
3788
- // src/builders/scan-builder.ts
3789
- var ScanBuilder = class _ScanBuilder extends FilterBuilder {
3790
- executor;
3791
- constructor(executor) {
3792
- super();
3793
- this.executor = executor;
3794
- }
3795
- /**
3796
- * Creates a deep clone of this ScanBuilder instance.
3797
- *
3798
- * @returns A new ScanBuilder instance with the same configuration
3799
- */
3800
- clone() {
3801
- const clone = new _ScanBuilder(this.executor);
3802
- clone.options = {
3803
- ...this.options,
3804
- filter: this.deepCloneFilter(this.options.filter)
3805
- };
3806
- clone.selectedFields = new Set(this.selectedFields);
3807
- return clone;
3808
- }
3809
- deepCloneFilter(filter) {
3810
- if (!filter) return filter;
3811
- if (filter.type === "and" || filter.type === "or") {
3812
- return {
3813
- ...filter,
3814
- conditions: filter.conditions?.map((condition) => this.deepCloneFilter(condition)).filter((c) => c !== void 0)
3815
- };
3816
- }
3817
- return { ...filter };
3818
- }
3819
- /**
3820
- * Executes the scan against DynamoDB and returns a generator that behaves like an array.
3821
- *
3822
- * The generator automatically handles pagination and provides array-like methods
3823
- * for processing results efficiently without loading everything into memory at once.
3824
- *
3825
- * @example
3826
- * ```typescript
3827
- * try {
3828
- * // Find all dinosaurs with high aggression levels with automatic pagination
3829
- * const results = await new ScanBuilder(executor)
3830
- * .filter(op =>
3831
- * op.and([
3832
- * op.eq('status', 'ACTIVE'),
3833
- * op.gt('aggressionLevel', 7)
3834
- * ])
3835
- * )
3836
- * .execute();
3837
- *
3838
- * // Use like an array with automatic pagination
3839
- * for await (const dinosaur of results) {
3840
- * console.log(`Processing dangerous dinosaur: ${dinosaur.name}`);
3841
- * }
3842
- *
3843
- * // Or convert to array and use array methods
3844
- * const allItems = await results.toArray();
3845
- * const criticalThreats = allItems.filter(dino => dino.aggressionLevel > 9);
3846
- * const totalCount = allItems.length;
3847
- * } catch (error) {
3848
- * console.error('Security scan failed:', error);
3849
- * }
3850
- * ```
3851
- *
3852
- * @returns A promise that resolves to a ResultGenerator that behaves like an array
3853
- */
3854
- async execute() {
3855
- const directExecutor = () => this.executor(this.options);
3856
- return new ResultIterator(this, directExecutor);
3857
- }
3858
- };
3859
-
3860
- // src/utils/chunk-array.ts
3861
- function* chunkArray(array, size) {
3862
- if (size <= 0) {
3863
- throw new Error("Chunk size must be greater than 0");
3864
- }
3865
- for (let i = 0; i < array.length; i += size) {
3866
- yield array.slice(i, i + size);
3867
- }
3868
- }
3869
-
3870
- // src/table.ts
3871
- var DDB_BATCH_WRITE_LIMIT = 25;
3872
- var DDB_BATCH_GET_LIMIT = 100;
3873
- var Table = class {
3874
- dynamoClient;
3875
- tableName;
3876
- /**
3877
- * The column name of the partitionKey for the Table
3878
- */
3879
- partitionKey;
3880
- /**
3881
- * The column name of the sortKey for the Table
3882
- */
3883
- sortKey;
3884
- /**
3885
- * The Global Secondary Indexes that are configured on this table
3886
- */
3887
- gsis;
3888
- constructor(config) {
3889
- this.dynamoClient = config.client;
3890
- this.tableName = config.tableName;
3891
- this.partitionKey = config.indexes.partitionKey;
3892
- this.sortKey = config.indexes.sortKey;
3893
- this.gsis = config.indexes.gsis || {};
3894
- }
3895
- createKeyForPrimaryIndex(keyCondition) {
3896
- const primaryCondition = { [this.partitionKey]: keyCondition.pk };
3897
- if (this.sortKey) {
3898
- if (!keyCondition.sk) {
3899
- throw new Error("Sort key has not been provided but the Table has a sort key");
3900
- }
3901
- primaryCondition[this.sortKey] = keyCondition.sk;
3902
- }
3903
- return primaryCondition;
3904
- }
3905
- /**
3906
- * Creates a new item in the table, it will fail if the item already exists.
3907
- *
3908
- * By default, this method returns the input values passed to the create operation
3909
- * upon successful creation.
3910
- *
3911
- * You can customise the return behaviour by chaining the `.returnValues()` method:
3912
- *
3913
- * @param item The item to create
3914
- * @returns A PutBuilder instance for chaining additional conditions and executing the create operation
3915
- *
3916
- * @example
3917
- * ```ts
3918
- * // Create with default behavior (returns input values)
3919
- * const result = await table.create({
3920
- * id: 'user-123',
3921
- * name: 'John Doe',
3922
- * email: 'john@example.com'
3923
- * }).execute();
3924
- * console.log(result); // Returns the input object
3925
- *
3926
- * // Create with no return value for better performance
3927
- * await table.create(userData).returnValues('NONE').execute();
3928
- *
3929
- * // Create and get fresh data from dynamodb using a strongly consistent read
3930
- * const freshData = await table.create(userData).returnValues('CONSISTENT').execute();
3931
- *
3932
- * // Create and get previous values (if the item was overwritten)
3933
- * const oldData = await table.create(userData).returnValues('ALL_OLD').execute();
3934
- * ```
3935
- */
3936
- create(item) {
3937
- return this.put(item).condition((op) => op.attributeNotExists(this.partitionKey)).returnValues("INPUT");
3938
- }
3939
- get(keyCondition) {
3940
- const executor = async (params) => {
3941
- try {
3942
- const result = await this.dynamoClient.get({
3943
- TableName: params.tableName,
3944
- Key: this.createKeyForPrimaryIndex(keyCondition),
3945
- ProjectionExpression: params.projectionExpression,
3946
- ExpressionAttributeNames: params.expressionAttributeNames,
3947
- ConsistentRead: params.consistentRead
3948
- });
3949
- return {
3950
- item: result.Item ? result.Item : void 0
3951
- };
3952
- } catch (error) {
3953
- console.error("Error getting item:", error);
3954
- throw error;
3955
- }
3956
- };
3957
- return new GetBuilder(executor, keyCondition, this.tableName);
3958
- }
3959
- /**
3960
- * Updates an item in the table
3961
- *
3962
- * @param item The item to update
3963
- * @returns A PutBuilder instance for chaining conditions and executing the put operation
3964
- */
3965
- put(item) {
3966
- const executor = async (params) => {
3967
- try {
3968
- const result = await this.dynamoClient.put({
3969
- TableName: params.tableName,
3970
- Item: params.item,
3971
- ConditionExpression: params.conditionExpression,
3972
- ExpressionAttributeNames: params.expressionAttributeNames,
3973
- ExpressionAttributeValues: params.expressionAttributeValues,
3974
- // CONSISTENT and INPUT are not valid ReturnValues for DDB, so we set NONE as we are not interested in its
3975
- // response and will be handling these cases separately
3976
- ReturnValues: params.returnValues === "CONSISTENT" || params.returnValues === "INPUT" ? "NONE" : params.returnValues
3977
- });
3978
- if (params.returnValues === "INPUT") {
3979
- return params.item;
3980
- }
3981
- if (params.returnValues === "CONSISTENT") {
3982
- const getResult = await this.dynamoClient.get({
3983
- TableName: params.tableName,
3984
- Key: this.createKeyForPrimaryIndex({
3985
- pk: params.item[this.partitionKey],
3986
- ...this.sortKey && { sk: params.item[this.sortKey] }
3987
- }),
3988
- ConsistentRead: true
3989
- });
3990
- return getResult.Item;
3991
- }
3992
- return result.Attributes;
3993
- } catch (error) {
3994
- console.error("Error creating item:", error);
3995
- throw error;
3996
- }
3997
- };
3998
- return new PutBuilder(executor, item, this.tableName);
3999
- }
4000
- /**
4001
- * Creates a query builder for complex queries
4002
- * If useIndex is called on the returned QueryBuilder, it will use the GSI configuration
4003
- */
4004
- query(keyCondition) {
4005
- const pkAttributeName = this.partitionKey;
4006
- const skAttributeName = this.sortKey;
4007
- let keyConditionExpression = eq(pkAttributeName, keyCondition.pk);
4008
- if (keyCondition.sk) {
4009
- if (!skAttributeName) {
4010
- throw new Error("Sort key is not defined for Index");
4011
- }
4012
- const keyConditionOperator = {
4013
- eq: (value) => eq(skAttributeName, value),
4014
- lt: (value) => lt(skAttributeName, value),
4015
- lte: (value) => lte(skAttributeName, value),
4016
- gt: (value) => gt(skAttributeName, value),
4017
- gte: (value) => gte(skAttributeName, value),
4018
- between: (lower, upper) => between(skAttributeName, lower, upper),
4019
- beginsWith: (value) => beginsWith(skAttributeName, value),
4020
- and: (...conditions) => and(...conditions)
4021
- };
4022
- const skCondition = keyCondition.sk(keyConditionOperator);
4023
- keyConditionExpression = and(eq(pkAttributeName, keyCondition.pk), skCondition);
4024
- }
4025
- const executor = async (originalKeyCondition, options) => {
4026
- let finalKeyCondition = originalKeyCondition;
4027
- if (options.indexName) {
4028
- const gsiName = String(options.indexName);
4029
- const gsi = this.gsis[gsiName];
4030
- if (!gsi) {
4031
- throw new Error(`GSI with name "${gsiName}" does not exist on table "${this.tableName}"`);
4032
- }
4033
- const gsiPkAttributeName = gsi.partitionKey;
4034
- const gsiSkAttributeName = gsi.sortKey;
4035
- let pkValue;
4036
- let skValue;
4037
- let extractedSkCondition;
4038
- if (originalKeyCondition.type === "eq") {
4039
- pkValue = originalKeyCondition.value;
4040
- } else if (originalKeyCondition.type === "and" && originalKeyCondition.conditions) {
4041
- const pkCondition = originalKeyCondition.conditions.find(
4042
- (c) => c.type === "eq" && c.attr === pkAttributeName
4043
- );
4044
- if (pkCondition && pkCondition.type === "eq") {
4045
- pkValue = pkCondition.value;
4046
- }
4047
- const skConditions = originalKeyCondition.conditions.filter((c) => c.attr === skAttributeName);
4048
- if (skConditions.length > 0) {
4049
- if (skConditions.length === 1) {
4050
- extractedSkCondition = skConditions[0];
4051
- if (extractedSkCondition && extractedSkCondition.type === "eq") {
4052
- skValue = extractedSkCondition.value;
4053
- }
4054
- } else if (skConditions.length > 1) {
4055
- extractedSkCondition = and(...skConditions);
4056
- }
4057
- }
4058
- }
4059
- if (!pkValue) {
4060
- throw new Error("Could not extract partition key value from key condition");
4061
- }
4062
- let gsiKeyCondition = eq(gsiPkAttributeName, pkValue);
4063
- if (skValue && gsiSkAttributeName) {
4064
- gsiKeyCondition = and(gsiKeyCondition, eq(gsiSkAttributeName, skValue));
4065
- } else if (extractedSkCondition && gsiSkAttributeName) {
4066
- if (extractedSkCondition.attr === skAttributeName) {
4067
- const updatedSkCondition = {
4068
- ...extractedSkCondition,
4069
- attr: gsiSkAttributeName
4070
- };
4071
- gsiKeyCondition = and(gsiKeyCondition, updatedSkCondition);
4072
- } else {
4073
- gsiKeyCondition = and(gsiKeyCondition, extractedSkCondition);
4074
- }
4075
- }
4076
- finalKeyCondition = gsiKeyCondition;
4077
- }
4078
- const expressionParams = {
4079
- expressionAttributeNames: {},
4080
- expressionAttributeValues: {},
4081
- valueCounter: { count: 0 }
4082
- };
4083
- const keyConditionExpression2 = buildExpression(finalKeyCondition, expressionParams);
4084
- let filterExpression;
4085
- if (options.filter) {
4086
- filterExpression = buildExpression(options.filter, expressionParams);
4087
- }
4088
- const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
4089
- const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
4090
- const { indexName, limit, consistentRead, scanIndexForward, lastEvaluatedKey } = options;
4091
- const params = {
4092
- TableName: this.tableName,
4093
- KeyConditionExpression: keyConditionExpression2,
4094
- FilterExpression: filterExpression,
4095
- ExpressionAttributeNames: expressionAttributeNames,
4096
- ExpressionAttributeValues: expressionAttributeValues,
4097
- IndexName: indexName,
4098
- Limit: limit,
4099
- ConsistentRead: consistentRead,
4100
- ScanIndexForward: scanIndexForward,
4101
- ProjectionExpression: projectionExpression,
4102
- ExclusiveStartKey: lastEvaluatedKey
4103
- };
4104
- try {
4105
- const result = await this.dynamoClient.query(params);
4106
- return {
4107
- items: result.Items,
4108
- lastEvaluatedKey: result.LastEvaluatedKey
4109
- };
4110
- } catch (error) {
4111
- console.log(debugCommand(params));
4112
- console.error("Error querying items:", error);
4113
- throw error;
4114
- }
4115
- };
4116
- return new QueryBuilder(executor, keyConditionExpression);
4117
- }
4118
- /**
4119
- * Creates a scan builder for scanning the entire table
4120
- * Use this when you need to:
4121
- * - Process all items in a table
4122
- * - Apply filters to a large dataset
4123
- * - Use a GSI for scanning
4124
- *
4125
- * @returns A ScanBuilder instance for chaining operations
4126
- */
4127
- scan() {
4128
- const executor = async (options) => {
4129
- const expressionParams = {
4130
- expressionAttributeNames: {},
4131
- expressionAttributeValues: {},
4132
- valueCounter: { count: 0 }
4133
- };
4134
- let filterExpression;
4135
- if (options.filter) {
4136
- filterExpression = buildExpression(options.filter, expressionParams);
4137
- }
4138
- const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
4139
- const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
4140
- const { indexName, limit, consistentRead, lastEvaluatedKey } = options;
4141
- const params = {
4142
- TableName: this.tableName,
4143
- FilterExpression: filterExpression,
4144
- ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
4145
- ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
4146
- IndexName: indexName,
4147
- Limit: limit,
4148
- ConsistentRead: consistentRead,
4149
- ProjectionExpression: projectionExpression,
4150
- ExclusiveStartKey: lastEvaluatedKey
4151
- };
4152
- try {
4153
- const result = await this.dynamoClient.scan(params);
4154
- return {
4155
- items: result.Items,
4156
- lastEvaluatedKey: result.LastEvaluatedKey
4157
- };
4158
- } catch (error) {
4159
- console.log(debugCommand(params));
4160
- console.error("Error scanning items:", error);
4161
- throw error;
4162
- }
4163
- };
4164
- return new ScanBuilder(executor);
4165
- }
4166
- delete(keyCondition) {
4167
- const executor = async (params) => {
4168
- try {
4169
- const result = await this.dynamoClient.delete({
4170
- TableName: params.tableName,
4171
- Key: this.createKeyForPrimaryIndex(keyCondition),
4172
- ConditionExpression: params.conditionExpression,
4173
- ExpressionAttributeNames: params.expressionAttributeNames,
4174
- ExpressionAttributeValues: params.expressionAttributeValues,
4175
- ReturnValues: params.returnValues
4176
- });
4177
- return {
4178
- item: result.Attributes
4179
- };
4180
- } catch (error) {
4181
- console.error("Error deleting item:", error);
4182
- throw error;
4183
- }
4184
- };
4185
- return new DeleteBuilder(executor, this.tableName, keyCondition);
4186
- }
4187
- /**
4188
- * Updates an item in the table
4189
- *
4190
- * @param keyCondition The primary key of the item to update
4191
- * @returns An UpdateBuilder instance for chaining update operations and conditions
4192
- */
4193
- update(keyCondition) {
4194
- const executor = async (params) => {
4195
- try {
4196
- const result = await this.dynamoClient.update({
4197
- TableName: params.tableName,
4198
- Key: this.createKeyForPrimaryIndex(keyCondition),
4199
- UpdateExpression: params.updateExpression,
4200
- ConditionExpression: params.conditionExpression,
4201
- ExpressionAttributeNames: params.expressionAttributeNames,
4202
- ExpressionAttributeValues: params.expressionAttributeValues,
4203
- ReturnValues: params.returnValues
4204
- });
4205
- return {
4206
- item: result.Attributes
4207
- };
4208
- } catch (error) {
4209
- console.error("Error updating item:", error);
4210
- throw error;
4211
- }
4212
- };
4213
- return new UpdateBuilder(executor, this.tableName, keyCondition);
4214
- }
4215
- /**
4216
- * Creates a transaction builder for performing multiple operations atomically
4217
- */
4218
- transactionBuilder() {
4219
- const executor = async (params) => {
4220
- await this.dynamoClient.transactWrite(params);
4221
- };
4222
- return new TransactionBuilder(executor, {
4223
- partitionKey: this.partitionKey,
4224
- sortKey: this.sortKey
4225
- });
4226
- }
4227
- /**
4228
- * Creates a batch builder for performing multiple operations efficiently with optional type inference
4229
- *
4230
- * @example Basic Usage
4231
- * ```typescript
4232
- * const batch = table.batchBuilder();
4233
- *
4234
- * // Add operations
4235
- * userRepo.create(newUser).withBatch(batch);
4236
- * orderRepo.get({ id: 'order-1' }).withBatch(batch);
4237
- *
4238
- * // Execute operations
4239
- * const result = await batch.execute();
4240
- * ```
4241
- *
4242
- * @example Typed Usage
4243
- * ```typescript
4244
- * // Define entity types for the batch
4245
- * const batch = table.batchBuilder<{
4246
- * User: UserEntity;
4247
- * Order: OrderEntity;
4248
- * Product: ProductEntity;
4249
- * }>();
4250
- *
4251
- * // Add operations with type information
4252
- * userRepo.create(newUser).withBatch(batch, 'User');
4253
- * orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
4254
- * productRepo.delete({ id: 'old-product' }).withBatch(batch, 'Product');
4255
- *
4256
- * // Execute and get typed results
4257
- * const result = await batch.execute();
4258
- * const users: UserEntity[] = result.reads.itemsByType.User;
4259
- * const orders: OrderEntity[] = result.reads.itemsByType.Order;
4260
- * ```
4261
- */
4262
- batchBuilder() {
4263
- const batchWriteExecutor = async (operations) => {
4264
- return this.batchWrite(operations);
4265
- };
4266
- const batchGetExecutor = async (keys) => {
4267
- return this.batchGet(keys);
4268
- };
4269
- return new BatchBuilder(batchWriteExecutor, batchGetExecutor, {
4270
- partitionKey: this.partitionKey,
4271
- sortKey: this.sortKey
4272
- });
4273
- }
4274
- /**
4275
- * Executes a transaction using a callback function
4276
- *
4277
- * @param callback A function that receives a transaction context and performs operations on it
4278
- * @param options Optional transaction options
4279
- * @returns A promise that resolves when the transaction is complete
4280
- */
4281
- async transaction(callback, options) {
4282
- const transactionExecutor = async (params) => {
4283
- await this.dynamoClient.transactWrite(params);
4284
- };
4285
- const transaction = new TransactionBuilder(transactionExecutor, {
4286
- partitionKey: this.partitionKey,
4287
- sortKey: this.sortKey
4288
- });
4289
- if (options) {
4290
- transaction.withOptions(options);
4291
- }
4292
- const result = await callback(transaction);
4293
- await transaction.execute();
4294
- return result;
4295
- }
4296
- /**
4297
- * Creates a condition check operation for use in transactions
4298
- *
4299
- * This is useful for when you require a transaction to succeed only when a specific condition is met on a
4300
- * a record within the database that you are not directly updating.
4301
- *
4302
- * For example, you are updating a record and you want to ensure that another record exists and/or has a specific value before proceeding.
4303
- */
4304
- conditionCheck(keyCondition) {
4305
- return new ConditionCheckBuilder(this.tableName, keyCondition);
4306
- }
4307
- /**
4308
- * Performs a batch get operation to retrieve multiple items at once
4309
- *
4310
- * @param keys Array of primary keys to retrieve
4311
- * @returns A promise that resolves to the retrieved items
4312
- */
4313
- async batchGet(keys) {
4314
- const allItems = [];
4315
- const allUnprocessedKeys = [];
4316
- for (const chunk of chunkArray(keys, DDB_BATCH_GET_LIMIT)) {
4317
- const formattedKeys = chunk.map((key) => ({
4318
- [this.partitionKey]: key.pk,
4319
- ...this.sortKey ? { [this.sortKey]: key.sk } : {}
4320
- }));
4321
- const params = {
4322
- RequestItems: {
4323
- [this.tableName]: {
4324
- Keys: formattedKeys
4325
- }
4326
- }
4327
- };
4328
- try {
4329
- const result = await this.dynamoClient.batchGet(params);
4330
- if (result.Responses?.[this.tableName]) {
4331
- allItems.push(...result.Responses[this.tableName]);
4332
- }
4333
- const unprocessedKeysArray = result.UnprocessedKeys?.[this.tableName]?.Keys || [];
4334
- const unprocessedKeys = unprocessedKeysArray.map((key) => ({
4335
- pk: key[this.partitionKey],
4336
- sk: this.sortKey ? key[this.sortKey] : void 0
4337
- }));
4338
- if (unprocessedKeys.length > 0) {
4339
- allUnprocessedKeys.push(...unprocessedKeys);
4340
- }
4341
- } catch (error) {
4342
- console.error("Error in batch get operation:", error);
4343
- throw error;
4344
- }
4345
- }
4346
- return {
4347
- items: allItems,
4348
- unprocessedKeys: allUnprocessedKeys
4349
- };
4350
- }
4351
- /**
4352
- * Performs a batch write operation to put or delete multiple items at once
4353
- *
4354
- * @param operations Array of put or delete operations
4355
- * @returns A promise that resolves to any unprocessed operations
4356
- */
4357
- async batchWrite(operations) {
4358
- const allUnprocessedItems = [];
4359
- for (const chunk of chunkArray(operations, DDB_BATCH_WRITE_LIMIT)) {
4360
- const writeRequests = chunk.map((operation) => {
4361
- if (operation.type === "put") {
4362
- return {
4363
- PutRequest: {
4364
- Item: operation.item
4365
- }
4366
- };
4367
- }
4368
- return {
4369
- DeleteRequest: {
4370
- Key: this.createKeyForPrimaryIndex(operation.key)
4371
- }
4372
- };
4373
- });
4374
- const params = {
4375
- RequestItems: {
4376
- [this.tableName]: writeRequests
4377
- }
4378
- };
4379
- try {
4380
- const result = await this.dynamoClient.batchWrite(params);
4381
- const unprocessedRequestsArray = result.UnprocessedItems?.[this.tableName] || [];
4382
- if (unprocessedRequestsArray.length > 0) {
4383
- const unprocessedItems = unprocessedRequestsArray.map((request) => {
4384
- if (request?.PutRequest?.Item) {
4385
- return {
4386
- type: "put",
4387
- item: request.PutRequest.Item
4388
- };
4389
- }
4390
- if (request?.DeleteRequest?.Key) {
4391
- return {
4392
- type: "delete",
4393
- key: {
4394
- pk: request.DeleteRequest.Key[this.partitionKey],
4395
- sk: this.sortKey ? request.DeleteRequest.Key[this.sortKey] : void 0
4396
- }
4397
- };
4398
- }
4399
- throw new Error("Invalid unprocessed item format returned from DynamoDB");
4400
- });
4401
- allUnprocessedItems.push(...unprocessedItems);
4402
- }
4403
- } catch (error) {
4404
- console.error("Error in batch write operation:", error);
4405
- throw error;
4406
- }
4407
- }
4408
- return {
4409
- unprocessedItems: allUnprocessedItems
4410
- };
4411
- }
4412
- };
4413
-
4414
- // src/utils/partition-key-template.ts
4415
- function partitionKey(strings, ...keys) {
4416
- return (params) => {
4417
- let result = strings[0] ?? "";
4418
- for (let i = 0; i < keys.length; i++) {
4419
- const key = keys[i];
4420
- if (key) {
4421
- result += params[key] + (strings[i + 1] ?? "");
4422
- }
4423
- }
4424
- return result;
4425
- };
4426
- }
4427
-
4428
- // src/utils/sort-key-template.ts
4429
- function sortKey(strings, ...keys) {
4430
- return (params) => {
4431
- let result = strings[0] ?? "";
4432
- for (let i = 0; i < keys.length; i++) {
4433
- const key = keys[i];
4434
- if (key && params && key in params) {
4435
- result += params[key] + (strings[i + 1] ?? "");
4436
- }
4437
- }
4438
- return result;
4439
- };
4440
- }
4441
-
4442
- exports.BatchBuilder = BatchBuilder;
4443
- exports.BatchError = BatchError;
4444
- exports.DeleteBuilder = DeleteBuilder;
4445
- exports.PutBuilder = PutBuilder;
4446
- exports.QueryBuilder = QueryBuilder;
4447
- exports.Table = Table;
4448
- exports.TransactionBuilder = TransactionBuilder;
4449
- exports.UpdateBuilder = UpdateBuilder;
4450
- exports.and = and;
4451
- exports.attributeExists = attributeExists;
4452
- exports.attributeNotExists = attributeNotExists;
4453
- exports.beginsWith = beginsWith;
4454
- exports.between = between;
4455
- exports.contains = contains;
4456
- exports.createIndex = createIndex;
4457
- exports.createQueries = createQueries;
4458
- exports.defineEntity = defineEntity;
4459
- exports.eq = eq;
4460
- exports.gt = gt;
4461
- exports.gte = gte;
4462
- exports.inArray = inArray;
4463
- exports.lt = lt;
4464
- exports.lte = lte;
4465
- exports.ne = ne;
4466
- exports.not = not;
4467
- exports.or = or;
4468
- exports.partitionKey = partitionKey;
4469
- exports.sortKey = sortKey;
4470
- //# sourceMappingURL=index.cjs.map
4471
- //# sourceMappingURL=index.cjs.map