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/entity.cjs CHANGED
@@ -1,13 +1,5 @@
1
1
  'use strict';
2
2
 
3
- // src/conditions.ts
4
- var createComparisonCondition = (type) => (attr, value) => ({
5
- type,
6
- attr,
7
- value
8
- });
9
- var eq = createComparisonCondition("eq");
10
-
11
3
  // src/builders/entity-aware-builders.ts
12
4
  function createEntityAwareBuilder(builder, entityName) {
13
5
  return new Proxy(builder, {
@@ -36,27 +28,141 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
36
28
  return createEntityAwareBuilder(builder, entityName);
37
29
  }
38
30
 
39
- // src/entity.ts
40
- function defineEntity(config) {
41
- const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
42
- const buildIndexes = (dataForKeyGeneration, table) => {
43
- return Object.entries(config.indexes ?? {}).reduce(
44
- (acc, [indexName, index]) => {
45
- const key = index.generateKey(dataForKeyGeneration);
46
- const gsiConfig = table.gsis[indexName];
31
+ // src/conditions.ts
32
+ var createComparisonCondition = (type) => (attr, value) => ({
33
+ type,
34
+ attr,
35
+ value
36
+ });
37
+ var eq = createComparisonCondition("eq");
38
+
39
+ // src/entity/ddb-indexing.ts
40
+ var IndexBuilder = class {
41
+ /**
42
+ * Creates a new IndexBuilder instance
43
+ *
44
+ * @param table - The DynamoDB table instance
45
+ * @param indexes - The index definitions
46
+ */
47
+ constructor(table, indexes = {}) {
48
+ this.table = table;
49
+ this.indexes = indexes;
50
+ }
51
+ /**
52
+ * Build index attributes for item creation
53
+ *
54
+ * @param item - The item to generate indexes for
55
+ * @param options - Options for building indexes
56
+ * @returns Record of GSI attribute names to their values
57
+ */
58
+ buildForCreate(item, options = {}) {
59
+ const attributes = {};
60
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
61
+ if (options.excludeReadOnly && indexDef.isReadOnly) {
62
+ continue;
63
+ }
64
+ const key = indexDef.generateKey(item);
65
+ const gsiConfig = this.table.gsis[indexName];
66
+ if (!gsiConfig) {
67
+ throw new Error(`GSI configuration not found for index: ${indexName}`);
68
+ }
69
+ if (key.pk) {
70
+ attributes[gsiConfig.partitionKey] = key.pk;
71
+ }
72
+ if (key.sk && gsiConfig.sortKey) {
73
+ attributes[gsiConfig.sortKey] = key.sk;
74
+ }
75
+ }
76
+ return attributes;
77
+ }
78
+ /**
79
+ * Build index attributes for item updates
80
+ *
81
+ * @param currentData - The current data before update
82
+ * @param updates - The update data
83
+ * @param options - Options for building indexes
84
+ * @returns Record of GSI attribute names to their updated values
85
+ */
86
+ buildForUpdate(currentData, updates) {
87
+ const attributes = {};
88
+ const updatedItem = { ...currentData, ...updates };
89
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
90
+ if (indexDef.isReadOnly) {
91
+ continue;
92
+ }
93
+ let shouldUpdateIndex = false;
94
+ try {
95
+ const currentKey = indexDef.generateKey(currentData);
96
+ const updatedKey = indexDef.generateKey(updatedItem);
97
+ if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
98
+ shouldUpdateIndex = true;
99
+ }
100
+ } catch {
101
+ shouldUpdateIndex = true;
102
+ }
103
+ if (!shouldUpdateIndex) {
104
+ continue;
105
+ }
106
+ try {
107
+ const key = indexDef.generateKey(updatedItem);
108
+ if (this.hasUndefinedValues(key)) {
109
+ throw new Error(
110
+ `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.`
111
+ );
112
+ }
113
+ const gsiConfig = this.table.gsis[indexName];
47
114
  if (!gsiConfig) {
48
115
  throw new Error(`GSI configuration not found for index: ${indexName}`);
49
116
  }
50
117
  if (key.pk) {
51
- acc[gsiConfig.partitionKey] = key.pk;
118
+ attributes[gsiConfig.partitionKey] = key.pk;
52
119
  }
53
120
  if (key.sk && gsiConfig.sortKey) {
54
- acc[gsiConfig.sortKey] = key.sk;
121
+ attributes[gsiConfig.sortKey] = key.sk;
122
+ }
123
+ } catch (error) {
124
+ if (error instanceof Error && error.message.includes("insufficient data")) {
125
+ throw error;
55
126
  }
56
- return acc;
57
- },
58
- {}
59
- );
127
+ throw new Error(
128
+ `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.`
129
+ );
130
+ }
131
+ }
132
+ return attributes;
133
+ }
134
+ /**
135
+ * Check if a key has undefined values
136
+ *
137
+ * @param key - The index key to check
138
+ * @returns True if the key contains undefined values, false otherwise
139
+ */
140
+ hasUndefinedValues(key) {
141
+ return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
142
+ }
143
+ };
144
+
145
+ // src/entity/index-utils.ts
146
+ function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
147
+ if (!indexes) {
148
+ return {};
149
+ }
150
+ const indexBuilder = new IndexBuilder(table, indexes);
151
+ return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
152
+ }
153
+ function buildIndexUpdates(currentData, updates, table, indexes) {
154
+ if (!indexes) {
155
+ return {};
156
+ }
157
+ const indexBuilder = new IndexBuilder(table, indexes);
158
+ return indexBuilder.buildForUpdate(currentData, updates);
159
+ }
160
+
161
+ // src/entity/entity.ts
162
+ function defineEntity(config) {
163
+ const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
164
+ const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
165
+ return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
60
166
  };
