dyno-table 1.6.0 → 1.8.0-next.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.
- package/README.md +53 -140
- package/dist/batch-builder-BOBwOIUE.d.ts +398 -0
- package/dist/batch-builder-CKYnMRyz.d.cts +398 -0
- package/dist/{builder-types-DlaUSc-b.d.cts → builder-types-BTVhQSHI.d.cts} +55 -5
- package/dist/{builder-types-B_tCpn9F.d.ts → builder-types-CzuLR4Th.d.ts} +55 -5
- package/dist/builders/condition-check-builder.cjs +0 -13
- package/dist/builders/condition-check-builder.cjs.map +1 -1
- package/dist/builders/condition-check-builder.d.cts +1 -14
- package/dist/builders/condition-check-builder.d.ts +1 -14
- package/dist/builders/condition-check-builder.js +0 -13
- package/dist/builders/condition-check-builder.js.map +1 -1
- package/dist/builders/delete-builder.cjs +38 -0
- package/dist/builders/delete-builder.cjs.map +1 -1
- package/dist/builders/delete-builder.d.cts +37 -1
- package/dist/builders/delete-builder.d.ts +37 -1
- package/dist/builders/delete-builder.js +38 -0
- package/dist/builders/delete-builder.js.map +1 -1
- package/dist/builders/paginator.cjs +21 -27
- package/dist/builders/paginator.cjs.map +1 -1
- package/dist/builders/paginator.d.cts +3 -27
- package/dist/builders/paginator.d.ts +3 -27
- package/dist/builders/paginator.js +21 -27
- package/dist/builders/paginator.js.map +1 -1
- package/dist/builders/put-builder.cjs +39 -8
- package/dist/builders/put-builder.cjs.map +1 -1
- package/dist/builders/put-builder.d.cts +38 -9
- package/dist/builders/put-builder.d.ts +38 -9
- package/dist/builders/put-builder.js +39 -8
- package/dist/builders/put-builder.js.map +1 -1
- package/dist/builders/query-builder.cjs +115 -75
- package/dist/builders/query-builder.cjs.map +1 -1
- package/dist/builders/query-builder.d.cts +2 -2
- package/dist/builders/query-builder.d.ts +2 -2
- package/dist/builders/query-builder.js +115 -75
- package/dist/builders/query-builder.js.map +1 -1
- package/dist/builders/transaction-builder.cjs +0 -47
- package/dist/builders/transaction-builder.cjs.map +1 -1
- package/dist/builders/transaction-builder.d.cts +1 -48
- package/dist/builders/transaction-builder.d.ts +1 -48
- package/dist/builders/transaction-builder.js +0 -47
- package/dist/builders/transaction-builder.js.map +1 -1
- package/dist/builders/update-builder.cjs +2 -2
- package/dist/builders/update-builder.cjs.map +1 -1
- package/dist/builders/update-builder.d.cts +3 -3
- package/dist/builders/update-builder.d.ts +3 -3
- package/dist/builders/update-builder.js +2 -2
- package/dist/builders/update-builder.js.map +1 -1
- package/dist/conditions.cjs.map +1 -1
- package/dist/conditions.js.map +1 -1
- package/dist/entity.cjs +69 -37
- package/dist/entity.cjs.map +1 -1
- package/dist/entity.d.cts +30 -10
- package/dist/entity.d.ts +30 -10
- package/dist/entity.js +69 -37
- package/dist/entity.js.map +1 -1
- package/dist/index.cjs +661 -218
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +660 -219
- package/dist/index.js.map +1 -1
- package/dist/{query-builder-BhrR31oO.d.ts → query-builder-CaHzZmDf.d.ts} +31 -63
- package/dist/{query-builder-CbHvimBk.d.cts → query-builder-DFkxojBM.d.cts} +31 -63
- package/dist/{table-CY9byPEg.d.cts → table-CHitMHXE.d.cts} +55 -169
- package/dist/{table-Des8C2od.d.ts → table-m7DQk5dK.d.ts} +55 -169
- package/dist/table.cjs +590 -181
- package/dist/table.cjs.map +1 -1
- package/dist/table.d.cts +4 -3
- package/dist/table.d.ts +4 -3
- package/dist/table.js +590 -181
- package/dist/table.js.map +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
# 🦖 dyno-table
|
|
3
|
+
# 🦖 dyno-table ALPHA
|
|
4
4
|
|
|
5
5
|
### **Tame Your DynamoDB Data with Type-Safe Precision**
|
|
6
6
|
|
|
@@ -101,8 +101,8 @@ await dinoTable
|
|
|
101
101
|
- [Nested Object Support](#nested-object-support)
|
|
102
102
|
- [Type-Safe Conditions](#type-safe-conditions)
|
|
103
103
|
- [🔄 Batch Operations](#-batch-operations)
|
|
104
|
-
- [Batch
|
|
105
|
-
- [Batch
|
|
104
|
+
- [Entity-Based Batch Operations](#-entity-based-batch-operations)
|
|
105
|
+
- [Table-Direct Batch Operations](#-table-direct-batch-operations)
|
|
106
106
|
- [🔒 Transaction Operations](#-transaction-operations)
|
|
107
107
|
- [Transaction Builder](#transaction-builder)
|
|
108
108
|
- [Transaction Options](#transaction-options)
|
|
@@ -947,16 +947,6 @@ async function main() {
|
|
|
947
947
|
}
|
|
948
948
|
```
|
|
949
949
|
|
|
950
|
-
**Key benefits:**
|
|
951
|
-
- 🎯 **Semantic Data Access**: Method names like `getDinosaursBySpecies()` clearly express business intent
|
|
952
|
-
- 🚫 **Prevents Accidental Cross-Type Access**: Type-safe operations prevent data corruption
|
|
953
|
-
- 🔍 **Self-Documenting Code**: No need to remember what `gsi1` or `gsi2` does
|
|
954
|
-
- 🛡️ **Consistent Key Structure**: Ensures uniform key patterns across entities
|
|
955
|
-
- 📦 **Encapsulated Domain Logic**: Business rules are contained within entity definitions
|
|
956
|
-
- 🧪 **Schema Validation**: Automatic data validation with your preferred schema library
|
|
957
|
-
- 🔄 **Full Type Inference**: Complete TypeScript support from schema to queries
|
|
958
|
-
- 👥 **Team Collaboration**: New developers understand the codebase immediately
|
|
959
|
-
|
|
960
950
|
## 🧩 Advanced Features
|
|
961
951
|
|
|
962
952
|
### Transactional Operations
|
|
@@ -1060,108 +1050,6 @@ await dinoTable.transaction(
|
|
|
1060
1050
|
);
|
|
1061
1051
|
```
|
|
1062
1052
|
|
|
1063
|
-
**Benefits of this transaction approach:**
|
|
1064
|
-
- 🔄 Uses the same familiar API as non-transactional operations
|
|
1065
|
-
- 🧠 Maintains consistent mental model for developers
|
|
1066
|
-
- 🔒 All operations within the callback are executed as a single transaction
|
|
1067
|
-
- 🛡️ Prevents race conditions and data inconsistencies
|
|
1068
|
-
- 📊 Supports up to 100 actions per transaction
|
|
1069
|
-
|
|
1070
|
-
### Batch Processing
|
|
1071
|
-
|
|
1072
|
-
**Efficient dinosaur park management with bulk operations**
|
|
1073
|
-
```ts
|
|
1074
|
-
// SCENARIO 1: Morning health check for multiple dinosaurs across enclosures
|
|
1075
|
-
// Retrieve health status for multiple dinosaurs in a single operation
|
|
1076
|
-
const healthCheckKeys = [
|
|
1077
|
-
{ pk: "ENCLOSURE#A", sk: "DINO#001" }, // T-Rex in Paddock A
|
|
1078
|
-
{ pk: "ENCLOSURE#B", sk: "DINO#002" }, // Velociraptor in Paddock B
|
|
1079
|
-
{ pk: "ENCLOSURE#C", sk: "DINO#003" } // Stegosaurus in Paddock C
|
|
1080
|
-
];
|
|
1081
|
-
|
|
1082
|
-
// Perform batch get operation to retrieve all dinosaurs at once
|
|
1083
|
-
// This is much more efficient than individual gets
|
|
1084
|
-
const { items: dinosaurs, unprocessedKeys } = await dinoTable.batchGet<Dinosaur>(healthCheckKeys);
|
|
1085
|
-
console.log(`Health check completed for ${dinosaurs.length} dinosaurs`);
|
|
1086
|
-
|
|
1087
|
-
// Process health check results and identify any dinosaurs needing attention
|
|
1088
|
-
dinosaurs.forEach(dino => {
|
|
1089
|
-
if (dino.health < 80) {
|
|
1090
|
-
console.log(`Health alert for ${dino.name} in Enclosure ${dino.enclosureId}`);
|
|
1091
|
-
// In a real application, you might trigger alerts or schedule veterinary visits
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
// SCENARIO 2: Adding new herbivores to the park after quarantine
|
|
1096
|
-
// Prepare data for multiple new herbivores joining the collection
|
|
1097
|
-
const newHerbivores = [
|
|
1098
|
-
{
|
|
1099
|
-
pk: "ENCLOSURE#D", sk: "DINO#004",
|
|
1100
|
-
name: "Triceratops Alpha", // Three-horned herbivore
|
|
1101
|
-
species: "Triceratops",
|
|
1102
|
-
diet: "Herbivore",
|
|
1103
|
-
status: "HEALTHY",
|
|
1104
|
-
health: 95, // Excellent health after quarantine
|
|
1105
|
-
lastFed: new Date().toISOString() // Just fed before joining main enclosure
|
|
1106
|
-
},
|
|
1107
|
-
{
|
|
1108
|
-
pk: "ENCLOSURE#D", sk: "DINO#005",
|
|
1109
|
-
name: "Brachy", // Long-necked herbivore
|
|
1110
|
-
species: "Brachiosaurus",
|
|
1111
|
-
diet: "Herbivore",
|
|
1112
|
-
status: "HEALTHY",
|
|
1113
|
-
health: 90,
|
|
1114
|
-
lastFed: new Date().toISOString()
|
|
1115
|
-
}
|
|
1116
|
-
];
|
|
1117
|
-
|
|
1118
|
-
// Add all new herbivores to the enclosure in a single batch operation
|
|
1119
|
-
// More efficient than individual writes and ensures consistent state
|
|
1120
|
-
await dinoTable.batchWrite(
|
|
1121
|
-
newHerbivores.map(dino => ({
|
|
1122
|
-
type: "put", // Create or replace operation
|
|
1123
|
-
item: dino // Full dinosaur record
|
|
1124
|
-
}))
|
|
1125
|
-
);
|
|
1126
|
-
|
|
1127
|
-
// SCENARIO 3: Releasing a dinosaur from quarantine to general population
|
|
1128
|
-
// Multiple related operations performed as a batch
|
|
1129
|
-
await dinoTable.batchWrite([
|
|
1130
|
-
// Step 1: Remove dinosaur from quarantine enclosure
|
|
1131
|
-
{
|
|
1132
|
-
type: "delete",
|
|
1133
|
-
key: { pk: "ENCLOSURE#QUARANTINE", sk: "DINO#006" }
|
|
1134
|
-
},
|
|
1135
|
-
|
|
1136
|
-
// Step 2: Add recovered dinosaur to main raptor enclosure
|
|
1137
|
-
{
|
|
1138
|
-
type: "put",
|
|
1139
|
-
item: {
|
|
1140
|
-
pk: "ENCLOSURE#E", sk: "DINO#006",
|
|
1141
|
-
name: "Raptor Beta", // Juvenile Velociraptor
|
|
1142
|
-
species: "Velociraptor",
|
|
1143
|
-
diet: "Carnivore",
|
|
1144
|
-
status: "HEALTHY", // Now healthy after treatment
|
|
1145
|
-
health: 100,
|
|
1146
|
-
lastFed: new Date().toISOString()
|
|
1147
|
-
}
|
|
1148
|
-
},
|
|
1149
|
-
|
|
1150
|
-
// Step 3: Clear quarantine status record
|
|
1151
|
-
{
|
|
1152
|
-
type: "delete",
|
|
1153
|
-
key: { pk: "ENCLOSURE#QUARANTINE", sk: "STATUS#DINO#006" }
|
|
1154
|
-
}
|
|
1155
|
-
]);
|
|
1156
|
-
|
|
1157
|
-
// SCENARIO 4: Daily park-wide health monitoring
|
|
1158
|
-
// Handle large-scale operations across all dinosaurs
|
|
1159
|
-
// The library automatically handles chunking for large batches:
|
|
1160
|
-
// - 25 items per batch write
|
|
1161
|
-
// - 100 items per batch get
|
|
1162
|
-
const dailyHealthUpdates = generateDinosaurHealthUpdates(); // Hundreds of updates
|
|
1163
|
-
await dinoTable.batchWrite(dailyHealthUpdates); // Automatically chunked into multiple requests
|
|
1164
|
-
```
|
|
1165
1053
|
|
|
1166
1054
|
### Pagination Made Simple
|
|
1167
1055
|
|
|
@@ -1244,11 +1132,11 @@ Dyno-table provides comprehensive query methods that match DynamoDB's capabiliti
|
|
|
1244
1132
|
| **Greater Than or Equal** | `.filter(op => op.gte("rating", 4))` | `rating >= :v1` |
|
|
1245
1133
|
| **Between** | `.filter(op => op.between("age", 18, 65))` | `age BETWEEN :v1 AND :v2` |
|
|
1246
1134
|
| **In Array** | `.filter(op => op.inArray("status", ["ACTIVE", "PENDING"]))` | `status IN (:v1, :v2)` |
|
|
1247
|
-
| **Begins With** | `.filter(op => op.beginsWith("email", "@example.com"))`
|
|
1248
|
-
| **Contains** | `.filter(op => op.contains("tags", "important"))`
|
|
1249
|
-
| **Attribute Exists** | `.filter(op => op.attributeExists("email"))`
|
|
1250
|
-
| **Attribute Not Exists** | `.filter(op => op.attributeNotExists("deletedAt"))`
|
|
1251
|
-
| **Nested Attributes** | `.filter(op => op.eq("address.city", "London"))`
|
|
1135
|
+
| **Begins With** | `.filter(op => op.beginsWith("email", "@example.com"))` | `begins_with(email, :v1)` |
|
|
1136
|
+
| **Contains** | `.filter(op => op.contains("tags", "important"))` | `contains(tags, :v1)` |
|
|
1137
|
+
| **Attribute Exists** | `.filter(op => op.attributeExists("email"))` | `attribute_exists(email)` |
|
|
1138
|
+
| **Attribute Not Exists** | `.filter(op => op.attributeNotExists("deletedAt"))` | `attribute_not_exists(deletedAt)` |
|
|
1139
|
+
| **Nested Attributes** | `.filter(op => op.eq("address.city", "London"))` | `address.city = :v1` |
|
|
1252
1140
|
|
|
1253
1141
|
### Logical Operators
|
|
1254
1142
|
|
|
@@ -1449,24 +1337,58 @@ await table.query<DinosaurMonitoring>({
|
|
|
1449
1337
|
|
|
1450
1338
|
## 🔄 Batch Operations
|
|
1451
1339
|
|
|
1452
|
-
|
|
1340
|
+
Efficiently handle multiple items in a single request with automatic chunking and type safety.
|
|
1341
|
+
|
|
1342
|
+
### 🏗️ Entity-Based Batch Operations
|
|
1343
|
+
|
|
1344
|
+
**Type-safe batch operations with automatic entity type inference**
|
|
1453
1345
|
|
|
1454
|
-
### Batch Get
|
|
1455
1346
|
```ts
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1347
|
+
// Create a typed batch builder
|
|
1348
|
+
const batch = table.batchBuilder<{
|
|
1349
|
+
Dinosaur: DinosaurEntity;
|
|
1350
|
+
Fossil: FossilEntity;
|
|
1351
|
+
}>();
|
|
1352
|
+
|
|
1353
|
+
// Add operations - entity type is automatically inferred
|
|
1354
|
+
dinosaurRepo.create(newDinosaur).withBatch(batch);
|
|
1355
|
+
dinosaurRepo.get({ id: 'dino-123', diet: 'carnivore', species: 'Tyrannosaurus Rex' }).withBatch(batch);
|
|
1356
|
+
fossilRepo.create(newFossil).withBatch(batch);
|
|
1357
|
+
|
|
1358
|
+
// Execute and get typed results
|
|
1359
|
+
const result = await batch.execute();
|
|
1360
|
+
const dinosaurs: DinosaurEntity[] = result.reads.itemsByType.Dinosaur;
|
|
1361
|
+
const fossils: FossilEntity[] = result.reads.itemsByType.Fossil;
|
|
1460
1362
|
```
|
|
1461
1363
|
|
|
1462
|
-
### Batch
|
|
1364
|
+
### 📋 Table-Direct Batch Operations
|
|
1365
|
+
|
|
1366
|
+
**Direct table access for maximum control**
|
|
1367
|
+
|
|
1463
1368
|
```ts
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
{
|
|
1467
|
-
|
|
1369
|
+
// Batch get - retrieve multiple items
|
|
1370
|
+
const keys = [
|
|
1371
|
+
{ pk: "DIET#carnivore", sk: "SPECIES#Tyrannosaurus Rex#ID#dino-123" },
|
|
1372
|
+
{ pk: "FOSSIL#456", sk: "DISCOVERY#2024" }
|
|
1373
|
+
];
|
|
1374
|
+
|
|
1375
|
+
const { items, unprocessedKeys } = await table.batchGet<DynamoItem>(keys);
|
|
1376
|
+
|
|
1377
|
+
// Batch write - mix of operations
|
|
1378
|
+
const operations = [
|
|
1379
|
+
{ type: "put" as const, item: { pk: "DIET#herbivore", sk: "SPECIES#Triceratops#ID#dino-789", name: "Spike", dangerLevel: 3 } },
|
|
1380
|
+
{ type: "delete" as const, key: { pk: "FOSSIL#OLD", sk: "DISCOVERY#1990" } }
|
|
1381
|
+
];
|
|
1382
|
+
|
|
1383
|
+
const { unprocessedItems } = await table.batchWrite(operations);
|
|
1384
|
+
|
|
1385
|
+
// Handle unprocessed items (retry if needed)
|
|
1386
|
+
if (unprocessedItems.length > 0) {
|
|
1387
|
+
await table.batchWrite(unprocessedItems);
|
|
1388
|
+
}
|
|
1468
1389
|
```
|
|
1469
1390
|
|
|
1391
|
+
|
|
1470
1392
|
## 🔒 Transaction Operations
|
|
1471
1393
|
|
|
1472
1394
|
Perform multiple operations atomically with transaction support:
|
|
@@ -1666,15 +1588,6 @@ const quarantinedDinos = await dinoTable
|
|
|
1666
1588
|
.execute();
|
|
1667
1589
|
```
|
|
1668
1590
|
|
|
1669
|
-
Available key conditions for dinosaur queries:
|
|
1670
|
-
- `eq(value)` - Exact match (e.g., specific enclosure)
|
|
1671
|
-
- `lt(value)` - Earlier than date/time
|
|
1672
|
-
- `lte(value)` - Up to and including date/time
|
|
1673
|
-
- `gt(value)` - Later than date/time
|
|
1674
|
-
- `gte(value)` - From date/time onwards
|
|
1675
|
-
- `between(lower, upper)` - Range (e.g., weight range, date range)
|
|
1676
|
-
- `beginsWith(value)` - Prefix match (e.g., all health checks today)
|
|
1677
|
-
|
|
1678
1591
|
## 🔮 Future Roadmap
|
|
1679
1592
|
|
|
1680
1593
|
- [ ] Enhanced query plan visualization
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { DynamoItem } from './types.js';
|
|
2
|
+
import { r as PrimaryKeyWithoutExpression } from './conditions-BtynAviC.js';
|
|
3
|
+
import { a as PutCommandParams, D as DeleteCommandParams } from './builder-types-CzuLR4Th.js';
|
|
4
|
+
|
|
5
|
+
type BatchWriteOperation<T extends Record<string, unknown>> = {
|
|
6
|
+
type: "put";
|
|
7
|
+
item: T;
|
|
8
|
+
} | {
|
|
9
|
+
type: "delete";
|
|
10
|
+
key: PrimaryKeyWithoutExpression;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parameters for the DynamoDB get command.
|
|
15
|
+
*/
|
|
16
|
+
interface GetCommandParams {
|
|
17
|
+
/** The name of the DynamoDB table */
|
|
18
|
+
tableName: string;
|
|
19
|
+
/** The primary key of the item to get */
|
|
20
|
+
key: PrimaryKeyWithoutExpression;
|
|
21
|
+
/** Comma-separated list of attributes to return */
|
|
22
|
+
projectionExpression?: string;
|
|
23
|
+
/** Map of expression attribute name placeholders to actual names */
|
|
24
|
+
expressionAttributeNames?: Record<string, string>;
|
|
25
|
+
/** Whether to use strongly consistent reads */
|
|
26
|
+
consistentRead?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Function type for executing DynamoDB get operations.
|
|
30
|
+
* @typeParam T - The type of item being retrieved
|
|
31
|
+
*/
|
|
32
|
+
type GetExecutor<T extends DynamoItem> = (params: GetCommandParams) => Promise<{
|
|
33
|
+
item: T | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Builder for creating DynamoDB get operations.
|
|
37
|
+
* Use this builder when you need to:
|
|
38
|
+
* - Retrieve a single dinosaur by its primary key
|
|
39
|
+
* - Project specific dinosaur attributes
|
|
40
|
+
* - Use consistent reads for critical dinosaur data
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Simple get
|
|
45
|
+
* const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
|
|
46
|
+
* .execute();
|
|
47
|
+
*
|
|
48
|
+
* // Get with projection and consistent read
|
|
49
|
+
* const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
|
|
50
|
+
* .select(['species', 'name', 'diet'])
|
|
51
|
+
* .consistentRead()
|
|
52
|
+
* .execute();
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @typeParam T - The type of item being retrieved
|
|
56
|
+
*/
|
|
57
|
+
declare class GetBuilder<T extends DynamoItem> {
|
|
58
|
+
private readonly executor;
|
|
59
|
+
private readonly params;
|
|
60
|
+
private options;
|
|
61
|
+
private selectedFields;
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new GetBuilder instance.
|
|
64
|
+
*
|
|
65
|
+
* @param executor - Function that executes the get operation
|
|
66
|
+
* @param key - Primary key of the item to retrieve
|
|
67
|
+
* @param tableName - Name of the DynamoDB table
|
|
68
|
+
*/
|
|
69
|
+
constructor(executor: GetExecutor<T>, key: PrimaryKeyWithoutExpression, tableName: string);
|
|
70
|
+
/**
|
|
71
|
+
* Specifies which attributes to return in the get results.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // Select single attribute
|
|
76
|
+
* builder.select('species')
|
|
77
|
+
*
|
|
78
|
+
* // Select multiple attributes
|
|
79
|
+
* builder.select(['id', 'species', 'diet'])
|
|
80
|
+
*
|
|
81
|
+
* // Chain multiple select calls
|
|
82
|
+
* builder
|
|
83
|
+
* .select('id')
|
|
84
|
+
* .select(['species', 'diet'])
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @param fields - A single field name or an array of field names to return
|
|
88
|
+
* @returns The builder instance for method chaining
|
|
89
|
+
*/
|
|
90
|
+
select(fields: string | string[]): GetBuilder<T>;
|
|
91
|
+
/**
|
|
92
|
+
* Sets whether to use strongly consistent reads for the get operation.
|
|
93
|
+
* Use this method when you need:
|
|
94
|
+
* - The most up-to-date dinosaur data
|
|
95
|
+
* - To ensure you're reading the latest dinosaur status
|
|
96
|
+
* - Critical safety information about dangerous species
|
|
97
|
+
*
|
|
98
|
+
* Note: Consistent reads consume twice the throughput
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Get the latest T-Rex data
|
|
103
|
+
* const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
|
|
104
|
+
* .consistentRead()
|
|
105
|
+
* .execute();
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @param consistentRead - Whether to use consistent reads (defaults to true)
|
|
109
|
+
* @returns The builder instance for method chaining
|
|
110
|
+
*/
|
|
111
|
+
consistentRead(consistentRead?: boolean): GetBuilder<T>;
|
|
112
|
+
/**
|
|
113
|
+
* Adds this get operation to a batch with optional entity type information.
|
|
114
|
+
*
|
|
115
|
+
* @example Basic Usage
|
|
116
|
+
* ```ts
|
|
117
|
+
* const batch = table.batchBuilder();
|
|
118
|
+
*
|
|
119
|
+
* // Add multiple get operations to batch
|
|
120
|
+
* dinosaurRepo.get({ id: 'dino-1' }).withBatch(batch);
|
|
121
|
+
* dinosaurRepo.get({ id: 'dino-2' }).withBatch(batch);
|
|
122
|
+
* dinosaurRepo.get({ id: 'dino-3' }).withBatch(batch);
|
|
123
|
+
*
|
|
124
|
+
* // Execute all gets efficiently
|
|
125
|
+
* const results = await batch.execute();
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @example Typed Usage
|
|
129
|
+
* ```ts
|
|
130
|
+
* const batch = table.batchBuilder<{
|
|
131
|
+
* User: UserEntity;
|
|
132
|
+
* Order: OrderEntity;
|
|
133
|
+
* }>();
|
|
134
|
+
*
|
|
135
|
+
* // Add operations with type information
|
|
136
|
+
* userRepo.get({ id: 'user-1' }).withBatch(batch, 'User');
|
|
137
|
+
* orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
|
|
138
|
+
*
|
|
139
|
+
* // Execute and get typed results
|
|
140
|
+
* const result = await batch.execute();
|
|
141
|
+
* const users: UserEntity[] = result.reads.itemsByType.User;
|
|
142
|
+
* const orders: OrderEntity[] = result.reads.itemsByType.Order;
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @param batch - The batch builder to add this operation to
|
|
146
|
+
* @param entityType - Optional entity type key for type tracking
|
|
147
|
+
*/
|
|
148
|
+
withBatch<TEntities extends Record<string, DynamoItem> = Record<string, DynamoItem>, K extends keyof TEntities = keyof TEntities>(batch: BatchBuilder<TEntities>, entityType?: K): void;
|
|
149
|
+
/**
|
|
150
|
+
* Converts the builder configuration to a DynamoDB command
|
|
151
|
+
*/
|
|
152
|
+
private toDynamoCommand;
|
|
153
|
+
/**
|
|
154
|
+
* Executes the get operation against DynamoDB.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* try {
|
|
159
|
+
* const result = await new GetBuilder(executor, { pk: 'dinosaur#123', sk: 'profile' })
|
|
160
|
+
* .select(['species', 'name', 'diet'])
|
|
161
|
+
* .consistentRead()
|
|
162
|
+
* .execute();
|
|
163
|
+
*
|
|
164
|
+
* if (result.item) {
|
|
165
|
+
* console.log('Dinosaur found:', result.item);
|
|
166
|
+
* } else {
|
|
167
|
+
* console.log('Dinosaur not found');
|
|
168
|
+
* }
|
|
169
|
+
* } catch (error) {
|
|
170
|
+
* console.error('Error getting dinosaur:', error);
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @returns A promise that resolves to an object containing:
|
|
175
|
+
* - item: The retrieved dinosaur or undefined if not found
|
|
176
|
+
*/
|
|
177
|
+
execute(): Promise<{
|
|
178
|
+
item: T | undefined;
|
|
179
|
+
}>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Configuration for batch operations
|
|
184
|
+
*/
|
|
185
|
+
interface BatchConfig {
|
|
186
|
+
partitionKey: string;
|
|
187
|
+
sortKey?: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Executor function for batch write operations
|
|
191
|
+
*/
|
|
192
|
+
type BatchWriteExecutor = (operations: Array<BatchWriteOperation<DynamoItem>>) => Promise<{
|
|
193
|
+
unprocessedItems: Array<BatchWriteOperation<DynamoItem>>;
|
|
194
|
+
}>;
|
|
195
|
+
/**
|
|
196
|
+
* Executor function for batch get operations
|
|
197
|
+
*/
|
|
198
|
+
type BatchGetExecutor = (keys: Array<PrimaryKeyWithoutExpression>) => Promise<{
|
|
199
|
+
items: DynamoItem[];
|
|
200
|
+
unprocessedKeys: PrimaryKeyWithoutExpression[];
|
|
201
|
+
}>;
|
|
202
|
+
/**
|
|
203
|
+
* Error class for batch operation failures
|
|
204
|
+
*/
|
|
205
|
+
declare class BatchError extends Error {
|
|
206
|
+
readonly operation: "write" | "read";
|
|
207
|
+
readonly cause?: Error;
|
|
208
|
+
constructor(message: string, operation: "write" | "read", cause?: Error);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Result structure for batch operations
|
|
212
|
+
*/
|
|
213
|
+
interface BatchResult {
|
|
214
|
+
/** Whether the batch operation completed successfully */
|
|
215
|
+
success: boolean;
|
|
216
|
+
/** Write operation results */
|
|
217
|
+
writes: {
|
|
218
|
+
/** Number of write operations processed successfully */
|
|
219
|
+
processed: number;
|
|
220
|
+
/** Write operations that were not processed and may need retry */
|
|
221
|
+
unprocessed: Array<BatchWriteOperation<DynamoItem>>;
|
|
222
|
+
};
|
|
223
|
+
/** Read operation results */
|
|
224
|
+
reads: {
|
|
225
|
+
/** Items retrieved from the batch get operations */
|
|
226
|
+
items: DynamoItem[];
|
|
227
|
+
/** Number of items found and returned */
|
|
228
|
+
found: number;
|
|
229
|
+
/** Keys that were not processed and may need retry */
|
|
230
|
+
unprocessed: PrimaryKeyWithoutExpression[];
|
|
231
|
+
};
|
|
232
|
+
/** Total number of operations in the batch */
|
|
233
|
+
totalOperations: number;
|
|
234
|
+
/** Any errors that occurred during batch processing */
|
|
235
|
+
errors?: BatchError[];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Typed result structure for batch operations with entity type information
|
|
239
|
+
*/
|
|
240
|
+
interface TypedBatchResult<TEntities extends Record<string, DynamoItem> = Record<string, DynamoItem>> {
|
|
241
|
+
/** Whether the batch operation completed successfully */
|
|
242
|
+
success: boolean;
|
|
243
|
+
/** Write operation results */
|
|
244
|
+
writes: {
|
|
245
|
+
/** Number of write operations processed successfully */
|
|
246
|
+
processed: number;
|
|
247
|
+
/** Write operations that were not processed and may need retry */
|
|
248
|
+
unprocessed: Array<BatchWriteOperation<DynamoItem>>;
|
|
249
|
+
};
|
|
250
|
+
/** Read operation results with typed items */
|
|
251
|
+
reads: {
|
|
252
|
+
/** Items retrieved from the batch get operations, grouped by entity type */
|
|
253
|
+
itemsByType: {
|
|
254
|
+
[K in keyof TEntities]: TEntities[K][];
|
|
255
|
+
};
|
|
256
|
+
/** All items retrieved (typed as union of all entity types) */
|
|
257
|
+
items: TEntities[keyof TEntities][];
|
|
258
|
+
/** Number of items found and returned */
|
|
259
|
+
found: number;
|
|
260
|
+
/** Keys that were not processed and may need retry */
|
|
261
|
+
unprocessed: PrimaryKeyWithoutExpression[];
|
|
262
|
+
};
|
|
263
|
+
/** Total number of operations in the batch */
|
|
264
|
+
totalOperations: number;
|
|
265
|
+
/** Any errors that occurred during batch execution */
|
|
266
|
+
errors?: BatchError[];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Builder for creating and executing DynamoDB batch operations with full entity support and type inference.
|
|
270
|
+
*
|
|
271
|
+
* Use BatchBuilder when you need to:
|
|
272
|
+
* - Perform multiple operations efficiently (up to 25 writes, 100 reads per batch)
|
|
273
|
+
* - Maintain entity validation, key generation, and type safety
|
|
274
|
+
* - Mix read and write operations in a single batch
|
|
275
|
+
* - Get typed results grouped by entity type
|
|
276
|
+
*
|
|
277
|
+
* @example Basic Usage
|
|
278
|
+
* ```typescript
|
|
279
|
+
* // Define entity types for the batch
|
|
280
|
+
* const batch = table.batchBuilder<{
|
|
281
|
+
* User: UserEntity;
|
|
282
|
+
* Order: OrderEntity;
|
|
283
|
+
* }>();
|
|
284
|
+
*
|
|
285
|
+
* // Add operations using entity repositories
|
|
286
|
+
* userRepo.create(newUser).withBatch(batch, 'User')
|
|
287
|
+
* userRepo.delete({ id: 'old-user' }).withBatch(batch, 'User')
|
|
288
|
+
* orderRepo.get({ id: 'existing-order' }).withBatch(batch, 'Order')
|
|
289
|
+
*
|
|
290
|
+
* // Execute all operations and get typed results
|
|
291
|
+
* const result = await batch.execute()
|
|
292
|
+
* const users: UserEntity[] = result.reads.itemsByType.User
|
|
293
|
+
* const orders: OrderEntity[] = result.reads.itemsByType.Order
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @example Error Handling
|
|
297
|
+
* ```typescript
|
|
298
|
+
* try {
|
|
299
|
+
* const result = await batch.execute()
|
|
300
|
+
*
|
|
301
|
+
* if (result.writes.unprocessed.length > 0) {
|
|
302
|
+
* console.warn('Some writes were not processed:', result.writes.unprocessed)
|
|
303
|
+
* }
|
|
304
|
+
* } catch (error) {
|
|
305
|
+
* if (error instanceof BatchError) {
|
|
306
|
+
* console.error('Batch operation failed:', error.message)
|
|
307
|
+
* }
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
declare class BatchBuilder<TEntities extends Record<string, DynamoItem> = Record<string, DynamoItem>> {
|
|
312
|
+
private batchWriteExecutor;
|
|
313
|
+
private batchGetExecutor;
|
|
314
|
+
private config;
|
|
315
|
+
private writeItems;
|
|
316
|
+
private getItems;
|
|
317
|
+
constructor(batchWriteExecutor: BatchWriteExecutor, batchGetExecutor: BatchGetExecutor, config: BatchConfig);
|
|
318
|
+
/**
|
|
319
|
+
* Checks if the batch is empty (contains no operations)
|
|
320
|
+
*
|
|
321
|
+
* @returns true if the batch contains no operations
|
|
322
|
+
*/
|
|
323
|
+
isEmpty(): boolean;
|
|
324
|
+
/**
|
|
325
|
+
* Gets the count of operations in the batch
|
|
326
|
+
*
|
|
327
|
+
* @returns Object containing the count of write and read operations
|
|
328
|
+
*/
|
|
329
|
+
getOperationCount(): {
|
|
330
|
+
writes: number;
|
|
331
|
+
reads: number;
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* Validates that the batch is not empty before execution
|
|
335
|
+
*
|
|
336
|
+
* @throws {BatchError} If the batch is empty
|
|
337
|
+
*/
|
|
338
|
+
private validateNotEmpty;
|
|
339
|
+
/**
|
|
340
|
+
* Adds a put operation to the batch with entity type information.
|
|
341
|
+
* This method is used internally by entity builders.
|
|
342
|
+
*
|
|
343
|
+
* @param command - The complete put command configuration
|
|
344
|
+
* @param entityType - The entity type name for type tracking
|
|
345
|
+
* @returns The batch builder for method chaining
|
|
346
|
+
* @internal
|
|
347
|
+
*/
|
|
348
|
+
putWithCommand<K extends keyof TEntities>(command: PutCommandParams, entityType?: K): this;
|
|
349
|
+
/**
|
|
350
|
+
* Adds a delete operation to the batch with entity type information.
|
|
351
|
+
* This method is used internally by entity builders.
|
|
352
|
+
*
|
|
353
|
+
* @param command - The complete delete command configuration
|
|
354
|
+
* @param entityType - The entity type name for type tracking
|
|
355
|
+
* @returns The batch builder for method chaining
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
358
|
+
deleteWithCommand<K extends keyof TEntities>(command: DeleteCommandParams, entityType?: K): this;
|
|
359
|
+
/**
|
|
360
|
+
* Adds a get operation to the batch with entity type information.
|
|
361
|
+
* This method is used internally by entity builders.
|
|
362
|
+
*
|
|
363
|
+
* @param command - The complete get command configuration
|
|
364
|
+
* @param entityType - The entity type name for type tracking
|
|
365
|
+
* @returns The batch builder for method chaining
|
|
366
|
+
* @internal
|
|
367
|
+
*/
|
|
368
|
+
getWithCommand<K extends keyof TEntities>(command: GetCommandParams, entityType?: K): this;
|
|
369
|
+
/**
|
|
370
|
+
* Executes all write operations in the batch.
|
|
371
|
+
*
|
|
372
|
+
* @returns A promise that resolves to any unprocessed operations
|
|
373
|
+
* @private
|
|
374
|
+
*/
|
|
375
|
+
private executeWrites;
|
|
376
|
+
/**
|
|
377
|
+
* Executes all get operations in the batch.
|
|
378
|
+
*
|
|
379
|
+
* @returns A promise that resolves to the retrieved items
|
|
380
|
+
* @private
|
|
381
|
+
*/
|
|
382
|
+
private executeGets;
|
|
383
|
+
/**
|
|
384
|
+
* Groups retrieved items by their entity type.
|
|
385
|
+
* @private
|
|
386
|
+
*/
|
|
387
|
+
private groupItemsByType;
|
|
388
|
+
/**
|
|
389
|
+
* Executes all operations in the batch with typed results.
|
|
390
|
+
* Performs write operations first, then get operations.
|
|
391
|
+
*
|
|
392
|
+
* @returns A promise that resolves to a TypedBatchResult with entity type information
|
|
393
|
+
* @throws {BatchError} If the batch is empty or if operations fail
|
|
394
|
+
*/
|
|
395
|
+
execute(): Promise<TypedBatchResult<TEntities>>;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export { BatchBuilder as B, GetBuilder as G, BatchError as a, type BatchResult as b, type BatchWriteOperation as c };
|