dyno-table 2.0.1 → 2.1.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.
Files changed (72) hide show
  1. package/README.md +10 -0
  2. package/dist/{batch-builder-TpkmiBp5.d.ts → batch-builder-BytHNL_u.d.ts} +1 -1
  3. package/dist/{batch-builder-CzNAxWNT.d.cts → batch-builder-CcxFDKhe.d.cts} +1 -1
  4. package/dist/builders/condition-check-builder.cjs +19 -0
  5. package/dist/builders/condition-check-builder.cjs.map +1 -1
  6. package/dist/builders/condition-check-builder.d.cts +12 -3
  7. package/dist/builders/condition-check-builder.d.ts +12 -3
  8. package/dist/builders/condition-check-builder.js +19 -0
  9. package/dist/builders/condition-check-builder.js.map +1 -1
  10. package/dist/builders/delete-builder.cjs +19 -0
  11. package/dist/builders/delete-builder.cjs.map +1 -1
  12. package/dist/builders/delete-builder.d.cts +12 -3
  13. package/dist/builders/delete-builder.d.ts +12 -3
  14. package/dist/builders/delete-builder.js +19 -0
  15. package/dist/builders/delete-builder.js.map +1 -1
  16. package/dist/builders/paginator.cjs.map +1 -1
  17. package/dist/builders/paginator.js.map +1 -1
  18. package/dist/builders/put-builder.cjs +19 -0
  19. package/dist/builders/put-builder.cjs.map +1 -1
  20. package/dist/builders/put-builder.d.cts +12 -3
  21. package/dist/builders/put-builder.d.ts +12 -3
  22. package/dist/builders/put-builder.js +19 -0
  23. package/dist/builders/put-builder.js.map +1 -1
  24. package/dist/builders/query-builder.cjs.map +1 -1
  25. package/dist/builders/query-builder.d.cts +2 -2
  26. package/dist/builders/query-builder.d.ts +2 -2
  27. package/dist/builders/query-builder.js.map +1 -1
  28. package/dist/builders/transaction-builder.cjs +19 -0
  29. package/dist/builders/transaction-builder.cjs.map +1 -1
  30. package/dist/builders/transaction-builder.d.cts +2 -2
  31. package/dist/builders/transaction-builder.d.ts +2 -2
  32. package/dist/builders/transaction-builder.js +19 -0
  33. package/dist/builders/transaction-builder.js.map +1 -1
  34. package/dist/builders/update-builder.cjs +19 -0
  35. package/dist/builders/update-builder.cjs.map +1 -1
  36. package/dist/builders/update-builder.d.cts +11 -2
  37. package/dist/builders/update-builder.d.ts +11 -2
  38. package/dist/builders/update-builder.js +19 -0
  39. package/dist/builders/update-builder.js.map +1 -1
  40. package/dist/{conditions-3ae5znV_.d.cts → conditions-CC3NDfUU.d.cts} +15 -13
  41. package/dist/{conditions-BtynAviC.d.ts → conditions-DD0bvyHm.d.ts} +15 -13
  42. package/dist/conditions.cjs.map +1 -1
  43. package/dist/conditions.d.cts +1 -1
  44. package/dist/conditions.d.ts +1 -1
  45. package/dist/conditions.js.map +1 -1
  46. package/dist/entity.cjs +192 -42
  47. package/dist/entity.cjs.map +1 -1
  48. package/dist/entity.d.cts +20 -8
  49. package/dist/entity.d.ts +20 -8
  50. package/dist/entity.js +192 -42
  51. package/dist/entity.js.map +1 -1
  52. package/dist/index.cjs +203 -34
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.cts +4 -4
  55. package/dist/index.d.ts +4 -4
  56. package/dist/index.js +203 -34
  57. package/dist/index.js.map +1 -1
  58. package/dist/{query-builder-BK9eM-gt.d.ts → query-builder-BNWRCrJW.d.ts} +1 -1
  59. package/dist/{query-builder-BALW-vW_.d.cts → query-builder-DZ9JKgBN.d.cts} +1 -1
  60. package/dist/{table-CEL7Lt1r.d.ts → table-BhEeYauU.d.ts} +3 -3
  61. package/dist/{table-DhvYVXQ6.d.cts → table-BpNOboD9.d.cts} +3 -3
  62. package/dist/table.cjs +19 -0
  63. package/dist/table.cjs.map +1 -1
  64. package/dist/table.d.cts +4 -4
  65. package/dist/table.d.ts +4 -4
  66. package/dist/table.js +19 -0
  67. package/dist/table.js.map +1 -1
  68. package/dist/types.d.cts +9 -2
  69. package/dist/types.d.ts +9 -2
  70. package/dist/utils.cjs.map +1 -1
  71. package/dist/utils.js.map +1 -1
  72. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,12 +1,12 @@
