@ftschopp/dynatable-core 1.4.2 → 1.4.4

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 (86) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/builders/query/create-query-builder.d.ts.map +1 -1
  3. package/dist/builders/query/create-query-builder.js +19 -2
  4. package/dist/builders/update/create-update-builder.d.ts.map +1 -1
  5. package/dist/builders/update/create-update-builder.js +25 -0
  6. package/dist/core/types.d.ts +5 -0
  7. package/dist/core/types.d.ts.map +1 -1
  8. package/dist/utils/model-utils.d.ts +9 -3
  9. package/dist/utils/model-utils.d.ts.map +1 -1
  10. package/dist/utils/model-utils.js +15 -7
  11. package/package.json +19 -2
  12. package/eslint.config.mjs +0 -4
  13. package/jest.config.js +0 -11
  14. package/src/builders/README.md +0 -339
  15. package/src/builders/batch-get/README.md +0 -43
  16. package/src/builders/batch-get/create-batch-get-builder.test.ts +0 -193
  17. package/src/builders/batch-get/create-batch-get-builder.ts +0 -108
  18. package/src/builders/batch-get/index.ts +0 -2
  19. package/src/builders/batch-get/types.ts +0 -28
  20. package/src/builders/batch-write/README.md +0 -204
  21. package/src/builders/batch-write/create-batch-write-builder.test.ts +0 -173
  22. package/src/builders/batch-write/create-batch-write-builder.ts +0 -49
  23. package/src/builders/batch-write/index.ts +0 -2
  24. package/src/builders/batch-write/types.ts +0 -33
  25. package/src/builders/delete/create-delete-builder.test.ts +0 -294
  26. package/src/builders/delete/create-delete-builder.ts +0 -100
  27. package/src/builders/delete/index.ts +0 -2
  28. package/src/builders/delete/types.ts +0 -17
  29. package/src/builders/get/create-get-builder.test.ts +0 -272
  30. package/src/builders/get/create-get-builder.ts +0 -117
  31. package/src/builders/get/index.ts +0 -2
  32. package/src/builders/get/types.ts +0 -39
  33. package/src/builders/index.ts +0 -14
  34. package/src/builders/put/create-put-builder.test.ts +0 -213
  35. package/src/builders/put/create-put-builder.ts +0 -160
  36. package/src/builders/put/index.ts +0 -2
  37. package/src/builders/put/types.ts +0 -24
  38. package/src/builders/query/create-query-builder.test.ts +0 -770
  39. package/src/builders/query/create-query-builder.ts +0 -455
  40. package/src/builders/query/index.ts +0 -2
  41. package/src/builders/query/types.ts +0 -109
  42. package/src/builders/scan/create-scan-builder.test.ts +0 -416
  43. package/src/builders/scan/create-scan-builder.ts +0 -265
  44. package/src/builders/scan/index.ts +0 -2
  45. package/src/builders/scan/types.ts +0 -88
  46. package/src/builders/shared/conditions.ts +0 -58
  47. package/src/builders/shared/index.ts +0 -4
  48. package/src/builders/shared/operators.test.ts +0 -270
  49. package/src/builders/shared/operators.ts +0 -208
  50. package/src/builders/shared/projection.ts +0 -28
  51. package/src/builders/shared/types.ts +0 -101
  52. package/src/builders/transact-get/README.md +0 -167
  53. package/src/builders/transact-get/create-transact-get-builder.test.ts +0 -239
  54. package/src/builders/transact-get/create-transact-get-builder.ts +0 -67
  55. package/src/builders/transact-get/index.ts +0 -2
  56. package/src/builders/transact-get/types.ts +0 -31
  57. package/src/builders/transact-write/README.md +0 -202
  58. package/src/builders/transact-write/create-transact-write-builder.test.ts +0 -288
  59. package/src/builders/transact-write/create-transact-write-builder.ts +0 -124
  60. package/src/builders/transact-write/index.ts +0 -2
  61. package/src/builders/transact-write/types.ts +0 -71
  62. package/src/builders/update/create-update-builder.test.ts +0 -573
  63. package/src/builders/update/create-update-builder.ts +0 -375
  64. package/src/builders/update/index.ts +0 -2
  65. package/src/builders/update/types.ts +0 -63
  66. package/src/core/types.test.ts +0 -641
  67. package/src/core/types.ts +0 -366
  68. package/src/entity/create-entity-api.ts +0 -224
  69. package/src/entity/index.ts +0 -19
  70. package/src/entity/middleware/factories.ts +0 -15
  71. package/src/entity/middleware/types.ts +0 -7
  72. package/src/entity/middleware/with-middleware.ts +0 -37
  73. package/src/entity/types.ts +0 -79
  74. package/src/entity/validation/key-validation.ts +0 -34
  75. package/src/index.ts +0 -23
  76. package/src/table.ts +0 -116
  77. package/src/utils/dynamodb-logger.test.ts +0 -246
  78. package/src/utils/dynamodb-logger.ts +0 -175
  79. package/src/utils/model-utils.test.ts +0 -452
  80. package/src/utils/model-utils.ts +0 -188
  81. package/src/utils/zod-utils.test.ts +0 -363
  82. package/src/utils/zod-utils.ts +0 -56
  83. package/tests/integration/instagram-clone.integration.test.ts +0 -1019
  84. package/tests/integration/pagination-timestamps.integration.test.ts +0 -374
  85. package/tests/integration/transactions.integration.test.ts +0 -528
  86. package/tsconfig.json +0 -12
