dinah 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,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_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);
@@ -9,6 +9,7 @@ function dinahErrorMessage(details) {
9
9
  case "NOT_FOUND": return `${details.resource ?? "Item"} not found: ${JSON.stringify(details.key)}`;
10
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
11
  case "VALIDATION": return `Validation error: ${details.message}`;
12
+ case "DATA_INTEGRITY": return `Data integrity error: ${details.message}`;
12
13
  case "TRANSACTION_CANCELED": return `Transaction canceled: ${details.reasons.map((r) => r.type).join(", ")}`;
13
14
  }
14
15
  }
@@ -251,8 +252,8 @@ var Repo = class {
251
252
  const t = this.table.def.name;
252
253
  return t.charAt(0).toUpperCase() + t.slice(1);
253
254
  }
254
- get defaultPutData() {
255
- return this.config.defaultPutData?.() ?? {};
255
+ get defaultCreateData() {
256
+ return this.config.defaultCreateData?.() ?? {};
256
257
  }
257
258
  get defaultUpdateData() {
258
259
  return this.config.defaultUpdateData?.() ?? {};
@@ -260,9 +261,6 @@ var Repo = class {
260
261
  transformOutput(item) {
261
262
  return this.config.transformOutput?.(item) ?? item;
262
263
  }
263
- transformInput(item) {
264
- return this.config.transformInput?.(item) ?? item;
265
- }
266
264
  extractKey(item) {
267
265
  const { partitionKey, sortKey } = this.table.def;
268
266
  if (sortKey) return {
@@ -272,43 +270,44 @@ var Repo = class {
272
270
  return { [partitionKey]: item[partitionKey] };
273
271
  }
274
272
  async get(key, options) {
273
+ const { filter, ...restOptions } = options ?? {};
275
274
  const item = await this.db.get({
276
275
  table: this.tableName,
277
276
  key: this.extractKey(key),
278
- ...options
277
+ filter,
278
+ ...restOptions
279
279
  });
280
280
  return item && this.applyTransformIfNeeded(item, options);
281
281
  }
282
282
  async getOrThrow(key, options) {
283
+ const { filter, ...restOptions } = options ?? {};
283
284
  const item = await this.db.getOrThrow({
284
285
  table: this.tableName,
285
286
  key: this.extractKey(key),
286
287
  resource: this.resourceName,
287
- ...options
288
+ filter,
289
+ ...restOptions
288
290
  });
289
291
  return this.applyTransformIfNeeded(item, options);
290
292
  }
291
293
  async put(item, options) {
292
- const itemWithDefaults = this.applyPutTransforms(item);
293
294
  const result = await this.db.put({
294
295
  table: this.tableName,
295
- item: itemWithDefaults,
296
+ item,
296
297
  resource: this.resourceName,
297
298
  ...options
298
299
  });
299
300
  return this.applyTransformIfNeeded(result);
300
301
  }
301
302
  async update(key, update, options) {
302
- const updateWithDefaults = this.applyNormalizersToExpression({
303
- ...this.defaultUpdateData,
304
- ...update
305
- });
303
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
306
304
  const result = await this.db.update({
307
305
  table: this.tableName,
308
306
  key: this.extractKey(key),
309
307
  resource: this.resourceName,
310
308
  update: updateWithDefaults,
311
- ...options
309
+ ...options,
310
+ condition: this.withDiscriminatorCondition(key, options?.condition)
312
311
  });
313
312
  return this.applyTransformIfNeeded(result);
314
313
  }
@@ -316,10 +315,15 @@ var Repo = class {
316
315
  const { condition: otherCondition, ...otherOptions } = options ?? {};
317
316
  const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
318
317
  if (otherCondition) condition.$and.push(otherCondition);
319
- 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,
320
323
  condition,
321
324
  ...otherOptions
322
325
  });
326
+ return this.applyTransformIfNeeded(result);
323
327
  }
324
328
  async delete(key, options) {
325
329
  const item = await this.db.delete({
@@ -377,6 +381,35 @@ var Repo = class {
377
381
  gsi
378
382
  });
379
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
+ }
380
413
  async scan(options) {
381
414
  const items = await this.db.scan({
382
415
  table: this.tableName,
@@ -427,9 +460,11 @@ var Repo = class {
427
460
  });
428
461
  }
429
462
  async batchGet(keys, options) {
463
+ const { filter, ...restOptions } = options ?? {};
430
464
  const { items, unprocessed } = await this.db.batchGet({ [this.tableName]: {
431
465
  keys: keys.map((key) => this.extractKey(key)),
432
- ...options
466
+ filter,
467
+ ...restOptions
433
468
  } });
434
469
  const tableItems = items[this.tableName];
435
470
  return {
@@ -438,10 +473,12 @@ var Repo = class {
438
473
  };
439
474
  }
440
475
  async batchGetOrThrow(keys, options) {
476
+ const { filter, ...restOptions } = options ?? {};
441
477
  const result = await this.db.batchGetOrThrow({ [this.tableName]: {
442
478
  keys: keys.map((key) => this.extractKey(key)),
443
479
  resource: this.resourceName,
444
- ...options
480
+ filter,
481
+ ...restOptions
445
482
  } });
446
483
  return this.applyTransformsIfNeeded(result[this.tableName] ?? [], options);
447
484
  }
@@ -453,7 +490,7 @@ var Repo = class {
453
490
  };
454
491
  else return {
455
492
  type: "PUT",
456
- item: this.applyPutTransforms(request.item)
493
+ item: request.item
457
494
  };
458
495
  }) });
459
496
  return {
@@ -462,28 +499,29 @@ var Repo = class {
462
499
  };
463
500
  }
464
501
  async batchUpdate(keys, update) {
465
- const updateWithDefaults = this.applyNormalizersToExpression({
466
- ...this.defaultUpdateData,
467
- ...update
468
- });
502
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
469
503
  return { unprocessed: (await this.db.batchUpdate({ [this.tableName]: {
470
504
  keys: keys.map((key) => this.extractKey(key)),
471
505
  update: updateWithDefaults
472
506
  } })).unprocessed?.[this.tableName]?.keys };
473
507
  }
474
508
  async trxGet(keys, options) {
509
+ const { filter, ...restOptions } = options ?? {};
475
510
  return (await this.db.trxGet(...keys.map((key) => ({
476
511
  table: this.tableName,
477
512
  key: this.extractKey(key),
478
- ...options
513
+ filter,
514
+ ...restOptions
479
515
  })))).map((item) => item && this.applyTransformIfNeeded(item, options));
480
516
  }
481
517
  async trxGetOrThrow(keys, options) {
518
+ const { filter, ...restOptions } = options ?? {};
482
519
  const items = await this.db.trxGetOrThrow(...keys.map((key) => ({
483
520
  table: this.tableName,
484
521
  key: this.extractKey(key),
485
522
  resource: this.resourceName,
486
- ...options
523
+ filter,
524
+ ...restOptions
487
525
  })));
488
526
  return this.applyTransformsIfNeeded(items, options);
489
527
  }
@@ -491,8 +529,8 @@ var Repo = class {
491
529
  await this.db.trxWrite(...requests.map((request) => {
492
530
  switch (request.type) {
493
531
  case "CONDITION": {
494
- const { key, condition, ...options } = request;
495
- return this.trxConditionRequest(key, condition, options);
532
+ const { key, condition } = request;
533
+ return this.trxConditionRequest(key, condition);
496
534
  }
497
535
  case "DELETE": {
498
536
  const { key, ...options } = request;
@@ -537,85 +575,125 @@ var Repo = class {
537
575
  ...options
538
576
  };
539
577
  }
540
- trxConditionRequest(key, condition, options) {
578
+ trxConditionRequest(key, condition) {
541
579
  return {
542
580
  table: this.tableName,
543
581
  type: "CONDITION",
544
582
  key: this.extractKey(key),
545
- condition,
546
- ...options
583
+ condition
547
584
  };
548
585
  }
549
586
  trxPutRequest(item, options) {
550
- const itemWithDefaults = this.applyPutTransforms(item);
551
587
  return {
552
588
  table: this.tableName,
553
589
  type: "PUT",
554
- item: itemWithDefaults,
590
+ item,
555
591
  ...options
556
592
  };
557
593
  }
558
594
  trxUpdateRequest(key, update, options) {
559
- const updateWithDefaults = this.applyNormalizersToExpression({
560
- ...this.defaultUpdateData,
561
- ...update
562
- });
595
+ const updateWithDefaults = this.applyNormalizersToExpression(update);
563
596
  return {
564
597
  table: this.tableName,
565
598
  type: "UPDATE",
566
599
  key: this.extractKey(key),
567
600
  update: updateWithDefaults,
568
- ...options
601
+ ...options,
602
+ condition: this.withDiscriminatorCondition(key, options?.condition)
569
603
  };
570
604
  }
571
605
  trxCreateRequest(item, options) {
572
606
  const { condition: otherCondition, ...otherOptions } = options ?? {};
573
607
  const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
574
608
  if (otherCondition) condition.$and.push(otherCondition);
575
- const itemWithDefaults = this.applyPutTransforms(item);
609
+ const normalizedItem = this.applyCreateTransforms(item);
576
610
  return {
577
611
  table: this.tableName,
578
612
  type: "PUT",
579
- item: itemWithDefaults,
613
+ item: normalizedItem,
580
614
  condition,
581
615
  ...otherOptions
582
616
  };
583
617
  }
584
- applyPutTransforms(item) {
585
- const result = { ...this.transformInput({
586
- ...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,
587
628
  ...item
588
- }) };
589
- for (const key of this.config.derivedAttributes ?? []) delete result[key];
590
- return result;
591
- }
592
- applyNormalizersToExpression(expression) {
593
- const partial = {};
594
- for (const [key, val] of Object.entries(expression)) if (val === void 0 || isOperation(val) && val.$remove === true) partial[key] = void 0;
595
- else if (!isOperation(val)) partial[key] = val;
596
- else if (val.$set !== void 0) partial[key] = val.$set;
597
- else if (val.$ifNotExists !== void 0) partial[key] = Array.isArray(val.$ifNotExists) ? val.$ifNotExists[1] : val.$ifNotExists;
598
- const normalized = this.transformInput(partial);
599
- const result = { ...expression };
600
- for (const [key, normalizedVal] of Object.entries(normalized)) {
601
- if (normalizedVal === void 0) continue;
602
- if (key in partial && partial[key] === void 0 || !(key in partial)) result[key] = normalizedVal;
603
- else {
604
- const original = expression[key];
605
- if (!isOperation(original)) result[key] = normalizedVal;
606
- else if (original.$set !== void 0) result[key] = {
607
- ...original,
608
- $set: normalizedVal
609
- };
610
- else if (original.$ifNotExists !== void 0) result[key] = {
611
- ...original,
612
- $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)
613
671
  };
614
672
  }
615
673
  }
616
- 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
+ }
617
686
  return result;
618
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
+ }
619
697
  applyTransformsIfNeeded(items, options) {
620
698
  if (options?.projection?.length) return items;
621
699
  if (options?.gsi) {
@@ -685,7 +763,7 @@ var Db = class {
685
763
  ExpressionAttributeNames: exp.attributeNames
686
764
  });
687
765
  const output = await this.client.send(input);
688
- if (output.Item && data.filter && !data.filter(output.Item)) return;
766
+ if (output.Item && data.filter && !require_util.matchesPartial(data.filter, output.Item)) return;
689
767
  return output.Item;
690
768
  }
691
769
  async getOrThrow(data) {
@@ -888,7 +966,7 @@ var Db = class {
888
966
  const output = await this.client.send(input);
889
967
  for (const [table, items] of Object.entries(output.Responses ?? {})) {
890
968
  if (!result.items[table]) continue;
891
- 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;
892
970
  result.items[table].push(...filteredItems);
893
971
  }
894
972
  let unprocessedKeyCount = 0;
@@ -1049,7 +1127,7 @@ var Db = class {
1049
1127
  return (await this.client.send(input)).Responses?.map((response, i) => {
1050
1128
  if (!response.Item) return;
1051
1129
  if (!requests[i]?.filter) return response.Item;
1052
- return requests[i].filter(response.Item) ? response.Item : void 0;
1130
+ return require_util.matchesPartial(requests[i].filter, response.Item) ? response.Item : void 0;
1053
1131
  }) ?? [];
1054
1132
  }
1055
1133
  async trxGetOrThrow(...requests) {
@@ -1121,6 +1199,7 @@ exports.chunk = require_util.chunk;
1121
1199
  exports.extractTableDesc = require_util.extractTableDesc;
1122
1200
  exports.isDinahError = isDinahError;
1123
1201
  exports.makeRepo = makeRepo;
1202
+ exports.matchesPartial = require_util.matchesPartial;
1124
1203
  exports.removeUndefined = require_util.removeUndefined;
1125
1204
  exports.resolveAttrType = require_util.resolveAttrType;
1126
1205