1
- export { T as Table } from './table-DhvYVXQ6.cjs';
1
+ export { T as Table } from './table-BpNOboD9.cjs';
2
2
  export { EntityConfig, EntityRepository, IndexDefinition, QueryEntity, QueryRecord, createIndex, createQueries, defineEntity } from './entity.cjs';
3
- export { p as ComparisonOperator, C as Condition, q as ConditionOperator, E as ExpressionParams, K as KeyConditionOperator, L as LogicalOperator, P as PrimaryKey, r as PrimaryKeyWithoutExpression, k as and, h as attributeExists, j as attributeNotExists, d as beginsWith, c as between, f as contains, e as eq, g as gt, b as gte, i as inArray, l as lt, a as lte, n as ne, m as not, o as or } from './conditions-3ae5znV_.cjs';
4
- export { Q as QueryBuilder, a as QueryOptions } from './query-builder-BALW-vW_.cjs';
3
+ export { p as ComparisonOperator, C as Condition, q as ConditionOperator, E as ExpressionParams, K as KeyConditionOperator, L as LogicalOperator, P as PrimaryKey, r as PrimaryKeyWithoutExpression, k as and, h as attributeExists, j as attributeNotExists, d as beginsWith, c as between, f as contains, e as eq, g as gt, b as gte, i as inArray, l as lt, a as lte, n as ne, m as not, o as or } from './conditions-CC3NDfUU.cjs';
4
+ export { Q as QueryBuilder, a as QueryOptions } from './query-builder-DZ9JKgBN.cjs';
5
5
  export { PutBuilder, PutOptions } from './builders/put-builder.cjs';
6
6
  export { UpdateBuilder, UpdateOptions } from './builders/update-builder.cjs';
7
7
  export { DeleteBuilder, DeleteOptions } from './builders/delete-builder.cjs';
8
8
  export { TransactionBuilder, TransactionOptions } from './builders/transaction-builder.cjs';
9
- export { B as BatchBuilder, a as BatchError, b as BatchResult } from './batch-builder-CzNAxWNT.cjs';
9
+ export { B as BatchBuilder, a as BatchError, b as BatchResult } from './batch-builder-CcxFDKhe.cjs';
10
10
  export { partitionKey, sortKey } from './utils.cjs';
11
11
  import './types.cjs';
12
12
  import '@aws-sdk/lib-dynamodb';
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- export { T as Table } from './table-CEL7Lt1r.js';
1
+ export { T as Table } from './table-BhEeYauU.js';
2
2
  export { EntityConfig, EntityRepository, IndexDefinition, QueryEntity, QueryRecord, createIndex, createQueries, defineEntity } from './entity.js';
3
- export { p as ComparisonOperator, C as Condition, q as ConditionOperator, E as ExpressionParams, K as KeyConditionOperator, L as LogicalOperator, P as PrimaryKey, r as PrimaryKeyWithoutExpression, k as and, h as attributeExists, j as attributeNotExists, d as beginsWith, c as between, f as contains, e as eq, g as gt, b as gte, i as inArray, l as lt, a as lte, n as ne, m as not, o as or } from './conditions-BtynAviC.js';
4
- export { Q as QueryBuilder, a as QueryOptions } from './query-builder-BK9eM-gt.js';
3
+ export { p as ComparisonOperator, C as Condition, q as ConditionOperator, E as ExpressionParams, K as KeyConditionOperator, L as LogicalOperator, P as PrimaryKey, r as PrimaryKeyWithoutExpression, k as and, h as attributeExists, j as attributeNotExists, d as beginsWith, c as between, f as contains, e as eq, g as gt, b as gte, i as inArray, l as lt, a as lte, n as ne, m as not, o as or } from './conditions-DD0bvyHm.js';
4
+ export { Q as QueryBuilder, a as QueryOptions } from './query-builder-BNWRCrJW.js';
5
5
  export { PutBuilder, PutOptions } from './builders/put-builder.js';
