@ftschopp/dynatable-core 1.0.0 → 1.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 (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +442 -2
  3. package/dist/builders/delete/types.d.ts +6 -1
  4. package/dist/builders/delete/types.d.ts.map +1 -1
  5. package/dist/builders/get/types.d.ts +6 -1
  6. package/dist/builders/get/types.d.ts.map +1 -1
  7. package/dist/builders/put/types.d.ts +6 -1
  8. package/dist/builders/put/types.d.ts.map +1 -1
  9. package/dist/builders/query/create-query-builder.d.ts +1 -1
  10. package/dist/builders/query/create-query-builder.d.ts.map +1 -1
  11. package/dist/builders/query/types.d.ts +9 -10
  12. package/dist/builders/query/types.d.ts.map +1 -1
  13. package/dist/builders/transact-write/create-transact-write-builder.d.ts.map +1 -1
  14. package/dist/builders/transact-write/create-transact-write-builder.js +1 -1
  15. package/dist/builders/transact-write/types.d.ts +33 -6
  16. package/dist/builders/transact-write/types.d.ts.map +1 -1
  17. package/dist/builders/update/create-update-builder.d.ts.map +1 -1
  18. package/dist/builders/update/create-update-builder.js +30 -9
  19. package/dist/builders/update/types.d.ts +8 -2
  20. package/dist/builders/update/types.d.ts.map +1 -1
  21. package/dist/core/types.d.ts +1 -0
  22. package/dist/core/types.d.ts.map +1 -1
  23. package/dist/entity/create-entity-api.d.ts +15 -0
  24. package/dist/entity/create-entity-api.d.ts.map +1 -0
  25. package/dist/entity/create-entity-api.js +124 -0
  26. package/dist/entity/index.d.ts +12 -0
  27. package/dist/entity/index.d.ts.map +1 -0
  28. package/dist/entity/index.js +18 -0
  29. package/dist/entity/middleware/factories.d.ts +6 -0
  30. package/dist/entity/middleware/factories.d.ts.map +1 -0
  31. package/dist/entity/middleware/factories.js +15 -0
  32. package/dist/entity/middleware/types.d.ts +8 -0
  33. package/dist/entity/middleware/types.d.ts.map +1 -0
  34. package/dist/entity/middleware/types.js +2 -0
  35. package/dist/entity/middleware/with-middleware.d.ts +8 -0
  36. package/dist/entity/middleware/with-middleware.d.ts.map +1 -0
  37. package/dist/entity/middleware/with-middleware.js +29 -0
  38. package/dist/{entity.d.ts → entity/types.d.ts} +6 -17
  39. package/dist/entity/types.d.ts.map +1 -0
  40. package/dist/entity/types.js +2 -0
  41. package/dist/entity/validation/key-validation.d.ts +7 -0
  42. package/dist/entity/validation/key-validation.d.ts.map +1 -0
  43. package/dist/entity/validation/key-validation.js +25 -0
  44. package/dist/table.d.ts +8 -1
  45. package/dist/table.d.ts.map +1 -1
  46. package/dist/table.js +1 -0
  47. package/dist/utils/model-utils.d.ts +7 -1
  48. package/dist/utils/model-utils.d.ts.map +1 -1
  49. package/dist/utils/model-utils.js +29 -1
  50. package/dist/utils/zod-utils.d.ts +1 -1
  51. package/package.json +3 -2
  52. package/src/builders/README.md +68 -1
  53. package/src/builders/delete/types.ts +7 -1
  54. package/src/builders/get/types.ts +7 -1
  55. package/src/builders/put/types.ts +7 -1
  56. package/src/builders/query/create-query-builder.ts +4 -6
  57. package/src/builders/query/types.ts +9 -12
  58. package/src/builders/transact-write/README.md +37 -1
  59. package/src/builders/transact-write/create-transact-write-builder.ts +20 -14
  60. package/src/builders/transact-write/types.ts +44 -6
  61. package/src/builders/update/create-update-builder.test.ts +43 -0
  62. package/src/builders/update/create-update-builder.ts +47 -9
  63. package/src/builders/update/types.ts +9 -2
  64. package/src/core/types.ts +1 -0
  65. package/src/entity/create-entity-api.ts +212 -0
  66. package/src/entity/index.ts +19 -0
  67. package/src/entity/middleware/factories.ts +15 -0
  68. package/src/entity/middleware/types.ts +7 -0
  69. package/src/entity/middleware/with-middleware.ts +37 -0
  70. package/src/entity/types.ts +79 -0
  71. package/src/entity/validation/key-validation.ts +34 -0
  72. package/src/table.ts +10 -3
  73. package/src/utils/model-utils.test.ts +131 -1
  74. package/src/utils/model-utils.ts +32 -0
  75. package/dist/entity.d.ts.map +0 -1
  76. package/dist/entity.js +0 -161
  77. package/src/entity.ts +0 -337
package/dist/entity.js DELETED
@@ -1,161 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createEntityAPI = void 0;
4
- const model_utils_1 = require("./utils/model-utils");
5
- const zod_utils_1 = require("./utils/zod-utils");
6
- const builders_1 = require("./builders");
7
- /**
8
- * Creates an entity API instance with validation, key resolution, and builder creation.
9
- *
10
- * @param modelName - The name of the model/entity
11
- * @param model - The model definition
12
- * @param client - DynamoDB client instance
13
- * @param options - Optional configuration (logger, timestamps)
14
- * @returns EntityAPI with get and put methods
15
- */
16
- const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
17
- const { logger, timestamps = false } = options;
18
- // Build a Zod schema from the model
19
- const zodSchema = (0, zod_utils_1.modelToZod)(model);
20
- return {
21
- get(key) {
22
- // Extract required fields from key templates
23
- const requiredFields = new Set();
24
- if (model.key) {
25
- for (const keyDef of Object.values(model.key)) {
26
- (0, model_utils_1.extractTemplateVars)(keyDef.value).forEach((field) => requiredFields.add(field));
27
- }
28
- }
29
- // Check if all required fields are present
30
- const keyRecord = key;
31
- const missingFields = Array.from(requiredFields).filter((field) => keyRecord[field] === undefined);
32
- if (missingFields.length > 0) {
33
- throw new Error(`[${modelName}] Missing required key field(s) for get(): ${missingFields.join(', ')}. ` +
34
- `Required fields: ${Array.from(requiredFields).join(', ')}`);
35
- }
36
- // Resolve any key defaults or computed keys
37
- const fullKey = (0, model_utils_1.resolveKeys)(model, key);
38
- return (0, builders_1.createGetBuilder)(tableName, fullKey, client, undefined, logger);
39
- },
40
- put(item) {
41
- // Validate full input data
42
- const parsed = zodSchema.parse(item);
43
- // Apply post-processing defaults from the model (including timestamps for new items)
44
- const withDefaults = (0, model_utils_1.applyPostDefaults)(model, parsed, {
45
- isUpdate: false,
46
- timestamps,
47
- });
48
- // Resolve keys again with defaults
49
- const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
50
- // Combine keys and data into full item, adding _type field
51
- const fullItem = {
52
- ...withDefaults,
53
- ...fullKey,
54
- _type: modelName, // Add entity type identifier
55
- };
56
- return (0, builders_1.createPutBuilder)(tableName, fullItem, client, [], false, 'NONE', false, logger);
57
- },
58
- query() {
59
- return (0, builders_1.createQueryBuilder)(tableName, client, model, logger);
60
- },
61
- scan() {
62
- return (0, builders_1.createScanBuilder)(tableName, client, [], [], undefined, false, undefined, undefined, undefined, logger);
63
- },
64
- update(key) {
65
- // Extract required fields from key templates
66
- const requiredFields = new Set();
67
- if (model.key) {
68
- for (const keyDef of Object.values(model.key)) {
69
- (0, model_utils_1.extractTemplateVars)(keyDef.value).forEach((field) => requiredFields.add(field));
70
- }
71
- }
72
- // Check if all required fields are present
73
- const keyRecord = key;
74
- const missingFields = Array.from(requiredFields).filter((field) => keyRecord[field] === undefined);
75
- if (missingFields.length > 0) {
76
- throw new Error(`[${modelName}] Missing required key field(s) for update(): ${missingFields.join(', ')}. ` +
77
- `Required fields: ${Array.from(requiredFields).join(', ')}`);
78
- }
79
- // Resolve any key defaults or computed keys
80
- const fullKey = (0, model_utils_1.resolveKeys)(model, key);
81
- return (0, builders_1.createUpdateBuilder)(tableName, fullKey, client, [], { set: [], remove: [], add: [], delete: [] }, 'NONE', 0, timestamps, logger);
82
- },
83
- delete(key) {
84
- // Extract required fields from key templates
85
- const requiredFields = new Set();
86
- if (model.key) {
87
- for (const keyDef of Object.values(model.key)) {
88
- (0, model_utils_1.extractTemplateVars)(keyDef.value).forEach((field) => requiredFields.add(field));
89
- }
90
- }
91
- // Check if all required fields are present
92
- const keyRecord = key;
93
- const missingFields = Array.from(requiredFields).filter((field) => keyRecord[field] === undefined);
94
- if (missingFields.length > 0) {
95
- throw new Error(`[${modelName}] Missing required key field(s) for delete(): ${missingFields.join(', ')}. ` +
96
- `Required fields: ${Array.from(requiredFields).join(', ')}`);
97
- }
98
- // Resolve any key defaults or computed keys
99
- const fullKey = (0, model_utils_1.resolveKeys)(model, key);
100
- return (0, builders_1.createDeleteBuilder)(tableName, fullKey, client, [], 'NONE', logger);
101
- },
102
- batchGet(keys) {
103
- // Extract required fields from key templates
104
- const requiredFields = new Set();
105
- if (model.key) {
106
- for (const keyDef of Object.values(model.key)) {
107
- (0, model_utils_1.extractTemplateVars)(keyDef.value).forEach((field) => requiredFields.add(field));
108
- }
109
- }
110
- // Process all keys and validate them
111
- const resolvedKeys = keys.map((key) => {
112
- // Check if all required fields are present
113
- const keyRecord = key;
114
- const missingFields = Array.from(requiredFields).filter((field) => keyRecord[field] === undefined);
115
- if (missingFields.length > 0) {
116
- throw new Error(`[${modelName}] Missing required key field(s) for batchGet(): ${missingFields.join(', ')}. ` +
117
- `Required fields: ${Array.from(requiredFields).join(', ')}`);
118
- }
119
- // Resolve any key defaults or computed keys
120
- return (0, model_utils_1.resolveKeys)(model, key);
121
- });
122
- // Create the request items in the format expected by BatchGetItem
123
- const requestItems = {
124
- [tableName]: {
125
- Keys: resolvedKeys,
126
- },
127
- };
128
- return (0, builders_1.createBatchGetBuilder)(requestItems, client, undefined, logger);
129
- },
130
- batchWrite(items) {
131
- // Validate and process all items
132
- const processedItems = items.map((item) => {
133
- // Validate full input data
134
- const parsed = zodSchema.parse(item);
135
- // Apply post-processing defaults from the model (including timestamps for new items)
136
- const withDefaults = (0, model_utils_1.applyPostDefaults)(model, parsed, {
137
- isUpdate: false,
138
- timestamps,
139
- });
140
- // Resolve keys again with defaults
141
- const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
142
- // Combine keys and data into full item, adding _type field
143
- return {
144
- ...withDefaults,
145
- ...fullKey,
146
- _type: modelName, // Add entity type identifier
147
- };
148
- });
149
- // Create the request items in the format expected by BatchWriteItem
150
- const requestItems = {
151
- [tableName]: processedItems.map((item) => ({
152
- PutRequest: {
153
- Item: item,
154
- },
155
- })),
156
- };
157
- return (0, builders_1.createBatchWriteBuilder)(requestItems, client, logger);
158
- },
159
- };
160
- };
161
- exports.createEntityAPI = createEntityAPI;
package/src/entity.ts DELETED
@@ -1,337 +0,0 @@
1
- import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
- import { InferInput, InferKeyInput, InferModel, ModelDefinition } from './core/types';
3
- import { applyPostDefaults, resolveKeys, extractTemplateVars } from './utils/model-utils';
4
- import { modelToZod } from './utils/zod-utils';
5
- import {
6
- createGetBuilder,
7
- createPutBuilder,
8
- createQueryBuilder,
9
- createUpdateBuilder,
10
- createDeleteBuilder,
11
- createScanBuilder,
12
- createBatchGetBuilder,
13
- createBatchWriteBuilder,
14
- GetBuilder,
15
- PutBuilder,
16
- QueryBuilder,
17
- UpdateBuilder,
18
- DeleteBuilder,
19
- ScanBuilder,
20
- BatchGetBuilder,
21
- BatchWriteBuilder,
22
- WriteRequest,
23
- } from './builders';
24
- import { DynamoDBLogger } from './utils/dynamodb-logger';
25
-
26
- /**
27
- * Options for creating the Entity API
28
- */
29
- export type EntityAPIOptions = {
30
- logger?: DynamoDBLogger;
31
- timestamps?: boolean;
32
- };
33
-
34
- /**
35
- * Entity API interface for a model
36
- */
37
- export type EntityAPI<Model, Input, KeyInput, ModelDef extends ModelDefinition = any> = {
38
- /**
39
- * Retrieves an item by its key.
40
- * @param key - Partial or full key object to identify the item
41
- * @returns GetBuilder configured for the item
42
- */
43
- get: (key: KeyInput) => GetBuilder<KeyInput, Model>;
44
-
45
- /**
46
- * Puts an item into the table after validation and applying defaults.
47
- * @param item - The input data to put
48
- * @returns PutBuilder configured for the item
49
- */
50
- put: (item: Input) => PutBuilder<Model>;
51
-
52
- /**
53
- * Queries items using key conditions.
54
- * @returns QueryBuilder for building and executing the query
55
- */
56
- query: () => QueryBuilder<Model, ModelDef>;
57
-
58
- /**
59
- * Scans the entire table or index without key conditions.
60
- * @returns ScanBuilder for building and executing the scan
61
- */
62
- scan: () => ScanBuilder<Model>;
63
-
64
- /**
65
- * Updates an item by its key.
66
- * @param key - Partial or full key object to identify the item
67
- * @returns UpdateBuilder configured for the item
68
- */
69
- update: (key: KeyInput) => UpdateBuilder<Model>;
70
-
71
- /**
72
- * Deletes an item by its key.
73
- * @param key - Partial or full key object to identify the item
74
- * @returns DeleteBuilder configured for the item
75
- */
76
- delete: (key: KeyInput) => DeleteBuilder<Model>;
77
-
78
- /**
79
- * Retrieves multiple items by their keys in a single batch operation.
80
- * @param keys - Array of key objects to retrieve
81
- * @returns BatchGetBuilder configured for the items
82
- */
83
- batchGet: (keys: KeyInput[]) => BatchGetBuilder<Model>;
84
-
85
- /**
86
- * Writes multiple items in a single batch operation (puts or deletes).
87
- * @param items - Array of items to put
88
- * @returns BatchWriteBuilder configured for the items
89
- */
90
- batchWrite: (items: Input[]) => BatchWriteBuilder;
91
- };
92
-
93
- /**
94
- * Creates an entity API instance with validation, key resolution, and builder creation.
95
- *
96
- * @param modelName - The name of the model/entity
97
- * @param model - The model definition
98
- * @param client - DynamoDB client instance
99
- * @param options - Optional configuration (logger, timestamps)
100
- * @returns EntityAPI with get and put methods
101
- */
102
- export const createEntityAPI = <Model extends ModelDefinition>(
103
- tableName: string,
104
- modelName: string,
105
- model: Model,
106
- client: DynamoDBClient,
107
- options: EntityAPIOptions = {}
108
- ): EntityAPI<InferModel<Model>, InferInput<Model>, InferKeyInput<Model>, Model> => {
109
- const { logger, timestamps = false } = options;
110
-
111
- // Build a Zod schema from the model
112
- const zodSchema = modelToZod(model);
113
-
114
- return {
115
- get(key) {
116
- // Extract required fields from key templates
117
- const requiredFields = new Set<string>();
118
- if (model.key) {
119
- for (const keyDef of Object.values(model.key)) {
120
- extractTemplateVars(keyDef.value).forEach((field) => requiredFields.add(field));
121
- }
122
- }
123
-
124
- // Check if all required fields are present
125
- const keyRecord = key as Record<string, unknown>;
126
- const missingFields = Array.from(requiredFields).filter(
127
- (field) => keyRecord[field] === undefined
128
- );
129
-
130
- if (missingFields.length > 0) {
131
- throw new Error(
132
- `[${modelName}] Missing required key field(s) for get(): ${missingFields.join(', ')}. ` +
133
- `Required fields: ${Array.from(requiredFields).join(', ')}`
134
- );
135
- }
136
-
137
- // Resolve any key defaults or computed keys
138
- const fullKey = resolveKeys(model, key);
139
-
140
- return createGetBuilder<InferKeyInput<Model>, InferModel<Model>>(
141
- tableName,
142
- fullKey,
143
- client,
144
- undefined,
145
- logger
146
- );
147
- },
148
-
149
- put(item) {
150
- // Validate full input data
151
- const parsed = zodSchema.parse(item);
152
-
153
- // Apply post-processing defaults from the model (including timestamps for new items)
154
- const withDefaults = applyPostDefaults(model, parsed, {
155
- isUpdate: false,
156
- timestamps,
157
- });
158
-
159
- // Resolve keys again with defaults
160
- const fullKey = resolveKeys(model, withDefaults);
161
-
162
- // Combine keys and data into full item, adding _type field
163
- const fullItem = {
164
- ...withDefaults,
165
- ...fullKey,
166
- _type: modelName, // Add entity type identifier
167
- };
168
-
169
- return createPutBuilder(tableName, fullItem, client, [], false, 'NONE', false, logger);
170
- },
171
-
172
- query() {
173
- return createQueryBuilder<InferModel<Model>, Model>(tableName, client, model, logger);
174
- },
175
-
176
- scan() {
177
- return createScanBuilder<InferModel<Model>>(
178
- tableName,
179
- client,
180
- [],
181
- [],
182
- undefined,
183
- false,
184
- undefined,
185
- undefined,
186
- undefined,
187
- logger
188
- );
189
- },
190
-
191
- update(key) {
192
- // Extract required fields from key templates
193
- const requiredFields = new Set<string>();
194
- if (model.key) {
195
- for (const keyDef of Object.values(model.key)) {
196
- extractTemplateVars(keyDef.value).forEach((field) => requiredFields.add(field));
197
- }
198
- }
199
-
200
- // Check if all required fields are present
201
- const keyRecord = key as Record<string, unknown>;
202
- const missingFields = Array.from(requiredFields).filter(
203
- (field) => keyRecord[field] === undefined
204
- );
205
-
206
- if (missingFields.length > 0) {
207
- throw new Error(
208
- `[${modelName}] Missing required key field(s) for update(): ${missingFields.join(', ')}. ` +
209
- `Required fields: ${Array.from(requiredFields).join(', ')}`
210
- );
211
- }
212
-
213
- // Resolve any key defaults or computed keys
214
- const fullKey = resolveKeys(model, key);
215
-
216
- return createUpdateBuilder<InferModel<Model>>(
217
- tableName,
218
- fullKey as Partial<InferModel<Model>>,
219
- client,
220
- [],
221
- { set: [], remove: [], add: [], delete: [] },
222
- 'NONE',
223
- 0,
224
- timestamps,
225
- logger
226
- );
227
- },
228
-
229
- delete(key) {
230
- // Extract required fields from key templates
231
- const requiredFields = new Set<string>();
232
- if (model.key) {
233
- for (const keyDef of Object.values(model.key)) {
234
- extractTemplateVars(keyDef.value).forEach((field) => requiredFields.add(field));
235
- }
236
- }
237
-
238
- // Check if all required fields are present
239
- const keyRecord = key as Record<string, unknown>;
240
- const missingFields = Array.from(requiredFields).filter(
241
- (field) => keyRecord[field] === undefined
242
- );
243
-
244
- if (missingFields.length > 0) {
245
- throw new Error(
246
- `[${modelName}] Missing required key field(s) for delete(): ${missingFields.join(', ')}. ` +
247
- `Required fields: ${Array.from(requiredFields).join(', ')}`
248
- );
249
- }
250
-
251
- // Resolve any key defaults or computed keys
252
- const fullKey = resolveKeys(model, key);
253
-
254
- return createDeleteBuilder<InferModel<Model>>(
255
- tableName,
256
- fullKey as Partial<InferModel<Model>>,
257
- client,
258
- [],
259
- 'NONE',
260
- logger
261
- );
262
- },
263
-
264
- batchGet(keys) {
265
- // Extract required fields from key templates
266
- const requiredFields = new Set<string>();
267
- if (model.key) {
268
- for (const keyDef of Object.values(model.key)) {
269
- extractTemplateVars(keyDef.value).forEach((field) => requiredFields.add(field));
270
- }
271
- }
272
-
273
- // Process all keys and validate them
274
- const resolvedKeys = keys.map((key) => {
275
- // Check if all required fields are present
276
- const keyRecord = key as Record<string, unknown>;
277
- const missingFields = Array.from(requiredFields).filter(
278
- (field) => keyRecord[field] === undefined
279
- );
280
-
281
- if (missingFields.length > 0) {
282
- throw new Error(
283
- `[${modelName}] Missing required key field(s) for batchGet(): ${missingFields.join(', ')}. ` +
284
- `Required fields: ${Array.from(requiredFields).join(', ')}`
285
- );
286
- }
287
-
288
- // Resolve any key defaults or computed keys
289
- return resolveKeys(model, key);
290
- });
291
-
292
- // Create the request items in the format expected by BatchGetItem
293
- const requestItems = {
294
- [tableName]: {
295
- Keys: resolvedKeys,
296
- },
297
- };
298
-
299
- return createBatchGetBuilder<InferModel<Model>>(requestItems, client, undefined, logger);
300
- },
301
-
302
- batchWrite(items) {
303
- // Validate and process all items
304
- const processedItems = items.map((item) => {
305
- // Validate full input data
306
- const parsed = zodSchema.parse(item);
307
-
308
- // Apply post-processing defaults from the model (including timestamps for new items)
309
- const withDefaults = applyPostDefaults(model, parsed, {
310
- isUpdate: false,
311
- timestamps,
312
- });
313
-
314
- // Resolve keys again with defaults
315
- const fullKey = resolveKeys(model, withDefaults);
316
-
317
- // Combine keys and data into full item, adding _type field
318
- return {
319
- ...withDefaults,
320
- ...fullKey,
321
- _type: modelName, // Add entity type identifier
322
- };
323
- });
324
-
325
- // Create the request items in the format expected by BatchWriteItem
326
- const requestItems: Record<string, WriteRequest[]> = {
327
- [tableName]: processedItems.map((item) => ({
328
- PutRequest: {
329
- Item: item,
330
- },
331
- })),
332
- };
333
-
334
- return createBatchWriteBuilder(requestItems, client, logger);
335
- },
336
- };
337
- };