dinah 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -15
- package/dist/cdk.cjs +33 -0
- package/dist/cdk.cjs.map +1 -0
- package/dist/cdk.d.cts +10 -0
- package/dist/cdk.d.cts.map +1 -0
- package/dist/cdk.d.mts +10 -0
- package/dist/cdk.d.mts.map +1 -0
- package/dist/cdk.mjs +30 -0
- package/dist/cdk.mjs.map +1 -0
- package/dist/index.cjs +924 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +914 -0
- package/dist/index.mjs.map +1 -0
- package/dist/streams.cjs +84 -0
- package/dist/streams.cjs.map +1 -0
- package/dist/streams.d.cts +21 -0
- package/dist/streams.d.cts.map +1 -0
- package/dist/streams.d.mts +21 -0
- package/dist/streams.d.mts.map +1 -0
- package/dist/streams.mjs +82 -0
- package/dist/streams.mjs.map +1 -0
- package/dist/table-DBf8kWHl.d.cts +520 -0
- package/dist/table-DBf8kWHl.d.cts.map +1 -0
- package/dist/table-DqJt-sXI.d.mts +520 -0
- package/dist/table-DqJt-sXI.d.mts.map +1 -0
- package/dist/util-DDOj_NRD.mjs +76 -0
- package/dist/util-DDOj_NRD.mjs.map +1 -0
- package/dist/util-zAG7brd9.cjs +128 -0
- package/dist/util-zAG7brd9.cjs.map +1 -0
- package/package.json +52 -5
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
import { i as resolveAttrType, n as extractTableDesc, r as removeUndefined, t as chunk } from "./util-DDOj_NRD.mjs";
|
|
2
|
+
import { CreateTableCommand, DeleteTableCommand, DynamoDB, DynamoDBClient, ListTablesCommand } from "@aws-sdk/client-dynamodb";
|
|
3
|
+
import * as Lib from "@aws-sdk/lib-dynamodb";
|
|
4
|
+
import sift from "sift";
|
|
5
|
+
//#region src/expression-builder.ts
|
|
6
|
+
const attrTypes = [
|
|
7
|
+
"S",
|
|
8
|
+
"SS",
|
|
9
|
+
"N",
|
|
10
|
+
"NS",
|
|
11
|
+
"B",
|
|
12
|
+
"BS",
|
|
13
|
+
"BOOL",
|
|
14
|
+
"NULL",
|
|
15
|
+
"L",
|
|
16
|
+
"M"
|
|
17
|
+
];
|
|
18
|
+
const comparatorOps = {
|
|
19
|
+
$eq: "=",
|
|
20
|
+
$ne: "<>",
|
|
21
|
+
$gt: ">",
|
|
22
|
+
$gte: ">=",
|
|
23
|
+
$lt: "<",
|
|
24
|
+
$lte: "<="
|
|
25
|
+
};
|
|
26
|
+
const isOperation = (val) => {
|
|
27
|
+
if (!val || typeof val !== "object" || Array.isArray(val)) return false;
|
|
28
|
+
return Object.keys(val).every((op) => op.startsWith("$"));
|
|
29
|
+
};
|
|
30
|
+
var ExpressionBuilder = class {
|
|
31
|
+
attrNames = /* @__PURE__ */ new Map();
|
|
32
|
+
attrValues = /* @__PURE__ */ new Map();
|
|
33
|
+
get attributeNames() {
|
|
34
|
+
return this.attrNames.size ? Object.fromEntries([...this.attrNames.entries()].map(([name, placeholder]) => [placeholder, name])) : void 0;
|
|
35
|
+
}
|
|
36
|
+
get attributeValues() {
|
|
37
|
+
return this.attrValues.size ? Object.fromEntries([...this.attrValues.entries()].map(([value, placeholder]) => [placeholder, value])) : void 0;
|
|
38
|
+
}
|
|
39
|
+
reset() {
|
|
40
|
+
this.attrNames.clear();
|
|
41
|
+
this.attrValues.clear();
|
|
42
|
+
}
|
|
43
|
+
getPathSub(path) {
|
|
44
|
+
const segments = path.split(".");
|
|
45
|
+
const placeholders = [];
|
|
46
|
+
for (const segment of segments) {
|
|
47
|
+
let placeholder = this.attrNames.get(segment);
|
|
48
|
+
if (!placeholder) {
|
|
49
|
+
placeholder = `#${this.attrNames.size}`;
|
|
50
|
+
this.attrNames.set(segment, placeholder);
|
|
51
|
+
}
|
|
52
|
+
placeholders.push(placeholder);
|
|
53
|
+
}
|
|
54
|
+
return placeholders.join(".");
|
|
55
|
+
}
|
|
56
|
+
getValueSub(value) {
|
|
57
|
+
let placeholder = this.attrValues.get(value);
|
|
58
|
+
if (!placeholder) {
|
|
59
|
+
placeholder = `:${this.attrValues.size}`;
|
|
60
|
+
this.attrValues.set(value, placeholder);
|
|
61
|
+
}
|
|
62
|
+
return placeholder;
|
|
63
|
+
}
|
|
64
|
+
getValueOrPathSub(pathOrValue) {
|
|
65
|
+
if (isOperation(pathOrValue)) {
|
|
66
|
+
if (pathOrValue.$path) return this.getPathSub(pathOrValue.$path);
|
|
67
|
+
throw new Error("Expected $path or operand.");
|
|
68
|
+
}
|
|
69
|
+
return this.getValueSub(pathOrValue);
|
|
70
|
+
}
|
|
71
|
+
projection(paths) {
|
|
72
|
+
return paths?.map((path) => this.getPathSub(path)).join(", ");
|
|
73
|
+
}
|
|
74
|
+
condition(expression) {
|
|
75
|
+
if (!expression) return void 0;
|
|
76
|
+
const expressions = [];
|
|
77
|
+
for (const [key, val] of Object.entries(expression)) if (key === "$and") {
|
|
78
|
+
if (!Array.isArray(val)) throw new Error("$and expects an array operand.");
|
|
79
|
+
expressions.push(`(${val.map((v) => this.condition(v)).join(" AND ")})`);
|
|
80
|
+
} else if (key === "$or" && Array.isArray(val)) {
|
|
81
|
+
if (!Array.isArray(val)) throw new Error("$or expects an array operand.");
|
|
82
|
+
expressions.push(`(${val.map((v) => this.condition(v)).join(" OR ")})`);
|
|
83
|
+
} else if (key === "$not") {
|
|
84
|
+
if (!val || typeof val !== "object") throw new Error("$not expects an object operand.");
|
|
85
|
+
expressions.push(`NOT ${this.condition(val)}`);
|
|
86
|
+
} else if (!key.startsWith("$")) expressions.push(this.resolveCondition(key, val));
|
|
87
|
+
else throw new Error(`Unexpected operator "${key}". Expected attribute path or compound operator.`);
|
|
88
|
+
return expressions.join(" AND ");
|
|
89
|
+
}
|
|
90
|
+
update(expression) {
|
|
91
|
+
if (expression === void 0) return void 0;
|
|
92
|
+
const setOperations = [];
|
|
93
|
+
const removeOperations = [];
|
|
94
|
+
const setAddOperations = [];
|
|
95
|
+
const setDelOperations = [];
|
|
96
|
+
for (const [path, valOrOperation] of Object.entries(expression)) {
|
|
97
|
+
const placeholder = this.getPathSub(path);
|
|
98
|
+
if (valOrOperation === void 0) removeOperations.push(placeholder);
|
|
99
|
+
else if (isOperation(valOrOperation)) if (valOrOperation.$remove === true) removeOperations.push(placeholder);
|
|
100
|
+
else if (valOrOperation.$setAdd !== void 0 || valOrOperation.$setDel !== void 0) {
|
|
101
|
+
const operations = valOrOperation.$setAdd ? setAddOperations : setDelOperations;
|
|
102
|
+
const operand = valOrOperation.$setAdd ?? valOrOperation.$setDel;
|
|
103
|
+
operations.push(`${placeholder} ${this.getValueSub(operand instanceof Set ? operand : new Set([operand].flat()))}`);
|
|
104
|
+
} else setOperations.push(`${placeholder} = ${this.resolveSetOperand(path, valOrOperation)}`);
|
|
105
|
+
else setOperations.push(`${placeholder} = ${this.getValueSub(valOrOperation)}`);
|
|
106
|
+
}
|
|
107
|
+
let updateExpression = "";
|
|
108
|
+
if (setOperations.length) updateExpression += `SET ${setOperations.join(", ")} `;
|
|
109
|
+
if (removeOperations.length) updateExpression += `REMOVE ${removeOperations.join(", ")} `;
|
|
110
|
+
if (setAddOperations.length) updateExpression += `ADD ${setAddOperations.join(", ")} `;
|
|
111
|
+
if (setDelOperations.length) updateExpression += `DELETE ${setDelOperations.join(", ")} `;
|
|
112
|
+
return updateExpression;
|
|
113
|
+
}
|
|
114
|
+
resolveCondition(path, exp) {
|
|
115
|
+
if (!exp || typeof exp !== "object" || !Object.keys(exp).at(0)?.startsWith("$")) return this.resolveCondition(path, { $eq: exp });
|
|
116
|
+
const placeholder = this.getPathSub(path);
|
|
117
|
+
const [operator, operand] = Object.entries(exp).at(0) ?? [""];
|
|
118
|
+
if (comparatorOps[operator]) {
|
|
119
|
+
if (isOperation(operand) && operand.$path) return `${placeholder} ${comparatorOps[operator]} ${this.getPathSub(operand.$path)}`;
|
|
120
|
+
if (![
|
|
121
|
+
"string",
|
|
122
|
+
"boolean",
|
|
123
|
+
"number"
|
|
124
|
+
].includes(typeof operand)) throw new Error(`${operator} expects a primitive/scalar operand.`);
|
|
125
|
+
return `${placeholder} ${comparatorOps[operator]} ${this.getValueSub(operand)}`;
|
|
126
|
+
}
|
|
127
|
+
switch (operator) {
|
|
128
|
+
case "$in":
|
|
129
|
+
if (!Array.isArray(operand)) throw new Error("$in operator expects an array operand.");
|
|
130
|
+
return `${placeholder} IN (${operand.map((op) => this.getValueSub(op)).join(", ")})`;
|
|
131
|
+
case "$nin":
|
|
132
|
+
if (!Array.isArray(operand)) throw new Error("$nin operator expects an array operand.");
|
|
133
|
+
return `${placeholder} NOT IN (${operand.map((op) => this.getValueSub(op)).join(", ")})`;
|
|
134
|
+
case "$prefix":
|
|
135
|
+
if (typeof operand !== "string") throw new Error("$prefix operator expects a string operand.");
|
|
136
|
+
return `begins_with(${placeholder}, ${this.getValueSub(operand)})`;
|
|
137
|
+
case "$includes": return `contains(${placeholder}, ${this.getValueSub(operand)})`;
|
|
138
|
+
case "$between":
|
|
139
|
+
if (!Array.isArray(operand) || operand.length !== 2) throw new Error("$between operator expects an array operand of exactly two values.");
|
|
140
|
+
return `${placeholder} BETWEEN ${this.getValueSub(operand[0])} AND ${this.getValueSub(operand[1])}`;
|
|
141
|
+
case "$exists":
|
|
142
|
+
if (typeof operand !== "boolean") throw new Error("$exists operator expects a boolean operand.");
|
|
143
|
+
return operand ? `attribute_exists(${placeholder})` : `attribute_not_exists(${placeholder})`;
|
|
144
|
+
case "$type":
|
|
145
|
+
if (!attrTypes.includes(operand)) throw new Error(`$type operator expects operand to be one of ${attrTypes.join(",")}`);
|
|
146
|
+
return `attribute_type(${placeholder})`;
|
|
147
|
+
case "$size": {
|
|
148
|
+
const sizeExp = typeof operand === "number" ? { $eq: operand } : operand;
|
|
149
|
+
if (!sizeExp || typeof sizeExp !== "object" || !Object.keys(sizeExp).at(0)?.startsWith("$")) throw new Error("$size operator expects a number operand or a comparator object.");
|
|
150
|
+
const [sizeOperator, sizeOperand] = Object.entries(sizeExp).at(0) ?? [""];
|
|
151
|
+
if (!comparatorOps[sizeOperator]) throw new Error("$size operator expects a number operand or a comparator object.");
|
|
152
|
+
if (![
|
|
153
|
+
"string",
|
|
154
|
+
"boolean",
|
|
155
|
+
"number"
|
|
156
|
+
].includes(typeof sizeOperand)) throw new Error(`${operator} expects a primitive/scalar operand.`);
|
|
157
|
+
return `size(${placeholder}) ${comparatorOps[sizeOperator]} ${this.getValueSub(sizeOperand)}`;
|
|
158
|
+
}
|
|
159
|
+
default: throw new Error(`Invalid operator "${operator}"`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
resolveSetOperand(path, operand) {
|
|
163
|
+
if (isOperation(operand)) {
|
|
164
|
+
if (operand.$ifNotExists) {
|
|
165
|
+
const params = Array.isArray(operand.$ifNotExists) ? operand.$ifNotExists : [path, operand.$ifNotExists];
|
|
166
|
+
return `if_not_exists(${this.getPathSub(params[0])}, ${this.resolveSetOperand(path, params[1])})`;
|
|
167
|
+
}
|
|
168
|
+
if (operand.$set) return this.resolveSetOperand(path, operand.$set);
|
|
169
|
+
if (operand.$plus !== void 0 || operand.$minus !== void 0) {
|
|
170
|
+
const mathOperator = operand.$plus ? "+" : "-";
|
|
171
|
+
const mathOperand = operand.$plus ?? operand.$minus;
|
|
172
|
+
return `${(Array.isArray(mathOperand) ? mathOperand : [{ $path: path }, mathOperand]).map((param) => this.resolveSetOperand(path, param)).join(` ${mathOperator} `)}`;
|
|
173
|
+
}
|
|
174
|
+
if (operand.$append !== void 0 || operand.$prepend !== void 0) {
|
|
175
|
+
const listOperand = operand.$append ?? operand.$prepend;
|
|
176
|
+
const resolvedListOperand = isOperation(listOperand) ? listOperand : [listOperand].flat();
|
|
177
|
+
return `list_append(${(operand.$append ? [{ $path: path }, resolvedListOperand] : [resolvedListOperand, { $path: path }]).map((param) => this.resolveSetOperand(path, param)).join(", ")})`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return this.getValueOrPathSub(operand);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/repo.ts
|
|
185
|
+
var AbstractRepo = class {
|
|
186
|
+
db;
|
|
187
|
+
constructor(db) {
|
|
188
|
+
this.db = db;
|
|
189
|
+
}
|
|
190
|
+
get tableName() {
|
|
191
|
+
return `${this.db.config?.tableNamePrefix ?? ""}${this.table.def.name}`;
|
|
192
|
+
}
|
|
193
|
+
get defaultPutData() {
|
|
194
|
+
return {};
|
|
195
|
+
}
|
|
196
|
+
get defaultUpdateData() {
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
199
|
+
transformItem(item) {
|
|
200
|
+
return item;
|
|
201
|
+
}
|
|
202
|
+
extractKey(item) {
|
|
203
|
+
const { partitionKey, sortKey } = this.table.def;
|
|
204
|
+
if (sortKey) return {
|
|
205
|
+
[partitionKey]: item[partitionKey],
|
|
206
|
+
[sortKey]: item[sortKey]
|
|
207
|
+
};
|
|
208
|
+
return { [partitionKey]: item[partitionKey] };
|
|
209
|
+
}
|
|
210
|
+
async get(key, options) {
|
|
211
|
+
const item = await this.db.get({
|
|
212
|
+
table: this.tableName,
|
|
213
|
+
key: this.extractKey(key),
|
|
214
|
+
...options
|
|
215
|
+
});
|
|
216
|
+
return item && this.applyTransformIfNeeded(item, options);
|
|
217
|
+
}
|
|
218
|
+
async getOrThrow(key, options) {
|
|
219
|
+
const item = await this.db.getOrThrow({
|
|
220
|
+
table: this.tableName,
|
|
221
|
+
key: this.extractKey(key),
|
|
222
|
+
...options
|
|
223
|
+
});
|
|
224
|
+
return this.applyTransformIfNeeded(item, options);
|
|
225
|
+
}
|
|
226
|
+
async put(item, options) {
|
|
227
|
+
const itemWithDefaults = {
|
|
228
|
+
...this.defaultPutData,
|
|
229
|
+
...item
|
|
230
|
+
};
|
|
231
|
+
const result = await this.db.put({
|
|
232
|
+
table: this.tableName,
|
|
233
|
+
item: itemWithDefaults,
|
|
234
|
+
...options
|
|
235
|
+
});
|
|
236
|
+
return this.applyTransformIfNeeded(result);
|
|
237
|
+
}
|
|
238
|
+
async update(key, update, options) {
|
|
239
|
+
const updateWithDefaults = {
|
|
240
|
+
...this.defaultUpdateData,
|
|
241
|
+
...update
|
|
242
|
+
};
|
|
243
|
+
const result = await this.db.update({
|
|
244
|
+
table: this.tableName,
|
|
245
|
+
key: this.extractKey(key),
|
|
246
|
+
update: updateWithDefaults,
|
|
247
|
+
...options
|
|
248
|
+
});
|
|
249
|
+
return this.applyTransformIfNeeded(result);
|
|
250
|
+
}
|
|
251
|
+
async create(item, options) {
|
|
252
|
+
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
253
|
+
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
254
|
+
if (otherCondition) condition.$and.push(otherCondition);
|
|
255
|
+
return this.put(item, {
|
|
256
|
+
condition,
|
|
257
|
+
...otherOptions
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async delete(key, options) {
|
|
261
|
+
const item = await this.db.delete({
|
|
262
|
+
table: this.tableName,
|
|
263
|
+
key: this.extractKey(key),
|
|
264
|
+
...options
|
|
265
|
+
});
|
|
266
|
+
return item && this.applyTransformIfNeeded(item);
|
|
267
|
+
}
|
|
268
|
+
async deleteOrThrow(key, options) {
|
|
269
|
+
const item = await this.db.deleteOrThrow({
|
|
270
|
+
table: this.tableName,
|
|
271
|
+
key: this.extractKey(key),
|
|
272
|
+
...options
|
|
273
|
+
});
|
|
274
|
+
return this.applyTransformIfNeeded(item);
|
|
275
|
+
}
|
|
276
|
+
async query(query, options) {
|
|
277
|
+
const items = await this.db.query({
|
|
278
|
+
table: this.tableName,
|
|
279
|
+
query,
|
|
280
|
+
...options
|
|
281
|
+
});
|
|
282
|
+
return this.applyTransformsIfNeeded(items, options);
|
|
283
|
+
}
|
|
284
|
+
async *queryPaged(query, options) {
|
|
285
|
+
for await (const page of this.db.queryPaged({
|
|
286
|
+
table: this.tableName,
|
|
287
|
+
query,
|
|
288
|
+
...options
|
|
289
|
+
})) yield this.applyTransformsIfNeeded(page, options);
|
|
290
|
+
}
|
|
291
|
+
async queryGsi(gsi, query, options) {
|
|
292
|
+
const items = await this.db.query({
|
|
293
|
+
table: this.tableName,
|
|
294
|
+
index: gsi,
|
|
295
|
+
query,
|
|
296
|
+
...options
|
|
297
|
+
});
|
|
298
|
+
return this.applyTransformsIfNeeded(items, {
|
|
299
|
+
...options,
|
|
300
|
+
gsi
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async *queryGsiPaged(gsi, query, options) {
|
|
304
|
+
for await (const page of this.db.queryPaged({
|
|
305
|
+
table: this.tableName,
|
|
306
|
+
index: gsi,
|
|
307
|
+
query,
|
|
308
|
+
...options
|
|
309
|
+
})) yield this.applyTransformsIfNeeded(page, {
|
|
310
|
+
...options,
|
|
311
|
+
gsi
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async scan(options) {
|
|
315
|
+
const items = await this.db.scan({
|
|
316
|
+
table: this.tableName,
|
|
317
|
+
...options
|
|
318
|
+
});
|
|
319
|
+
return this.applyTransformsIfNeeded(items, options);
|
|
320
|
+
}
|
|
321
|
+
async *scanPaged(options) {
|
|
322
|
+
for await (const page of this.db.scanPaged({
|
|
323
|
+
table: this.tableName,
|
|
324
|
+
...options
|
|
325
|
+
})) yield this.applyTransformsIfNeeded(page, options);
|
|
326
|
+
}
|
|
327
|
+
async scanGsi(gsi, options) {
|
|
328
|
+
const items = await this.db.scan({
|
|
329
|
+
table: this.tableName,
|
|
330
|
+
index: gsi,
|
|
331
|
+
...options
|
|
332
|
+
});
|
|
333
|
+
return this.applyTransformsIfNeeded(items, {
|
|
334
|
+
...options,
|
|
335
|
+
gsi
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async *scanGsiPaged(gsi, options) {
|
|
339
|
+
for await (const page of this.db.scanPaged({
|
|
340
|
+
table: this.tableName,
|
|
341
|
+
index: gsi,
|
|
342
|
+
...options
|
|
343
|
+
})) yield this.applyTransformsIfNeeded(page, {
|
|
344
|
+
...options,
|
|
345
|
+
gsi
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async exists(options) {
|
|
349
|
+
return this.db.exists({
|
|
350
|
+
table: this.tableName,
|
|
351
|
+
projection: [this.table.def.partitionKey],
|
|
352
|
+
...options
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
async existsGsi(gsi, options) {
|
|
356
|
+
return this.db.exists({
|
|
357
|
+
table: this.tableName,
|
|
358
|
+
index: gsi,
|
|
359
|
+
projection: [this.table.def.partitionKey],
|
|
360
|
+
...options
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
async batchGet(keys, options) {
|
|
364
|
+
const { items, unprocessed } = await this.db.batchGet({ [this.tableName]: {
|
|
365
|
+
keys: keys.map((key) => this.extractKey(key)),
|
|
366
|
+
...options
|
|
367
|
+
} });
|
|
368
|
+
const tableItems = items[this.tableName];
|
|
369
|
+
return {
|
|
370
|
+
items: tableItems && this.applyTransformsIfNeeded(tableItems, options),
|
|
371
|
+
unprocessed: unprocessed?.[this.tableName]?.keys
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
async batchGetOrThrow(keys, options) {
|
|
375
|
+
const result = await this.db.batchGetOrThrow({ [this.tableName]: {
|
|
376
|
+
keys: keys.map((key) => this.extractKey(key)),
|
|
377
|
+
...options
|
|
378
|
+
} });
|
|
379
|
+
return this.applyTransformsIfNeeded(result[this.tableName] ?? [], options);
|
|
380
|
+
}
|
|
381
|
+
async batchWrite(requests) {
|
|
382
|
+
const { items, unprocessed } = await this.db.batchWrite({ [this.tableName]: requests.map((request) => {
|
|
383
|
+
if (request.type === "DELETE") return {
|
|
384
|
+
type: "DELETE",
|
|
385
|
+
key: this.extractKey(request.key)
|
|
386
|
+
};
|
|
387
|
+
else return {
|
|
388
|
+
type: "PUT",
|
|
389
|
+
item: {
|
|
390
|
+
...this.defaultPutData,
|
|
391
|
+
...request.item
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}) });
|
|
395
|
+
return {
|
|
396
|
+
items: items[this.tableName],
|
|
397
|
+
unprocessed: unprocessed?.[this.tableName]
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async trxGet(keys, options) {
|
|
401
|
+
return (await this.db.trxGet(...keys.map((key) => ({
|
|
402
|
+
table: this.tableName,
|
|
403
|
+
key: this.extractKey(key),
|
|
404
|
+
...options
|
|
405
|
+
})))).map((item) => item && this.applyTransformIfNeeded(item, options));
|
|
406
|
+
}
|
|
407
|
+
async trxGetOrThrow(keys, options) {
|
|
408
|
+
const items = await this.db.trxGetOrThrow(...keys.map((key) => ({
|
|
409
|
+
table: this.tableName,
|
|
410
|
+
key: this.extractKey(key),
|
|
411
|
+
...options
|
|
412
|
+
})));
|
|
413
|
+
return this.applyTransformsIfNeeded(items, options);
|
|
414
|
+
}
|
|
415
|
+
async trxWrite(...requests) {
|
|
416
|
+
await this.db.trxWrite(...requests.map((request) => {
|
|
417
|
+
switch (request.type) {
|
|
418
|
+
case "CONDITION": {
|
|
419
|
+
const { key, condition, ...options } = request;
|
|
420
|
+
return this.trxConditionRequest(key, condition, options);
|
|
421
|
+
}
|
|
422
|
+
case "DELETE": {
|
|
423
|
+
const { key, ...options } = request;
|
|
424
|
+
return this.trxDeleteRequest(key, options);
|
|
425
|
+
}
|
|
426
|
+
case "PUT": {
|
|
427
|
+
const { item, ...options } = request;
|
|
428
|
+
return this.trxPutRequest(item, options);
|
|
429
|
+
}
|
|
430
|
+
case "UPDATE": {
|
|
431
|
+
const { key, update, ...options } = request;
|
|
432
|
+
return this.trxUpdateRequest(key, update, options);
|
|
433
|
+
}
|
|
434
|
+
default: throw new Error("Unexpected request type.");
|
|
435
|
+
}
|
|
436
|
+
}));
|
|
437
|
+
}
|
|
438
|
+
async trxDelete(keys, options) {
|
|
439
|
+
return this.db.trxWrite(...keys.map((key) => this.trxDeleteRequest(key, options)));
|
|
440
|
+
}
|
|
441
|
+
async trxPut(items, options) {
|
|
442
|
+
return this.db.trxWrite(...items.map((item) => this.trxPutRequest(item, options)));
|
|
443
|
+
}
|
|
444
|
+
async trxUpdate(keys, update, options) {
|
|
445
|
+
return this.db.trxWrite(...keys.map((key) => this.trxUpdateRequest(key, update, options)));
|
|
446
|
+
}
|
|
447
|
+
async trxCreate(items, options) {
|
|
448
|
+
return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item, options)));
|
|
449
|
+
}
|
|
450
|
+
trxGetRequest(key, options) {
|
|
451
|
+
return {
|
|
452
|
+
table: this.tableName,
|
|
453
|
+
key: this.extractKey(key),
|
|
454
|
+
...options
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
trxDeleteRequest(key, options) {
|
|
458
|
+
return {
|
|
459
|
+
table: this.tableName,
|
|
460
|
+
type: "DELETE",
|
|
461
|
+
key: this.extractKey(key),
|
|
462
|
+
...options
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
trxConditionRequest(key, condition, options) {
|
|
466
|
+
return {
|
|
467
|
+
table: this.tableName,
|
|
468
|
+
type: "CONDITION",
|
|
469
|
+
key: this.extractKey(key),
|
|
470
|
+
condition,
|
|
471
|
+
...options
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
trxPutRequest(item, options) {
|
|
475
|
+
const itemWithDefaults = {
|
|
476
|
+
...this.defaultPutData,
|
|
477
|
+
...item
|
|
478
|
+
};
|
|
479
|
+
return {
|
|
480
|
+
table: this.tableName,
|
|
481
|
+
type: "PUT",
|
|
482
|
+
item: itemWithDefaults,
|
|
483
|
+
...options
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
trxUpdateRequest(key, update, options) {
|
|
487
|
+
const updateWithDefaults = {
|
|
488
|
+
...this.defaultUpdateData,
|
|
489
|
+
...update
|
|
490
|
+
};
|
|
491
|
+
return {
|
|
492
|
+
table: this.tableName,
|
|
493
|
+
type: "UPDATE",
|
|
494
|
+
key: this.extractKey(key),
|
|
495
|
+
update: updateWithDefaults,
|
|
496
|
+
...options
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
trxCreateRequest(item, options) {
|
|
500
|
+
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
501
|
+
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
502
|
+
if (otherCondition) condition.$and.push(otherCondition);
|
|
503
|
+
const itemWithDefaults = {
|
|
504
|
+
...this.defaultPutData,
|
|
505
|
+
...item
|
|
506
|
+
};
|
|
507
|
+
return {
|
|
508
|
+
table: this.tableName,
|
|
509
|
+
type: "PUT",
|
|
510
|
+
item: itemWithDefaults,
|
|
511
|
+
condition,
|
|
512
|
+
...otherOptions
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
applyTransformsIfNeeded(items, options) {
|
|
516
|
+
if (options?.projection?.length) return items;
|
|
517
|
+
if (options?.gsi) {
|
|
518
|
+
const gsiProj = this.table.def.gsis?.[options.gsi]?.projection;
|
|
519
|
+
if (gsiProj === "KEYS_ONLY" || Array.isArray(gsiProj)) return items;
|
|
520
|
+
}
|
|
521
|
+
return items.map((item) => this.transformItem(item));
|
|
522
|
+
}
|
|
523
|
+
applyTransformIfNeeded(item, options) {
|
|
524
|
+
const [transformedItem] = this.applyTransformsIfNeeded([item], options);
|
|
525
|
+
return transformedItem;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var Repo = class extends AbstractRepo {
|
|
529
|
+
table;
|
|
530
|
+
constructor(db, table) {
|
|
531
|
+
super(db);
|
|
532
|
+
this.table = table;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
//#endregion
|
|
536
|
+
//#region src/db.ts
|
|
537
|
+
var Db = class {
|
|
538
|
+
client;
|
|
539
|
+
config;
|
|
540
|
+
constructor(clientOrConfig, dbConfig) {
|
|
541
|
+
if (clientOrConfig instanceof DynamoDBClient || clientOrConfig instanceof DynamoDB) this.client = Lib.DynamoDBDocumentClient.from(new DynamoDBClient(clientOrConfig));
|
|
542
|
+
else {
|
|
543
|
+
const { endpoint, ...clientConfig } = clientOrConfig;
|
|
544
|
+
this.client = Lib.DynamoDBDocumentClient.from(new DynamoDBClient({
|
|
545
|
+
endpoint: endpoint || void 0,
|
|
546
|
+
...clientConfig
|
|
547
|
+
}));
|
|
548
|
+
}
|
|
549
|
+
this.config = dbConfig;
|
|
550
|
+
}
|
|
551
|
+
createRepo(table) {
|
|
552
|
+
return new Repo(this, table);
|
|
553
|
+
}
|
|
554
|
+
async createTable(table) {
|
|
555
|
+
await this.client.send(new CreateTableCommand(extractTableDesc(table)));
|
|
556
|
+
}
|
|
557
|
+
async deleteTable(tableName) {
|
|
558
|
+
await this.client.send(new DeleteTableCommand({ TableName: tableName }));
|
|
559
|
+
}
|
|
560
|
+
async listTables(data) {
|
|
561
|
+
const tables = [];
|
|
562
|
+
let lastEvaluatedTableName;
|
|
563
|
+
do {
|
|
564
|
+
const output = await this.client.send(new ListTablesCommand({
|
|
565
|
+
Limit: data?.limit,
|
|
566
|
+
ExclusiveStartTableName: lastEvaluatedTableName
|
|
567
|
+
}));
|
|
568
|
+
tables.push(...output.TableNames ?? []);
|
|
569
|
+
lastEvaluatedTableName = output.LastEvaluatedTableName;
|
|
570
|
+
} while (lastEvaluatedTableName);
|
|
571
|
+
return tables;
|
|
572
|
+
}
|
|
573
|
+
async get(data) {
|
|
574
|
+
const exp = new ExpressionBuilder();
|
|
575
|
+
const input = new Lib.GetCommand({
|
|
576
|
+
TableName: data.table,
|
|
577
|
+
Key: data.key,
|
|
578
|
+
ConsistentRead: data.consistent,
|
|
579
|
+
ProjectionExpression: exp.projection(data.projection),
|
|
580
|
+
ExpressionAttributeNames: exp.attributeNames
|
|
581
|
+
});
|
|
582
|
+
const output = await this.client.send(input);
|
|
583
|
+
if (output.Item && data.condition) {
|
|
584
|
+
if (!sift(data.condition)(output.Item)) return;
|
|
585
|
+
}
|
|
586
|
+
return output.Item;
|
|
587
|
+
}
|
|
588
|
+
async getOrThrow(data) {
|
|
589
|
+
const item = await this.get(data);
|
|
590
|
+
if (!item) throw new Error(`Item not found in "${data.table}" table.`);
|
|
591
|
+
return item;
|
|
592
|
+
}
|
|
593
|
+
async put(data) {
|
|
594
|
+
const exp = new ExpressionBuilder();
|
|
595
|
+
const item = removeUndefined(data.item);
|
|
596
|
+
const input = new Lib.PutCommand({
|
|
597
|
+
TableName: data.table,
|
|
598
|
+
Item: item,
|
|
599
|
+
ReturnValues: data.returnOld ? "ALL_OLD" : "NONE",
|
|
600
|
+
ReturnValuesOnConditionCheckFailure: "ALL_OLD",
|
|
601
|
+
ConditionExpression: exp.condition(data.condition),
|
|
602
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
603
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
604
|
+
});
|
|
605
|
+
const output = await this.client.send(input);
|
|
606
|
+
return data.returnOld ? output.Attributes : item;
|
|
607
|
+
}
|
|
608
|
+
async update(data) {
|
|
609
|
+
const exp = new ExpressionBuilder();
|
|
610
|
+
const condition = { $and: Object.keys(data.key).map((field) => ({ [field]: { $exists: true } })) };
|
|
611
|
+
if (data.condition) condition.$and.push(data.condition);
|
|
612
|
+
const input = new Lib.UpdateCommand({
|
|
613
|
+
TableName: data.table,
|
|
614
|
+
Key: data.key,
|
|
615
|
+
ReturnValues: "ALL_NEW",
|
|
616
|
+
ReturnValuesOnConditionCheckFailure: "ALL_OLD",
|
|
617
|
+
UpdateExpression: exp.update(data.update),
|
|
618
|
+
ConditionExpression: exp.condition(condition),
|
|
619
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
620
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
621
|
+
});
|
|
622
|
+
return (await this.client.send(input)).Attributes;
|
|
623
|
+
}
|
|
624
|
+
async delete(data) {
|
|
625
|
+
const exp = new ExpressionBuilder();
|
|
626
|
+
const input = new Lib.DeleteCommand({
|
|
627
|
+
TableName: data.table,
|
|
628
|
+
Key: data.key,
|
|
629
|
+
ReturnValues: "ALL_OLD",
|
|
630
|
+
ConditionExpression: exp.condition(data.condition),
|
|
631
|
+
ReturnValuesOnConditionCheckFailure: "ALL_OLD",
|
|
632
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
633
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
634
|
+
});
|
|
635
|
+
return (await this.client.send(input)).Attributes;
|
|
636
|
+
}
|
|
637
|
+
async deleteOrThrow(data) {
|
|
638
|
+
const item = await this.delete(data);
|
|
639
|
+
if (!item) throw new Error(`Item not found in "${data.table}" table.`);
|
|
640
|
+
return item;
|
|
641
|
+
}
|
|
642
|
+
async *queryPaged(data) {
|
|
643
|
+
const exp = new ExpressionBuilder();
|
|
644
|
+
let lastEvaluatedKey = data.startKey;
|
|
645
|
+
do {
|
|
646
|
+
const input = new Lib.QueryCommand({
|
|
647
|
+
TableName: data.table,
|
|
648
|
+
IndexName: data.index,
|
|
649
|
+
ExclusiveStartKey: lastEvaluatedKey,
|
|
650
|
+
Limit: data.limit,
|
|
651
|
+
ScanIndexForward: data.sort !== "DESC",
|
|
652
|
+
ConsistentRead: data.consistent,
|
|
653
|
+
KeyConditionExpression: exp.condition(data.query),
|
|
654
|
+
ProjectionExpression: exp.projection(data.projection),
|
|
655
|
+
FilterExpression: exp.condition(data.filter),
|
|
656
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
657
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
658
|
+
});
|
|
659
|
+
const output = await this.client.send(input);
|
|
660
|
+
if (output.Items?.length) yield output.Items;
|
|
661
|
+
lastEvaluatedKey = output.LastEvaluatedKey;
|
|
662
|
+
} while (lastEvaluatedKey);
|
|
663
|
+
}
|
|
664
|
+
async query(data) {
|
|
665
|
+
const items = [];
|
|
666
|
+
for await (const page of this.queryPaged(data)) items.push(...page);
|
|
667
|
+
return items;
|
|
668
|
+
}
|
|
669
|
+
async *scanPaged(data) {
|
|
670
|
+
const exp = new ExpressionBuilder();
|
|
671
|
+
const totalSegments = data.parallel ?? 1;
|
|
672
|
+
const segments = [...Array(totalSegments).keys()];
|
|
673
|
+
const lastEvaluatedKeys = segments.map(() => data.startKey);
|
|
674
|
+
do {
|
|
675
|
+
const items = (await Promise.all(segments.map(async (segment) => {
|
|
676
|
+
if (lastEvaluatedKeys[segment] === null) return [];
|
|
677
|
+
const input = new Lib.ScanCommand({
|
|
678
|
+
ExclusiveStartKey: lastEvaluatedKeys[segment],
|
|
679
|
+
Segment: segment,
|
|
680
|
+
TotalSegments: totalSegments,
|
|
681
|
+
TableName: data.table,
|
|
682
|
+
IndexName: data.index,
|
|
683
|
+
Limit: data.limit,
|
|
684
|
+
ConsistentRead: data.consistent,
|
|
685
|
+
ProjectionExpression: exp.projection(data.projection),
|
|
686
|
+
FilterExpression: exp.condition(data.filter),
|
|
687
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
688
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
689
|
+
});
|
|
690
|
+
const output = await this.client.send(input);
|
|
691
|
+
lastEvaluatedKeys[segment] = output.LastEvaluatedKey ?? null;
|
|
692
|
+
return output.Items ?? [];
|
|
693
|
+
}))).flat();
|
|
694
|
+
if (items.length) yield items;
|
|
695
|
+
} while (lastEvaluatedKeys.some((key) => key !== null));
|
|
696
|
+
}
|
|
697
|
+
async scan(data) {
|
|
698
|
+
const items = [];
|
|
699
|
+
for await (const page of this.scanPaged(data)) items.push(...page);
|
|
700
|
+
return items;
|
|
701
|
+
}
|
|
702
|
+
async exists(data) {
|
|
703
|
+
const { query, ...otherOptions } = data;
|
|
704
|
+
const sharedOptions = {
|
|
705
|
+
...otherOptions,
|
|
706
|
+
limit: data.filter ? void 0 : 1
|
|
707
|
+
};
|
|
708
|
+
const pagedItems = query ? this.queryPaged({
|
|
709
|
+
query,
|
|
710
|
+
...sharedOptions
|
|
711
|
+
}) : this.scanPaged(sharedOptions);
|
|
712
|
+
for await (const items of pagedItems) if (items.length) return true;
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
async batchGet(data) {
|
|
716
|
+
const result = { items: {} };
|
|
717
|
+
const tableData = Object.fromEntries(Object.entries(data).map(([table, request]) => {
|
|
718
|
+
const exp = new ExpressionBuilder();
|
|
719
|
+
const sifter = request?.condition ? sift(request?.condition) : void 0;
|
|
720
|
+
result.items[table] = [];
|
|
721
|
+
return [table, {
|
|
722
|
+
request: {
|
|
723
|
+
ConsistentRead: request?.consistent,
|
|
724
|
+
ProjectionExpression: exp.projection(request?.projection),
|
|
725
|
+
ExpressionAttributeNames: exp.attributeNames
|
|
726
|
+
},
|
|
727
|
+
keyNames: Object.keys(request.keys.at(0) ?? {}),
|
|
728
|
+
itemIndexMap: /* @__PURE__ */ new Map(),
|
|
729
|
+
sifter
|
|
730
|
+
}];
|
|
731
|
+
}));
|
|
732
|
+
const flattenedBatches = Object.entries(data).flatMap(([table, request]) => {
|
|
733
|
+
return request.keys.map((key, i) => {
|
|
734
|
+
const itemIndexKey = tableData[table]?.keyNames.map((keyName) => key[keyName]).join("|") ?? "";
|
|
735
|
+
tableData[table]?.itemIndexMap.set(itemIndexKey, i);
|
|
736
|
+
return [table, key];
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
let batchSize = 100;
|
|
740
|
+
let retryCount = 0;
|
|
741
|
+
while (flattenedBatches.length && retryCount < 5) {
|
|
742
|
+
const batch = flattenedBatches.splice(0, batchSize || 1);
|
|
743
|
+
const request = {};
|
|
744
|
+
for (const [table, key] of batch) {
|
|
745
|
+
if (!request[table]) request[table] = {
|
|
746
|
+
...tableData[table]?.request,
|
|
747
|
+
Keys: []
|
|
748
|
+
};
|
|
749
|
+
request[table].Keys?.push(key);
|
|
750
|
+
}
|
|
751
|
+
const input = new Lib.BatchGetCommand({ RequestItems: request });
|
|
752
|
+
const output = await this.client.send(input);
|
|
753
|
+
for (const [table, items] of Object.entries(output.Responses ?? {})) {
|
|
754
|
+
if (!result.items[table]) continue;
|
|
755
|
+
for (const item of items) {
|
|
756
|
+
if (tableData[table]?.sifter && !tableData[table].sifter(item)) continue;
|
|
757
|
+
result.items[table].push(item);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
let unprocessedKeyCount = 0;
|
|
761
|
+
for (const [table, request] of Object.entries(output.UnprocessedKeys ?? {})) {
|
|
762
|
+
const unprocessed = (request.Keys ?? []).map((key) => [table, key]);
|
|
763
|
+
flattenedBatches.push(...unprocessed);
|
|
764
|
+
unprocessedKeyCount += unprocessed.length;
|
|
765
|
+
}
|
|
766
|
+
if (!unprocessedKeyCount) {
|
|
767
|
+
retryCount = 0;
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
batchSize -= Math.floor(unprocessedKeyCount / 2);
|
|
771
|
+
retryCount++;
|
|
772
|
+
}
|
|
773
|
+
if (flattenedBatches.length) {
|
|
774
|
+
result.unprocessed = {};
|
|
775
|
+
for (const [table, key] of flattenedBatches) {
|
|
776
|
+
if (!result.unprocessed[table]) result.unprocessed[table] = {
|
|
777
|
+
...tableData[table]?.request,
|
|
778
|
+
keys: []
|
|
779
|
+
};
|
|
780
|
+
result.unprocessed[table].keys.push(key);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
for (const [table, items] of Object.entries(result.items)) {
|
|
784
|
+
const itemIndexMap = tableData[table]?.itemIndexMap;
|
|
785
|
+
const keyNames = tableData[table]?.keyNames;
|
|
786
|
+
if (!itemIndexMap || !keyNames) continue;
|
|
787
|
+
items.sort((item1, item2) => {
|
|
788
|
+
const item1IndexKey = keyNames.map((keyName) => item1[keyName]).join("|");
|
|
789
|
+
const item2IndexKey = keyNames.map((keyName) => item2[keyName]).join("|");
|
|
790
|
+
return (itemIndexMap.get(item1IndexKey) ?? 0) - (itemIndexMap.get(item2IndexKey) ?? 0);
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
async batchGetOrThrow(data) {
|
|
796
|
+
const { items, unprocessed } = await this.batchGet(data);
|
|
797
|
+
for (const table of Object.keys(data)) {
|
|
798
|
+
if (unprocessed?.[table]) throw new Error(`One or more batch get requests were not processed in "${table}" table.`);
|
|
799
|
+
if (items[table]?.length !== data[table]?.keys?.length) throw new Error(`One or more items were not found in "${table}" table.`);
|
|
800
|
+
}
|
|
801
|
+
return items;
|
|
802
|
+
}
|
|
803
|
+
async batchWrite(data) {
|
|
804
|
+
const result = { items: {} };
|
|
805
|
+
const flattenedBatches = Object.keys(data).flatMap((table) => {
|
|
806
|
+
return data[table]?.map((r) => {
|
|
807
|
+
if (r.type === "DELETE") return [table, { DeleteRequest: { Key: r.key } }];
|
|
808
|
+
else return [table, { PutRequest: { Item: r.item } }];
|
|
809
|
+
}) ?? [];
|
|
810
|
+
});
|
|
811
|
+
let batchSize = 25;
|
|
812
|
+
let retryCount = 0;
|
|
813
|
+
while (flattenedBatches.length && retryCount < 5) {
|
|
814
|
+
const batch = flattenedBatches.splice(0, batchSize || 1);
|
|
815
|
+
const request = {};
|
|
816
|
+
for (const [table, req] of batch) {
|
|
817
|
+
if (!request[table]) request[table] = [];
|
|
818
|
+
request[table].push(req);
|
|
819
|
+
}
|
|
820
|
+
const input = new Lib.BatchWriteCommand({ RequestItems: request });
|
|
821
|
+
const output = await this.client.send(input);
|
|
822
|
+
let unprocessedKeyCount = 0;
|
|
823
|
+
for (const [table, requests] of Object.entries(output.UnprocessedItems ?? {})) for (const request of requests) {
|
|
824
|
+
flattenedBatches.push([table, request]);
|
|
825
|
+
unprocessedKeyCount++;
|
|
826
|
+
}
|
|
827
|
+
if (!unprocessedKeyCount) {
|
|
828
|
+
retryCount = 0;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
batchSize -= Math.floor(unprocessedKeyCount / 2);
|
|
832
|
+
retryCount++;
|
|
833
|
+
}
|
|
834
|
+
if (flattenedBatches.length) {
|
|
835
|
+
result.unprocessed = {};
|
|
836
|
+
for (const [table, request] of flattenedBatches) {
|
|
837
|
+
if (!result.unprocessed[table]) result.unprocessed[table] = [];
|
|
838
|
+
if ("DeleteRequest" in request && request.DeleteRequest?.Key) result.unprocessed[table].push({
|
|
839
|
+
type: "DELETE",
|
|
840
|
+
key: request.DeleteRequest.Key
|
|
841
|
+
});
|
|
842
|
+
else if ("PutRequest" in request && request.PutRequest?.Item) result.unprocessed[table].push({
|
|
843
|
+
type: "PUT",
|
|
844
|
+
item: request.PutRequest.Item
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return result;
|
|
849
|
+
}
|
|
850
|
+
async trxGet(...requests) {
|
|
851
|
+
const trxItems = requests.map((request) => {
|
|
852
|
+
const exp = new ExpressionBuilder();
|
|
853
|
+
return { Get: {
|
|
854
|
+
Key: request.key,
|
|
855
|
+
TableName: request.table,
|
|
856
|
+
ProjectionExpression: exp.projection(request?.projection),
|
|
857
|
+
ExpressionAttributeNames: exp.attributeNames
|
|
858
|
+
} };
|
|
859
|
+
});
|
|
860
|
+
const input = new Lib.TransactGetCommand({ TransactItems: trxItems });
|
|
861
|
+
return (await this.client.send(input)).Responses?.map((response, i) => {
|
|
862
|
+
if (requests[i]?.condition && !sift(requests[i].condition)(response.Item)) return;
|
|
863
|
+
return response.Item;
|
|
864
|
+
}) ?? [];
|
|
865
|
+
}
|
|
866
|
+
async trxGetOrThrow(...requests) {
|
|
867
|
+
const items = await this.trxGet(...requests);
|
|
868
|
+
for (let i = 0; i < items.length; i++) if (!items[i]) throw new Error(`One or more items were not found in "${requests[i]?.table}" table.`);
|
|
869
|
+
return items;
|
|
870
|
+
}
|
|
871
|
+
async trxWrite(...requests) {
|
|
872
|
+
const trxItems = requests.map((request) => {
|
|
873
|
+
const exp = new ExpressionBuilder();
|
|
874
|
+
if (request.type === "CONDITION" || request.type === "DELETE") return { [request.type === "CONDITION" ? "ConditionCheck" : "Delete"]: {
|
|
875
|
+
TableName: request.table,
|
|
876
|
+
Key: request.key,
|
|
877
|
+
ConditionExpression: exp.condition(request.condition),
|
|
878
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
879
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
880
|
+
} };
|
|
881
|
+
if (request.type === "PUT") return { Put: {
|
|
882
|
+
TableName: request.table,
|
|
883
|
+
Item: removeUndefined(request.item),
|
|
884
|
+
ConditionExpression: exp.condition(request.condition),
|
|
885
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
886
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
887
|
+
} };
|
|
888
|
+
return { Update: {
|
|
889
|
+
Key: request.key,
|
|
890
|
+
TableName: request.table,
|
|
891
|
+
UpdateExpression: exp.update(request.update),
|
|
892
|
+
ConditionExpression: exp.condition(request.condition),
|
|
893
|
+
ExpressionAttributeNames: exp.attributeNames,
|
|
894
|
+
ExpressionAttributeValues: exp.attributeValues
|
|
895
|
+
} };
|
|
896
|
+
});
|
|
897
|
+
const input = new Lib.TransactWriteCommand({ TransactItems: trxItems });
|
|
898
|
+
await this.client.send(input);
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
//#endregion
|
|
902
|
+
//#region src/table.ts
|
|
903
|
+
var Table = class {
|
|
904
|
+
schema;
|
|
905
|
+
def;
|
|
906
|
+
constructor(schema, def) {
|
|
907
|
+
this.schema = schema;
|
|
908
|
+
this.def = def;
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
//#endregion
|
|
912
|
+
export { AbstractRepo, Db, Repo, Table, chunk, extractTableDesc, removeUndefined, resolveAttrType };
|
|
913
|
+
|
|
914
|
+
//# sourceMappingURL=index.mjs.map
|