@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.
Files changed (50) hide show
  1. package/dist/auth-tables.js +18 -137
  2. package/dist/column.js +1 -26
  3. package/dist/custom/audits.js +20 -54
  4. package/dist/custom/errors.js +16 -46
  5. package/dist/custom/index.js +1 -3
  6. package/dist/custom/jobs.js +13 -137
  7. package/dist/database.js +1 -181
  8. package/dist/datetime-columns.js +8 -85
  9. package/dist/ddl-constraints.js +7 -111
  10. package/dist/defaults.js +1 -48
  11. package/dist/dialect.js +1 -79
  12. package/dist/driver-config.js +1 -172
  13. package/dist/drivers/defaults/index.js +1 -1
  14. package/dist/drivers/defaults/traits.js +1 -29
  15. package/dist/drivers/dynamodb.js +1 -607
  16. package/dist/drivers/helpers.js +1 -206
  17. package/dist/drivers/index.js +1 -9
  18. package/dist/drivers/mysql.js +58 -299
  19. package/dist/drivers/postgres.js +78 -368
  20. package/dist/drivers/sqlite.js +61 -379
  21. package/dist/ensure-database.js +1 -145
  22. package/dist/fk-audit.js +3 -187
  23. package/dist/index.js +1 -64
  24. package/dist/managed-columns.js +1 -59
  25. package/dist/migration-dialect.js +4 -107
  26. package/dist/migration-ledger.js +1 -382
  27. package/dist/migration-lock.js +1 -143
  28. package/dist/migrations.js +15 -1118
  29. package/dist/model-sources.js +1 -76
  30. package/dist/notification-tables.js +4 -49
  31. package/dist/query-logger.js +2 -241
  32. package/dist/query-parser.js +1 -93
  33. package/dist/rbac-tables.js +6 -61
  34. package/dist/relation-columns.js +1 -66
  35. package/dist/replicas.js +1 -74
  36. package/dist/safe-migrations.js +2 -52
  37. package/dist/schema.js +1 -10
  38. package/dist/seeder.js +1 -457
  39. package/dist/sql-helpers.js +1 -50
  40. package/dist/table.js +1 -26
  41. package/dist/tools/setup.js +1 -6
  42. package/dist/trait-tables.js +8 -153
  43. package/dist/transaction-context.js +1 -62
  44. package/dist/types.js +1 -98
  45. package/dist/unique-audit.js +3 -155
  46. package/dist/utils.js +1 -285
  47. package/dist/uuid-columns.js +1 -68
  48. package/dist/validators.js +1 -122
  49. package/dist/vschema.js +2 -121
  50. package/package.json +20 -13
@@ -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};