@ftschopp/dynatable-core 1.2.2 → 1.2.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.
- package/CHANGELOG.md +14 -0
- package/README.md +39 -56
- package/dist/builders/get/types.d.ts.map +1 -1
- package/dist/builders/put/create-put-builder.d.ts.map +1 -1
- package/dist/builders/query/create-query-builder.d.ts.map +1 -1
- package/dist/builders/query/create-query-builder.js +3 -24
- package/dist/entity/create-entity-api.js +4 -4
- package/dist/utils/zod-utils.d.ts.map +1 -1
- package/dist/utils/zod-utils.js +2 -1
- package/package.json +1 -1
- package/src/builders/get/types.ts +4 -1
- package/src/builders/put/create-put-builder.ts +10 -1
- package/src/builders/query/create-query-builder.ts +8 -27
- package/src/entity/create-entity-api.ts +4 -4
- package/src/utils/zod-utils.test.ts +2 -6
- package/src/utils/zod-utils.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## @ftschopp/dynatable-core [1.2.4](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.3...@ftschopp/dynatable-core@1.2.4) (2026-04-29)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* get keyname for attribute index ([5bd47c3](https://github.com/ftschopp/dynatable/commit/5bd47c38585c78153ff51f5e457777e928d6e667))
|
|
7
|
+
|
|
8
|
+
## @ftschopp/dynatable-core [1.2.3](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.2...@ftschopp/dynatable-core@1.2.3) (2026-04-27)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* resolve both required and optional keys in generate fields ([2c06b43](https://github.com/ftschopp/dynatable/commit/2c06b435d4578cb28f773798c80c19bf4eb9ef28))
|
|
14
|
+
|
|
1
15
|
## @ftschopp/dynatable-core [1.2.2](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.1...@ftschopp/dynatable-core@1.2.2) (2026-04-15)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -84,9 +84,7 @@ async function example() {
|
|
|
84
84
|
}).execute();
|
|
85
85
|
|
|
86
86
|
// Update
|
|
87
|
-
await table.entities.User.update({ username: 'alice' })
|
|
88
|
-
.set('name', 'Alice Johnson')
|
|
89
|
-
.execute();
|
|
87
|
+
await table.entities.User.update({ username: 'alice' }).set('name', 'Alice Johnson').execute();
|
|
90
88
|
|
|
91
89
|
// Query
|
|
92
90
|
const users = await table.entities.User.query()
|
|
@@ -150,8 +148,8 @@ const schema = {
|
|
|
150
148
|
},
|
|
151
149
|
|
|
152
150
|
params: {
|
|
153
|
-
timestamps: true,
|
|
154
|
-
isoDates: true,
|
|
151
|
+
timestamps: true, // Auto createdAt/updatedAt
|
|
152
|
+
isoDates: true, // Use ISO 8601 dates
|
|
155
153
|
cleanInternalKeys: false, // Hide PK/SK from results
|
|
156
154
|
},
|
|
157
155
|
} as const;
|
|
@@ -276,12 +274,7 @@ await table.entities.User.delete({ username: 'alice' })
|
|
|
276
274
|
|
|
277
275
|
// QUERY - Query with conditions
|
|
278
276
|
const photos = await table.entities.Photo.query()
|
|
279
|
-
.where((attr, op) =>
|
|
280
|
-
op.and(
|
|
281
|
-
op.eq(attr.username, 'alice'),
|
|
282
|
-
op.gt(attr.likesCount, 10)
|
|
283
|
-
)
|
|
284
|
-
)
|
|
277
|
+
.where((attr, op) => op.and(op.eq(attr.username, 'alice'), op.gt(attr.likesCount, 10)))
|
|
285
278
|
.limit(20)
|
|
286
279
|
.scanIndexForward(false)
|
|
287
280
|
.execute();
|
|
@@ -312,7 +305,8 @@ Atomic operations across multiple items:
|
|
|
312
305
|
|
|
313
306
|
```typescript
|
|
314
307
|
// TransactWrite - Atomic writes
|
|
315
|
-
await table
|
|
308
|
+
await table
|
|
309
|
+
.transactWrite()
|
|
316
310
|
.addPut(
|
|
317
311
|
table.entities.Like.put({
|
|
318
312
|
photoId: 'photo1',
|
|
@@ -321,15 +315,12 @@ await table.transactWrite()
|
|
|
321
315
|
.ifNotExists()
|
|
322
316
|
.dbParams()
|
|
323
317
|
)
|
|
324
|
-
.addUpdate(
|
|
325
|
-
table.entities.Photo.update({ photoId: 'photo1' })
|
|
326
|
-
.add('likesCount', 1)
|
|
327
|
-
.dbParams()
|
|
328
|
-
)
|
|
318
|
+
.addUpdate(table.entities.Photo.update({ photoId: 'photo1' }).add('likesCount', 1).dbParams())
|
|
329
319
|
.execute();
|
|
330
320
|
|
|
331
321
|
// TransactGet - Atomic reads
|
|
332
|
-
const result = await table
|
|
322
|
+
const result = await table
|
|
323
|
+
.transactGet()
|
|
333
324
|
.addGet(table.entities.User.get({ username: 'alice' }).dbParams())
|
|
334
325
|
.addGet(table.entities.Photo.get({ photoId: 'photo1' }).dbParams())
|
|
335
326
|
.execute();
|
|
@@ -342,6 +333,7 @@ const [user, photo] = result.items;
|
|
|
342
333
|
Build complex conditions with type-safe operators:
|
|
343
334
|
|
|
344
335
|
### Comparison
|
|
336
|
+
|
|
345
337
|
- `eq(attr, value)` - Equals
|
|
346
338
|
- `ne(attr, value)` - Not equals
|
|
347
339
|
- `lt(attr, value)` - Less than
|
|
@@ -351,19 +343,23 @@ Build complex conditions with type-safe operators:
|
|
|
351
343
|
- `between(attr, low, high)` - Between values
|
|
352
344
|
|
|
353
345
|
### String
|
|
346
|
+
|
|
354
347
|
- `beginsWith(attr, prefix)` - Begins with prefix
|
|
355
348
|
- `contains(attr, value)` - Contains value (strings, sets, lists)
|
|
356
349
|
|
|
357
350
|
### Existence
|
|
351
|
+
|
|
358
352
|
- `exists(attr)` - Attribute exists
|
|
359
353
|
- `notExists(attr)` - Attribute doesn't exist
|
|
360
354
|
|
|
361
355
|
### Advanced
|
|
356
|
+
|
|
362
357
|
- `attributeType(attr, type)` - Check attribute type ('S', 'N', 'M', 'L', etc.)
|
|
363
358
|
- `in(attr, values[])` - Value in array
|
|
364
359
|
- `size(attr)` - Get size, returns object with `.eq()`, `.gt()`, etc.
|
|
365
360
|
|
|
366
361
|
### Logical
|
|
362
|
+
|
|
367
363
|
- `and(...conditions)` - Combine with AND
|
|
368
364
|
- `or(...conditions)` - Combine with OR
|
|
369
365
|
- `not(condition)` - Negate condition
|
|
@@ -379,12 +375,7 @@ await table.entities.User.update({ username: 'alice' })
|
|
|
379
375
|
|
|
380
376
|
// Contains
|
|
381
377
|
const users = await table.entities.User.query()
|
|
382
|
-
.where((attr, op) =>
|
|
383
|
-
op.and(
|
|
384
|
-
op.eq(attr.username, 'alice'),
|
|
385
|
-
op.contains(attr.tags, 'premium')
|
|
386
|
-
)
|
|
387
|
-
)
|
|
378
|
+
.where((attr, op) => op.and(op.eq(attr.username, 'alice'), op.contains(attr.tags, 'premium')))
|
|
388
379
|
.execute();
|
|
389
380
|
|
|
390
381
|
// IN operator
|
|
@@ -394,12 +385,7 @@ const activeUsers = await table.entities.User.scan()
|
|
|
394
385
|
|
|
395
386
|
// Size function
|
|
396
387
|
const posts = await table.entities.Post.query()
|
|
397
|
-
.where((attr, op) =>
|
|
398
|
-
op.and(
|
|
399
|
-
op.eq(attr.userId, 'alice'),
|
|
400
|
-
op.size(attr.tags).gte(3)
|
|
401
|
-
)
|
|
402
|
-
)
|
|
388
|
+
.where((attr, op) => op.and(op.eq(attr.userId, 'alice'), op.size(attr.tags).gte(3)))
|
|
403
389
|
.execute();
|
|
404
390
|
|
|
405
391
|
// Complex nested conditions
|
|
@@ -409,10 +395,7 @@ await table.entities.Photo.query()
|
|
|
409
395
|
op.eq(attr.username, 'alice'),
|
|
410
396
|
op.or(
|
|
411
397
|
op.gt(attr.likesCount, 100),
|
|
412
|
-
op.and(
|
|
413
|
-
op.gt(attr.commentCount, 50),
|
|
414
|
-
op.exists(attr.featured)
|
|
415
|
-
)
|
|
398
|
+
op.and(op.gt(attr.commentCount, 50), op.exists(attr.featured))
|
|
416
399
|
)
|
|
417
400
|
)
|
|
418
401
|
)
|
|
@@ -469,28 +452,28 @@ if (page1.lastEvaluatedKey) {
|
|
|
469
452
|
|
|
470
453
|
```typescript
|
|
471
454
|
export {
|
|
472
|
-
Table,
|
|
473
|
-
type SchemaDefinition,
|
|
474
|
-
type ModelDefinition,
|
|
475
|
-
type AttributeDefinition,
|
|
476
|
-
type ScalarAttributeDefinition,
|
|
477
|
-
type ObjectAttributeDefinition,
|
|
478
|
-
type ArrayAttributeDefinition,
|
|
479
|
-
type PrimaryKeyDefinition,
|
|
480
|
-
type KeyDefinition,
|
|
481
|
-
type IndexDefinition,
|
|
482
|
-
type IndexesDefinition,
|
|
483
|
-
type SchemaParams,
|
|
484
|
-
type InferModel,
|
|
485
|
-
type InferInput,
|
|
486
|
-
type InferKeyInput,
|
|
487
|
-
type InferModelFromSchema,
|
|
488
|
-
type InferInputFromSchema,
|
|
489
|
-
type TimestampFields,
|
|
490
|
-
type ArrayItem,
|
|
491
|
-
createDynamoDBLogger,
|
|
492
|
-
type DynamoDBLogger,
|
|
493
|
-
type DynamoDBLoggerConfig,
|
|
455
|
+
Table, // Main Table class
|
|
456
|
+
type SchemaDefinition, // Schema type
|
|
457
|
+
type ModelDefinition, // Model type
|
|
458
|
+
type AttributeDefinition, // Union of all attribute types
|
|
459
|
+
type ScalarAttributeDefinition, // String/Number/Boolean/Date attribute
|
|
460
|
+
type ObjectAttributeDefinition, // Nested object attribute with schema
|
|
461
|
+
type ArrayAttributeDefinition, // Typed array attribute with items
|
|
462
|
+
type PrimaryKeyDefinition, // PK + SK key definition
|
|
463
|
+
type KeyDefinition, // Single key definition
|
|
464
|
+
type IndexDefinition, // Index (hash + optional sort)
|
|
465
|
+
type IndexesDefinition, // All table indexes
|
|
466
|
+
type SchemaParams, // Global schema params
|
|
467
|
+
type InferModel, // Infer model type (deprecated)
|
|
468
|
+
type InferInput, // Infer input type (deprecated)
|
|
469
|
+
type InferKeyInput, // Infer key type
|
|
470
|
+
type InferModelFromSchema, // Infer from full schema (preferred)
|
|
471
|
+
type InferInputFromSchema, // Infer input from full schema (preferred)
|
|
472
|
+
type TimestampFields, // createdAt/updatedAt fields type
|
|
473
|
+
type ArrayItem, // Extract item type from array attribute
|
|
474
|
+
createDynamoDBLogger, // Logger factory
|
|
475
|
+
type DynamoDBLogger, // Logger type
|
|
476
|
+
type DynamoDBLoggerConfig, // Logger config
|
|
494
477
|
};
|
|
495
478
|
```
|
|
496
479
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/get/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAE,SAAQ,IAAI,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/get/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAE,SAAQ,IAAI,CACvD,iBAAiB,CAAC,KAAK,GAAG,SAAS,CAAC,EACpC,UAAU,CACX;IACC;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE5D;;;OAGG;IACH,cAAc,IAAI,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE9C;;;;;;;;OAQG;IACH,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAExF;;OAEG;IACH,QAAQ,IAAI,eAAe,CAAC;CAC7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-put-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/put/create-put-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAAmB,MAAM,WAAW,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAgB7D;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EACpC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,EACX,MAAM,EAAE,cAAc,EACtB,cAAc,GAAE,SAAS,EAAO,EAChC,oBAAoB,UAAQ,EAC5B,UAAU,GAAE,MAAM,GAAG,SAAkB,EACvC,gBAAgB,UAAQ,EACxB,MAAM,CAAC,EAAE,cAAc,GACtB,UAAU,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"create-put-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/put/create-put-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAAmB,MAAM,WAAW,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAgB7D;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EACpC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,EACX,MAAM,EAAE,cAAc,EACtB,cAAc,GAAE,SAAS,EAAO,EAChC,oBAAoB,UAAQ,EAC5B,UAAU,GAAE,MAAM,GAAG,SAAkB,EACvC,gBAAgB,UAAQ,EACxB,MAAM,CAAC,EAAE,cAAc,GACtB,UAAU,CAAC,KAAK,CAAC,CA8HnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAoT7D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,cAAc,GACtB,YAAY,CAAC,KAAK,CAAC,CA4BrB"}
|
|
@@ -5,30 +5,6 @@ const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
|
5
5
|
const operators_1 = require("../shared/operators");
|
|
6
6
|
const conditions_1 = require("../shared/conditions");
|
|
7
7
|
const model_utils_1 = require("../../utils/model-utils");
|
|
8
|
-
/**
|
|
9
|
-
* Determines if a field is a key field (used in pk/sk or index key templates)
|
|
10
|
-
*/
|
|
11
|
-
function isKeyField(fieldName, model, indexName) {
|
|
12
|
-
if (!model?.key)
|
|
13
|
-
return false;
|
|
14
|
-
// When an index is specified, also check model.index for key templates
|
|
15
|
-
if (indexName && model.index) {
|
|
16
|
-
for (const [, keyDef] of Object.entries(model.index)) {
|
|
17
|
-
const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
|
|
18
|
-
if (templateVars.includes(fieldName)) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const keyEntries = Object.entries(model.key);
|
|
24
|
-
for (const [, keyDef] of keyEntries) {
|
|
25
|
-
const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
|
|
26
|
-
if (templateVars.includes(fieldName)) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
8
|
/**
|
|
33
9
|
* Maps a model attribute name to its corresponding DynamoDB key name (PK/SK)
|
|
34
10
|
* Also returns the key template for value transformation
|
|
@@ -39,6 +15,9 @@ function getKeyNameForAttribute(fieldName, model, indexName) {
|
|
|
39
15
|
// When an index is specified, check model.index first for key templates
|
|
40
16
|
if (indexName && model.index) {
|
|
41
17
|
for (const [keyName, keyDef] of Object.entries(model.index)) {
|
|
18
|
+
if (!keyName.startsWith(indexName)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
42
21
|
const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
|
|
43
22
|
if (templateVars.includes(fieldName)) {
|
|
44
23
|
return { keyName, template: keyDef.value };
|
|
@@ -38,8 +38,8 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
38
38
|
isUpdate: false,
|
|
39
39
|
timestamps,
|
|
40
40
|
});
|
|
41
|
-
// Resolve keys again with defaults
|
|
42
|
-
const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
|
|
41
|
+
// Resolve keys again with defaults (including GSI index keys)
|
|
42
|
+
const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults, 'both');
|
|
43
43
|
// Combine keys and data into full item, adding _type field
|
|
44
44
|
const fullItem = {
|
|
45
45
|
...withDefaults,
|
|
@@ -106,8 +106,8 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
106
106
|
isUpdate: false,
|
|
107
107
|
timestamps,
|
|
108
108
|
});
|
|
109
|
-
// Resolve keys again with defaults
|
|
110
|
-
const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
|
|
109
|
+
// Resolve keys again with defaults (including GSI index keys)
|
|
110
|
+
const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults, 'both');
|
|
111
111
|
// Combine keys and data into full item, adding _type field
|
|
112
112
|
return {
|
|
113
113
|
...withDefaults,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,
|
|
1
|
+
{"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,OAgCrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
|
package/dist/utils/zod-utils.js
CHANGED
|
@@ -39,7 +39,8 @@ const typeToZod = (attr) => {
|
|
|
39
39
|
? zod_1.z.date()
|
|
40
40
|
: zod_1.z.unknown();
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
const isGenerated = 'generate' in attr;
|
|
43
|
+
return attr.required && !isGenerated ? zodType : zodType.optional();
|
|
43
44
|
};
|
|
44
45
|
exports.typeToZod = typeToZod;
|
|
45
46
|
/**
|
package/package.json
CHANGED
|
@@ -5,7 +5,10 @@ import { ExecutableBuilder } from '../shared';
|
|
|
5
5
|
* Builder for DynamoDB GetItem operations.
|
|
6
6
|
* Supports projection, consistent read, and inspection of the final parameters.
|
|
7
7
|
*/
|
|
8
|
-
export interface GetBuilder<KeyInput, Model> extends Omit<
|
|
8
|
+
export interface GetBuilder<KeyInput, Model> extends Omit<
|
|
9
|
+
ExecutableBuilder<Model | undefined>,
|
|
10
|
+
'dbParams'
|
|
11
|
+
> {
|
|
9
12
|
/**
|
|
10
13
|
* Adds a projection expression to only return specific attributes.
|
|
11
14
|
* Returns a new immutable builder.
|
|
@@ -57,7 +57,16 @@ export function createPutBuilder<Model>(
|
|
|
57
57
|
},
|
|
58
58
|
|
|
59
59
|
ifNotExists() {
|
|
60
|
-
return createPutBuilder(
|
|
60
|
+
return createPutBuilder(
|
|
61
|
+
tableName,
|
|
62
|
+
item,
|
|
63
|
+
client,
|
|
64
|
+
conditions,
|
|
65
|
+
true,
|
|
66
|
+
returnMode,
|
|
67
|
+
enableTimestamps,
|
|
68
|
+
logger
|
|
69
|
+
);
|
|
61
70
|
},
|
|
62
71
|
|
|
63
72
|
returning(mode) {
|
|
@@ -26,32 +26,6 @@ type QueryState<Model> = {
|
|
|
26
26
|
logger?: DynamoDBLogger;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
* Determines if a field is a key field (used in pk/sk or index key templates)
|
|
31
|
-
*/
|
|
32
|
-
function isKeyField(fieldName: string, model?: ModelDefinition, indexName?: string): boolean {
|
|
33
|
-
if (!model?.key) return false;
|
|
34
|
-
|
|
35
|
-
// When an index is specified, also check model.index for key templates
|
|
36
|
-
if (indexName && model.index) {
|
|
37
|
-
for (const [, keyDef] of Object.entries(model.index)) {
|
|
38
|
-
const templateVars = extractTemplateVars(keyDef.value);
|
|
39
|
-
if (templateVars.includes(fieldName)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const keyEntries = Object.entries(model.key);
|
|
46
|
-
for (const [, keyDef] of keyEntries) {
|
|
47
|
-
const templateVars = extractTemplateVars(keyDef.value);
|
|
48
|
-
if (templateVars.includes(fieldName)) {
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
29
|
/**
|
|
56
30
|
* Maps a model attribute name to its corresponding DynamoDB key name (PK/SK)
|
|
57
31
|
* Also returns the key template for value transformation
|
|
@@ -66,6 +40,9 @@ function getKeyNameForAttribute(
|
|
|
66
40
|
// When an index is specified, check model.index first for key templates
|
|
67
41
|
if (indexName && model.index) {
|
|
68
42
|
for (const [keyName, keyDef] of Object.entries(model.index)) {
|
|
43
|
+
if (!keyName.startsWith(indexName)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
69
46
|
const templateVars = extractTemplateVars(keyDef.value);
|
|
70
47
|
if (templateVars.includes(fieldName)) {
|
|
71
48
|
return { keyName, template: keyDef.value };
|
|
@@ -253,7 +230,11 @@ function createQueryExecutor<Model>(state: QueryState<Model>): QueryExecutor<Mod
|
|
|
253
230
|
}
|
|
254
231
|
|
|
255
232
|
// Separate key conditions from filter conditions
|
|
256
|
-
const { keyConditions, filterConditions } = separateConditions(
|
|
233
|
+
const { keyConditions, filterConditions } = separateConditions(
|
|
234
|
+
state.condition,
|
|
235
|
+
state.model,
|
|
236
|
+
state.indexName
|
|
237
|
+
);
|
|
257
238
|
|
|
258
239
|
// Build KeyConditionExpression (simple AND)
|
|
259
240
|
const keyExpr = buildSimpleAndExpression(keyConditions);
|
|
@@ -71,8 +71,8 @@ export const createEntityAPI = <Model extends ModelDefinition>(
|
|
|
71
71
|
timestamps,
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
// Resolve keys again with defaults
|
|
75
|
-
const fullKey = resolveKeys(model, withDefaults);
|
|
74
|
+
// Resolve keys again with defaults (including GSI index keys)
|
|
75
|
+
const fullKey = resolveKeys(model, withDefaults, 'both');
|
|
76
76
|
|
|
77
77
|
// Combine keys and data into full item, adding _type field
|
|
78
78
|
const fullItem = {
|
|
@@ -194,8 +194,8 @@ export const createEntityAPI = <Model extends ModelDefinition>(
|
|
|
194
194
|
timestamps,
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
-
// Resolve keys again with defaults
|
|
198
|
-
const fullKey = resolveKeys(model, withDefaults);
|
|
197
|
+
// Resolve keys again with defaults (including GSI index keys)
|
|
198
|
+
const fullKey = resolveKeys(model, withDefaults, 'both');
|
|
199
199
|
|
|
200
200
|
// Combine keys and data into full item, adding _type field
|
|
201
201
|
return {
|
|
@@ -138,9 +138,7 @@ describe('zod-utils', () => {
|
|
|
138
138
|
const zodType = typeToZod(attr);
|
|
139
139
|
|
|
140
140
|
expect(() => zodType.parse([{ status: 'PENDING' }])).not.toThrow();
|
|
141
|
-
expect(() =>
|
|
142
|
-
zodType.parse([{ date: '2024-01-01', status: 'DONE' }])
|
|
143
|
-
).not.toThrow();
|
|
141
|
+
expect(() => zodType.parse([{ date: '2024-01-01', status: 'DONE' }])).not.toThrow();
|
|
144
142
|
expect(() => zodType.parse([{ date: '2024-01-01' }])).toThrow(); // missing status
|
|
145
143
|
});
|
|
146
144
|
|
|
@@ -172,9 +170,7 @@ describe('zod-utils', () => {
|
|
|
172
170
|
};
|
|
173
171
|
const zodType = typeToZod(attr);
|
|
174
172
|
|
|
175
|
-
expect(() =>
|
|
176
|
-
zodType.parse({ value: 100, breakdown: { base: 90, fees: 10 } })
|
|
177
|
-
).not.toThrow();
|
|
173
|
+
expect(() => zodType.parse({ value: 100, breakdown: { base: 90, fees: 10 } })).not.toThrow();
|
|
178
174
|
expect(() => zodType.parse({ value: 100 })).not.toThrow(); // breakdown optional
|
|
179
175
|
expect(() => zodType.parse({})).toThrow(); // value required
|
|
180
176
|
});
|
package/src/utils/zod-utils.ts
CHANGED
|
@@ -38,7 +38,8 @@ export const typeToZod = (attr: AttributeDefinition): ZodType => {
|
|
|
38
38
|
: z.unknown();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
const isGenerated = 'generate' in attr;
|
|
42
|
+
return attr.required && !isGenerated ? zodType : zodType.optional();
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
/**
|