@ftschopp/dynatable-core 1.4.2 → 1.4.3

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 (81) hide show
  1. package/CHANGELOG.md +7 -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/core/types.d.ts +5 -0
  5. package/dist/core/types.d.ts.map +1 -1
  6. package/package.json +11 -2
  7. package/eslint.config.mjs +0 -4
  8. package/jest.config.js +0 -11
  9. package/src/builders/README.md +0 -339
  10. package/src/builders/batch-get/README.md +0 -43
  11. package/src/builders/batch-get/create-batch-get-builder.test.ts +0 -193
  12. package/src/builders/batch-get/create-batch-get-builder.ts +0 -108
  13. package/src/builders/batch-get/index.ts +0 -2
  14. package/src/builders/batch-get/types.ts +0 -28
  15. package/src/builders/batch-write/README.md +0 -204
  16. package/src/builders/batch-write/create-batch-write-builder.test.ts +0 -173
  17. package/src/builders/batch-write/create-batch-write-builder.ts +0 -49
  18. package/src/builders/batch-write/index.ts +0 -2
  19. package/src/builders/batch-write/types.ts +0 -33
  20. package/src/builders/delete/create-delete-builder.test.ts +0 -294
  21. package/src/builders/delete/create-delete-builder.ts +0 -100
  22. package/src/builders/delete/index.ts +0 -2
  23. package/src/builders/delete/types.ts +0 -17
  24. package/src/builders/get/create-get-builder.test.ts +0 -272
  25. package/src/builders/get/create-get-builder.ts +0 -117
  26. package/src/builders/get/index.ts +0 -2
  27. package/src/builders/get/types.ts +0 -39
  28. package/src/builders/index.ts +0 -14
  29. package/src/builders/put/create-put-builder.test.ts +0 -213
  30. package/src/builders/put/create-put-builder.ts +0 -160
  31. package/src/builders/put/index.ts +0 -2
  32. package/src/builders/put/types.ts +0 -24
  33. package/src/builders/query/create-query-builder.test.ts +0 -770
  34. package/src/builders/query/create-query-builder.ts +0 -455
  35. package/src/builders/query/index.ts +0 -2
  36. package/src/builders/query/types.ts +0 -109
  37. package/src/builders/scan/create-scan-builder.test.ts +0 -416
  38. package/src/builders/scan/create-scan-builder.ts +0 -265
  39. package/src/builders/scan/index.ts +0 -2
  40. package/src/builders/scan/types.ts +0 -88
  41. package/src/builders/shared/conditions.ts +0 -58
  42. package/src/builders/shared/index.ts +0 -4
  43. package/src/builders/shared/operators.test.ts +0 -270
  44. package/src/builders/shared/operators.ts +0 -208
  45. package/src/builders/shared/projection.ts +0 -28
  46. package/src/builders/shared/types.ts +0 -101
  47. package/src/builders/transact-get/README.md +0 -167
  48. package/src/builders/transact-get/create-transact-get-builder.test.ts +0 -239
  49. package/src/builders/transact-get/create-transact-get-builder.ts +0 -67
  50. package/src/builders/transact-get/index.ts +0 -2
  51. package/src/builders/transact-get/types.ts +0 -31
  52. package/src/builders/transact-write/README.md +0 -202
  53. package/src/builders/transact-write/create-transact-write-builder.test.ts +0 -288
  54. package/src/builders/transact-write/create-transact-write-builder.ts +0 -124
  55. package/src/builders/transact-write/index.ts +0 -2
  56. package/src/builders/transact-write/types.ts +0 -71
  57. package/src/builders/update/create-update-builder.test.ts +0 -573
  58. package/src/builders/update/create-update-builder.ts +0 -375
  59. package/src/builders/update/index.ts +0 -2
  60. package/src/builders/update/types.ts +0 -63
  61. package/src/core/types.test.ts +0 -641
  62. package/src/core/types.ts +0 -366
  63. package/src/entity/create-entity-api.ts +0 -224
  64. package/src/entity/index.ts +0 -19
  65. package/src/entity/middleware/factories.ts +0 -15
  66. package/src/entity/middleware/types.ts +0 -7
  67. package/src/entity/middleware/with-middleware.ts +0 -37
  68. package/src/entity/types.ts +0 -79
  69. package/src/entity/validation/key-validation.ts +0 -34
  70. package/src/index.ts +0 -23
  71. package/src/table.ts +0 -116
  72. package/src/utils/dynamodb-logger.test.ts +0 -246
  73. package/src/utils/dynamodb-logger.ts +0 -175
  74. package/src/utils/model-utils.test.ts +0 -452
  75. package/src/utils/model-utils.ts +0 -188
  76. package/src/utils/zod-utils.test.ts +0 -363
  77. package/src/utils/zod-utils.ts +0 -56
  78. package/tests/integration/instagram-clone.integration.test.ts +0 -1019
  79. package/tests/integration/pagination-timestamps.integration.test.ts +0 -374
  80. package/tests/integration/transactions.integration.test.ts +0 -528
  81. 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
- }