dyno-table 2.6.0 → 2.6.2

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.
@@ -1,579 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk3DR6VOFW_cjs = require('./chunk-3DR6VOFW.cjs');
4
- var chunkELULXDSB_cjs = require('./chunk-ELULXDSB.cjs');
5
- var chunk7UJJ7JXM_cjs = require('./chunk-7UJJ7JXM.cjs');
6
-
7
- // src/utils/chunk-array.ts
8
- function* chunkArray(array, size) {
9
- if (size <= 0) {
10
- throw chunkELULXDSB_cjs.ConfigurationErrors.invalidChunkSize(size);
11
- }
12
- for (let i = 0; i < array.length; i += size) {
13
- yield array.slice(i, i + size);
14
- }
15
- }
16
-
17
- // src/table.ts
18
- var DDB_BATCH_WRITE_LIMIT = 25;
19
- var DDB_BATCH_GET_LIMIT = 100;
20
- var Table = class {
21
- dynamoClient;
22
- tableName;
23
- /**
24
- * The column name of the partitionKey for the Table
25
- */
26
- partitionKey;
27
- /**
28
- * The column name of the sortKey for the Table
29
- */
30
- sortKey;
31
- /**
32
- * The Global Secondary Indexes that are configured on this table
33
- */
34
- gsis;
35
- constructor(config) {
36
- this.dynamoClient = config.client;
37
- this.tableName = config.tableName;
38
- this.partitionKey = config.indexes.partitionKey;
39
- this.sortKey = config.indexes.sortKey;
40
- this.gsis = config.indexes.gsis || {};
41
- }
42
- getIndexAttributeNames() {
43
- const names = /* @__PURE__ */ new Set();
44
- for (const gsi of Object.values(this.gsis)) {
45
- names.add(gsi.partitionKey);
46
- if (gsi.sortKey) {
47
- names.add(gsi.sortKey);
48
- }
49
- }
50
- return Array.from(names);
51
- }
52
- createKeyForPrimaryIndex(keyCondition) {
53
- const primaryCondition = { [this.partitionKey]: keyCondition.pk };
54
- if (this.sortKey) {
55
- if (!keyCondition.sk) {
56
- throw chunkELULXDSB_cjs.ConfigurationErrors.sortKeyRequired(this.tableName, this.partitionKey, this.sortKey);
57
- }
58
- primaryCondition[this.sortKey] = keyCondition.sk;
59
- }
60
- return primaryCondition;
61
- }
62
- /**
63
- * Creates a new item in the table, it will fail if the item already exists.
64
- *
65
- * By default, this method returns the input values passed to the create operation
66
- * upon successful creation.
67
- *
68
- * You can customise the return behaviour by chaining the `.returnValues()` method:
69
- *
70
- * @param item The item to create
71
- * @returns A PutBuilder instance for chaining additional conditions and executing the create operation
72
- *
73
- * @example
74
- * ```ts
75
- * // Create with default behavior (returns input values)
76
- * const result = await table.create({
77
- * id: 'user-123',
78
- * name: 'John Doe',
79
- * email: 'john@example.com'
80
- * }).execute();
81
- * console.log(result); // Returns the input object
82
- *
83
- * // Create with no return value for better performance
84
- * await table.create(userData).returnValues('NONE').execute();
85
- *
86
- * // Create and get fresh data from dynamodb using a strongly consistent read
87
- * const freshData = await table.create(userData).returnValues('CONSISTENT').execute();
88
- *
89
- * // Create and get previous values (if the item was overwritten)
90
- * const oldData = await table.create(userData).returnValues('ALL_OLD').execute();
91
- * ```
92
- */
93
- create(item) {
94
- return this.put(item).condition((op) => op.attributeNotExists(this.partitionKey)).returnValues("INPUT");
95
- }
96
- get(keyCondition) {
97
- const indexAttributeNames = this.getIndexAttributeNames();
98
- const executor = async (params) => {
99
- try {
100
- const result = await this.dynamoClient.get({
101
- TableName: params.tableName,
102
- Key: this.createKeyForPrimaryIndex(keyCondition),
103
- ProjectionExpression: params.projectionExpression,
104
- ExpressionAttributeNames: params.expressionAttributeNames,
105
- ConsistentRead: params.consistentRead
106
- });
107
- return {
108
- item: result.Item ? result.Item : void 0
109
- };
110
- } catch (error) {
111
- throw chunkELULXDSB_cjs.OperationErrors.getFailed(params.tableName, keyCondition, error instanceof Error ? error : void 0);
112
- }
113
- };
114
- return new chunk3DR6VOFW_cjs.GetBuilder(executor, keyCondition, this.tableName, indexAttributeNames);
115
- }
116
- /**
117
- * Updates an item in the table
118
- *
119
- * @param item The item to update
120
- * @returns A PutBuilder instance for chaining conditions and executing the put operation
121
- */
122
- put(item) {
123
- const executor = async (params) => {
124
- try {
125
- const result = await this.dynamoClient.put({
126
- TableName: params.tableName,
127
- Item: params.item,
128
- ConditionExpression: params.conditionExpression,
129
- ExpressionAttributeNames: params.expressionAttributeNames,
130
- ExpressionAttributeValues: params.expressionAttributeValues,
131
- // CONSISTENT and INPUT are not valid ReturnValues for DDB, so we set NONE as we are not interested in its
132
- // response and will be handling these cases separately
133
- ReturnValues: params.returnValues === "CONSISTENT" || params.returnValues === "INPUT" ? "NONE" : params.returnValues
134
- });
135
- if (params.returnValues === "INPUT") {
136
- return params.item;
137
- }
138
- if (params.returnValues === "CONSISTENT") {
139
- const getResult = await this.dynamoClient.get({
140
- TableName: params.tableName,
141
- Key: this.createKeyForPrimaryIndex({
142
- pk: params.item[this.partitionKey],
143
- ...this.sortKey && { sk: params.item[this.sortKey] }
144
- }),
145
- ConsistentRead: true
146
- });
147
- return getResult.Item;
148
- }
149
- return result.Attributes;
150
- } catch (error) {
151
- throw chunkELULXDSB_cjs.OperationErrors.putFailed(params.tableName, params.item, error instanceof Error ? error : void 0);
152
- }
153
- };
154
- return new chunk3DR6VOFW_cjs.PutBuilder(executor, item, this.tableName);
155
- }
156
- /**
157
- * Creates a query builder for complex queries
158
- * If useIndex is called on the returned QueryBuilder, it will use the GSI configuration
159
- */
160
- query(keyCondition) {
161
- const indexAttributeNames = this.getIndexAttributeNames();
162
- const pkAttributeName = this.partitionKey;
163
- const skAttributeName = this.sortKey;
164
- let keyConditionExpression = chunk7UJJ7JXM_cjs.eq(pkAttributeName, keyCondition.pk);
165
- if (keyCondition.sk) {
166
- if (!skAttributeName) {
167
- throw chunkELULXDSB_cjs.ConfigurationErrors.sortKeyNotDefined(this.tableName, pkAttributeName);
168
- }
169
- const keyConditionOperator = {
170
- eq: (value) => chunk7UJJ7JXM_cjs.eq(skAttributeName, value),
171
- lt: (value) => chunk7UJJ7JXM_cjs.lt(skAttributeName, value),
172
- lte: (value) => chunk7UJJ7JXM_cjs.lte(skAttributeName, value),
173
- gt: (value) => chunk7UJJ7JXM_cjs.gt(skAttributeName, value),
174
- gte: (value) => chunk7UJJ7JXM_cjs.gte(skAttributeName, value),
175
- between: (lower, upper) => chunk7UJJ7JXM_cjs.between(skAttributeName, lower, upper),
176
- beginsWith: (value) => chunk7UJJ7JXM_cjs.beginsWith(skAttributeName, value),
177
- and: (...conditions) => chunk7UJJ7JXM_cjs.and(...conditions)
178
- };
179
- const skCondition = keyCondition.sk(keyConditionOperator);
180
- keyConditionExpression = chunk7UJJ7JXM_cjs.and(chunk7UJJ7JXM_cjs.eq(pkAttributeName, keyCondition.pk), skCondition);
181
- }
182
- const executor = async (originalKeyCondition, options) => {
183
- let finalKeyCondition = originalKeyCondition;
184
- if (options.indexName) {
185
- const gsiName = String(options.indexName);
186
- const gsi = this.gsis[gsiName];
187
- if (!gsi) {
188
- throw chunkELULXDSB_cjs.ConfigurationErrors.gsiNotFound(gsiName, this.tableName, Object.keys(this.gsis));
189
- }
190
- const gsiPkAttributeName = gsi.partitionKey;
191
- const gsiSkAttributeName = gsi.sortKey;
192
- let pkValue;
193
- let skValue;
194
- let extractedSkCondition;
195
- if (originalKeyCondition.type === "eq") {
196
- pkValue = originalKeyCondition.value;
197
- } else if (originalKeyCondition.type === "and" && originalKeyCondition.conditions) {
198
- const pkCondition = originalKeyCondition.conditions.find(
199
- (c) => c.type === "eq" && c.attr === pkAttributeName
200
- );
201
- if (pkCondition && pkCondition.type === "eq") {
202
- pkValue = pkCondition.value;
203
- }
204
- const skConditions = originalKeyCondition.conditions.filter((c) => c.attr === skAttributeName);
205
- if (skConditions.length > 0) {
206
- if (skConditions.length === 1) {
207
- extractedSkCondition = skConditions[0];
208
- if (extractedSkCondition && extractedSkCondition.type === "eq") {
209
- skValue = extractedSkCondition.value;
210
- }
211
- } else if (skConditions.length > 1) {
212
- extractedSkCondition = chunk7UJJ7JXM_cjs.and(...skConditions);
213
- }
214
- }
215
- }
216
- if (!pkValue) {
217
- throw chunkELULXDSB_cjs.ConfigurationErrors.pkExtractionFailed(this.tableName, options.indexName, originalKeyCondition);
218
- }
219
- let gsiKeyCondition = chunk7UJJ7JXM_cjs.eq(gsiPkAttributeName, pkValue);
220
- if (skValue && gsiSkAttributeName) {
221
- gsiKeyCondition = chunk7UJJ7JXM_cjs.and(gsiKeyCondition, chunk7UJJ7JXM_cjs.eq(gsiSkAttributeName, skValue));
222
- } else if (extractedSkCondition && gsiSkAttributeName) {
223
- if (extractedSkCondition.attr === skAttributeName) {
224
- const updatedSkCondition = {
225
- ...extractedSkCondition,
226
- attr: gsiSkAttributeName
227
- };
228
- gsiKeyCondition = chunk7UJJ7JXM_cjs.and(gsiKeyCondition, updatedSkCondition);
229
- } else {
230
- gsiKeyCondition = chunk7UJJ7JXM_cjs.and(gsiKeyCondition, extractedSkCondition);
231
- }
232
- }
233
- finalKeyCondition = gsiKeyCondition;
234
- }
235
- const expressionParams = {
236
- expressionAttributeNames: {},
237
- expressionAttributeValues: {},
238
- valueCounter: { count: 0 }
239
- };
240
- const keyConditionExpression2 = chunk3DR6VOFW_cjs.buildExpression(finalKeyCondition, expressionParams);
241
- let filterExpression;
242
- if (options.filter) {
243
- filterExpression = chunk3DR6VOFW_cjs.buildExpression(options.filter, expressionParams);
244
- }
245
- const projectionExpression = options.projection?.map((p) => chunk3DR6VOFW_cjs.generateAttributeName(expressionParams, p)).join(", ");
246
- const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
247
- const { indexName, limit, consistentRead, scanIndexForward, lastEvaluatedKey } = options;
248
- const params = {
249
- TableName: this.tableName,
250
- KeyConditionExpression: keyConditionExpression2,
251
- FilterExpression: filterExpression,
252
- ExpressionAttributeNames: expressionAttributeNames,
253
- ExpressionAttributeValues: expressionAttributeValues,
254
- IndexName: indexName,
255
- Limit: limit,
256
- ConsistentRead: consistentRead,
257
- ScanIndexForward: scanIndexForward,
258
- ProjectionExpression: projectionExpression,
259
- ExclusiveStartKey: lastEvaluatedKey
260
- };
261
- try {
262
- const result = await this.dynamoClient.query(params);
263
- return {
264
- items: result.Items,
265
- lastEvaluatedKey: result.LastEvaluatedKey
266
- };
267
- } catch (error) {
268
- throw chunkELULXDSB_cjs.OperationErrors.queryFailed(
269
- this.tableName,
270
- { indexName, keyConditionExpression: keyConditionExpression2, filterExpression },
271
- error instanceof Error ? error : void 0
272
- );
273
- }
274
- };
275
- return new chunk3DR6VOFW_cjs.QueryBuilder(executor, keyConditionExpression, indexAttributeNames);
276
- }
277
- /**
278
- * Creates a scan builder for scanning the entire table
279
- * Use this when you need to:
280
- * - Process all items in a table
281
- * - Apply filters to a large dataset
282
- * - Use a GSI for scanning
283
- *
284
- * @returns A ScanBuilder instance for chaining operations
285
- */
286
- scan() {
287
- const executor = async (options) => {
288
- const expressionParams = {
289
- expressionAttributeNames: {},
290
- expressionAttributeValues: {},
291
- valueCounter: { count: 0 }
292
- };
293
- let filterExpression;
294
- if (options.filter) {
295
- filterExpression = chunk3DR6VOFW_cjs.buildExpression(options.filter, expressionParams);
296
- }
297
- const projectionExpression = options.projection?.map((p) => chunk3DR6VOFW_cjs.generateAttributeName(expressionParams, p)).join(", ");
298
- const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
299
- const { indexName, limit, consistentRead, lastEvaluatedKey } = options;
300
- const params = {
301
- TableName: this.tableName,
302
- FilterExpression: filterExpression,
303
- ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
304
- ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
305
- IndexName: indexName,
306
- Limit: limit,
307
- ConsistentRead: consistentRead,
308
- ProjectionExpression: projectionExpression,
309
- ExclusiveStartKey: lastEvaluatedKey
310
- };
311
- try {
312
- const result = await this.dynamoClient.scan(params);
313
- return {
314
- items: result.Items,
315
- lastEvaluatedKey: result.LastEvaluatedKey
316
- };
317
- } catch (error) {
318
- throw chunkELULXDSB_cjs.OperationErrors.scanFailed(
319
- this.tableName,
320
- { indexName: options.indexName, filterExpression },
321
- error instanceof Error ? error : void 0
322
- );
323
- }
324
- };
325
- return new chunk3DR6VOFW_cjs.ScanBuilder(executor);
326
- }
327
- delete(keyCondition) {
328
- const executor = async (params) => {
329
- try {
330
- const result = await this.dynamoClient.delete({
331
- TableName: params.tableName,
332
- Key: this.createKeyForPrimaryIndex(keyCondition),
333
- ConditionExpression: params.conditionExpression,
334
- ExpressionAttributeNames: params.expressionAttributeNames,
335
- ExpressionAttributeValues: params.expressionAttributeValues,
336
- ReturnValues: params.returnValues
337
- });
338
- return {
339
- item: result.Attributes
340
- };
341
- } catch (error) {
342
- throw chunkELULXDSB_cjs.OperationErrors.deleteFailed(params.tableName, keyCondition, error instanceof Error ? error : void 0);
343
- }
344
- };
345
- return new chunk3DR6VOFW_cjs.DeleteBuilder(executor, this.tableName, keyCondition);
346
- }
347
- /**
348
- * Updates an item in the table
349
- *
350
- * @param keyCondition The primary key of the item to update
351
- * @returns An UpdateBuilder instance for chaining update operations and conditions
352
- */
353
- update(keyCondition) {
354
- const executor = async (params) => {
355
- try {
356
- const result = await this.dynamoClient.update({
357
- TableName: params.tableName,
358
- Key: this.createKeyForPrimaryIndex(keyCondition),
359
- UpdateExpression: params.updateExpression,
360
- ConditionExpression: params.conditionExpression,
361
- ExpressionAttributeNames: params.expressionAttributeNames,
362
- ExpressionAttributeValues: params.expressionAttributeValues,
363
- ReturnValues: params.returnValues
364
- });
365
- return {
366
- item: result.Attributes
367
- };
368
- } catch (error) {
369
- throw chunkELULXDSB_cjs.OperationErrors.updateFailed(params.tableName, keyCondition, error instanceof Error ? error : void 0);
370
- }
371
- };
372
- return new chunk3DR6VOFW_cjs.UpdateBuilder(executor, this.tableName, keyCondition);
373
- }
374
- /**
375
- * Creates a transaction builder for performing multiple operations atomically
376
- */
377
- transactionBuilder() {
378
- const executor = async (params) => {
379
- await this.dynamoClient.transactWrite(params);
380
- };
381
- return new chunk3DR6VOFW_cjs.TransactionBuilder(executor, {
382
- partitionKey: this.partitionKey,
383
- sortKey: this.sortKey
384
- });
385
- }
386
- /**
387
- * Creates a batch builder for performing multiple operations efficiently with optional type inference
388
- *
389
- * @example Basic Usage
390
- * ```typescript
391
- * const batch = table.batchBuilder();
392
- *
393
- * // Add operations
394
- * userRepo.create(newUser).withBatch(batch);
395
- * orderRepo.get({ id: 'order-1' }).withBatch(batch);
396
- *
397
- * // Execute operations
398
- * const result = await batch.execute();
399
- * ```
400
- *
401
- * @example Typed Usage
402
- * ```typescript
403
- * // Define entity types for the batch
404
- * const batch = table.batchBuilder<{
405
- * User: UserEntity;
406
- * Order: OrderEntity;
407
- * Product: ProductEntity;
408
- * }>();
409
- *
410
- * // Add operations with type information
411
- * userRepo.create(newUser).withBatch(batch, 'User');
412
- * orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
413
- * productRepo.delete({ id: 'old-product' }).withBatch(batch, 'Product');
414
- *
415
- * // Execute and get typed results
416
- * const result = await batch.execute();
417
- * const users: UserEntity[] = result.reads.itemsByType.User;
418
- * const orders: OrderEntity[] = result.reads.itemsByType.Order;
419
- * ```
420
- */
421
- batchBuilder() {
422
- const batchWriteExecutor = async (operations) => {
423
- return this.batchWrite(operations);
424
- };
425
- const batchGetExecutor = async (keys) => {
426
- return this.batchGet(keys);
427
- };
428
- return new chunk3DR6VOFW_cjs.BatchBuilder(batchWriteExecutor, batchGetExecutor, {
429
- partitionKey: this.partitionKey,
430
- sortKey: this.sortKey
431
- });
432
- }
433
- /**
434
- * Executes a transaction using a callback function
435
- *
436
- * @param callback A function that receives a transaction context and performs operations on it
437
- * @param options Optional transaction options
438
- * @returns A promise that resolves when the transaction is complete
439
- */
440
- async transaction(callback, options) {
441
- const transactionExecutor = async (params) => {
442
- await this.dynamoClient.transactWrite(params);
443
- };
444
- const transaction = new chunk3DR6VOFW_cjs.TransactionBuilder(transactionExecutor, {
445
- partitionKey: this.partitionKey,
446
- sortKey: this.sortKey
447
- });
448
- if (options) {
449
- transaction.withOptions(options);
450
- }
451
- const result = await callback(transaction);
452
- await transaction.execute();
453
- return result;
454
- }
455
- /**
456
- * Creates a condition check operation for use in transactions
457
- *
458
- * This is useful for when you require a transaction to succeed only when a specific condition is met on a
459
- * a record within the database that you are not directly updating.
460
- *
461
- * For example, you are updating a record and you want to ensure that another record exists and/or has a specific value before proceeding.
462
- */
463
- conditionCheck(keyCondition) {
464
- return new chunk3DR6VOFW_cjs.ConditionCheckBuilder(this.tableName, keyCondition);
465
- }
466
- /**
467
- * Performs a batch get operation to retrieve multiple items at once
468
- *
469
- * @param keys Array of primary keys to retrieve
470
- * @returns A promise that resolves to the retrieved items
471
- */
472
- async batchGet(keys) {
473
- const allItems = [];
474
- const allUnprocessedKeys = [];
475
- for (const chunk of chunkArray(keys, DDB_BATCH_GET_LIMIT)) {
476
- const formattedKeys = chunk.map((key) => ({
477
- [this.partitionKey]: key.pk,
478
- ...this.sortKey ? { [this.sortKey]: key.sk } : {}
479
- }));
480
- const params = {
481
- RequestItems: {
482
- [this.tableName]: {
483
- Keys: formattedKeys
484
- }
485
- }
486
- };
487
- try {
488
- const result = await this.dynamoClient.batchGet(params);
489
- if (result.Responses?.[this.tableName]) {
490
- allItems.push(...result.Responses[this.tableName]);
491
- }
492
- const unprocessedKeysArray = result.UnprocessedKeys?.[this.tableName]?.Keys || [];
493
- const unprocessedKeys = unprocessedKeysArray.map((key) => ({
494
- pk: key[this.partitionKey],
495
- sk: this.sortKey ? key[this.sortKey] : void 0
496
- }));
497
- if (unprocessedKeys.length > 0) {
498
- allUnprocessedKeys.push(...unprocessedKeys);
499
- }
500
- } catch (error) {
501
- throw chunkELULXDSB_cjs.OperationErrors.batchGetFailed(
502
- this.tableName,
503
- { requestedKeys: keys.length },
504
- error instanceof Error ? error : void 0
505
- );
506
- }
507
- }
508
- return {
509
- items: allItems,
510
- unprocessedKeys: allUnprocessedKeys
511
- };
512
- }
513
- /**
514
- * Performs a batch write operation to put or delete multiple items at once
515
- *
516
- * @param operations Array of put or delete operations
517
- * @returns A promise that resolves to any unprocessed operations
518
- */
519
- async batchWrite(operations) {
520
- const allUnprocessedItems = [];
521
- for (const chunk of chunkArray(operations, DDB_BATCH_WRITE_LIMIT)) {
522
- const writeRequests = chunk.map((operation) => {
523
- if (operation.type === "put") {
524
- return {
525
- PutRequest: {
526
- Item: operation.item
527
- }
528
- };
529
- }
530
- return {
531
- DeleteRequest: {
532
- Key: this.createKeyForPrimaryIndex(operation.key)
533
- }
534
- };
535
- });
536
- const params = {
537
- RequestItems: {
538
- [this.tableName]: writeRequests
539
- }
540
- };
541
- try {
542
- const result = await this.dynamoClient.batchWrite(params);
543
- const unprocessedRequestsArray = result.UnprocessedItems?.[this.tableName] || [];
544
- if (unprocessedRequestsArray.length > 0) {
545
- const unprocessedItems = unprocessedRequestsArray.map((request) => {
546
- if (request?.PutRequest?.Item) {
547
- return {
548
- type: "put",
549
- item: request.PutRequest.Item
550
- };
551
- }
552
- if (request?.DeleteRequest?.Key) {
553
- return {
554
- type: "delete",
555
- key: {
556
- pk: request.DeleteRequest.Key[this.partitionKey],
557
- sk: this.sortKey ? request.DeleteRequest.Key[this.sortKey] : void 0
558
- }
559
- };
560
- }
561
- throw new Error("Invalid unprocessed item format returned from DynamoDB");
562
- });
563
- allUnprocessedItems.push(...unprocessedItems);
564
- }
565
- } catch (error) {
566
- throw chunkELULXDSB_cjs.OperationErrors.batchWriteFailed(
567
- this.tableName,
568
- { requestedOperations: operations.length },
569
- error instanceof Error ? error : void 0
570
- );
571
- }
572
- }
573
- return {
574
- unprocessedItems: allUnprocessedItems
575
- };
576
- }
577
- };
578
-
579
- exports.Table = Table;