@@ -1,375 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
3
- import { UpdateCommand } from '@aws-sdk/lib-dynamodb';
4
- import { buildExpression, AttrBuilder, Condition, createOpBuilder, AttrRef } from '../shared';
5
- import { UpdateBuilder, UpdateAction, IndexContext } from './types';
6
- import { DynamoDBLogger } from '../../utils/dynamodb-logger';
7
- import { computeIndexUpdates } from '../../utils/model-utils';
8
-
9
- /**
10
- * Creates an UpdateBuilder for an item key and table.
11
- *
12
- * When `indexContext` is provided, fields written via `.set()` that participate
13
- * in any secondary-index template are detected and the affected index keys are
14
- * recomputed automatically and included in the SET expression. If a template
15
- * cannot be fully resolved from the primary-key vars plus the updates, building
16
- * the params throws — the caller must include the missing fields in `.set()`.
17
- */
18
- export function createUpdateBuilder<Model>(
19
- tableName: string,
20
- key: Partial<Model>,
21
- client: DynamoDBClient,
22
- prevConditions: Condition[] = [],
23
- updateActions: {
24
- set: UpdateAction[];
25
- remove: UpdateAction[];
26
- add: UpdateAction[];
27
- delete: UpdateAction[];
28
- } = { set: [], remove: [], add: [], delete: [] },
29
- returnMode: 'NONE' | 'ALL_OLD' | 'ALL_NEW' | 'UPDATED_OLD' | 'UPDATED_NEW' = 'NONE',
30
- valueCounter = 0,
31
- enableTimestamps = false,
32
- logger?: DynamoDBLogger,
33
- indexContext?: IndexContext,
34
- setInputs: Record<string, any> = {}
35
- ): UpdateBuilder<Model> {
36
- const conditions = [...prevConditions];
37
-
38
- const getUniqueValueName = (baseName: string): string => {
39
- return `${baseName}_${valueCounter++}`;
40
- };
41
-
42
- const normalizeAttr = (attr: keyof Model | AttrRef): string => {
43
- if (typeof attr === 'string') {
44
- return attr;
45
- }
46
- return (attr as AttrRef).name;
47
- };
48
-
49
- const build = (): UpdateBuilder<Model> => ({
50
- where(fn) {
51
- const attrs = new Proxy({} as AttrBuilder<Model>, {
52
- get(_, prop: string) {
53
- return { name: prop };
54
- },
55
- });
56
- const opBuilder = createOpBuilder();
57
- const condition = fn(attrs, opBuilder);
58
- return createUpdateBuilder(
59
- tableName,
60
- key,
61
- client,
62
- [...conditions, condition],
63
- updateActions,
64
- returnMode,
65
- valueCounter,
66
- enableTimestamps,
67
- logger,
68
- indexContext,
69
- setInputs
70
- );
71
- },
72
-
73
- set(attrOrUpdates: keyof Model | AttrRef | Partial<Model>, value?: any) {
74
- // When no value is provided and the first arg is a plain object,
75
- // treat it as a Partial<Model>. The AttrRef overload always passes
76
- // a value, so it's handled by the single-update path below.
77
- if (
78
- value === undefined &&
79
- typeof attrOrUpdates === 'object' &&
80
- attrOrUpdates !== null
81
- ) {
82
- // Multiple updates case
83
- const updates = attrOrUpdates as Partial<Model>;
84
- const newActions: UpdateAction[] = [];
85
- const newSetInputs = { ...setInputs };
86
-
87
- for (const [attr, val] of Object.entries(updates)) {
88
- const attrName = attr;
89
- const valueName = getUniqueValueName(attrName);
90
- newActions.push({
91
- expression: `#${attrName} = :${valueName}`,
92
- names: { [`#${attrName}`]: attrName },
93
- values: { [`:${valueName}`]: val },
94
- });
95
- newSetInputs[attrName] = val;
96
- }
97
-
98
- return createUpdateBuilder(
99
- tableName,
100
- key,
101
- client,
102
- conditions,
103
- { ...updateActions, set: [...updateActions.set, ...newActions] },
104
- returnMode,
105
- valueCounter,
106
- enableTimestamps,
107
- logger,
108
- indexContext,
109
- newSetInputs
110
- );
111
- }
112
-
113
- // Single update case
114
- const attrName = normalizeAttr(attrOrUpdates as keyof Model | AttrRef);
115
- const valueName = getUniqueValueName(attrName);
116
- const action: UpdateAction = {
117
- expression: `#${attrName} = :${valueName}`,
118
- names: { [`#${attrName}`]: attrName },
119
- values: { [`:${valueName}`]: value },
120
- };
121
- return createUpdateBuilder(
122
- tableName,
123
- key,
124
- client,
125
- conditions,
126
- { ...updateActions, set: [...updateActions.set, action] },
127
- returnMode,
128
- valueCounter,
129
- enableTimestamps,
130
- logger,
131
- indexContext,
132
- { ...setInputs, [attrName]: value }
133
- );
134
- },
135
-
136
- remove(attr) {
137
- const attrName = normalizeAttr(attr);
138
- const action: UpdateAction = {
139
- expression: `#${attrName}`,
140
- names: { [`#${attrName}`]: attrName },
141
- };
142
- return createUpdateBuilder(
143
- tableName,
144
- key,
145
- client,
146
- conditions,
147
- { ...updateActions, remove: [...updateActions.remove, action] },
148
- returnMode,
149
- valueCounter,
150
- enableTimestamps,
151
- logger,
152
- indexContext,
153
- setInputs
154
- );
155
- },
156
-
157
- add(attr, value) {
158
- const attrName = normalizeAttr(attr);
159
- const valueName = getUniqueValueName(attrName);
160
- const action: UpdateAction = {
161
- expression: `#${attrName} :${valueName}`,
162
- names: { [`#${attrName}`]: attrName },
163
- values: { [`:${valueName}`]: value },
164
- };
165
- return createUpdateBuilder(
166
- tableName,
167
- key,
168
- client,
169
- conditions,
170
- { ...updateActions, add: [...updateActions.add, action] },
171
- returnMode,
172
- valueCounter,
173
- enableTimestamps,
174
- logger,
175
- indexContext,
176
- setInputs
177
- );
178
- },
179
-
180
- delete(attr, value) {
181
- const attrName = normalizeAttr(attr);
182
- const valueName = getUniqueValueName(attrName);
183
- const action: UpdateAction = {
184
- expression: `#${attrName} :${valueName}`,
185
- names: { [`#${attrName}`]: attrName },
186
- values: { [`:${valueName}`]: value },
187
- };
188
- return createUpdateBuilder(
189
- tableName,
190
- key,
191
- client,
192
- conditions,
193
- { ...updateActions, delete: [...updateActions.delete, action] },
194
- returnMode,
195
- valueCounter,
196
- enableTimestamps,
197
- logger,
198
- indexContext,
199
- setInputs
200
- );
201
- },
202
-
203
- returning(mode) {
204
- return createUpdateBuilder(
205
- tableName,
206
- key,
207
- client,
208
- conditions,
209
- updateActions,
210
- mode,
211
- valueCounter,
212
- enableTimestamps,
213
- logger,
214
- indexContext,
215
- setInputs
216
- );
217
- },
218
-
219
- dbParams() {
220
- // Build UpdateExpression from actions
221
- const updateParts: string[] = [];
222
- const allNames: Record<string, string> = {};
223
- const allValues: Record<string, any> = {};
224
-
225
- // Clone updateActions to avoid mutation
226
- const actionsToProcess = { ...updateActions, set: [...updateActions.set] };
227
-
228
- // Auto-recompute secondary-index keys whose templates depend on any
229
- // updated field. If a template can't be fully resolved from the primary
230
- // key vars + the .set() payload, we throw — recomputing from the
231
- // existing item would require an extra read the builder won't do.
232
- if (indexContext) {
233
- const { actions: idxActions, missing } = computeIndexUpdates(
234
- indexContext.model,
235
- indexContext.keyVars,
236
- setInputs
237
- );
238
- if (missing.length > 0) {
239
- const details = missing
240
- .map(
241
- (m) =>
242
- ` - ${m.index} ("${m.template}"): missing ${m.missing.join(', ')}`
243
- )
244
- .join('\n');
245
- throw new Error(
246
- `Update touches fields that participate in secondary index templates, ` +
247
- `but the templates cannot be fully resolved from the update payload. ` +
248
- `Include the missing fields in .set():\n${details}`
249
- );
250
- }
251
- for (const [indexName, resolved] of Object.entries(idxActions)) {
252
- const valueName = getUniqueValueName(indexName);
253
- actionsToProcess.set.push({
254
- expression: `#${indexName} = :${valueName}`,
255
- names: { [`#${indexName}`]: indexName },
256
- values: { [`:${valueName}`]: resolved },
257
- });
258
- }
259
- }
260
-
261
- // Add updatedAt timestamp if enabled
262
- if (enableTimestamps) {
263
- const now = new Date().toISOString();
264
- const timestampAction: UpdateAction = {
265
- expression: `#updatedAt = :updatedAt_ts`,
266
- names: { '#updatedAt': 'updatedAt' },
267
- values: { ':updatedAt_ts': now },
268
- };
269
- actionsToProcess.set = [...actionsToProcess.set, timestampAction];
270
- }
271
-
272
- if (actionsToProcess.set.length > 0) {
273
- const setExpressions = actionsToProcess.set.map((action) => {
274
- Object.assign(allNames, action.names || {});
275
- Object.assign(allValues, action.values || {});
276
- return action.expression;
277
- });
278
- updateParts.push(`SET ${setExpressions.join(', ')}`);
279
- }
280
-
281
- if (updateActions.remove.length > 0) {
282
- const removeExpressions = updateActions.remove.map((action) => {
283
- Object.assign(allNames, action.names || {});
284
- return action.expression;
285
- });
286
- updateParts.push(`REMOVE ${removeExpressions.join(', ')}`);
287
- }
288
-
289
- if (updateActions.add.length > 0) {
290
- const addExpressions = updateActions.add.map((action) => {
291
- Object.assign(allNames, action.names || {});
292
- Object.assign(allValues, action.values || {});
293
- return action.expression;
294
- });
295
- updateParts.push(`ADD ${addExpressions.join(', ')}`);
296
- }
297
-
298
- if (updateActions.delete.length > 0) {
299
- const deleteExpressions = updateActions.delete.map((action) => {
300
- Object.assign(allNames, action.names || {});
301
- Object.assign(allValues, action.values || {});
302
- return action.expression;
303
- });
304
- updateParts.push(`DELETE ${deleteExpressions.join(', ')}`);
305
- }
306
-
307
- const updateExpression = updateParts.join(' ');
308
-
309
- // Build ConditionExpression from conditions
310
- let conditionExpression = '';
311
- let conditionNames = {};
312
- let conditionValues = {};
313
-
314
- if (conditions.length > 0) {
315
- const combinedCondition =
316
- conditions.length === 1 && conditions[0]
317
- ? conditions[0]
318
- : {
319
- expression: '',
320
- operator: 'AND' as const,
321
- children: conditions,
322
- };
323
-
324
- const result = buildExpression(combinedCondition);
325
- conditionExpression = result.expression;
326
- conditionNames = result.names;
327
- conditionValues = result.values;
328
- }
329
-
330
- // Merge names and values
331
- const expressionAttributeNames = {
332
- ...allNames,
333
- ...conditionNames,
334
- };
335
- const expressionAttributeValues = {
336
- ...allValues,
337
- ...conditionValues,
338
- };
339
-
340
- const extra: any = returnMode !== 'NONE' ? { ReturnValues: returnMode } : {};
341
-
342
- return {
343
- TableName: tableName,
344
- Key: key,
345
- ...(updateExpression && { UpdateExpression: updateExpression }),
346
- ...(conditionExpression && {
347
- ConditionExpression: conditionExpression,
348
- }),
349
- ...(Object.keys(expressionAttributeNames).length && {
350
- ExpressionAttributeNames: expressionAttributeNames,
351
- }),
352
- ...(Object.keys(expressionAttributeValues).length && {
353
- ExpressionAttributeValues: expressionAttributeValues,
354
- }),
355
- ...extra,
356
- };
357
- },
358
-
359
- async execute() {
360
- const params = build().dbParams();
361
- const response = await client.send(new UpdateCommand(params));
362
- logger?.log('UpdateCommand', params, response);
363
-
364
- // Return the item based on returnMode
365
- if (response.Attributes) {
366
- return response.Attributes as Model;
367
- }
368
-
369
- // If no attributes returned (NONE mode), return undefined
370
- return undefined as unknown as Model;
371
- },
372
- });
373
-
374
- return build();
375
- }
@@ -1,2 +0,0 @@
1
- export * from './types';
2
- export * from './create-update-builder';
@@ -1,63 +0,0 @@
1
- import type { UpdateCommandInput } from '@aws-sdk/lib-dynamodb';
2
- import { ModelDefinition } from '@/core/types';
3
- import { OperationBuilder, AttrRef } from '../shared';
4
-
5
- /**
6
- * Update actions that can be performed on attributes
7
- */
8
- export type UpdateAction = {
9
- expression: string;
10
- names?: Record<string, string>;
11
- values?: Record<string, any>;
12
- };
13
-
14
- /**
15
- * Context that lets the update builder auto-recompute secondary-index keys
16
- * when a `.set()` touches a field that participates in their templates.
17
- *
18
- * `keyVars` carries the original template-variable values used to build the
19
- * primary key (e.g. `{ id: '123' }` from `update({ id: '123' })`) so they can
20
- * be combined with the user's `.set()` payload when resolving index templates.
21
- */
22
- export type IndexContext = {
23
- model: ModelDefinition;
24
- keyVars: Record<string, any>;
25
- };
26
-
27
- /**
28
- * Builder interface for DynamoDB UpdateItem operations
29
- */
30
- export interface UpdateBuilder<Model> extends Omit<OperationBuilder<Model>, 'dbParams'> {
31
- /**
32
- * Sets an attribute to a specific value, or sets multiple attributes at once
33
- */
34
- set(attr: keyof Model | AttrRef, value: any): UpdateBuilder<Model>;
35
- set(updates: Partial<Model>): UpdateBuilder<Model>;
36
-
37
- /**
38
- * Removes an attribute from the item
39
- */
40
- remove(attr: keyof Model | AttrRef): UpdateBuilder<Model>;
41
-
42
- /**
43
- * Adds a value to a number attribute or adds elements to a set
44
- */
45
- add(attr: keyof Model | AttrRef, value: any): UpdateBuilder<Model>;
46
-
47
- /**
48
- * Deletes elements from a set attribute
49
- */
50
- delete(attr: keyof Model | AttrRef, value: any): UpdateBuilder<Model>;
51
-
52
- /**
53
- * Configures what values should be returned after the update operation
54
- */
55
- returning(
56
- mode: 'NONE' | 'ALL_OLD' | 'ALL_NEW' | 'UPDATED_OLD' | 'UPDATED_NEW'
57
- ): UpdateBuilder<Model>;
58
-
59
- /**
60
- * Converts the builder state to DynamoDB UpdateItem parameters
61
- */
62
- dbParams(): UpdateCommandInput;
63
- }