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.cjs CHANGED
@@ -47,6 +47,25 @@ var not = (condition) => ({
47
47
 
48
48
  // src/expression.ts
49
49
  var generateAttributeName = (params, attr) => {
50
+ if (attr.includes(".")) {
51
+ const pathSegments = attr.split(".");
52
+ const segmentNames = [];
53
+ for (const segment of pathSegments) {
54
+ let segmentName;
55
+ for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
56
+ if (existingAttr === segment) {
57
+ segmentName = existingName;
58
+ break;
59
+ }
60
+ }
61
+ if (!segmentName) {
62
+ segmentName = `#${Object.keys(params.expressionAttributeNames).length}`;
63
+ params.expressionAttributeNames[segmentName] = segment;
64
+ }
65
+ segmentNames.push(segmentName);
66
+ }
67
+ return segmentNames.join(".");
68
+ }
50
69
  for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
51
70
  if (existingAttr === attr) {
52
71
  return existingName;
@@ -3755,27 +3774,133 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
3755
3774
  return createEntityAwareBuilder(builder, entityName);
3756
3775
  }
3757
3776
 
3758
- // src/entity.ts
3759
- function defineEntity(config) {
3760
- const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
3761
- const buildIndexes = (dataForKeyGeneration, table) => {
3762
- return Object.entries(config.indexes ?? {}).reduce(
3763
- (acc, [indexName, index]) => {
3764
- const key = index.generateKey(dataForKeyGeneration);
3765
- const gsiConfig = table.gsis[indexName];
3777
+ // src/entity/ddb-indexing.ts
3778
+ var IndexBuilder = class {
3779
+ /**
3780
+ * Creates a new IndexBuilder instance
3781
+ *
3782
+ * @param table - The DynamoDB table instance
3783
+ * @param indexes - The index definitions
3784
+ */
3785
+ constructor(table, indexes = {}) {
3786
+ this.table = table;
3787
+ this.indexes = indexes;
3788
+ }
3789
+ /**
3790
+ * Build index attributes for item creation
3791
+ *
3792
+ * @param item - The item to generate indexes for
3793
+ * @param options - Options for building indexes
3794
+ * @returns Record of GSI attribute names to their values
3795
+ */
3796
+ buildForCreate(item, options = {}) {
3797
+ const attributes = {};
3798
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3799
+ if (options.excludeReadOnly && indexDef.isReadOnly) {
3800
+ continue;
3801
+ }
3802
+ const key = indexDef.generateKey(item);
3803
+ const gsiConfig = this.table.gsis[indexName];
3804
+ if (!gsiConfig) {
3805
+ throw new Error(`GSI configuration not found for index: ${indexName}`);
3806
+ }
3807
+ if (key.pk) {
3808
+ attributes[gsiConfig.partitionKey] = key.pk;
3809
+ }
3810
+ if (key.sk && gsiConfig.sortKey) {
3811
+ attributes[gsiConfig.sortKey] = key.sk;
3812
+ }
3813
+ }
3814
+ return attributes;
3815
+ }
3816
+ /**
3817
+ * Build index attributes for item updates
3818
+ *
3819
+ * @param currentData - The current data before update
3820
+ * @param updates - The update data
3821
+ * @param options - Options for building indexes
3822
+ * @returns Record of GSI attribute names to their updated values
3823
+ */
3824
+ buildForUpdate(currentData, updates) {
3825
+ const attributes = {};
3826
+ const updatedItem = { ...currentData, ...updates };
3827
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
3828
+ if (indexDef.isReadOnly) {
3829
+ continue;
3830
+ }
3831
+ let shouldUpdateIndex = false;
3832
+ try {
3833
+ const currentKey = indexDef.generateKey(currentData);
3834
+ const updatedKey = indexDef.generateKey(updatedItem);
3835
+ if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
3836
+ shouldUpdateIndex = true;
3837
+ }
3838
+ } catch {
3839
+ shouldUpdateIndex = true;
3840
+ }
3841
+ if (!shouldUpdateIndex) {
3842
+ continue;
3843
+ }
3844
+ try {
3845
+ const key = indexDef.generateKey(updatedItem);
3846
+ if (this.hasUndefinedValues(key)) {
3847
+ throw new Error(
3848
+ `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.`
3849
+ );
3850
+ }
3851
+ const gsiConfig = this.table.gsis[indexName];
3766
3852
  if (!gsiConfig) {
3767
3853
  throw new Error(`GSI configuration not found for index: ${indexName}`);
3768
3854
  }
3769
3855
  if (key.pk) {
3770
- acc[gsiConfig.partitionKey] = key.pk;
3856
+ attributes[gsiConfig.partitionKey] = key.pk;
3771
3857
  }
3772
3858
  if (key.sk && gsiConfig.sortKey) {
3773
- acc[gsiConfig.sortKey] = key.sk;
3859
+ attributes[gsiConfig.sortKey] = key.sk;
3774
3860
  }
3775
- return acc;
3776
- },
3777
- {}
3778
- );
3861
+ } catch (error) {
3862
+ if (error instanceof Error && error.message.includes("insufficient data")) {
3863
+ throw error;
3864
+ }
3865
+ throw new Error(
3866
+ `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.`
3867
+ );
3868
+ }
3869
+ }
3870
+ return attributes;
3871
+ }
3872
+ /**
3873
+ * Check if a key has undefined values
3874
+ *
3875
+ * @param key - The index key to check
3876
+ * @returns True if the key contains undefined values, false otherwise
3877
+ */
3878
+ hasUndefinedValues(key) {
3879
+ return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
3880
+ }
3881
+ };
3882
+
3883
+ // src/entity/index-utils.ts
3884
+ function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
3885
+ if (!indexes) {
3886
+ return {};
3887
+ }
3888
+ const indexBuilder = new IndexBuilder(table, indexes);
3889
+ return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
3890
+ }
3891
+ function buildIndexUpdates(currentData, updates, table, indexes) {
3892
+ if (!indexes) {
3893
+ return {};
3894
+ }
3895
+ const indexBuilder = new IndexBuilder(table, indexes);
3896
+ return indexBuilder.buildForUpdate(currentData, updates);
3897
+ }
3898
+
3899
+ // src/entity/entity.ts
3900
+ function defineEntity(config) {
3901
+ const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
3902
+ const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
3903
+ return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
3779
3904
  };
