dinah 0.11.1 → 0.13.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 CHANGED
@@ -99,13 +99,18 @@ const UserTable = new Table(
99
99
 
100
100
  ### Using the Repository Class
101
101
 
102
- `Repo` is the recommended way to interact with DynamoDB. For a plain repo with no configuration, use `db.createRepo`:
102
+ `Repo` is the recommended way to interact with DynamoDB. Use `db.makeRepo` to create a repo inline, with or without configuration:
103
103
 
104
104
  ```typescript
105
105
  const userRepo = db.makeRepo(UserTable);
106
+
107
+ const userRepo = db.makeRepo(UserTable, {
108
+ defaultPutData: () => ({ createdAt: Date.now() }),
109
+ defaultUpdateData: () => ({ updatedAt: Date.now() }),
110
+ });
106
111
  ```
107
112
 
108
- For repos with defaults, transforms, or attribute rules, use `makeRepo` to define a class (see [Repository Configuration](#repository-configuration)):
113
+ To create a named, reusable repo class, use the standalone `makeRepo` (see [Repository Configuration](#repository-configuration)):
109
114
 
110
115
  ```typescript
111
116
  import { makeRepo } from "dinah";
package/dist/cdk.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_util = require("./util-D88GS6Gt.cjs");
2
+ const require_util = require("./util-DT6vwquI.cjs");
3
3
  let aws_cdk_lib_aws_dynamodb = require("aws-cdk-lib/aws-dynamodb");
4
4
  aws_cdk_lib_aws_dynamodb = require_util.__toESM(aws_cdk_lib_aws_dynamodb, 1);
5
5
  //#region src/cdk.ts
package/dist/cdk.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Table } from "./table-BQvKnaNX.cjs";
1
+ import { t as Table } from "./table-D814Vwjm.cjs";
2
2
  import { TSchema } from "typebox";
3
3
  import * as cdk from "aws-cdk-lib/aws-dynamodb";
4
4
 
package/dist/cdk.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Table } from "./table-D_frY5EF.mjs";
1
+ import { t as Table } from "./table-CatYaz7J.mjs";
2
2
  import { TSchema } from "typebox";
3
3
  import * as cdk from "aws-cdk-lib/aws-dynamodb";
4
4
 
package/dist/cdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as resolveAttrType } from "./util-BfKXObBN.mjs";
1
+ import { a as resolveAttrType } from "./util-0kc6E2VD.mjs";
2
2
  import * as cdk from "aws-cdk-lib/aws-dynamodb";
3
3
  //#region src/cdk.ts
4
4
  const resolveAttrs = (schema, attrNames) => {
package/dist/index.cjs CHANGED
@@ -1,8 +1,28 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_util = require("./util-D88GS6Gt.cjs");
2
+ const require_util = require("./util-DT6vwquI.cjs");
3
3
  let _aws_sdk_client_dynamodb = require("@aws-sdk/client-dynamodb");
4
4
  let _aws_sdk_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
5
5
  _aws_sdk_lib_dynamodb = require_util.__toESM(_aws_sdk_lib_dynamodb, 1);
6
+ //#region src/error.ts
7
+ function dinahErrorMessage(details) {
8
+ switch (details.type) {
9
+ case "NOT_FOUND": return `${details.resource ?? "Item"} not found: ${JSON.stringify(details.key)}`;
10
+ case "CONDITIONAL_CHECK_FAILED": return details.key ? `${details.resource ?? "Item"} condition check failed: ${JSON.stringify(details.key)}` : `${details.resource ?? "Item"} condition check failed`;
11
+ case "VALIDATION": return `Validation error: ${details.message}`;
12
+ case "DATA_INTEGRITY": return `Data integrity error: ${details.message}`;
13
+ case "TRANSACTION_CANCELED": return `Transaction canceled: ${details.reasons.map((r) => r.type).join(", ")}`;
14
+ }
15
+ }
16
+ var DinahError = class extends Error {
17
+ details;
18
+ constructor(details, options) {
19
+ super(dinahErrorMessage(details), options);
20
+ this.name = "DinahError";
21
+ this.details = details;
22
+ }
23
+ };
24
+ const isDinahError = (err) => err instanceof DinahError;
25
+ //#endregion
6
26
  //#region src/expression-builder.ts
7
27
  const attrTypes = [
8
28
  "S",
@@ -225,8 +245,15 @@ var Repo = class {
225
245
  get tableName() {
226
246
  return `${this.db.config?.tableNamePrefix ?? ""}${this.table.def.name}`;
227
247
  }
228
- get defaultPutData() {
229
- return this.config.defaultPutData?.() ?? {};
248
+ get resourceName() {
249
+ if (this.config.resourceName) return this.config.resourceName;
250
+ const clsName = this.constructor.name;
251
+ if (clsName && clsName !== "Repo") return clsName.replace(/Repo$/i, "") || clsName;
252
+ const t = this.table.def.name;
253
+ return t.charAt(0).toUpperCase() + t.slice(1);
254
+ }
255
+ get defaultCreateData() {
256
+ return this.config.defaultCreateData?.() ?? {};
230
257
  }
231
258
  get defaultUpdateData() {
232
259
  return this.config.defaultUpdateData?.() ?? {};
@@ -234,9 +261,6 @@ var Repo = class {
234
261
  transformOutput(item) {
235
262
  return this.config.transformOutput?.(item) ?? item;
236
263
  }
237
- transformInput(item) {
238
- return this.config.transformInput?.(item) ?? item;
239
- }
240
264
  extractKey(item) {
241
265
  const { partitionKey, sortKey } = this.table.def;
242
266
  if (sortKey) return {
@@ -246,40 +270,44 @@ var Repo = class {
246
270
  return { [partitionKey]: item[partitionKey] };
247
271
  }
248
272
  async get(key, options) {
273
+ const { filter, ...restOptions } = options ?? {};
249
274
  const item = await this.db.get({
250
275
  table: this.tableName,
251
276
  key: this.extractKey(key),
252
- ...options
277
+ filter,
278
+ ...restOptions
253
279
  });
254
280
  return item && this.applyTransformIfNeeded(item, options);
255
281
  }
256
282
  async getOrThrow(key, options) {
283
+ const { filter, ...restOptions } = options ?? {};
257
284
  const item = await this.db.getOrThrow({
258
285
  table: this.tableName,
259
286
  key: this.extractKey(key),
260
- ...options
287
+ resource: this.resourceName,
288
+ filter,
289
+ ...restOptions
261
290
  });
262
291
  return this.applyTransformIfNeeded(item, options);
263
292
  }
264
293
  async put(item, options) {
265
- const itemWithDefaults = this.applyPutTransforms(item);
266
294
  const result = await this.db.put({
267
295
  table: this.tableName,
268
- item: itemWithDefaults,
296
+ item,
297
+ resource: this.resourceName,
269
298
  ...options
270
299
  });
271
300
  return this.applyTransformIfNeeded(result);
272
301
  }
273
302
  async update(key, update, options) {
274
- const updateWithDefaults = this.applyNormalizersToExpression({
275
- ...this.defaultUpdateData,
276
- ...update
277
- });
303
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
278
304
  const result = await this.db.update({
279
305
  table: this.tableName,
280
306
  key: this.extractKey(key),
307
+ resource: this.resourceName,
281
308
  update: updateWithDefaults,
282
- ...options
309
+ ...options,
310
+ condition: this.withDiscriminatorCondition(key, options?.condition)
283
311
  });
284
312
  return this.applyTransformIfNeeded(result);
285
313
  }
@@ -287,15 +315,21 @@ var Repo = class {
287
315
  const { condition: otherCondition, ...otherOptions } = options ?? {};
288
316
  const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
289
317
  if (otherCondition) condition.$and.push(otherCondition);
290
- return this.put(item, {
318
+ const normalizedItem = this.applyCreateTransforms(item);
319
+ const result = await this.db.put({
320
+ table: this.tableName,
321
+ item: normalizedItem,
322
+ resource: this.resourceName,
291
323
  condition,
292
324
  ...otherOptions
293
325
  });
326
+ return this.applyTransformIfNeeded(result);
294
327
  }
295
328
  async delete(key, options) {
296
329
  const item = await this.db.delete({
297
330
  table: this.tableName,
298
331
  key: this.extractKey(key),
332
+ resource: this.resourceName,
299
333
  ...options
300
334
  });
301
335
  return item && this.applyTransformIfNeeded(item);
@@ -304,6 +338,7 @@ var Repo = class {
304
338
  const item = await this.db.deleteOrThrow({
305
339
  table: this.tableName,
306
340
  key: this.extractKey(key),
341
+ resource: this.resourceName,
307
342
  ...options
308
343
  });
309
344
  return this.applyTransformIfNeeded(item);
@@ -346,6 +381,35 @@ var Repo = class {
346
381
  gsi
347
382
  });
348
383
  }
384
+ async getGsi(gsi, query, options) {
385
+ const { filter, ...restOptions } = options ?? {};
386
+ const items = await this.db.query({
387
+ table: this.tableName,
388
+ index: gsi,
389
+ query,
390
+ ...restOptions
391
+ });
392
+ if (items.length > 1) throw new DinahError({
393
+ type: "DATA_INTEGRITY",
394
+ message: `getGsi on "${gsi}" returned ${items.length} items; expected at most 1.`
395
+ });
396
+ const item = items[0];
397
+ if (!item) return void 0;
398
+ if (filter && !require_util.matchesPartial(filter, item)) return void 0;
399
+ return this.applyTransformIfNeeded(item, {
400
+ ...options,
401
+ gsi
402
+ });
403
+ }
404
+ async getGsiOrThrow(gsi, query, options) {
405
+ const item = await this.getGsi(gsi, query, options);
406
+ if (item === void 0) throw new DinahError({
407
+ type: "NOT_FOUND",
408
+ key: query,
409
+ resource: this.resourceName
410
+ });
411
+ return item;
412
+ }
349
413
  async scan(options) {
350
414
  const items = await this.db.scan({
351
415
  table: this.tableName,
@@ -396,9 +460,11 @@ var Repo = class {
396
460
  });
397
461
  }
398
462
  async batchGet(keys, options) {
463
+ const { filter, ...restOptions } = options ?? {};
399
464
  const { items, unprocessed } = await this.db.batchGet({ [this.tableName]: {
400
465
  keys: keys.map((key) => this.extractKey(key)),
401
- ...options
466
+ filter,
467
+ ...restOptions
402
468
  } });
403
469
  const tableItems = items[this.tableName];
404
470
  return {
@@ -407,9 +473,12 @@ var Repo = class {
407
473
  };
408
474
  }
409
475
  async batchGetOrThrow(keys, options) {
476
+ const { filter, ...restOptions } = options ?? {};
410
477
  const result = await this.db.batchGetOrThrow({ [this.tableName]: {
411
478
  keys: keys.map((key) => this.extractKey(key)),
412
- ...options
479
+ resource: this.resourceName,
480
+ filter,
481
+ ...restOptions
413
482
  } });
414
483
  return this.applyTransformsIfNeeded(result[this.tableName] ?? [], options);
415
484
  }
@@ -421,7 +490,7 @@ var Repo = class {
421
490
  };
422
491
  else return {
423
492
  type: "PUT",
424
- item: this.applyPutTransforms(request.item)
493
+ item: request.item
425
494
  };
426
495
  }) });
427
496
  return {
@@ -430,27 +499,29 @@ var Repo = class {
430
499
  };
431
500
  }
432
501
  async batchUpdate(keys, update) {
433
- const updateWithDefaults = this.applyNormalizersToExpression({
434
- ...this.defaultUpdateData,
435
- ...update
436
- });
502
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
437
503
  return { unprocessed: (await this.db.batchUpdate({ [this.tableName]: {
438
504
  keys: keys.map((key) => this.extractKey(key)),
439
505
  update: updateWithDefaults
440
506
  } })).unprocessed?.[this.tableName]?.keys };
441
507
  }
442
508
  async trxGet(keys, options) {
509
+ const { filter, ...restOptions } = options ?? {};
443
510
  return (await this.db.trxGet(...keys.map((key) => ({
444
511
  table: this.tableName,
445
512
  key: this.extractKey(key),
446
- ...options
513
+ filter,
514
+ ...restOptions
447
515
  })))).map((item) => item && this.applyTransformIfNeeded(item, options));
448
516
  }
449
517
  async trxGetOrThrow(keys, options) {
518
+ const { filter, ...restOptions } = options ?? {};
450
519
  const items = await this.db.trxGetOrThrow(...keys.map((key) => ({
451
520
  table: this.tableName,
452
521
  key: this.extractKey(key),
453
- ...options
522
+ resource: this.resourceName,
523
+ filter,
524
+ ...restOptions
454
525
  })));
455
526
  return this.applyTransformsIfNeeded(items, options);
456
527
  }
@@ -458,8 +529,8 @@ var Repo = class {
458
529
  await this.db.trxWrite(...requests.map((request) => {
459
530
  switch (request.type) {
460
531
  case "CONDITION": {
461
- const { key, condition, ...options } = request;
462
- return this.trxConditionRequest(key, condition, options);
532
+ const { key, condition } = request;
533
+ return this.trxConditionRequest(key, condition);
463
534
  }
464
535
  case "DELETE": {
465
536
  const { key, ...options } = request;
@@ -504,85 +575,125 @@ var Repo = class {
504
575
  ...options
505
576
  };
506
577
  }
507
- trxConditionRequest(key, condition, options) {
578
+ trxConditionRequest(key, condition) {
508
579
  return {
509
580
  table: this.tableName,
510
581
  type: "CONDITION",
511
582
  key: this.extractKey(key),
512
- condition,
513
- ...options
583
+ condition
514
584
  };
515
585
  }
516
586
  trxPutRequest(item, options) {
517
- const itemWithDefaults = this.applyPutTransforms(item);
518
587
  return {
519
588
  table: this.tableName,
520
589
  type: "PUT",
521
- item: itemWithDefaults,
590
+ item,
522
591
  ...options
523
592
  };
524
593
  }
525
594
  trxUpdateRequest(key, update, options) {
526
- const updateWithDefaults = this.applyNormalizersToExpression({
527
- ...this.defaultUpdateData,
528
- ...update
529
- });
595
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
530
596
  return {
531
597
  table: this.tableName,
532
598
  type: "UPDATE",
533
599
  key: this.extractKey(key),
534
600
  update: updateWithDefaults,
535
- ...options
601
+ ...options,
602
+ condition: this.withDiscriminatorCondition(key, options?.condition)
536
603
  };
537
604
  }
538
605
  trxCreateRequest(item, options) {
539
606
  const { condition: otherCondition, ...otherOptions } = options ?? {};
540
607
  const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
541
608
  if (otherCondition) condition.$and.push(otherCondition);
542
- const itemWithDefaults = this.applyPutTransforms(item);
609
+ const normalizedItem = this.applyCreateTransforms(item);
543
610
  return {
544
611
  table: this.tableName,
545
612
  type: "PUT",
546
- item: itemWithDefaults,
613
+ item: normalizedItem,
547
614
  condition,
548
615
  ...otherOptions
549
616
  };
550
617
  }
551
- applyPutTransforms(item) {
552
- const result = { ...this.transformInput({
553
- ...this.defaultPutData,
618
+ applyCreateTransforms(item) {
619
+ const { transformAttributes, computedAttributes } = this.config;
620
+ if (computedAttributes) {
621
+ for (const key of Object.keys(computedAttributes)) if (key in item) throw new DinahError({
622
+ type: "VALIDATION",
623
+ message: `Field "${key}" is a computed attribute and cannot be set directly. Remove it from the create input.`
624
+ });
625
+ }
626
+ const merged = {
627
+ ...this.defaultCreateData,
554
628
  ...item
555
- }) };
556
- for (const key of this.config.derivedAttributes ?? []) delete result[key];
557
- return result;
558
- }
559
- applyNormalizersToExpression(expression) {
560
- const partial = {};
561
- for (const [key, val] of Object.entries(expression)) if (val === void 0 || isOperation(val) && val.$remove === true) partial[key] = void 0;
562
- else if (!isOperation(val)) partial[key] = val;
563
- else if (val.$set !== void 0) partial[key] = val.$set;
564
- else if (val.$ifNotExists !== void 0) partial[key] = Array.isArray(val.$ifNotExists) ? val.$ifNotExists[1] : val.$ifNotExists;
565
- const normalized = this.transformInput(partial);
566
- const result = { ...expression };
567
- for (const [key, normalizedVal] of Object.entries(normalized)) {
568
- if (normalizedVal === void 0) continue;
569
- if (key in partial && partial[key] === void 0 || !(key in partial)) result[key] = normalizedVal;
570
- else {
571
- const original = expression[key];
572
- if (!isOperation(original)) result[key] = normalizedVal;
573
- else if (original.$set !== void 0) result[key] = {
574
- ...original,
575
- $set: normalizedVal
576
- };
577
- else if (original.$ifNotExists !== void 0) result[key] = {
578
- ...original,
579
- $ifNotExists: Array.isArray(original.$ifNotExists) ? [original.$ifNotExists[0], normalizedVal] : normalizedVal
629
+ };
630
+ if (transformAttributes) {
631
+ for (const [key, transform] of Object.entries(transformAttributes)) if (key in merged && merged[key] !== void 0) merged[key] = transform(merged[key]);
632
+ }
633
+ if (computedAttributes) for (const [key, def] of Object.entries(computedAttributes)) {
634
+ const { from, compute } = def;
635
+ const computed = compute(merged[from]);
636
+ if (computed !== void 0) merged[key] = computed;
637
+ else delete merged[key];
638
+ }
639
+ return merged;
640
+ }
641
+ applyNormalizersToExpression(userUpdate) {
642
+ const { transformAttributes, computedAttributes, immutableAttributes } = this.config;
643
+ if (computedAttributes) {
644
+ for (const key of Object.keys(computedAttributes)) if (key in userUpdate) throw new DinahError({
645
+ type: "VALIDATION",
646
+ message: `Field "${key}" is a computed attribute and cannot be set directly in an update. Update the source field instead.`
647
+ });
648
+ }
649
+ for (const key of immutableAttributes ?? []) if (key in userUpdate) throw new DinahError({
650
+ type: "VALIDATION",
651
+ message: `Field "${key}" is immutable and cannot be updated.`
652
+ });
653
+ const result = {
654
+ ...this.defaultUpdateData,
655
+ ...userUpdate
656
+ };
657
+ if (transformAttributes) for (const [key, transform] of Object.entries(transformAttributes)) {
658
+ if (!(key in result)) continue;
659
+ const val = result[key];
660
+ const fn = transform;
661
+ if (val === void 0 || isOperation(val) && val.$remove === true) {} else if (!isOperation(val)) result[key] = fn(val);
662
+ else if (val.$set !== void 0) result[key] = {
663
+ ...val,
664
+ $set: fn(val.$set)
665
+ };
666
+ else if (val.$ifNotExists !== void 0) {
667
+ const ifne = val.$ifNotExists;
668
+ result[key] = {
669
+ ...val,
670
+ $ifNotExists: Array.isArray(ifne) ? [ifne[0], fn(ifne[1])] : fn(ifne)
580
671
  };
581
672
  }
582
673
  }
583
- for (const key of [...this.config.derivedAttributes ?? [], ...this.config.immutableAttributes ?? []]) delete result[key];
674
+ if (computedAttributes) for (const [computedKey, def] of Object.entries(computedAttributes)) {
675
+ const { from, compute } = def;
676
+ if (!(from in result)) continue;
677
+ const val = result[from];
678
+ if (val === void 0 || isOperation(val) && val.$remove === true) result[computedKey] = void 0;
679
+ else if (!isOperation(val)) result[computedKey] = compute(val);
680
+ else if (val.$set !== void 0) result[computedKey] = compute(val.$set);
681
+ else throw new DinahError({
682
+ type: "VALIDATION",
683
+ message: `Field "${from}" drives computed field "${computedKey}" and cannot use arithmetic or list operators in updates.`
684
+ });
685
+ }
584
686
  return result;
585
687
  }
688
+ withDiscriminatorCondition(key, condition) {
689
+ const discriminator = this.config.discriminator;
690
+ if (!discriminator) return condition;
691
+ const value = key[discriminator];
692
+ if (value === void 0) return condition;
693
+ const guard = { [discriminator]: value };
694
+ if (!condition) return guard;
695
+ return { $and: [guard, condition] };
696
+ }
586
697
  applyTransformsIfNeeded(items, options) {
587
698
  if (options?.projection?.length) return items;
588
699
  if (options?.gsi) {
@@ -652,12 +763,16 @@ var Db = class {
652
763
  ExpressionAttributeNames: exp.attributeNames
653
764
  });
654
765
  const output = await this.client.send(input);
655
- if (output.Item && data.filter && !data.filter(output.Item)) return;
766
+ if (output.Item && data.filter && !require_util.matchesPartial(data.filter, output.Item)) return;
656
767
  return output.Item;
657
768
  }
658
769
  async getOrThrow(data) {
659
770
  const item = await this.get(data);
660
- if (!item) throw new Error(`Item not found in "${data.table}" table.`);
771
+ if (!item) throw new DinahError({
772
+ type: "NOT_FOUND",
773
+ key: data.key,
774
+ resource: data.resource
775
+ });
661
776
  return item;
662
777
  }
663
778
  async put(data) {
@@ -672,8 +787,16 @@ var Db = class {
672
787
  ExpressionAttributeNames: exp.attributeNames,
673
788
  ExpressionAttributeValues: exp.attributeValues
674
789
  });
675
- const output = await this.client.send(input);
676
- return data.returnOld ? output.Attributes : item;
790
+ try {
791
+ const output = await this.client.send(input);
792
+ return data.returnOld ? output.Attributes : item;
793
+ } catch (err) {
794
+ if (err instanceof _aws_sdk_client_dynamodb.ConditionalCheckFailedException) throw new DinahError({
795
+ type: "CONDITIONAL_CHECK_FAILED",
796
+ resource: data.resource
797
+ }, { cause: err });
798
+ throw err;
799
+ }
677
800
  }
678
801
  async update(data) {
679
802
  const exp = new ExpressionBuilder();
@@ -689,7 +812,16 @@ var Db = class {
689
812
  ExpressionAttributeNames: exp.attributeNames,
690
813
  ExpressionAttributeValues: exp.attributeValues
691
814
  });
692
- return (await this.client.send(input)).Attributes;
815
+ try {
816
+ return (await this.client.send(input)).Attributes;
817
+ } catch (err) {
818
+ if (err instanceof _aws_sdk_client_dynamodb.ConditionalCheckFailedException) throw new DinahError({
819
+ type: "CONDITIONAL_CHECK_FAILED",
820
+ key: data.key,
821
+ resource: data.resource
822
+ }, { cause: err });
823
+ throw err;
824
+ }
693
825
  }
694
826
  async delete(data) {
695
827
  const exp = new ExpressionBuilder();
@@ -702,11 +834,24 @@ var Db = class {
702
834
  ExpressionAttributeNames: exp.attributeNames,
703
835
  ExpressionAttributeValues: exp.attributeValues
704
836
  });
705
- return (await this.client.send(input)).Attributes;
837
+ try {
838
+ return (await this.client.send(input)).Attributes;
839
+ } catch (err) {
840
+ if (err instanceof _aws_sdk_client_dynamodb.ConditionalCheckFailedException) throw new DinahError({
841
+ type: "CONDITIONAL_CHECK_FAILED",
842
+ key: data.key,
843
+ resource: data.resource
844
+ }, { cause: err });
845
+ throw err;
846
+ }
706
847
  }
707
848
  async deleteOrThrow(data) {
708
849
  const item = await this.delete(data);
709
- if (!item) throw new Error(`Item not found in "${data.table}" table.`);
850
+ if (!item) throw new DinahError({
851
+ type: "NOT_FOUND",
852
+ key: data.key,
853
+ resource: data.resource
854
+ });
710
855
  return item;
711
856
  }
712
857
  async *queryPaged(data) {
@@ -821,7 +966,7 @@ var Db = class {
821
966
  const output = await this.client.send(input);
822
967
  for (const [table, items] of Object.entries(output.Responses ?? {})) {
823
968
  if (!result.items[table]) continue;
824
- const filteredItems = tableData[table]?.filter ? items.filter(tableData[table].filter) : items;
969
+ const filteredItems = tableData[table]?.filter ? items.filter((item) => require_util.matchesPartial(tableData[table].filter, item)) : items;
825
970
  result.items[table].push(...filteredItems);
826
971
  }
827
972
  let unprocessedKeyCount = 0;
@@ -863,7 +1008,11 @@ var Db = class {
863
1008
  const { items, unprocessed } = await this.batchGet(data);
864
1009
  for (const table of Object.keys(data)) {
865
1010
  if (unprocessed?.[table]) throw new Error(`One or more batch get requests were not processed in "${table}" table.`);
866
- if (items[table]?.length !== data[table]?.keys?.length) throw new Error(`One or more items were not found in "${table}" table.`);
1011
+ if (items[table]?.length !== data[table]?.keys?.length) throw new DinahError({
1012
+ type: "NOT_FOUND",
1013
+ key: {},
1014
+ resource: data[table]?.resource
1015
+ });
867
1016
  }
868
1017
  return items;
869
1018
  }
@@ -978,12 +1127,16 @@ var Db = class {
978
1127
  return (await this.client.send(input)).Responses?.map((response, i) => {
979
1128
  if (!response.Item) return;
980
1129
  if (!requests[i]?.filter) return response.Item;
981
- return requests[i].filter(response.Item) ? response.Item : void 0;
1130
+ return require_util.matchesPartial(requests[i].filter, response.Item) ? response.Item : void 0;
982
1131
  }) ?? [];
983
1132
  }
984
1133
  async trxGetOrThrow(...requests) {
985
1134
  const items = await this.trxGet(...requests);
986
- 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.`);
1135
+ for (let i = 0; i < items.length; i++) if (!items[i]) throw new DinahError({
1136
+ type: "NOT_FOUND",
1137
+ key: requests[i]?.key ?? {},
1138
+ resource: requests[i]?.resource
1139
+ });
987
1140
  return items;
988
1141
  }
989
1142
  async trxWrite(...requests) {
@@ -1013,7 +1166,18 @@ var Db = class {
1013
1166
  } };
1014
1167
  });
1015
1168
  const input = new _aws_sdk_lib_dynamodb.TransactWriteCommand({ TransactItems: trxItems });
1016
- await this.client.send(input);
1169
+ try {
1170
+ await this.client.send(input);
1171
+ } catch (err) {
1172
+ if (err instanceof _aws_sdk_client_dynamodb.TransactionCanceledException) throw new DinahError({
1173
+ type: "TRANSACTION_CANCELED",
1174
+ reasons: (err.CancellationReasons ?? []).map((r) => ({
1175
+ type: r.Code ?? "Unknown",
1176
+ message: r.Message
1177
+ }))
1178
+ }, { cause: err });
1179
+ throw err;
1180
+ }
1017
1181
  }
1018
1182
  };
1019
1183
  //#endregion
@@ -1028,11 +1192,14 @@ var Table = class {
1028
1192
  };
1029
1193
  //#endregion
1030
1194
  exports.Db = Db;
1195
+ exports.DinahError = DinahError;
1031
1196
  exports.Repo = Repo;
1032
1197
  exports.Table = Table;
1033
1198
  exports.chunk = require_util.chunk;
1034
1199
  exports.extractTableDesc = require_util.extractTableDesc;
1200
+ exports.isDinahError = isDinahError;
1035
1201
  exports.makeRepo = makeRepo;
1202
+ exports.matchesPartial = require_util.matchesPartial;
1036
1203
  exports.removeUndefined = require_util.removeUndefined;
1037
1204
  exports.resolveAttrType = require_util.resolveAttrType;
1038
1205