61
167
  const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
62
168
  const wrappedMethod = (...args) => {
@@ -108,7 +214,7 @@ function defineEntity(config) {
108
214
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
109
215
  };
110
216
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
111
- const indexes = buildIndexes(dataForKeyGeneration, table);
217
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
112
218
  const validatedItem = {
113
219
  ...dataForKeyGeneration,
114
220
  [entityTypeAttributeName]: config.name,
@@ -134,7 +240,7 @@ function defineEntity(config) {
134
240
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
135
241
  };
136
242
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
137
- const indexes = buildIndexes(dataForKeyGeneration, table);
243
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
138
244
  const validatedItem = {
139
245
  ...dataForKeyGeneration,
140
246
  [entityTypeAttributeName]: config.name,
@@ -176,7 +282,7 @@ function defineEntity(config) {
176
282
  ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
177
283
  };
178
284
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
179
- const indexes = buildIndexes(dataForKeyGeneration, table);
285
+ const indexes = buildIndexes2(dataForKeyGeneration, table, false);
180
286
  const validatedItem = {
181
287
  [table.partitionKey]: primaryKey.pk,
182
288
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -202,7 +308,7 @@ function defineEntity(config) {
202
308
  ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
203
309
  };
204
310
  const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
205
- const indexes = buildIndexes(dataForKeyGeneration, table);
311
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
206
312
  const validatedItem = {
207
313
  [table.partitionKey]: primaryKey.pk,
208
314
  ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
@@ -236,13 +342,21 @@ function defineEntity(config) {
236
342
  }
237
343
  return createEntityAwarePutBuilder(builder, config.name);
238
344
  },
239
- get: (key) => createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name),
345
+ get: (key) => {
346
+ return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
347
+ },
240
348
  update: (key, data) => {
241
349
  const primaryKeyObj = config.primaryKey.generateKey(key);
242
350
  const builder = table.update(primaryKeyObj);
243
351
  builder.condition(eq(entityTypeAttributeName, config.name));
244
352
  const timestamps = generateTimestamps(["updatedAt"], data);
245
- builder.set({ ...data, ...timestamps });
353
+ const indexUpdates = buildIndexUpdates(
354
+ { ...key },
355
+ { ...data, ...timestamps },
356
+ table,
357
+ config.indexes
358
+ );
359
+ builder.set({ ...data, ...timestamps, ...indexUpdates });
246
360
  return builder;
247
361
  },
248
362
  delete: (key) => {
@@ -313,21 +427,57 @@ function createQueries() {
313
427
  }
314
428
  function createIndex() {
315
429
  return {
316
- input: (schema) => ({
317
- partitionKey: (pkFn) => ({
318
- sortKey: (skFn) => ({
319
- name: "custom",
320
- partitionKey: "pk",
321
- sortKey: "sk",
322
- generateKey: (item) => ({ pk: pkFn(item), sk: skFn(item) })
430
+ input: (schema) => {
431
+ const createIndexBuilder = (isReadOnly = false) => ({
432
+ partitionKey: (pkFn) => ({
433
+ sortKey: (skFn) => {
434
+ const index = {
435
+ name: "custom",
436
+ partitionKey: "pk",
437
+ sortKey: "sk",
438
+ isReadOnly,
439
+ generateKey: (item) => {
440
+ const data = schema["~standard"].validate(item);
441
+ if ("issues" in data && data.issues) {
442
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
443
+ }
444
+ const validData = "value" in data ? data.value : item;
445
+ return { pk: pkFn(validData), sk: skFn(validData) };
446
+ }
447
+ };
448
+ return Object.assign(index, {
449
+ readOnly: (value = false) => ({
450
+ ...index,
451
+ isReadOnly: value
452
+ })
453
+ });
454
+ },
455
+ withoutSortKey: () => {
456
+ const index = {
457
+ name: "custom",
458
+ partitionKey: "pk",
459
+ isReadOnly,
460
+ generateKey: (item) => {
461
+ const data = schema["~standard"].validate(item);
462
+ if ("issues" in data && data.issues) {
463
+ throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
464
+ }
465
+ const validData = "value" in data ? data.value : item;
466
+ return { pk: pkFn(validData) };
467
+ }
468
+ };
469
+ return Object.assign(index, {
470
+ readOnly: (value = true) => ({
471
+ ...index,
472
+ isReadOnly: value
473
+ })
474
+ });
475
+ }
323
476
  }),
324
- withoutSortKey: () => ({
325
- name: "custom",
326
- partitionKey: "pk",
327
- generateKey: (item) => ({ pk: pkFn(item) })
328
- })
329
- })
330
- })
477
+ readOnly: (value = true) => createIndexBuilder(value)
478
+ });
479
+ return createIndexBuilder(false);
480
+ }
331
481
  };
332
482
  }
333
483