3780
3905
  const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
3781
3906
  const wrappedMethod = (...args) => {
@@ -3827,7 +3952,7 @@ function defineEntity(config) {
3827
3952
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3828
3953
  };
3829
3954
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3830
- const indexes = buildIndexes(dataForKeyGeneration, table);
3955
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3831
3956
  const validatedItem = {
3832
3957
  ...dataForKeyGeneration,
3833
3958
  [entityTypeAttributeName]: config.name,
@@ -3853,7 +3978,7 @@ function defineEntity(config) {
3853
3978
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3854
3979
  };
3855
3980
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3856
- const indexes = buildIndexes(dataForKeyGeneration, table);
3981
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3857
3982
  const validatedItem = {
3858
3983
  ...dataForKeyGeneration,
3859
3984
  [entityTypeAttributeName]: config.name,
@@ -3895,7 +4020,7 @@ function defineEntity(config) {
3895
4020
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
3896
4021
  };
3897
4022
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3898
- const indexes = buildIndexes(dataForKeyGeneration, table);
4023
+ const indexes = buildIndexes2(dataForKeyGeneration, table, false);
3899
4024
  const validatedItem = {
3900
4025
  [table.partitionKey]: primaryKey.pk,
3901
4026
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -3921,7 +4046,7 @@ function defineEntity(config) {
3921
4046
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
3922
4047
  };
3923
4048
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
3924
- const indexes = buildIndexes(dataForKeyGeneration, table);
4049
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
3925
4050
  const validatedItem = {
3926
4051
  [table.partitionKey]: primaryKey.pk,
3927
4052
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -3955,13 +4080,21 @@ function defineEntity(config) {
3955
4080
  }
3956
4081
  return createEntityAwarePutBuilder(builder, config.name);
3957
4082
  },
3958
- get: (key) => createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name),
4083
+ get: (key) => {
4084
+ return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
4085
+ },
3959
4086
  update: (key, data) => {
3960
4087
  const primaryKeyObj = config.primaryKey.generateKey(key);
3961
4088
  const builder = table.update(primaryKeyObj);
3962
4089
  builder.condition(eq(entityTypeAttributeName, config.name));
3963
4090
  const timestamps = generateTimestamps(["updatedAt"], data);
3964
- builder.set({ ...data, ...timestamps });
4091
+ const indexUpdates = buildIndexUpdates(
4092
+ { ...key },
4093
+ { ...data, ...timestamps },
4094
+ table,
4095
+ config.indexes
4096
+ );
4097
+ builder.set({ ...data, ...timestamps, ...indexUpdates });
3965
4098
  return builder;
3966
4099
  },
3967
4100
  delete: (key) => {
@@ -4032,21 +4165,57 @@ function createQueries() {
4032
4165
  }
4033
4166
  function createIndex() {
4034
4167
  return {
4035
- input: (schema) => ({
4036
- partitionKey: (pkFn) => ({
4037
- sortKey: (skFn) => ({
4038
- name: "custom",
4039
- partitionKey: "pk",
4040
- sortKey: "sk",
4041
- generateKey: (item) => ({ pk: pkFn(item), sk: skFn(item) })
4168
+ input: (schema) => {
4169
+ const createIndexBuilder = (isReadOnly = false) => ({
4170
+ partitionKey: (pkFn) => ({
4171
+ sortKey: (skFn) => {
4172
+ const index = {
4173
+ name: "custom",
4174
+ partitionKey: "pk",
4175
+ sortKey: "sk",
4176
+ isReadOnly,
4177
+ generateKey: (item) => {
4178
+ const data = schema["~standard"].validate(item);
4179
+ if ("issues" in data && data.issues) {
4180
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
4181
+ }
4182
+ const validData = "value" in data ? data.value : item;
4183
+ return { pk: pkFn(validData), sk: skFn(validData) };
4184
+ }
4185
+ };
4186
+ return Object.assign(index, {
4187
+ readOnly: (value = false) => ({
4188
+ ...index,
4189
+ isReadOnly: value
4190
+ })
4191
+ });
4192
+ },
4193
+ withoutSortKey: () => {
4194
+ const index = {
4195
+ name: "custom",
4196
+ partitionKey: "pk",
4197
+ isReadOnly,
4198
+ generateKey: (item) => {
4199
+ const data = schema["~standard"].validate(item);
4200
+ if ("issues" in data && data.issues) {
4201
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
4202
+ }
4203
+ const validData = "value" in data ? data.value : item;
4204
+ return { pk: pkFn(validData) };
4205
+ }
4206
+ };
4207
+ return Object.assign(index, {
4208
+ readOnly: (value = true) => ({
4209
+ ...index,
4210
+ isReadOnly: value
4211
+ })
4212
+ });
4213
+ }
4042
4214
  }),
4043
- withoutSortKey: () => ({
4044
- name: "custom",
4045
- partitionKey: "pk",
4046
- generateKey: (item) => ({ pk: pkFn(item) })
4047
- })
4048
- })
4049
- })
4215
+ readOnly: (value = true) => createIndexBuilder(value)
4216
+ });
4217
+ return createIndexBuilder(false);
4218
+ }
4050
4219
  };
4051
4220
  }
4052
4221