@stacksjs/database 0.70.257 → 0.70.259
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/dist/auth-tables.js +18 -137
- package/dist/column.js +1 -26
- package/dist/custom/audits.js +20 -54
- package/dist/custom/errors.js +16 -46
- package/dist/custom/index.js +1 -3
- package/dist/custom/jobs.js +13 -137
- package/dist/database.js +1 -181
- package/dist/datetime-columns.js +8 -85
- package/dist/ddl-constraints.js +7 -111
- package/dist/defaults.js +1 -48
- package/dist/dialect.js +1 -79
- package/dist/driver-config.js +1 -172
- package/dist/drivers/defaults/index.js +1 -1
- package/dist/drivers/defaults/traits.js +1 -29
- package/dist/drivers/dynamodb.js +1 -607
- package/dist/drivers/helpers.js +1 -206
- package/dist/drivers/index.js +1 -9
- package/dist/drivers/mysql.js +58 -299
- package/dist/drivers/postgres.js +78 -368
- package/dist/drivers/sqlite.js +61 -379
- package/dist/ensure-database.js +1 -145
- package/dist/fk-audit.js +3 -187
- package/dist/index.js +1 -64
- package/dist/managed-columns.js +1 -59
- package/dist/migration-dialect.js +4 -107
- package/dist/migration-ledger.js +1 -382
- package/dist/migration-lock.js +1 -143
- package/dist/migrations.js +15 -1118
- package/dist/model-sources.js +1 -76
- package/dist/notification-tables.js +4 -49
- package/dist/query-logger.js +2 -241
- package/dist/query-parser.js +1 -93
- package/dist/rbac-tables.js +6 -61
- package/dist/relation-columns.js +1 -66
- package/dist/replicas.js +1 -74
- package/dist/safe-migrations.js +2 -52
- package/dist/schema.js +1 -10
- package/dist/seeder.js +1 -457
- package/dist/sql-helpers.js +1 -50
- package/dist/table.js +1 -26
- package/dist/tools/setup.js +1 -6
- package/dist/trait-tables.js +8 -153
- package/dist/transaction-context.js +1 -62
- package/dist/types.js +1 -98
- package/dist/unique-audit.js +3 -155
- package/dist/utils.js +1 -285
- package/dist/uuid-columns.js +1 -68
- package/dist/validators.js +1 -122
- package/dist/vschema.js +2 -121
- package/package.json +20 -13
package/dist/drivers/dynamodb.js
CHANGED
|
@@ -1,607 +1 @@
|
|
|
1
|
-
function toDynamoValue(value) {
|
|
2
|
-
if (value === null || value === void 0)
|
|
3
|
-
return { NULL: !0 };
|
|
4
|
-
if (typeof value === "string")
|
|
5
|
-
return { S: value };
|
|
6
|
-
if (typeof value === "number")
|
|
7
|
-
return { N: String(value) };
|
|
8
|
-
if (typeof value === "boolean")
|
|
9
|
-
return { BOOL: value };
|
|
10
|
-
if (Array.isArray(value))
|
|
11
|
-
return { L: value.map((v) => toDynamoValue(v)) };
|
|
12
|
-
if (typeof value === "object") {
|
|
13
|
-
const m = {};
|
|
14
|
-
for (const [k, v] of Object.entries(value))
|
|
15
|
-
m[k] = toDynamoValue(v);
|
|
16
|
-
return { M: m };
|
|
17
|
-
}
|
|
18
|
-
return { S: String(value) };
|
|
19
|
-
}
|
|
20
|
-
function fromDynamoValue(attr) {
|
|
21
|
-
if ("NULL" in attr && attr.NULL)
|
|
22
|
-
return null;
|
|
23
|
-
if ("S" in attr && attr.S !== void 0)
|
|
24
|
-
return attr.S;
|
|
25
|
-
if ("N" in attr && attr.N !== void 0)
|
|
26
|
-
return Number(attr.N);
|
|
27
|
-
if ("BOOL" in attr && attr.BOOL !== void 0)
|
|
28
|
-
return attr.BOOL;
|
|
29
|
-
if ("L" in attr && attr.L !== void 0)
|
|
30
|
-
return attr.L.map((v) => fromDynamoValue(v));
|
|
31
|
-
if ("M" in attr && attr.M !== void 0) {
|
|
32
|
-
const obj = {};
|
|
33
|
-
for (const [k, v] of Object.entries(attr.M))
|
|
34
|
-
obj[k] = fromDynamoValue(v);
|
|
35
|
-
return obj;
|
|
36
|
-
}
|
|
37
|
-
if ("SS" in attr && attr.SS !== void 0)
|
|
38
|
-
return attr.SS;
|
|
39
|
-
if ("NS" in attr && attr.NS !== void 0)
|
|
40
|
-
return attr.NS.map((n) => Number(n));
|
|
41
|
-
if ("B" in attr && attr.B !== void 0)
|
|
42
|
-
return attr.B;
|
|
43
|
-
if ("BS" in attr && attr.BS !== void 0)
|
|
44
|
-
return attr.BS;
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
function marshall(obj) {
|
|
48
|
-
const result = {};
|
|
49
|
-
for (const [key, value] of Object.entries(obj))
|
|
50
|
-
result[key] = toDynamoValue(value);
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
function marshallValue(value) {
|
|
54
|
-
const marshalled = marshall({ value }).value;
|
|
55
|
-
if (!marshalled)
|
|
56
|
-
throw Error("Failed to marshal DynamoDB attribute value");
|
|
57
|
-
return marshalled;
|
|
58
|
-
}
|
|
59
|
-
function unmarshall(obj) {
|
|
60
|
-
const result = {};
|
|
61
|
-
for (const [key, value] of Object.entries(obj))
|
|
62
|
-
result[key] = fromDynamoValue(value);
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class EntityQueryBuilder {
|
|
67
|
-
client;
|
|
68
|
-
tableName;
|
|
69
|
-
pkAttribute;
|
|
70
|
-
skAttribute;
|
|
71
|
-
entityTypeAttr;
|
|
72
|
-
delimiter;
|
|
73
|
-
_entityType;
|
|
74
|
-
_pkValue;
|
|
75
|
-
_skCondition;
|
|
76
|
-
_indexName;
|
|
77
|
-
_projectionAttrs = [];
|
|
78
|
-
_filterConditions = [];
|
|
79
|
-
_limitValue;
|
|
80
|
-
_scanForward = !0;
|
|
81
|
-
_consistentRead = !1;
|
|
82
|
-
_startKey;
|
|
83
|
-
constructor(client, tableName, config) {
|
|
84
|
-
this.client = client;
|
|
85
|
-
this.tableName = tableName;
|
|
86
|
-
this.pkAttribute = config.pkAttribute;
|
|
87
|
-
this.skAttribute = config.skAttribute;
|
|
88
|
-
this.entityTypeAttr = config.entityTypeAttribute;
|
|
89
|
-
this.delimiter = config.keyDelimiter;
|
|
90
|
-
}
|
|
91
|
-
entity(entityType) {
|
|
92
|
-
this._entityType = entityType;
|
|
93
|
-
return this;
|
|
94
|
-
}
|
|
95
|
-
pk(value) {
|
|
96
|
-
this._pkValue = value;
|
|
97
|
-
return this;
|
|
98
|
-
}
|
|
99
|
-
get sk() {
|
|
100
|
-
const self = this;
|
|
101
|
-
return {
|
|
102
|
-
equals(value) {
|
|
103
|
-
self._skCondition = { type: "eq", value };
|
|
104
|
-
return self;
|
|
105
|
-
},
|
|
106
|
-
beginsWith(prefix) {
|
|
107
|
-
self._skCondition = { type: "begins_with", value: prefix };
|
|
108
|
-
return self;
|
|
109
|
-
},
|
|
110
|
-
between(start, end) {
|
|
111
|
-
self._skCondition = { type: "between", value: start, value2: end };
|
|
112
|
-
return self;
|
|
113
|
-
},
|
|
114
|
-
lt(value) {
|
|
115
|
-
self._skCondition = { type: "lt", value };
|
|
116
|
-
return self;
|
|
117
|
-
},
|
|
118
|
-
lte(value) {
|
|
119
|
-
self._skCondition = { type: "lte", value };
|
|
120
|
-
return self;
|
|
121
|
-
},
|
|
122
|
-
gt(value) {
|
|
123
|
-
self._skCondition = { type: "gt", value };
|
|
124
|
-
return self;
|
|
125
|
-
},
|
|
126
|
-
gte(value) {
|
|
127
|
-
self._skCondition = { type: "gte", value };
|
|
128
|
-
return self;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
index(indexName) {
|
|
133
|
-
this._indexName = indexName;
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
project(...attributes) {
|
|
137
|
-
this._projectionAttrs.push(...attributes);
|
|
138
|
-
return this;
|
|
139
|
-
}
|
|
140
|
-
filter(attribute, operator, value) {
|
|
141
|
-
this._filterConditions.push({ attribute, operator, value });
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
where(attribute, value) {
|
|
145
|
-
return this.filter(attribute, "=", value);
|
|
146
|
-
}
|
|
147
|
-
whereIn(attribute, values) {
|
|
148
|
-
this._filterConditions.push({ attribute, operator: "IN", values });
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
limit(count) {
|
|
152
|
-
this._limitValue = count;
|
|
153
|
-
return this;
|
|
154
|
-
}
|
|
155
|
-
asc() {
|
|
156
|
-
this._scanForward = !0;
|
|
157
|
-
return this;
|
|
158
|
-
}
|
|
159
|
-
desc() {
|
|
160
|
-
this._scanForward = !1;
|
|
161
|
-
return this;
|
|
162
|
-
}
|
|
163
|
-
consistent() {
|
|
164
|
-
this._consistentRead = !0;
|
|
165
|
-
return this;
|
|
166
|
-
}
|
|
167
|
-
startFrom(key) {
|
|
168
|
-
this._startKey = key;
|
|
169
|
-
return this;
|
|
170
|
-
}
|
|
171
|
-
toRequest() {
|
|
172
|
-
const request = {
|
|
173
|
-
TableName: this.tableName
|
|
174
|
-
};
|
|
175
|
-
if (this._indexName)
|
|
176
|
-
request.IndexName = this._indexName;
|
|
177
|
-
const keyConditions = [], exprNames = {}, exprValues = {};
|
|
178
|
-
let idx = 0;
|
|
179
|
-
if (this._pkValue) {
|
|
180
|
-
const nameKey = `#pk${idx}`, valueKey = `:pk${idx}`;
|
|
181
|
-
exprNames[nameKey] = this.pkAttribute;
|
|
182
|
-
exprValues[valueKey] = { S: this._pkValue };
|
|
183
|
-
keyConditions.push(`${nameKey} = ${valueKey}`);
|
|
184
|
-
idx++;
|
|
185
|
-
}
|
|
186
|
-
if (this._skCondition) {
|
|
187
|
-
const nameKey = `#sk${idx}`;
|
|
188
|
-
exprNames[nameKey] = this.skAttribute;
|
|
189
|
-
switch (this._skCondition.type) {
|
|
190
|
-
case "eq": {
|
|
191
|
-
const valueKey = `:sk${idx}`;
|
|
192
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
193
|
-
keyConditions.push(`${nameKey} = ${valueKey}`);
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
case "begins_with": {
|
|
197
|
-
const valueKey = `:sk${idx}`;
|
|
198
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
199
|
-
keyConditions.push(`begins_with(${nameKey}, ${valueKey})`);
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
case "between": {
|
|
203
|
-
if (this._skCondition.value2 === void 0)
|
|
204
|
-
throw Error("DynamoDB between condition requires a second value");
|
|
205
|
-
const valueKey1 = `:sk${idx}a`, valueKey2 = `:sk${idx}b`;
|
|
206
|
-
exprValues[valueKey1] = { S: this._skCondition.value };
|
|
207
|
-
exprValues[valueKey2] = { S: this._skCondition.value2 };
|
|
208
|
-
keyConditions.push(`${nameKey} BETWEEN ${valueKey1} AND ${valueKey2}`);
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
case "lt": {
|
|
212
|
-
const valueKey = `:sk${idx}`;
|
|
213
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
214
|
-
keyConditions.push(`${nameKey} < ${valueKey}`);
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
case "lte": {
|
|
218
|
-
const valueKey = `:sk${idx}`;
|
|
219
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
220
|
-
keyConditions.push(`${nameKey} <= ${valueKey}`);
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
case "gt": {
|
|
224
|
-
const valueKey = `:sk${idx}`;
|
|
225
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
226
|
-
keyConditions.push(`${nameKey} > ${valueKey}`);
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
case "gte": {
|
|
230
|
-
const valueKey = `:sk${idx}`;
|
|
231
|
-
exprValues[valueKey] = { S: this._skCondition.value };
|
|
232
|
-
keyConditions.push(`${nameKey} >= ${valueKey}`);
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
idx++;
|
|
237
|
-
}
|
|
238
|
-
if (keyConditions.length > 0)
|
|
239
|
-
request.KeyConditionExpression = keyConditions.join(" AND ");
|
|
240
|
-
if (this._filterConditions.length > 0) {
|
|
241
|
-
const filterParts = [];
|
|
242
|
-
for (const cond of this._filterConditions) {
|
|
243
|
-
const nameKey = `#flt${idx}`;
|
|
244
|
-
exprNames[nameKey] = cond.attribute;
|
|
245
|
-
if (cond.operator === "IN" && cond.values) {
|
|
246
|
-
const valueKeys = cond.values.map((_, i) => `:flt${idx}_${i}`);
|
|
247
|
-
cond.values.forEach((val, i) => {
|
|
248
|
-
exprValues[`:flt${idx}_${i}`] = marshallValue(val);
|
|
249
|
-
});
|
|
250
|
-
filterParts.push(`${nameKey} IN (${valueKeys.join(", ")})`);
|
|
251
|
-
} else {
|
|
252
|
-
const valueKey = `:flt${idx}`;
|
|
253
|
-
exprValues[valueKey] = marshallValue(cond.value);
|
|
254
|
-
filterParts.push(`${nameKey} ${cond.operator} ${valueKey}`);
|
|
255
|
-
}
|
|
256
|
-
idx++;
|
|
257
|
-
}
|
|
258
|
-
request.FilterExpression = filterParts.join(" AND ");
|
|
259
|
-
}
|
|
260
|
-
if (this._projectionAttrs.length > 0) {
|
|
261
|
-
const projParts = [];
|
|
262
|
-
for (const attr of this._projectionAttrs) {
|
|
263
|
-
const nameKey = `#proj${idx}`;
|
|
264
|
-
exprNames[nameKey] = attr;
|
|
265
|
-
projParts.push(nameKey);
|
|
266
|
-
idx++;
|
|
267
|
-
}
|
|
268
|
-
request.ProjectionExpression = projParts.join(", ");
|
|
269
|
-
}
|
|
270
|
-
if (Object.keys(exprNames).length > 0)
|
|
271
|
-
request.ExpressionAttributeNames = exprNames;
|
|
272
|
-
if (Object.keys(exprValues).length > 0)
|
|
273
|
-
request.ExpressionAttributeValues = exprValues;
|
|
274
|
-
if (this._limitValue !== void 0)
|
|
275
|
-
request.Limit = this._limitValue;
|
|
276
|
-
request.ScanIndexForward = this._scanForward;
|
|
277
|
-
if (this._consistentRead)
|
|
278
|
-
request.ConsistentRead = !0;
|
|
279
|
-
if (this._startKey)
|
|
280
|
-
request.ExclusiveStartKey = marshall(this._startKey);
|
|
281
|
-
return request;
|
|
282
|
-
}
|
|
283
|
-
async get() {
|
|
284
|
-
if (!this.client)
|
|
285
|
-
throw Error("DynamoDB client not configured. Call dynamo.connection() first.");
|
|
286
|
-
const request = this.toRequest();
|
|
287
|
-
return ((this._pkValue !== void 0 ? await this.client.query(request) : await this.client.scan(request)).Items ?? []).map((item) => unmarshall(item));
|
|
288
|
-
}
|
|
289
|
-
async first() {
|
|
290
|
-
this._limitValue = 1;
|
|
291
|
-
return (await this.get())[0];
|
|
292
|
-
}
|
|
293
|
-
async getAll() {
|
|
294
|
-
const allItems = [];
|
|
295
|
-
let lastKey;
|
|
296
|
-
do {
|
|
297
|
-
if (lastKey)
|
|
298
|
-
this._startKey = lastKey;
|
|
299
|
-
const request = this.toRequest(), client = this.client;
|
|
300
|
-
if (!client)
|
|
301
|
-
throw Error("DynamoDB client is not configured");
|
|
302
|
-
const response = this._pkValue !== void 0 ? await client.query(request) : await client.scan(request), items = (response.Items ?? []).map((item) => unmarshall(item));
|
|
303
|
-
allItems.push(...items);
|
|
304
|
-
lastKey = response.LastEvaluatedKey ? unmarshall(response.LastEvaluatedKey) : void 0;
|
|
305
|
-
} while (lastKey);
|
|
306
|
-
return allItems;
|
|
307
|
-
}
|
|
308
|
-
async count() {
|
|
309
|
-
if (!this.client)
|
|
310
|
-
throw Error("DynamoDB client not configured. Call dynamo.connection() first.");
|
|
311
|
-
const request = this.toRequest();
|
|
312
|
-
request.Select = "COUNT";
|
|
313
|
-
return (this._pkValue !== void 0 ? await this.client.query(request) : await this.client.scan(request)).Count ?? 0;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
class DynamoClient {
|
|
318
|
-
client;
|
|
319
|
-
tableName = "";
|
|
320
|
-
pkAttribute = "pk";
|
|
321
|
-
skAttribute = "sk";
|
|
322
|
-
entityTypeAttr = "_et";
|
|
323
|
-
delimiter = "#";
|
|
324
|
-
entityMappings = new Map;
|
|
325
|
-
_configured = !1;
|
|
326
|
-
connection(config) {
|
|
327
|
-
this.tableName = config.table;
|
|
328
|
-
this.pkAttribute = config.pkAttribute ?? "pk";
|
|
329
|
-
this.skAttribute = config.skAttribute ?? "sk";
|
|
330
|
-
this.entityTypeAttr = config.entityTypeAttribute ?? "_et";
|
|
331
|
-
this.delimiter = config.keyDelimiter ?? "#";
|
|
332
|
-
this._configured = !0;
|
|
333
|
-
return this;
|
|
334
|
-
}
|
|
335
|
-
isConfigured() {
|
|
336
|
-
return this._configured;
|
|
337
|
-
}
|
|
338
|
-
setClient(client) {
|
|
339
|
-
this.client = client;
|
|
340
|
-
return this;
|
|
341
|
-
}
|
|
342
|
-
getClient() {
|
|
343
|
-
return this.client;
|
|
344
|
-
}
|
|
345
|
-
registerEntity(mapping) {
|
|
346
|
-
this.entityMappings.set(mapping.entityType, mapping);
|
|
347
|
-
return this;
|
|
348
|
-
}
|
|
349
|
-
registerModel(model) {
|
|
350
|
-
const name = model.name ?? "Unknown", upperName = name.toUpperCase(), mapping = {
|
|
351
|
-
entityType: name,
|
|
352
|
-
pkPattern: `${upperName}#{id}`,
|
|
353
|
-
skPattern: `${upperName}#{id}`
|
|
354
|
-
};
|
|
355
|
-
this.entityMappings.set(name, mapping);
|
|
356
|
-
return this;
|
|
357
|
-
}
|
|
358
|
-
getEntityMapping(entityType) {
|
|
359
|
-
return this.entityMappings.get(entityType);
|
|
360
|
-
}
|
|
361
|
-
entity(entityType) {
|
|
362
|
-
if (!this._configured)
|
|
363
|
-
throw Error("DynamoDB not configured. Call dynamo.connection() first.");
|
|
364
|
-
return new EntityQueryBuilder(this.client, this.tableName, {
|
|
365
|
-
pkAttribute: this.pkAttribute,
|
|
366
|
-
skAttribute: this.skAttribute,
|
|
367
|
-
entityTypeAttribute: this.entityTypeAttr,
|
|
368
|
-
keyDelimiter: this.delimiter
|
|
369
|
-
}).entity(entityType);
|
|
370
|
-
}
|
|
371
|
-
async batchWrite(operations) {
|
|
372
|
-
if (!this.client)
|
|
373
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
374
|
-
if (!this._configured)
|
|
375
|
-
throw Error("DynamoDB not configured. Call dynamo.connection() first.");
|
|
376
|
-
const requestItems = [];
|
|
377
|
-
for (const op of operations)
|
|
378
|
-
if (op.put) {
|
|
379
|
-
const item = {
|
|
380
|
-
...op.put.item,
|
|
381
|
-
[this.entityTypeAttr]: op.put.entity
|
|
382
|
-
};
|
|
383
|
-
requestItems.push({
|
|
384
|
-
PutRequest: {
|
|
385
|
-
Item: marshall(item)
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
} else if (op.delete)
|
|
389
|
-
requestItems.push({
|
|
390
|
-
DeleteRequest: {
|
|
391
|
-
Key: marshall({
|
|
392
|
-
[this.pkAttribute]: op.delete.pk,
|
|
393
|
-
[this.skAttribute]: op.delete.sk
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
if (requestItems.length > 0)
|
|
398
|
-
await this.client.batchWriteItem({
|
|
399
|
-
RequestItems: {
|
|
400
|
-
[this.tableName]: requestItems
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
async transactWrite(operations) {
|
|
405
|
-
if (!this.client)
|
|
406
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
407
|
-
if (!this._configured)
|
|
408
|
-
throw Error("DynamoDB not configured. Call dynamo.connection() first.");
|
|
409
|
-
const transactItems = [];
|
|
410
|
-
for (const op of operations)
|
|
411
|
-
if (op.put) {
|
|
412
|
-
const item = {
|
|
413
|
-
...op.put.item,
|
|
414
|
-
[this.entityTypeAttr]: op.put.entity
|
|
415
|
-
}, putPayload = {
|
|
416
|
-
TableName: this.tableName,
|
|
417
|
-
Item: marshall(item)
|
|
418
|
-
};
|
|
419
|
-
if (op.put.condition)
|
|
420
|
-
putPayload.ConditionExpression = op.put.condition;
|
|
421
|
-
transactItems.push({ Put: putPayload });
|
|
422
|
-
} else if (op.update) {
|
|
423
|
-
const key = {
|
|
424
|
-
[this.pkAttribute]: op.update.pk
|
|
425
|
-
};
|
|
426
|
-
if (op.update.sk)
|
|
427
|
-
key[this.skAttribute] = op.update.sk;
|
|
428
|
-
const updateParts = [], exprNames = {}, exprValues = {};
|
|
429
|
-
let idx = 0;
|
|
430
|
-
if (op.update.set) {
|
|
431
|
-
const setParts = [];
|
|
432
|
-
for (const [attr, value] of Object.entries(op.update.set)) {
|
|
433
|
-
const nameKey = `#set${idx}`, valueKey = `:set${idx}`;
|
|
434
|
-
exprNames[nameKey] = attr;
|
|
435
|
-
exprValues[valueKey] = marshallValue(value);
|
|
436
|
-
setParts.push(`${nameKey} = ${valueKey}`);
|
|
437
|
-
idx++;
|
|
438
|
-
}
|
|
439
|
-
if (setParts.length > 0)
|
|
440
|
-
updateParts.push(`SET ${setParts.join(", ")}`);
|
|
441
|
-
}
|
|
442
|
-
if (op.update.add) {
|
|
443
|
-
const addParts = [];
|
|
444
|
-
for (const [attr, value] of Object.entries(op.update.add)) {
|
|
445
|
-
const nameKey = `#add${idx}`, valueKey = `:add${idx}`;
|
|
446
|
-
exprNames[nameKey] = attr;
|
|
447
|
-
exprValues[valueKey] = { N: String(value) };
|
|
448
|
-
addParts.push(`${nameKey} ${valueKey}`);
|
|
449
|
-
idx++;
|
|
450
|
-
}
|
|
451
|
-
if (addParts.length > 0)
|
|
452
|
-
updateParts.push(`ADD ${addParts.join(", ")}`);
|
|
453
|
-
}
|
|
454
|
-
if (op.update.remove && op.update.remove.length > 0) {
|
|
455
|
-
const removeParts = [];
|
|
456
|
-
for (const attr of op.update.remove) {
|
|
457
|
-
const nameKey = `#rem${idx}`;
|
|
458
|
-
exprNames[nameKey] = attr;
|
|
459
|
-
removeParts.push(nameKey);
|
|
460
|
-
idx++;
|
|
461
|
-
}
|
|
462
|
-
updateParts.push(`REMOVE ${removeParts.join(", ")}`);
|
|
463
|
-
}
|
|
464
|
-
transactItems.push({
|
|
465
|
-
Update: {
|
|
466
|
-
TableName: this.tableName,
|
|
467
|
-
Key: marshall(key),
|
|
468
|
-
UpdateExpression: updateParts.join(" "),
|
|
469
|
-
ExpressionAttributeNames: exprNames,
|
|
470
|
-
ExpressionAttributeValues: exprValues
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
} else if (op.delete) {
|
|
474
|
-
const deletePayload = {
|
|
475
|
-
TableName: this.tableName,
|
|
476
|
-
Key: marshall({
|
|
477
|
-
[this.pkAttribute]: op.delete.pk,
|
|
478
|
-
[this.skAttribute]: op.delete.sk
|
|
479
|
-
})
|
|
480
|
-
};
|
|
481
|
-
if (op.delete.condition)
|
|
482
|
-
deletePayload.ConditionExpression = op.delete.condition;
|
|
483
|
-
transactItems.push({ Delete: deletePayload });
|
|
484
|
-
} else if (op.conditionCheck)
|
|
485
|
-
transactItems.push({
|
|
486
|
-
ConditionCheck: {
|
|
487
|
-
TableName: this.tableName,
|
|
488
|
-
Key: marshall({
|
|
489
|
-
[this.pkAttribute]: op.conditionCheck.pk,
|
|
490
|
-
[this.skAttribute]: op.conditionCheck.sk
|
|
491
|
-
}),
|
|
492
|
-
ConditionExpression: op.conditionCheck.condition
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
if (transactItems.length > 0)
|
|
496
|
-
await this.client.transactWriteItems({
|
|
497
|
-
TransactItems: transactItems
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
async put(entity, item) {
|
|
501
|
-
if (!this.client)
|
|
502
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
503
|
-
const fullItem = {
|
|
504
|
-
...item,
|
|
505
|
-
[this.entityTypeAttr]: entity
|
|
506
|
-
};
|
|
507
|
-
await this.client.putItem({
|
|
508
|
-
TableName: this.tableName,
|
|
509
|
-
Item: marshall(fullItem)
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
async get(pk, sk) {
|
|
513
|
-
if (!this.client)
|
|
514
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
515
|
-
const key = {
|
|
516
|
-
[this.pkAttribute]: pk
|
|
517
|
-
};
|
|
518
|
-
if (sk)
|
|
519
|
-
key[this.skAttribute] = sk;
|
|
520
|
-
const response = await this.client.getItem({
|
|
521
|
-
TableName: this.tableName,
|
|
522
|
-
Key: marshall(key)
|
|
523
|
-
});
|
|
524
|
-
if (!response.Item)
|
|
525
|
-
return;
|
|
526
|
-
return unmarshall(response.Item);
|
|
527
|
-
}
|
|
528
|
-
async delete(pk, sk) {
|
|
529
|
-
if (!this.client)
|
|
530
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
531
|
-
const key = {
|
|
532
|
-
[this.pkAttribute]: pk
|
|
533
|
-
};
|
|
534
|
-
if (sk)
|
|
535
|
-
key[this.skAttribute] = sk;
|
|
536
|
-
await this.client.deleteItem({
|
|
537
|
-
TableName: this.tableName,
|
|
538
|
-
Key: marshall(key)
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
async update(pk, sk, updates) {
|
|
542
|
-
if (!this.client)
|
|
543
|
-
throw Error("DynamoDB client not configured. Call setClient() first.");
|
|
544
|
-
const key = {
|
|
545
|
-
[this.pkAttribute]: pk
|
|
546
|
-
};
|
|
547
|
-
if (sk)
|
|
548
|
-
key[this.skAttribute] = sk;
|
|
549
|
-
const updateParts = [], exprNames = {}, exprValues = {};
|
|
550
|
-
let idx = 0;
|
|
551
|
-
const setParts = [];
|
|
552
|
-
for (const [attr, value] of Object.entries(updates)) {
|
|
553
|
-
const nameKey = `#upd${idx}`, valueKey = `:upd${idx}`;
|
|
554
|
-
exprNames[nameKey] = attr;
|
|
555
|
-
exprValues[valueKey] = marshallValue(value);
|
|
556
|
-
setParts.push(`${nameKey} = ${valueKey}`);
|
|
557
|
-
idx++;
|
|
558
|
-
}
|
|
559
|
-
if (setParts.length > 0)
|
|
560
|
-
updateParts.push(`SET ${setParts.join(", ")}`);
|
|
561
|
-
await this.client.updateItem({
|
|
562
|
-
TableName: this.tableName,
|
|
563
|
-
Key: marshall(key),
|
|
564
|
-
UpdateExpression: updateParts.join(" "),
|
|
565
|
-
ExpressionAttributeNames: exprNames,
|
|
566
|
-
ExpressionAttributeValues: exprValues
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
getTableName() {
|
|
570
|
-
return this.tableName;
|
|
571
|
-
}
|
|
572
|
-
getConfig() {
|
|
573
|
-
return {
|
|
574
|
-
tableName: this.tableName,
|
|
575
|
-
pkAttribute: this.pkAttribute,
|
|
576
|
-
skAttribute: this.skAttribute,
|
|
577
|
-
entityTypeAttribute: this.entityTypeAttr,
|
|
578
|
-
keyDelimiter: this.delimiter
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
export function generateKeyPattern(entityName, idField = "id") {
|
|
583
|
-
return `${entityName.toUpperCase()}#{${idField}}`;
|
|
584
|
-
}
|
|
585
|
-
export function parseKeyPattern(pattern, key) {
|
|
586
|
-
const result = {}, patternParts = pattern.split("#"), keyParts = key.split("#");
|
|
587
|
-
for (let i = 0;i < patternParts.length; i++) {
|
|
588
|
-
const patternPart = patternParts[i], keyPart = keyParts[i];
|
|
589
|
-
if (patternPart?.startsWith("{") && patternPart.endsWith("}")) {
|
|
590
|
-
const fieldName = patternPart.slice(1, -1);
|
|
591
|
-
result[fieldName] = keyPart ?? "";
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return result;
|
|
595
|
-
}
|
|
596
|
-
export function buildKey(pattern, values) {
|
|
597
|
-
let key = pattern;
|
|
598
|
-
for (const [field, value] of Object.entries(values))
|
|
599
|
-
key = key.replace(`{${field}}`, value);
|
|
600
|
-
return key;
|
|
601
|
-
}
|
|
602
|
-
export const dynamo = new DynamoClient;
|
|
603
|
-
export function createDynamo() {
|
|
604
|
-
return new DynamoClient;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
export { marshall, unmarshall };
|
|
1
|
+
function toDynamoValue(value){if(value===null||value===void 0)return{NULL:!0};if(typeof value==="string")return{S:value};if(typeof value==="number")return{N:String(value)};if(typeof value==="boolean")return{BOOL:value};if(Array.isArray(value))return{L:value.map((v)=>toDynamoValue(v))};if(typeof value==="object"){const m={};for(const[k,v]of Object.entries(value))m[k]=toDynamoValue(v);return{M:m}}return{S:String(value)}}function fromDynamoValue(attr){if("NULL"in attr&&attr.NULL)return null;if("S"in attr&&attr.S!==void 0)return attr.S;if("N"in attr&&attr.N!==void 0)return Number(attr.N);if("BOOL"in attr&&attr.BOOL!==void 0)return attr.BOOL;if("L"in attr&&attr.L!==void 0)return attr.L.map((v)=>fromDynamoValue(v));if("M"in attr&&attr.M!==void 0){const obj={};for(const[k,v]of Object.entries(attr.M))obj[k]=fromDynamoValue(v);return obj}if("SS"in attr&&attr.SS!==void 0)return attr.SS;if("NS"in attr&&attr.NS!==void 0)return attr.NS.map((n)=>Number(n));if("B"in attr&&attr.B!==void 0)return attr.B;if("BS"in attr&&attr.BS!==void 0)return attr.BS;return null}function marshall(obj){const result={};for(const[key,value]of Object.entries(obj))result[key]=toDynamoValue(value);return result}function marshallValue(value){const marshalled=marshall({value}).value;if(!marshalled)throw Error("Failed to marshal DynamoDB attribute value");return marshalled}function unmarshall(obj){const result={};for(const[key,value]of Object.entries(obj))result[key]=fromDynamoValue(value);return result}export class EntityQueryBuilder{client;tableName;pkAttribute;skAttribute;entityTypeAttr;delimiter;_entityType;_pkValue;_skCondition;_indexName;_projectionAttrs=[];_filterConditions=[];_limitValue;_scanForward=!0;_consistentRead=!1;_startKey;constructor(client,tableName,config){this.client=client;this.tableName=tableName;this.pkAttribute=config.pkAttribute;this.skAttribute=config.skAttribute;this.entityTypeAttr=config.entityTypeAttribute;this.delimiter=config.keyDelimiter}entity(entityType){this._entityType=entityType;return this}pk(value){this._pkValue=value;return this}get sk(){const self=this;return{equals(value){self._skCondition={type:"eq",value};return self},beginsWith(prefix){self._skCondition={type:"begins_with",value:prefix};return self},between(start,end){self._skCondition={type:"between",value:start,value2:end};return self},lt(value){self._skCondition={type:"lt",value};return self},lte(value){self._skCondition={type:"lte",value};return self},gt(value){self._skCondition={type:"gt",value};return self},gte(value){self._skCondition={type:"gte",value};return self}}}index(indexName){this._indexName=indexName;return this}project(...attributes){this._projectionAttrs.push(...attributes);return this}filter(attribute,operator,value){this._filterConditions.push({attribute,operator,value});return this}where(attribute,value){return this.filter(attribute,"=",value)}whereIn(attribute,values){this._filterConditions.push({attribute,operator:"IN",values});return this}limit(count){this._limitValue=count;return this}asc(){this._scanForward=!0;return this}desc(){this._scanForward=!1;return this}consistent(){this._consistentRead=!0;return this}startFrom(key){this._startKey=key;return this}toRequest(){const request={TableName:this.tableName};if(this._indexName)request.IndexName=this._indexName;const keyConditions=[],exprNames={},exprValues={};let idx=0;if(this._pkValue){const nameKey=`#pk${idx}`,valueKey=`:pk${idx}`;exprNames[nameKey]=this.pkAttribute;exprValues[valueKey]={S:this._pkValue};keyConditions.push(`${nameKey} = ${valueKey}`);idx++}if(this._skCondition){const nameKey=`#sk${idx}`;exprNames[nameKey]=this.skAttribute;switch(this._skCondition.type){case"eq":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`${nameKey} = ${valueKey}`);break}case"begins_with":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`begins_with(${nameKey}, ${valueKey})`);break}case"between":{if(this._skCondition.value2===void 0)throw Error("DynamoDB between condition requires a second value");const valueKey1=`:sk${idx}a`,valueKey2=`:sk${idx}b`;exprValues[valueKey1]={S:this._skCondition.value};exprValues[valueKey2]={S:this._skCondition.value2};keyConditions.push(`${nameKey} BETWEEN ${valueKey1} AND ${valueKey2}`);break}case"lt":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`${nameKey} < ${valueKey}`);break}case"lte":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`${nameKey} <= ${valueKey}`);break}case"gt":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`${nameKey} > ${valueKey}`);break}case"gte":{const valueKey=`:sk${idx}`;exprValues[valueKey]={S:this._skCondition.value};keyConditions.push(`${nameKey} >= ${valueKey}`);break}}idx++}if(keyConditions.length>0)request.KeyConditionExpression=keyConditions.join(" AND ");if(this._filterConditions.length>0){const filterParts=[];for(const cond of this._filterConditions){const nameKey=`#flt${idx}`;exprNames[nameKey]=cond.attribute;if(cond.operator==="IN"&&cond.values){const valueKeys=cond.values.map((_,i)=>`:flt${idx}_${i}`);cond.values.forEach((val,i)=>{exprValues[`:flt${idx}_${i}`]=marshallValue(val)});filterParts.push(`${nameKey} IN (${valueKeys.join(", ")})`)}else{const valueKey=`:flt${idx}`;exprValues[valueKey]=marshallValue(cond.value);filterParts.push(`${nameKey} ${cond.operator} ${valueKey}`)}idx++}request.FilterExpression=filterParts.join(" AND ")}if(this._projectionAttrs.length>0){const projParts=[];for(const attr of this._projectionAttrs){const nameKey=`#proj${idx}`;exprNames[nameKey]=attr;projParts.push(nameKey);idx++}request.ProjectionExpression=projParts.join(", ")}if(Object.keys(exprNames).length>0)request.ExpressionAttributeNames=exprNames;if(Object.keys(exprValues).length>0)request.ExpressionAttributeValues=exprValues;if(this._limitValue!==void 0)request.Limit=this._limitValue;request.ScanIndexForward=this._scanForward;if(this._consistentRead)request.ConsistentRead=!0;if(this._startKey)request.ExclusiveStartKey=marshall(this._startKey);return request}async get(){if(!this.client)throw Error("DynamoDB client not configured. Call dynamo.connection() first.");const request=this.toRequest();return((this._pkValue!==void 0?await this.client.query(request):await this.client.scan(request)).Items??[]).map((item)=>unmarshall(item))}async first(){this._limitValue=1;return(await this.get())[0]}async getAll(){const allItems=[];let lastKey;do{if(lastKey)this._startKey=lastKey;const request=this.toRequest(),client=this.client;if(!client)throw Error("DynamoDB client is not configured");const response=this._pkValue!==void 0?await client.query(request):await client.scan(request),items=(response.Items??[]).map((item)=>unmarshall(item));allItems.push(...items);lastKey=response.LastEvaluatedKey?unmarshall(response.LastEvaluatedKey):void 0}while(lastKey);return allItems}async count(){if(!this.client)throw Error("DynamoDB client not configured. Call dynamo.connection() first.");const request=this.toRequest();request.Select="COUNT";return(this._pkValue!==void 0?await this.client.query(request):await this.client.scan(request)).Count??0}}class DynamoClient{client;tableName="";pkAttribute="pk";skAttribute="sk";entityTypeAttr="_et";delimiter="#";entityMappings=new Map;_configured=!1;connection(config){this.tableName=config.table;this.pkAttribute=config.pkAttribute??"pk";this.skAttribute=config.skAttribute??"sk";this.entityTypeAttr=config.entityTypeAttribute??"_et";this.delimiter=config.keyDelimiter??"#";this._configured=!0;return this}isConfigured(){return this._configured}setClient(client){this.client=client;return this}getClient(){return this.client}registerEntity(mapping){this.entityMappings.set(mapping.entityType,mapping);return this}registerModel(model){const name=model.name??"Unknown",upperName=name.toUpperCase(),mapping={entityType:name,pkPattern:`${upperName}#{id}`,skPattern:`${upperName}#{id}`};this.entityMappings.set(name,mapping);return this}getEntityMapping(entityType){return this.entityMappings.get(entityType)}entity(entityType){if(!this._configured)throw Error("DynamoDB not configured. Call dynamo.connection() first.");return new EntityQueryBuilder(this.client,this.tableName,{pkAttribute:this.pkAttribute,skAttribute:this.skAttribute,entityTypeAttribute:this.entityTypeAttr,keyDelimiter:this.delimiter}).entity(entityType)}async batchWrite(operations){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");if(!this._configured)throw Error("DynamoDB not configured. Call dynamo.connection() first.");const requestItems=[];for(const op of operations)if(op.put){const item={...op.put.item,[this.entityTypeAttr]:op.put.entity};requestItems.push({PutRequest:{Item:marshall(item)}})}else if(op.delete)requestItems.push({DeleteRequest:{Key:marshall({[this.pkAttribute]:op.delete.pk,[this.skAttribute]:op.delete.sk})}});if(requestItems.length>0)await this.client.batchWriteItem({RequestItems:{[this.tableName]:requestItems}})}async transactWrite(operations){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");if(!this._configured)throw Error("DynamoDB not configured. Call dynamo.connection() first.");const transactItems=[];for(const op of operations)if(op.put){const item={...op.put.item,[this.entityTypeAttr]:op.put.entity},putPayload={TableName:this.tableName,Item:marshall(item)};if(op.put.condition)putPayload.ConditionExpression=op.put.condition;transactItems.push({Put:putPayload})}else if(op.update){const key={[this.pkAttribute]:op.update.pk};if(op.update.sk)key[this.skAttribute]=op.update.sk;const updateParts=[],exprNames={},exprValues={};let idx=0;if(op.update.set){const setParts=[];for(const[attr,value]of Object.entries(op.update.set)){const nameKey=`#set${idx}`,valueKey=`:set${idx}`;exprNames[nameKey]=attr;exprValues[valueKey]=marshallValue(value);setParts.push(`${nameKey} = ${valueKey}`);idx++}if(setParts.length>0)updateParts.push(`SET ${setParts.join(", ")}`)}if(op.update.add){const addParts=[];for(const[attr,value]of Object.entries(op.update.add)){const nameKey=`#add${idx}`,valueKey=`:add${idx}`;exprNames[nameKey]=attr;exprValues[valueKey]={N:String(value)};addParts.push(`${nameKey} ${valueKey}`);idx++}if(addParts.length>0)updateParts.push(`ADD ${addParts.join(", ")}`)}if(op.update.remove&&op.update.remove.length>0){const removeParts=[];for(const attr of op.update.remove){const nameKey=`#rem${idx}`;exprNames[nameKey]=attr;removeParts.push(nameKey);idx++}updateParts.push(`REMOVE ${removeParts.join(", ")}`)}transactItems.push({Update:{TableName:this.tableName,Key:marshall(key),UpdateExpression:updateParts.join(" "),ExpressionAttributeNames:exprNames,ExpressionAttributeValues:exprValues}})}else if(op.delete){const deletePayload={TableName:this.tableName,Key:marshall({[this.pkAttribute]:op.delete.pk,[this.skAttribute]:op.delete.sk})};if(op.delete.condition)deletePayload.ConditionExpression=op.delete.condition;transactItems.push({Delete:deletePayload})}else if(op.conditionCheck)transactItems.push({ConditionCheck:{TableName:this.tableName,Key:marshall({[this.pkAttribute]:op.conditionCheck.pk,[this.skAttribute]:op.conditionCheck.sk}),ConditionExpression:op.conditionCheck.condition}});if(transactItems.length>0)await this.client.transactWriteItems({TransactItems:transactItems})}async put(entity,item){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");const fullItem={...item,[this.entityTypeAttr]:entity};await this.client.putItem({TableName:this.tableName,Item:marshall(fullItem)})}async get(pk,sk){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");const key={[this.pkAttribute]:pk};if(sk)key[this.skAttribute]=sk;const response=await this.client.getItem({TableName:this.tableName,Key:marshall(key)});if(!response.Item)return;return unmarshall(response.Item)}async delete(pk,sk){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");const key={[this.pkAttribute]:pk};if(sk)key[this.skAttribute]=sk;await this.client.deleteItem({TableName:this.tableName,Key:marshall(key)})}async update(pk,sk,updates){if(!this.client)throw Error("DynamoDB client not configured. Call setClient() first.");const key={[this.pkAttribute]:pk};if(sk)key[this.skAttribute]=sk;const updateParts=[],exprNames={},exprValues={};let idx=0;const setParts=[];for(const[attr,value]of Object.entries(updates)){const nameKey=`#upd${idx}`,valueKey=`:upd${idx}`;exprNames[nameKey]=attr;exprValues[valueKey]=marshallValue(value);setParts.push(`${nameKey} = ${valueKey}`);idx++}if(setParts.length>0)updateParts.push(`SET ${setParts.join(", ")}`);await this.client.updateItem({TableName:this.tableName,Key:marshall(key),UpdateExpression:updateParts.join(" "),ExpressionAttributeNames:exprNames,ExpressionAttributeValues:exprValues})}getTableName(){return this.tableName}getConfig(){return{tableName:this.tableName,pkAttribute:this.pkAttribute,skAttribute:this.skAttribute,entityTypeAttribute:this.entityTypeAttr,keyDelimiter:this.delimiter}}}export function generateKeyPattern(entityName,idField="id"){return`${entityName.toUpperCase()}#{${idField}}`}export function parseKeyPattern(pattern,key){const result={},patternParts=pattern.split("#"),keyParts=key.split("#");for(let i=0;i<patternParts.length;i++){const patternPart=patternParts[i],keyPart=keyParts[i];if(patternPart?.startsWith("{")&&patternPart.endsWith("}")){const fieldName=patternPart.slice(1,-1);result[fieldName]=keyPart??""}}return result}export function buildKey(pattern,values){let key=pattern;for(const[field,value]of Object.entries(values))key=key.replace(`{${field}}`,value);return key}export const dynamo=new DynamoClient;export function createDynamo(){return new DynamoClient}export{marshall,unmarshall};
|