6
6
  export { UpdateBuilder, UpdateOptions } from './builders/update-builder.js';
7
7
  export { DeleteBuilder, DeleteOptions } from './builders/delete-builder.js';
8
8
  export { TransactionBuilder, TransactionOptions } from './builders/transaction-builder.js';
9
- export { B as BatchBuilder, a as BatchError, b as BatchResult } from './batch-builder-TpkmiBp5.js';
9
+ export { B as BatchBuilder, a as BatchError, b as BatchResult } from './batch-builder-BytHNL_u.js';
10
10
  export { partitionKey, sortKey } from './utils.js';
11
11
  import './types.js';
12
12
  import '@aws-sdk/lib-dynamodb';
package/dist/index.js CHANGED
@@ -45,6 +45,25 @@ var not = (condition) => ({
45
45
 
46
46
  // src/expression.ts
47
47
  var generateAttributeName = (params, attr) => {
48
+ if (attr.includes(".")) {
49
+ const pathSegments = attr.split(".");
50
+ const segmentNames = [];
51
+ for (const segment of pathSegments) {
52
+ let segmentName;
53
+ for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
54
+ if (existingAttr === segment) {
55
+ segmentName = existingName;
56
+ break;
57
+ }
58
+ }
59
+ if (!segmentName) {
60
+ segmentName = `#${Object.keys(params.expressionAttributeNames).length}`;
61
+ params.expressionAttributeNames[segmentName] = segment;
62
+ }
63
+ segmentNames.push(segmentName);
64
+ }
65
+ return segmentNames.join(".");
66
+ }
48
67
  for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
49
68
  if (existingAttr === attr) {
50
69
  return existingName;
@@ -3753,27 +3772,133 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
3753
3772
  return createEntityAwareBuilder(builder, entityName);
3754
3773
  }
3755
3774
 
3756
- // src/entity.ts
3757
- function defineEntity(config) {
3758
- const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
3759
- const buildIndexes = (dataForKeyGeneration, table) => {
3760
- return Object.entries(config.indexes ?? {}).reduce(
3761
- (acc, [indexName, index]) => {
3762
- const key = index.generateKey(dataForKeyGeneration);
3763
- const gsiConfig = table.gsis[indexName];
3775
+ // src/entity/ddb-indexing.ts
3776
+ var IndexBuilder = class {
3777
+ /**
3778
+ * Creates a new IndexBuilder instance
3779
+ *
3780
+ * @param table - The DynamoDB table instance
3781
+ * @param indexes - The index definitions
3782
+ */
3783
+ constructor(table, indexes = {}) {
3784
+ this.table = table;
3785
+ this.indexes = indexes;
3786
+ }
3787
+ /**
3788
+ * Build index attributes for item creation
3789
+ *
3790
+ * @param item - The item to generate indexes for
3791
+ * @param options - Options for building indexes
3792
+ * @returns Record of GSI attribute names to their values
3793
+ */
3794
+ buildForCreate(item, options = {}) {
3795
+ const attributes = {};
3796
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3797
+ if (options.excludeReadOnly && indexDef.isReadOnly) {
3798
+ continue;
3799
+ }
3800
+ const key = indexDef.generateKey(item);
3801
+ const gsiConfig = this.table.gsis[indexName];
3802
+ if (!gsiConfig) {
3803
+ throw new Error(`GSI configuration not found for index: ${indexName}`);
3804
+ }
3805
+ if (key.pk) {
3806
+ attributes[gsiConfig.partitionKey] = key.pk;
3807
+ }
3808
+ if (key.sk && gsiConfig.sortKey) {
3809
+ attributes[gsiConfig.sortKey] = key.sk;
3810
+ }
3811
+ }
3812
+ return attributes;
3813
+ }
3814
+ /**
3815
+ * Build index attributes for item updates
3816
+ *
3817
+ * @param currentData - The current data before update
3818
+ * @param updates - The update data
3819
+ * @param options - Options for building indexes
3820
+ * @returns Record of GSI attribute names to their updated values
3821
+ */
3822
+ buildForUpdate(currentData, updates) {
3823
+ const attributes = {};
3824
+ const updatedItem = { ...currentData, ...updates };
3825
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3826
+ if (indexDef.isReadOnly) {
3827
+ continue;
3828
+ }
3829
+ let shouldUpdateIndex = false;
3830
+ try {
3831
+ const currentKey = indexDef.generateKey(currentData);
3832
+ const updatedKey = indexDef.generateKey(updatedItem);
3833
+ if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
3834
+ shouldUpdateIndex = true;
3835
+ }
3836
+ } catch {
3837
+ shouldUpdateIndex = true;
3838
+ }
3839
+ if (!shouldUpdateIndex) {
3840
+ continue;
3841
+ }
3842
+ try {
3843
+ const key = indexDef.generateKey(updatedItem);
3844
+ if (this.hasUndefinedValues(key)) {
3845
+ throw new Error(
3846
+ `Cannot update entity: insufficient data to regenerate index "${indexName}". All attributes required by the index must be provided in the update operation, or the index must be marked as readOnly.`
3847
+ );
3848
+ }
3849
+ const gsiConfig = this.table.gsis[indexName];
3764
3850
  if (!gsiConfig) {
3765
3851
  throw new Error(`GSI configuration not found for index: ${indexName}`);
3766
3852
  }
3767
3853
  if (key.pk) {
3768
- acc[gsiConfig.partitionKey] = key.pk;
3854
+ attributes[gsiConfig.partitionKey] = key.pk;
3769
3855
  }
3770
3856
  if (key.sk && gsiConfig.sortKey) {
3771
- acc[gsiConfig.sortKey] = key.sk;
3857
+ attributes[gsiConfig.sortKey] = key.sk;
3772
3858
  }
3773
- return acc;
3774
- },
3775
- {}
3776
- );
3859
+ } catch (error) {
3860
+ if (error instanceof Error && error.message.includes("insufficient data")) {
3861
+ throw error;
3862
+ }
3863
+ throw new Error(
3864
+ `Cannot update entity: insufficient data to regenerate index "${indexName}". All attributes required by the index must be provided in the update operation, or the index must be readOnly.`
3865
+ );
3866
+ }
3867
+ }
3868
+ return attributes;
3869
+ }
3870
+ /**
3871
+ * Check if a key has undefined values
3872
+ *
3873
+ * @param key - The index key to check
3874
+ * @returns True if the key contains undefined values, false otherwise
3875
+ */
3876
+ hasUndefinedValues(key) {
3877
+ return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
3878
+ }
3879
+ };
3880
+
3881
+ // src/entity/index-utils.ts
3882
+ function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
3883
+ if (!indexes) {
3884
+ return {};
3885
+ }
3886
+ const indexBuilder = new IndexBuilder(table, indexes);
3887
+ return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
3888
+ }
3889
+ function buildIndexUpdates(currentData, updates, table, indexes) {
3890
+ if (!indexes) {
3891
+ return {};
3892
+ }
3893
+ const indexBuilder = new IndexBuilder(table, indexes);
3894
+ return indexBuilder.buildForUpdate(currentData, updates);
3895
+ }
3896
+
3897
+ // src/entity/entity.ts
3898
+ function defineEntity(config) {
3899
+ const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
3900
+ const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
3901
+ return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
3777
3902
  };
