@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 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, // Auto createdAt/updatedAt
154
- isoDates: true, // Use ISO 8601 dates
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.transactWrite()
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.transactGet()
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, // Main Table class
473
- type SchemaDefinition, // Schema type
474
- type ModelDefinition, // Model type
475
- type AttributeDefinition, // Union of all attribute types
476
- type ScalarAttributeDefinition, // String/Number/Boolean/Date attribute
477
- type ObjectAttributeDefinition, // Nested object attribute with schema
478
- type ArrayAttributeDefinition, // Typed array attribute with items
479
- type PrimaryKeyDefinition, // PK + SK key definition
480
- type KeyDefinition, // Single key definition
481
- type IndexDefinition, // Index (hash + optional sort)
482
- type IndexesDefinition, // All table indexes
483
- type SchemaParams, // Global schema params
484
- type InferModel, // Infer model type (deprecated)
485
- type InferInput, // Infer input type (deprecated)
486
- type InferKeyInput, // Infer key type
487
- type InferModelFromSchema, // Infer from full schema (preferred)
488
- type InferInputFromSchema, // Infer input from full schema (preferred)
489
- type TimestampFields, // createdAt/updatedAt fields type
490
- type ArrayItem, // Extract item type from array attribute
491
- createDynamoDBLogger, // Logger factory
492
- type DynamoDBLogger, // Logger type
493
- type DynamoDBLoggerConfig, // Logger config
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,CAAC,iBAAiB,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,UAAU,CAAC;IACzG;;;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
+ {"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,CAqHnB"}
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;AAuU7D;;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"}
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,OA+BrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
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"}
@@ -39,7 +39,8 @@ const typeToZod = (attr) => {
39
39
  ? zod_1.z.date()
40
40
  : zod_1.z.unknown();
41
41
  }
42
- return attr.required ? zodType : zodType.optional();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ftschopp/dynatable-core",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Core library for DynamoDB single table design",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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<ExecutableBuilder<Model | undefined>, 'dbParams'> {
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(tableName, item, client, conditions, true, returnMode, enableTimestamps, logger);
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(state.condition, state.model, state.indexName);
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
  });
@@ -38,7 +38,8 @@ export const typeToZod = (attr: AttributeDefinition): ZodType => {
38
38
  : z.unknown();
39
39
  }
40
40
 
41
- return attr.required ? zodType : zodType.optional();
41
+ const isGenerated = 'generate' in attr;
42
+ return attr.required && !isGenerated ? zodType : zodType.optional();
42
43
  };
43
44
 
44
45
  /**