3778
3903
  const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
3779
3904
  const wrappedMethod = (...args) => {
@@ -3825,7 +3950,7 @@ function defineEntity(config) {
3825
3950
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3826
3951
  };
3827
3952
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3828
- const indexes = buildIndexes(dataForKeyGeneration, table);
3953
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3829
3954
  const validatedItem = {
3830
3955
  ...dataForKeyGeneration,
3831
3956
  [entityTypeAttributeName]: config.name,
@@ -3851,7 +3976,7 @@ function defineEntity(config) {
3851
3976
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3852
3977
  };
3853
3978
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3854
- const indexes = buildIndexes(dataForKeyGeneration, table);
3979
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3855
3980
  const validatedItem = {
3856
3981
  ...dataForKeyGeneration,
3857
3982
  [entityTypeAttributeName]: config.name,
@@ -3893,7 +4018,7 @@ function defineEntity(config) {
3893
4018
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3894
4019
  };
3895
4020
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3896
- const indexes = buildIndexes(dataForKeyGeneration, table);
4021
+ const indexes = buildIndexes2(dataForKeyGeneration, table, false);
3897
4022
  const validatedItem = {
3898
4023
  [table.partitionKey]: primaryKey.pk,
3899
4024
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -3919,7 +4044,7 @@ function defineEntity(config) {
3919
4044
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3920
4045
  };
3921
4046
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3922
- const indexes = buildIndexes(dataForKeyGeneration, table);
4047
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3923
4048
  const validatedItem = {
3924
4049
  [table.partitionKey]: primaryKey.pk,
3925
4050
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -3953,13 +4078,21 @@ function defineEntity(config) {
3953
4078
  }
3954
4079
  return createEntityAwarePutBuilder(builder, config.name);
3955
4080
  },
3956
- get: (key) => createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name),
4081
+ get: (key) => {
4082
+ return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
4083
+ },
3957
4084
  update: (key, data) => {
3958
4085
  const primaryKeyObj = config.primaryKey.generateKey(key);
3959
4086
  const builder = table.update(primaryKeyObj);
3960
4087
  builder.condition(eq(entityTypeAttributeName, config.name));
3961
4088
  const timestamps = generateTimestamps(["updatedAt"], data);
3962
- builder.set({ ...data, ...timestamps });
4089
+ const indexUpdates = buildIndexUpdates(
4090
+ { ...key },
4091
+ { ...data, ...timestamps },
4092
+ table,
4093
+ config.indexes
4094
+ );
4095
+ builder.set({ ...data, ...timestamps, ...indexUpdates });
3963
4096
  return builder;
3964
4097
  },
3965
4098
  delete: (key) => {
@@ -4030,21 +4163,57 @@ function createQueries() {
4030
4163
  }
4031
4164
  function createIndex() {
4032
4165
  return {
4033
- input: (schema) => ({
4034
- partitionKey: (pkFn) => ({
4035
- sortKey: (skFn) => ({
4036
- name: "custom",
4037
- partitionKey: "pk",
4038
- sortKey: "sk",
4039
- generateKey: (item) => ({ pk: pkFn(item), sk: skFn(item) })
4166
+ input: (schema) => {
4167
+ const createIndexBuilder = (isReadOnly = false) => ({
4168
+ partitionKey: (pkFn) => ({
4169
+ sortKey: (skFn) => {
4170
+ const index = {
4171
+ name: "custom",
4172
+ partitionKey: "pk",
4173
+ sortKey: "sk",
4174
+ isReadOnly,
4175
+ generateKey: (item) => {
4176
+ const data = schema["~standard"].validate(item);
4177
+ if ("issues" in data && data.issues) {
4178
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
4179
+ }
4180
+ const validData = "value" in data ? data.value : item;
4181
+ return { pk: pkFn(validData), sk: skFn(validData) };
4182
+ }
4183
+ };
4184
+ return Object.assign(index, {
4185
+ readOnly: (value = false) => ({
4186
+ ...index,
4187
+ isReadOnly: value
4188
+ })
4189
+ });
4190
+ },
4191
+ withoutSortKey: () => {
4192
+ const index = {
4193
+ name: "custom",
4194
+ partitionKey: "pk",
4195
+ isReadOnly,
4196
+ generateKey: (item) => {
4197
+ const data = schema["~standard"].validate(item);
4198
+ if ("issues" in data && data.issues) {
4199
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
4200
+ }
4201
+ const validData = "value" in data ? data.value : item;
4202
+ return { pk: pkFn(validData) };
4203
+ }
4204
+ };
4205
+ return Object.assign(index, {
4206
+ readOnly: (value = true) => ({
4207
+ ...index,
4208
+ isReadOnly: value
4209
+ })
4210
+ });
4211
+ }
4040
4212
  }),
4041
- withoutSortKey: () => ({
4042
- name: "custom",
4043
- partitionKey: "pk",
4044
- generateKey: (item) => ({ pk: pkFn(item) })
4045
- })
4046
- })
4047
- })
4213
+ readOnly: (value = true) => createIndexBuilder(value)
4214
+ });
4215
+ return createIndexBuilder(false);
4216
+ }
4048
4217
  };
4049
4218
  }
4050
4219