@ftschopp/dynatable-core 1.1.0 → 1.2.1

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,24 @@
1
+ ## @ftschopp/dynatable-core [1.2.1](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.0...@ftschopp/dynatable-core@1.2.1) (2026-04-15)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * resolve GSI key attributes to KeyConditionExpression when using useIndex ([8b2ee35](https://github.com/ftschopp/dynatable/commit/8b2ee35ca56bae9a32c8f1fec764365e8496674d))
7
+
8
+ ## @ftschopp/dynatable-core [1.2.1](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.0...@ftschopp/dynatable-core@1.2.1) (2026-04-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * resolve GSI key attributes to KeyConditionExpression when using useIndex ([8b2ee35](https://github.com/ftschopp/dynatable/commit/8b2ee35ca56bae9a32c8f1fec764365e8496674d))
14
+
15
+ # @ftschopp/dynatable-core [1.2.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.1.0...@ftschopp/dynatable-core@1.2.0) (2026-03-20)
16
+
17
+
18
+ ### Features
19
+
20
+ * support nested objects and arrays schema ([c06e43e](https://github.com/ftschopp/dynatable/commit/c06e43e6b425bae09f127d2f767b5c8c62dd1142))
21
+
1
22
  # @ftschopp/dynatable-core [1.1.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.0.0...@ftschopp/dynatable-core@1.1.0) (2026-01-20)
2
23
 
3
24
 
package/README.md CHANGED
@@ -130,6 +130,21 @@ const schema = {
130
130
  email: { type: String },
131
131
  userId: { type: String, generate: 'ulid' },
132
132
  followerCount: { type: Number, default: 0 },
133
+ // Nested object with typed schema
134
+ address: {
135
+ type: Object,
136
+ schema: {
137
+ street: { type: String },
138
+ city: { type: String, required: true },
139
+ country: { type: String, required: true },
140
+ },
141
+ },
142
+ // Typed array with item schema
143
+ tags: {
144
+ type: Array,
145
+ default: [],
146
+ items: { type: String },
147
+ },
133
148
  },
134
149
  },
135
150
  },
@@ -142,24 +157,85 @@ const schema = {
142
157
  } as const;
143
158
  ```
144
159
 
160
+ ### Nested Objects and Arrays
161
+
162
+ Attributes support typed nested schemas for full TypeScript inference:
163
+
164
+ ```typescript
165
+ Story: {
166
+ attributes: {
167
+ // Typed array of objects
168
+ frames: {
169
+ type: Array,
170
+ default: [],
171
+ items: {
172
+ type: Object,
173
+ schema: {
174
+ url: { type: String, required: true },
175
+ duration: { type: Number },
176
+ mediaType: { type: String },
177
+ },
178
+ },
179
+ },
180
+ // Nested object with a typed schema
181
+ location: {
182
+ type: Object,
183
+ schema: {
184
+ city: { type: String },
185
+ country: { type: String },
186
+ lat: { type: Number },
187
+ lng: { type: Number },
188
+ },
189
+ },
190
+ },
191
+ }
192
+ ```
193
+
194
+ Use `ArrayItem<T>` to extract item types from array attributes:
195
+
196
+ ```typescript
197
+ import type { InferModelFromSchema, ArrayItem } from '@ftschopp/dynatable-core';
198
+
199
+ type StoryEntity = InferModelFromSchema<typeof schema, 'Story'>;
200
+ type StoryFrame = ArrayItem<StoryEntity['frames']>;
201
+ // → { url: string; duration?: number; mediaType?: string }
202
+ ```
203
+
145
204
  ### Type Inference
146
205
 
147
206
  Extract types from your schema:
148
207
 
149
208
  ```typescript
150
- import type { InferModel, InferInput, InferKeyInput } from '@ftschopp/dynatable-core';
151
-
152
- // Full model type (includes timestamps if enabled)
153
- type User = InferModel<typeof schema.models.User>;
209
+ import type {
210
+ InferModel,
211
+ InferInput,
212
+ InferKeyInput,
213
+ InferModelFromSchema,
214
+ InferInputFromSchema,
215
+ ArrayItem,
216
+ } from '@ftschopp/dynatable-core';
217
+
218
+ // Preferred: infer from the full schema (timestamps included automatically)
219
+ type User = InferModelFromSchema<typeof schema, 'User'>;
154
220
  // { username: string; name: string; email?: string; userId: string; followerCount: number; createdAt: string; updatedAt: string }
155
221
 
156
- // Input type (excludes generated fields and timestamps)
157
- type UserInput = InferInput<typeof schema.models.User>;
158
- // { username: string; name: string; email?: string; userId?: string; followerCount?: number }
222
+ type UserInput = InferInputFromSchema<typeof schema, 'User'>;
223
+ // { username: string; name: string; email?: string; followerCount?: number }
224
+
225
+ // Full model type (deprecated — use InferModelFromSchema)
226
+ type UserLegacy = InferModel<typeof schema.models.User>;
227
+
228
+ // Input type (deprecated — use InferInputFromSchema)
229
+ type UserInputLegacy = InferInput<typeof schema.models.User>;
159
230
 
160
231
  // Key input type (only key template variables)
161
232
  type UserKey = InferKeyInput<typeof schema.models.User>;
162
233
  // { username: string }
234
+
235
+ // Extract item type from an array attribute
236
+ type StoryEntity = InferModelFromSchema<typeof schema, 'Story'>;
237
+ type StoryFrame = ArrayItem<StoryEntity['frames']>;
238
+ // → { url: string; duration?: number; mediaType?: string }
163
239
  ```
164
240
 
165
241
  ### Builder Operations
@@ -393,18 +469,28 @@ if (page1.lastEvaluatedKey) {
393
469
 
394
470
  ```typescript
395
471
  export {
396
- Table, // Main Table class
397
- type SchemaDefinition, // Schema type
398
- type ModelDefinition, // Model type
399
- type InferModel, // Infer model type
400
- type InferInput, // Infer input type
401
- type InferKeyInput, // Infer key type
402
- type InferModelFromSchema, // Infer from schema
403
- type InferInputFromSchema, // Infer input from schema
404
- type TimestampFields, // Timestamp fields type
405
- createDynamoDBLogger, // Logger factory
406
- type DynamoDBLogger, // Logger type
407
- type DynamoDBLoggerConfig, // Logger config
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
408
494
  };
409
495
  ```
410
496
 
@@ -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;AAiT7D;;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;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"}
@@ -6,11 +6,20 @@ 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
8
  /**
9
- * Determines if a field is a key field (used in pk/sk templates)
9
+ * Determines if a field is a key field (used in pk/sk or index key templates)
10
10
  */
11
- function isKeyField(fieldName, model) {
11
+ function isKeyField(fieldName, model, indexName) {
12
12
  if (!model?.key)
13
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
+ }
14
23
  const keyEntries = Object.entries(model.key);
15
24
  for (const [, keyDef] of keyEntries) {
16
25
  const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
@@ -24,13 +33,22 @@ function isKeyField(fieldName, model) {
24
33
  * Maps a model attribute name to its corresponding DynamoDB key name (PK/SK)
25
34
  * Also returns the key template for value transformation
26
35
  */
27
- function getKeyNameForAttribute(fieldName, model) {
36
+ function getKeyNameForAttribute(fieldName, model, indexName) {
28
37
  if (!model?.key)
29
38
  return null;
39
+ // When an index is specified, check model.index first for key templates
40
+ if (indexName && model.index) {
41
+ for (const [keyName, keyDef] of Object.entries(model.index)) {
42
+ const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
43
+ if (templateVars.includes(fieldName)) {
44
+ return { keyName, template: keyDef.value };
45
+ }
46
+ }
47
+ }
30
48
  for (const [keyName, keyDef] of Object.entries(model.key)) {
31
49
  const templateVars = (0, model_utils_1.extractTemplateVars)(keyDef.value);
32
50
  if (templateVars.includes(fieldName)) {
33
- return { keyName, template: keyDef.value }; // Returns "PK" and "UP#${username}"
51
+ return { keyName, template: keyDef.value };
34
52
  }
35
53
  }
36
54
  return null;
@@ -46,7 +64,7 @@ function applyKeyTemplate(template, fieldName, fieldValue) {
46
64
  * Separates a condition tree into key conditions and filter conditions
47
65
  * Key conditions are rewritten to use actual DynamoDB key names (PK/SK)
48
66
  */
49
- function separateConditions(condition, model) {
67
+ function separateConditions(condition, model, indexName) {
50
68
  const keyConditions = [];
51
69
  const filterConditions = [];
52
70
  function traverse(cond) {
@@ -76,7 +94,7 @@ function separateConditions(condition, model) {
76
94
  const fieldMatch = cond.expression.match(/#(\w+)/);
77
95
  if (fieldMatch && fieldMatch[1]) {
78
96
  const fieldName = fieldMatch[1];
79
- const keyInfo = getKeyNameForAttribute(fieldName, model);
97
+ const keyInfo = getKeyNameForAttribute(fieldName, model, indexName);
80
98
  if (keyInfo) {
81
99
  // This is a key field - rewrite the condition to use the actual key name
82
100
  // and apply the template to the value
@@ -178,7 +196,7 @@ function createQueryExecutor(state) {
178
196
  throw new Error('No where condition specified');
179
197
  }
180
198
  // Separate key conditions from filter conditions
181
- const { keyConditions, filterConditions } = separateConditions(state.condition, state.model);
199
+ const { keyConditions, filterConditions } = separateConditions(state.condition, state.model, state.indexName);
182
200
  // Build KeyConditionExpression (simple AND)
183
201
  const keyExpr = buildSimpleAndExpression(keyConditions);
184
202
  // Build FilterExpression (can be complex with OR, NOT, etc.)
@@ -23,19 +23,61 @@
23
23
  * ```
24
24
  */
25
25
  /**
26
- * Attribute definition for model attributes
27
- *
28
- * @property type - The JavaScript constructor for the attribute type
29
- * @property [required] - Whether the attribute is required
30
- * @property [generate] - Auto-generation strategy ('ulid', 'uuid')
31
- * @property [default] - Default value or generator function
26
+ * Scalar attribute definition (String, Number, Boolean, Date)
32
27
  */
33
- export type AttributeDefinition = {
28
+ export type ScalarAttributeDefinition = {
34
29
  type: StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor;
35
30
  required?: boolean;
36
31
  generate?: 'ulid' | 'uuid';
37
32
  default?: any;
33
+ nulls?: boolean;
34
+ };
35
+ /**
36
+ * Object attribute definition with a nested schema
37
+ *
38
+ * @example
39
+ * address: {
40
+ * type: Object,
41
+ * schema: {
42
+ * street: { type: String },
43
+ * city: { type: String, required: true },
44
+ * }
45
+ * }
46
+ */
47
+ export type ObjectAttributeDefinition = {
48
+ type: ObjectConstructor;
49
+ schema: Record<string, AttributeDefinition>;
50
+ required?: boolean;
51
+ default?: any;
52
+ nulls?: boolean;
53
+ };
54
+ /**
55
+ * Array attribute definition with a typed items schema
56
+ *
57
+ * @example
58
+ * history: {
59
+ * type: Array,
60
+ * default: [],
61
+ * items: {
62
+ * type: Object,
63
+ * schema: {
64
+ * date: { type: String },
65
+ * status: { type: String },
66
+ * }
67
+ * }
68
+ * }
69
+ */
70
+ export type ArrayAttributeDefinition = {
71
+ type: ArrayConstructor;
72
+ items: AttributeDefinition;
73
+ required?: boolean;
74
+ default?: any;
75
+ nulls?: boolean;
38
76
  };
77
+ /**
78
+ * Attribute definition for model attributes — supports scalars, nested objects, and arrays
79
+ */
80
+ export type AttributeDefinition = ScalarAttributeDefinition | ObjectAttributeDefinition | ArrayAttributeDefinition;
39
81
  /**
40
82
  * Key definition for primary and secondary indexes
41
83
  *
@@ -101,7 +143,39 @@ export type SchemaDefinition = {
101
143
  models: Record<string, ModelDefinition>;
102
144
  params?: SchemaParams;
103
145
  };
104
- type InferAttr<T> = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends DateConstructor ? Date : unknown;
146
+ /**
147
+ * Recursively infers the TypeScript type from an AttributeDefinition.
148
+ * - Object → infers the nested schema shape
149
+ * - Array → infers an array of the items type
150
+ * - Scalars → maps to the corresponding primitive
151
+ */
152
+ type InferAttr<A> = A extends {
153
+ type: ObjectConstructor;
154
+ schema: infer S;
155
+ } ? InferObjectSchema<S> : A extends {
156
+ type: ArrayConstructor;
157
+ items: infer I;
158
+ } ? Array<InferAttr<I>> : A extends {
159
+ type: StringConstructor;
160
+ } ? string : A extends {
161
+ type: NumberConstructor;
162
+ } ? number : A extends {
163
+ type: BooleanConstructor;
164
+ } ? boolean : A extends {
165
+ type: DateConstructor;
166
+ } ? Date : unknown;
167
+ /**
168
+ * Infers the shape of a nested object schema, respecting required/optional fields.
169
+ */
170
+ type InferObjectSchema<S> = {
171
+ [K in keyof S as S[K] extends {
172
+ required: true;
173
+ } ? K : never]: InferAttr<S[K]>;
174
+ } & {
175
+ [K in keyof S as S[K] extends {
176
+ required: true;
177
+ } ? never : K]?: InferAttr<S[K]>;
178
+ };
105
179
  /**
106
180
  * Non-generated attributes for input
107
181
  * Splits into required and optional based on the 'required' field
@@ -111,7 +185,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
111
185
  generate: string;
112
186
  } ? never : M['attributes'][K] extends {
113
187
  required: true;
114
- } ? K : never]: InferAttr<M['attributes'][K]['type']>;
188
+ } ? K : never]: InferAttr<M['attributes'][K]>;
115
189
  } & {
116
190
  [K in keyof M['attributes'] as M['attributes'][K] extends {
117
191
  generate: string;
@@ -119,7 +193,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
119
193
  required: false;
120
194
  } ? K : M['attributes'][K] extends {
121
195
  required: true;
122
- } ? never : K]?: InferAttr<M['attributes'][K]['type']>;
196
+ } ? never : K]?: InferAttr<M['attributes'][K]>;
123
197
  };
124
198
  /**
125
199
  * Generated-only attributes for internal use
@@ -127,7 +201,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
127
201
  type GeneratedAttributes<M extends ModelDefinition> = {
128
202
  [K in keyof M['attributes'] as M['attributes'][K] extends {
129
203
  generate: string;
130
- } ? K : never]: InferAttr<M['attributes'][K]['type']>;
204
+ } ? K : never]: InferAttr<M['attributes'][K]>;
131
205
  };
132
206
  /**
133
207
  * Template string variable extraction
@@ -221,5 +295,15 @@ type KeyTemplateVars<M extends ModelDefinition> = PrimaryKeyVars<M>;
221
295
  export type InferKeyInput<M extends ModelDefinition> = {
222
296
  [K in KeyTemplateVars<M>]: string;
223
297
  };
298
+ /**
299
+ * Extracts the item type from an array attribute.
300
+ *
301
+ * @example
302
+ * type StoryFrame = ArrayItem<StoryEntity['frames']>;
303
+ * // → { url: string; duration?: number; mediaType?: string }
304
+ *
305
+ * type HistoryEntry = ArrayItem<TransactionEntity['history']>;
306
+ */
307
+ export type ArrayItem<T extends readonly unknown[] | undefined> = NonNullable<T>[number];
224
308
  export {};
225
309
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './table';
2
- export { type SchemaDefinition, type ModelDefinition, type PrimaryKeyDefinition, type KeyDefinition, type AttributeDefinition, type IndexDefinition, type IndexesDefinition, type SchemaParams, type InferInput, type InferModel, type InferKeyInput, type InferModelFromSchema, type InferInputFromSchema, type TimestampFields, } from './core/types';
2
+ export { type SchemaDefinition, type ModelDefinition, type PrimaryKeyDefinition, type KeyDefinition, type AttributeDefinition, type IndexDefinition, type IndexesDefinition, type SchemaParams, type InferInput, type InferModel, type InferKeyInput, type InferModelFromSchema, type InferInputFromSchema, type TimestampFields, type ArrayItem, } from './core/types';
3
3
  export { createDynamoDBLogger, type DynamoDBLogger, type DynamoDBLoggerConfig, } from './utils/dynamodb-logger';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-utils.d.ts","sourceRoot":"","sources":["../../src/utils/model-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,KAAG,MAAM,EAG5D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAoB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,eAAe,EACnD,OAAO,CAAC,EACR,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAM,KAAK,GAAG,OAAO,GAAG,MAAc,KACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAWvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,eAAe,EACzD,OAAO,CAAC,EACR,eAAe,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,UAAU;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,KACrD,UAAU,CAAC,CAAC,CAgCd,CAAC;AAOF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,KAAG,CAAC,GAAG,CAAC,EAAE,GAAG,SAoB1E,CAAC"}
1
+ {"version":3,"file":"model-utils.d.ts","sourceRoot":"","sources":["../../src/utils/model-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,KAAG,MAAM,EAG5D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAoB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,eAAe,EACnD,OAAO,CAAC,EACR,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAM,KAAK,GAAG,OAAO,GAAG,MAAc,KACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAWvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,eAAe,EACzD,OAAO,CAAC,EACR,eAAe,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,UAAU;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,KACrD,UAAU,CAAC,CAAC,CAiCd,CAAC;AAOF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,KAAG,CAAC,GAAG,CAAC,EAAE,GAAG,SAoB1E,CAAC"}
@@ -60,10 +60,11 @@ const applyPostDefaults = (model, validatedItem, options) => {
60
60
  const result = { ...validatedItem };
61
61
  for (const [key, attr] of Object.entries(model.attributes)) {
62
62
  if (result[key] === undefined) {
63
- if (attr.generate === 'ulid') {
63
+ const generate = 'generate' in attr ? attr.generate : undefined;
64
+ if (generate === 'ulid') {
64
65
  result[key] = (0, ulid_1.ulid)();
65
66
  }
66
- else if (attr.generate === 'uuid') {
67
+ else if (generate === 'uuid') {
67
68
  result[key] = crypto.randomUUID();
68
69
  }
69
70
  else if (attr.default !== undefined) {
@@ -1,7 +1,8 @@
1
1
  import { AttributeDefinition, ModelDefinition } from '../core/types';
2
2
  import { ZodObject, ZodType } from 'zod';
3
3
  /**
4
- * Converts an attribute definition to a Zod schema type
4
+ * Converts an attribute definition to a Zod schema type.
5
+ * Supports scalars, nested objects (with schema), and arrays (with items).
5
6
  */
6
7
  export declare const typeToZod: (attr: AttributeDefinition) => ZodType;
7
8
  /**
@@ -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;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,OAarD,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,OA+BrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
@@ -5,18 +5,40 @@ Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.modelToZod = exports.typeToZod = void 0;
6
6
  const zod_1 = require("zod");
7
7
  /**
8
- * Converts an attribute definition to a Zod schema type
8
+ * Converts an attribute definition to a Zod schema type.
9
+ * Supports scalars, nested objects (with schema), and arrays (with items).
9
10
  */
10
11
  const typeToZod = (attr) => {
11
- const zodType = attr.type === String
12
- ? zod_1.z.string()
13
- : attr.type === Number
14
- ? zod_1.z.number()
15
- : attr.type === Boolean
16
- ? zod_1.z.boolean()
17
- : attr.type === Date
18
- ? zod_1.z.date()
19
- : zod_1.z.unknown();
12
+ let zodType;
13
+ if (attr.type === Object) {
14
+ const schema = attr.schema;
15
+ if (schema) {
16
+ const shape = {};
17
+ for (const [key, nestedAttr] of Object.entries(schema)) {
18
+ shape[key] = (0, exports.typeToZod)(nestedAttr);
19
+ }
20
+ zodType = zod_1.z.looseObject(shape);
21
+ }
22
+ else {
23
+ zodType = zod_1.z.record(zod_1.z.string(), zod_1.z.unknown());
24
+ }
25
+ }
26
+ else if (attr.type === Array) {
27
+ const items = attr.items;
28
+ zodType = items ? zod_1.z.array((0, exports.typeToZod)(items)) : zod_1.z.array(zod_1.z.unknown());
29
+ }
30
+ else {
31
+ zodType =
32
+ attr.type === String
33
+ ? zod_1.z.string()
34
+ : attr.type === Number
35
+ ? zod_1.z.number()
36
+ : attr.type === Boolean
37
+ ? zod_1.z.boolean()
38
+ : attr.type === Date
39
+ ? zod_1.z.date()
40
+ : zod_1.z.unknown();
41
+ }
20
42
  return attr.required ? zodType : zodType.optional();
21
43
  };
22
44
  exports.typeToZod = typeToZod;
@@ -30,6 +52,6 @@ const modelToZod = (model) => {
30
52
  for (const [key, attr] of Object.entries(model.attributes)) {
31
53
  shape[key] = (0, exports.typeToZod)(attr);
32
54
  }
33
- return zod_1.z.object(shape).passthrough();
55
+ return zod_1.z.looseObject(shape);
34
56
  };
35
57
  exports.modelToZod = modelToZod;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ftschopp/dynatable-core",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Core library for DynamoDB single table design",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -228,3 +228,73 @@ describe('QueryBuilder - Pagination', () => {
228
228
  });
229
229
  });
230
230
  });
231
+
232
+ describe('QueryBuilder - GSI key recognition', () => {
233
+ const client = new DynamoDBClient({});
234
+ const tableName = 'TestTable';
235
+
236
+ interface UserModel {
237
+ id: string;
238
+ email: string;
239
+ name?: string;
240
+ }
241
+
242
+ const modelWithIndex: ModelDefinition = {
243
+ key: {
244
+ PK: { type: String, value: 'USER#${id}' },
245
+ SK: { type: String, value: 'USER#${id}' },
246
+ },
247
+ index: {
248
+ GSI1PK: { type: String, value: 'EMAIL#${email}' },
249
+ GSI1SK: { type: String, value: 'EMAIL#${email}' },
250
+ },
251
+ attributes: {
252
+ id: { type: String, required: true },
253
+ email: { type: String, required: true },
254
+ name: { type: String },
255
+ },
256
+ };
257
+
258
+ test('should place GSI index attribute into KeyConditionExpression when useIndex is set', () => {
259
+ const params = createQueryBuilder<UserModel>(tableName, client, modelWithIndex)
260
+ .where((attr, op) => op.eq(attr.email, 'alice@example.com'))
261
+ .useIndex('GSI1')
262
+ .dbParams();
263
+
264
+ // email should be recognized as a key field via model.index and placed in KeyConditionExpression
265
+ expect(params.KeyConditionExpression).toBeDefined();
266
+ expect(params.KeyConditionExpression).toContain('#GSI1PK');
267
+ expect(params.ExpressionAttributeValues).toBeDefined();
268
+ // The value should have the template applied
269
+ expect(Object.values(params.ExpressionAttributeValues!)).toContain('EMAIL#alice@example.com');
270
+ // It should NOT be in FilterExpression
271
+ expect(params.FilterExpression).toBeUndefined();
272
+ expect(params.IndexName).toBe('GSI1');
273
+ });
274
+
275
+ test('should place GSI attribute into FilterExpression when NO index is specified', () => {
276
+ const params = createQueryBuilder<UserModel>(tableName, client, modelWithIndex)
277
+ .where((attr, op) => op.and(op.eq(attr.id, '123'), op.eq(attr.email, 'alice@example.com')))
278
+ .dbParams();
279
+
280
+ // id maps to PK -> KeyConditionExpression
281
+ expect(params.KeyConditionExpression).toBeDefined();
282
+ expect(params.KeyConditionExpression).toContain('#PK');
283
+ // email is NOT a primary key field, so without useIndex it goes to filter
284
+ expect(params.FilterExpression).toBeDefined();
285
+ expect(params.FilterExpression).toContain('#email');
286
+ });
287
+
288
+ test('should handle both primary key and GSI key fields together with useIndex', () => {
289
+ const params = createQueryBuilder<UserModel>(tableName, client, modelWithIndex)
290
+ .where((attr, op) => op.and(op.eq(attr.email, 'alice@example.com'), op.eq(attr.id, '123')))
291
+ .useIndex('GSI1')
292
+ .dbParams();
293
+
294
+ // Both should be key conditions: email via index, id via primary key
295
+ expect(params.KeyConditionExpression).toBeDefined();
296
+ expect(params.KeyConditionExpression).toContain('#GSI1PK');
297
+ expect(params.KeyConditionExpression).toContain('#PK');
298
+ expect(params.FilterExpression).toBeUndefined();
299
+ });
300
+ });
@@ -27,11 +27,21 @@ type QueryState<Model> = {
27
27
  };
28
28
 
29
29
  /**
30
- * Determines if a field is a key field (used in pk/sk templates)
30
+ * Determines if a field is a key field (used in pk/sk or index key templates)
31
31
  */
32
- function isKeyField(fieldName: string, model?: ModelDefinition): boolean {
32
+ function isKeyField(fieldName: string, model?: ModelDefinition, indexName?: string): boolean {
33
33
  if (!model?.key) return false;
34
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
+
35
45
  const keyEntries = Object.entries(model.key);
36
46
  for (const [, keyDef] of keyEntries) {
37
47
  const templateVars = extractTemplateVars(keyDef.value);
@@ -48,14 +58,25 @@ function isKeyField(fieldName: string, model?: ModelDefinition): boolean {
48
58
  */
49
59
  function getKeyNameForAttribute(
50
60
  fieldName: string,
51
- model?: ModelDefinition
61
+ model?: ModelDefinition,
62
+ indexName?: string
52
63
  ): { keyName: string; template: string } | null {
53
64
  if (!model?.key) return null;
54
65
 
66
+ // When an index is specified, check model.index first for key templates
67
+ if (indexName && model.index) {
68
+ for (const [keyName, keyDef] of Object.entries(model.index)) {
69
+ const templateVars = extractTemplateVars(keyDef.value);
70
+ if (templateVars.includes(fieldName)) {
71
+ return { keyName, template: keyDef.value };
72
+ }
73
+ }
74
+ }
75
+
55
76
  for (const [keyName, keyDef] of Object.entries(model.key)) {
56
77
  const templateVars = extractTemplateVars(keyDef.value);
57
78
  if (templateVars.includes(fieldName)) {
58
- return { keyName, template: keyDef.value }; // Returns "PK" and "UP#${username}"
79
+ return { keyName, template: keyDef.value };
59
80
  }
60
81
  }
61
82
  return null;
@@ -75,7 +96,8 @@ function applyKeyTemplate(template: string, fieldName: string, fieldValue: any):
75
96
  */
76
97
  function separateConditions(
77
98
  condition: Condition,
78
- model?: ModelDefinition
99
+ model?: ModelDefinition,
100
+ indexName?: string
79
101
  ): { keyConditions: Condition[]; filterConditions: Condition[] } {
80
102
  const keyConditions: Condition[] = [];
81
103
  const filterConditions: Condition[] = [];
@@ -106,7 +128,7 @@ function separateConditions(
106
128
  const fieldMatch = cond.expression.match(/#(\w+)/);
107
129
  if (fieldMatch && fieldMatch[1]) {
108
130
  const fieldName = fieldMatch[1];
109
- const keyInfo = getKeyNameForAttribute(fieldName, model);
131
+ const keyInfo = getKeyNameForAttribute(fieldName, model, indexName);
110
132
 
111
133
  if (keyInfo) {
112
134
  // This is a key field - rewrite the condition to use the actual key name
@@ -231,7 +253,7 @@ function createQueryExecutor<Model>(state: QueryState<Model>): QueryExecutor<Mod
231
253
  }
232
254
 
233
255
  // Separate key conditions from filter conditions
234
- const { keyConditions, filterConditions } = separateConditions(state.condition, state.model);
256
+ const { keyConditions, filterConditions } = separateConditions(state.condition, state.model, state.indexName);
235
257
 
236
258
  // Build KeyConditionExpression (simple AND)
237
259
  const keyExpr = buildSimpleAndExpression(keyConditions);
@@ -504,3 +504,140 @@ describe('Timestamp Type Inference', () => {
504
504
  expect(timestamps.updatedAt).toBe('2024-01-01T00:00:00.000Z');
505
505
  });
506
506
  });
507
+
508
+ // ============================================================================
509
+ // Nested Object & Array Type Inference Tests
510
+ // ============================================================================
511
+
512
+ describe('Nested Object and Array Type Inference', () => {
513
+ const TransactionSchema = {
514
+ format: 'dynatable:1.0.0',
515
+ version: '1.0.0',
516
+ indexes: { primary: { hash: 'PK', sort: 'SK' } },
517
+ models: {
518
+ Transaction: {
519
+ key: {
520
+ PK: { type: String, value: 'USER#${userId}' },
521
+ SK: { type: String, value: 'TX#${txId}' },
522
+ },
523
+ attributes: {
524
+ userId: { type: String, required: true },
525
+ txId: { type: String, required: true },
526
+ status: { type: String, required: true },
527
+ // Nested object with schema
528
+ address: {
529
+ type: Object,
530
+ schema: {
531
+ street: { type: String },
532
+ city: { type: String, required: true },
533
+ country: { type: String, required: true },
534
+ },
535
+ },
536
+ // Array of scalar items
537
+ tags: {
538
+ type: Array,
539
+ items: { type: String },
540
+ default: [],
541
+ },
542
+ // Array of objects with schema (like history in the real example)
543
+ history: {
544
+ type: Array,
545
+ default: [],
546
+ items: {
547
+ type: Object,
548
+ schema: {
549
+ date: { type: String },
550
+ description: { type: String },
551
+ status: { type: String, required: true },
552
+ },
553
+ },
554
+ },
555
+ // Deeply nested object
556
+ amount: {
557
+ type: Object,
558
+ schema: {
559
+ value: { type: Number, required: true },
560
+ currency: { type: String, required: true },
561
+ breakdown: {
562
+ type: Object,
563
+ schema: {
564
+ base: { type: Number, required: true },
565
+ fees: { type: Number, required: true },
566
+ },
567
+ },
568
+ },
569
+ },
570
+ },
571
+ },
572
+ },
573
+ } as const satisfies SchemaDefinition;
574
+
575
+ type Transaction = InferModelFromSchema<typeof TransactionSchema, 'Transaction'>;
576
+
577
+ test('schema with nested Object attribute compiles', () => {
578
+ expect(TransactionSchema.models.Transaction.attributes.address.type).toBe(Object);
579
+ });
580
+
581
+ test('schema with Array of objects compiles', () => {
582
+ expect(TransactionSchema.models.Transaction.attributes.history.type).toBe(Array);
583
+ });
584
+
585
+ test('inferred type accepts valid nested object', () => {
586
+ const tx: Transaction = {
587
+ userId: 'u1',
588
+ txId: 'tx1',
589
+ status: 'PENDING',
590
+ address: { city: 'Buenos Aires', country: 'AR' },
591
+ tags: ['fast', 'verified'],
592
+ history: [
593
+ { date: '2024-01-01', description: 'Created', status: 'PENDING' },
594
+ { status: 'COMPLETED' },
595
+ ],
596
+ amount: {
597
+ value: 100,
598
+ currency: 'USD',
599
+ breakdown: { base: 90, fees: 10 },
600
+ },
601
+ };
602
+
603
+ expect(tx.userId).toBe('u1');
604
+ expect(tx.address?.city).toBe('Buenos Aires');
605
+ expect(tx.history?.[0]?.status).toBe('PENDING');
606
+ expect(tx.amount?.breakdown?.base).toBe(90);
607
+ expect(tx.tags?.[0]).toBe('fast');
608
+ });
609
+
610
+ test('inferred nested object type requires its required fields', () => {
611
+ type Address = NonNullable<Transaction['address']>;
612
+
613
+ // city and country are required — this must compile
614
+ const validAddress: Address = { city: 'BA', country: 'AR' };
615
+ expect(validAddress.city).toBe('BA');
616
+
617
+ // Verify the shape: optional `street`, required `city` and `country`
618
+ type AddressKeys = keyof Required<Address>;
619
+ const _keys: AddressKeys[] = ['street', 'city', 'country'];
620
+ expect(_keys).toContain('city');
621
+ });
622
+
623
+ test('model with nested attributes satisfies ModelDefinition', () => {
624
+ const model = {
625
+ key: {
626
+ PK: { type: String, value: 'ITEM#${id}' },
627
+ SK: { type: String, value: 'ITEM#${id}' },
628
+ },
629
+ attributes: {
630
+ id: { type: String, required: true },
631
+ meta: {
632
+ type: Object,
633
+ schema: {
634
+ createdBy: { type: String },
635
+ tags: { type: Array, items: { type: String } },
636
+ },
637
+ },
638
+ },
639
+ } as const satisfies ModelDefinition;
640
+
641
+ expect(model.attributes.meta.type).toBe(Object);
642
+ });
643
+ });
package/src/core/types.ts CHANGED
@@ -28,20 +28,68 @@
28
28
  // -------------------- Type Definitions --------------------
29
29
 
30
30
  /**
31
- * Attribute definition for model attributes
32
- *
33
- * @property type - The JavaScript constructor for the attribute type
34
- * @property [required] - Whether the attribute is required
35
- * @property [generate] - Auto-generation strategy ('ulid', 'uuid')
36
- * @property [default] - Default value or generator function
31
+ * Scalar attribute definition (String, Number, Boolean, Date)
37
32
  */
38
- export type AttributeDefinition = {
33
+ export type ScalarAttributeDefinition = {
39
34
  type: StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor;
40
35
  required?: boolean;
41
36
  generate?: 'ulid' | 'uuid';
42
37
  default?: any;
38
+ nulls?: boolean;
39
+ };
40
+
41
+ /**
42
+ * Object attribute definition with a nested schema
43
+ *
44
+ * @example
45
+ * address: {
46
+ * type: Object,
47
+ * schema: {
48
+ * street: { type: String },
49
+ * city: { type: String, required: true },
50
+ * }
51
+ * }
52
+ */
53
+ export type ObjectAttributeDefinition = {
54
+ type: ObjectConstructor;
55
+ schema: Record<string, AttributeDefinition>;
56
+ required?: boolean;
57
+ default?: any;
58
+ nulls?: boolean;
43
59
  };
44
60
 
61
+ /**
62
+ * Array attribute definition with a typed items schema
63
+ *
64
+ * @example
65
+ * history: {
66
+ * type: Array,
67
+ * default: [],
68
+ * items: {
69
+ * type: Object,
70
+ * schema: {
71
+ * date: { type: String },
72
+ * status: { type: String },
73
+ * }
74
+ * }
75
+ * }
76
+ */
77
+ export type ArrayAttributeDefinition = {
78
+ type: ArrayConstructor;
79
+ items: AttributeDefinition;
80
+ required?: boolean;
81
+ default?: any;
82
+ nulls?: boolean;
83
+ };
84
+
85
+ /**
86
+ * Attribute definition for model attributes — supports scalars, nested objects, and arrays
87
+ */
88
+ export type AttributeDefinition =
89
+ | ScalarAttributeDefinition
90
+ | ObjectAttributeDefinition
91
+ | ArrayAttributeDefinition;
92
+
45
93
  /**
46
94
  * Key definition for primary and secondary indexes
47
95
  *
@@ -114,17 +162,34 @@ export type SchemaDefinition = {
114
162
  params?: SchemaParams;
115
163
  };
116
164
 
117
- type InferAttr<T> = T extends StringConstructor
118
- ? string
119
- : T extends NumberConstructor
120
- ? number
121
- : T extends BooleanConstructor
122
- ? boolean
123
- : T extends DateConstructor
124
- ? Date
125
- : unknown;
165
+ /**
166
+ * Recursively infers the TypeScript type from an AttributeDefinition.
167
+ * - Object → infers the nested schema shape
168
+ * - Array → infers an array of the items type
169
+ * - Scalars → maps to the corresponding primitive
170
+ */
171
+ type InferAttr<A> = A extends { type: ObjectConstructor; schema: infer S }
172
+ ? InferObjectSchema<S>
173
+ : A extends { type: ArrayConstructor; items: infer I }
174
+ ? Array<InferAttr<I>>
175
+ : A extends { type: StringConstructor }
176
+ ? string
177
+ : A extends { type: NumberConstructor }
178
+ ? number
179
+ : A extends { type: BooleanConstructor }
180
+ ? boolean
181
+ : A extends { type: DateConstructor }
182
+ ? Date
183
+ : unknown;
126
184
 
127
- type IsOptional<T> = undefined extends T ? true : false;
185
+ /**
186
+ * Infers the shape of a nested object schema, respecting required/optional fields.
187
+ */
188
+ type InferObjectSchema<S> = {
189
+ [K in keyof S as S[K] extends { required: true } ? K : never]: InferAttr<S[K]>;
190
+ } & {
191
+ [K in keyof S as S[K] extends { required: true } ? never : K]?: InferAttr<S[K]>;
192
+ };
128
193
 
129
194
  /**
130
195
  * Non-generated attributes for input
@@ -135,7 +200,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
135
200
  ? never
136
201
  : M['attributes'][K] extends { required: true }
137
202
  ? K
138
- : never]: InferAttr<M['attributes'][K]['type']>;
203
+ : never]: InferAttr<M['attributes'][K]>;
139
204
  } & {
140
205
  [K in keyof M['attributes'] as M['attributes'][K] extends { generate: string }
141
206
  ? never
@@ -143,7 +208,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
143
208
  ? K
144
209
  : M['attributes'][K] extends { required: true }
145
210
  ? never
146
- : K]?: InferAttr<M['attributes'][K]['type']>;
211
+ : K]?: InferAttr<M['attributes'][K]>;
147
212
  };
148
213
 
149
214
  /**
@@ -152,7 +217,7 @@ type NonGeneratedAttributes<M extends ModelDefinition> = {
152
217
  type GeneratedAttributes<M extends ModelDefinition> = {
153
218
  [K in keyof M['attributes'] as M['attributes'][K] extends { generate: string }
154
219
  ? K
155
- : never]: InferAttr<M['attributes'][K]['type']>;
220
+ : never]: InferAttr<M['attributes'][K]>;
156
221
  };
157
222
 
158
223
  /**
@@ -289,3 +354,14 @@ type KeyTemplateVars<M extends ModelDefinition> = PrimaryKeyVars<M>;
289
354
  export type InferKeyInput<M extends ModelDefinition> = {
290
355
  [K in KeyTemplateVars<M>]: string;
291
356
  };
357
+
358
+ /**
359
+ * Extracts the item type from an array attribute.
360
+ *
361
+ * @example
362
+ * type StoryFrame = ArrayItem<StoryEntity['frames']>;
363
+ * // → { url: string; duration?: number; mediaType?: string }
364
+ *
365
+ * type HistoryEntry = ArrayItem<TransactionEntity['history']>;
366
+ */
367
+ export type ArrayItem<T extends readonly unknown[] | undefined> = NonNullable<T>[number];
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export {
14
14
  type InferModelFromSchema,
15
15
  type InferInputFromSchema,
16
16
  type TimestampFields,
17
+ type ArrayItem,
17
18
  } from './core/types';
18
19
  export {
19
20
  createDynamoDBLogger,
@@ -71,9 +71,10 @@ export const applyPostDefaults = <M extends ModelDefinition>(
71
71
 
72
72
  for (const [key, attr] of Object.entries(model.attributes)) {
73
73
  if (result[key] === undefined) {
74
- if (attr.generate === 'ulid') {
74
+ const generate = 'generate' in attr ? attr.generate : undefined;
75
+ if (generate === 'ulid') {
75
76
  result[key] = ulid();
76
- } else if (attr.generate === 'uuid') {
77
+ } else if (generate === 'uuid') {
77
78
  result[key] = crypto.randomUUID();
78
79
  } else if (attr.default !== undefined) {
79
80
  result[key] = typeof attr.default === 'function' ? attr.default() : attr.default;
@@ -71,9 +71,9 @@ describe('zod-utils', () => {
71
71
  expect(() => zodType.parse(undefined)).toThrow();
72
72
  });
73
73
 
74
- it('should return z.unknown() for unknown types', () => {
74
+ it('should return z.unknown() for unrecognized types', () => {
75
75
  const attr: AttributeDefinition = {
76
- type: Array as any,
76
+ type: Map as any,
77
77
  required: true,
78
78
  };
79
79
  const zodType = typeToZod(attr);
@@ -83,6 +83,101 @@ describe('zod-utils', () => {
83
83
  expect(() => zodType.parse(123)).not.toThrow();
84
84
  expect(() => zodType.parse([])).not.toThrow();
85
85
  });
86
+
87
+ it('should convert Object type with schema to z.looseObject()', () => {
88
+ const attr: AttributeDefinition = {
89
+ type: Object,
90
+ schema: {
91
+ city: { type: String, required: true },
92
+ zip: { type: String },
93
+ },
94
+ required: true,
95
+ };
96
+ const zodType = typeToZod(attr);
97
+
98
+ expect(() => zodType.parse({ city: 'BA' })).not.toThrow();
99
+ expect(() => zodType.parse({ city: 'BA', zip: '1000' })).not.toThrow();
100
+ expect(() => zodType.parse({ zip: '1000' })).toThrow(); // city required
101
+ expect(() => zodType.parse({})).toThrow();
102
+ });
103
+
104
+ it('should convert Object type without schema to z.record()', () => {
105
+ const attr: AttributeDefinition = {
106
+ type: Object,
107
+ schema: {},
108
+ };
109
+ const zodType = typeToZod(attr);
110
+ expect(() => zodType.parse({})).not.toThrow();
111
+ });
112
+
113
+ it('should convert Array type with scalar items', () => {
114
+ const attr: AttributeDefinition = {
115
+ type: Array,
116
+ items: { type: String },
117
+ required: true,
118
+ };
119
+ const zodType = typeToZod(attr);
120
+
121
+ expect(() => zodType.parse(['a', 'b'])).not.toThrow();
122
+ expect(() => zodType.parse([])).not.toThrow();
123
+ expect(() => zodType.parse([1, 2])).toThrow(); // numbers, not strings
124
+ });
125
+
126
+ it('should convert Array type with object items (like history)', () => {
127
+ const attr: AttributeDefinition = {
128
+ type: Array,
129
+ items: {
130
+ type: Object,
131
+ schema: {
132
+ date: { type: String },
133
+ status: { type: String, required: true },
134
+ },
135
+ },
136
+ required: true,
137
+ };
138
+ const zodType = typeToZod(attr);
139
+
140
+ expect(() => zodType.parse([{ status: 'PENDING' }])).not.toThrow();
141
+ expect(() =>
142
+ zodType.parse([{ date: '2024-01-01', status: 'DONE' }])
143
+ ).not.toThrow();
144
+ expect(() => zodType.parse([{ date: '2024-01-01' }])).toThrow(); // missing status
145
+ });
146
+
147
+ it('should handle optional Object attribute', () => {
148
+ const attr: AttributeDefinition = {
149
+ type: Object,
150
+ schema: { name: { type: String, required: true } },
151
+ };
152
+ const zodType = typeToZod(attr);
153
+
154
+ expect(() => zodType.parse(undefined)).not.toThrow();
155
+ expect(() => zodType.parse({ name: 'test' })).not.toThrow();
156
+ });
157
+
158
+ it('should handle deeply nested objects', () => {
159
+ const attr: AttributeDefinition = {
160
+ type: Object,
161
+ required: true,
162
+ schema: {
163
+ value: { type: Number, required: true },
164
+ breakdown: {
165
+ type: Object,
166
+ schema: {
167
+ base: { type: Number, required: true },
168
+ fees: { type: Number, required: true },
169
+ },
170
+ },
171
+ },
172
+ };
173
+ const zodType = typeToZod(attr);
174
+
175
+ expect(() =>
176
+ zodType.parse({ value: 100, breakdown: { base: 90, fees: 10 } })
177
+ ).not.toThrow();
178
+ expect(() => zodType.parse({ value: 100 })).not.toThrow(); // breakdown optional
179
+ expect(() => zodType.parse({})).toThrow(); // value required
180
+ });
86
181
  });
87
182
 
88
183
  describe('modelToZod', () => {
@@ -5,19 +5,38 @@ import { AttributeDefinition, ModelDefinition } from '@/core/types';
5
5
  import { z, ZodObject, ZodType } from 'zod';
6
6
 
7
7
  /**
8
- * Converts an attribute definition to a Zod schema type
8
+ * Converts an attribute definition to a Zod schema type.
9
+ * Supports scalars, nested objects (with schema), and arrays (with items).
9
10
  */
10
11
  export const typeToZod = (attr: AttributeDefinition): ZodType => {
11
- const zodType: ZodType =
12
- attr.type === String
13
- ? z.string()
14
- : attr.type === Number
15
- ? z.number()
16
- : attr.type === Boolean
17
- ? z.boolean()
18
- : attr.type === Date
19
- ? z.date()
20
- : z.unknown();
12
+ let zodType: ZodType;
13
+
14
+ if (attr.type === Object) {
15
+ const schema = (attr as { schema?: Record<string, AttributeDefinition> }).schema;
16
+ if (schema) {
17
+ const shape: Record<string, ZodType> = {};
18
+ for (const [key, nestedAttr] of Object.entries(schema)) {
19
+ shape[key] = typeToZod(nestedAttr);
20
+ }
21
+ zodType = z.looseObject(shape);
22
+ } else {
23
+ zodType = z.record(z.string(), z.unknown());
24
+ }
25
+ } else if (attr.type === Array) {
26
+ const items = (attr as { items?: AttributeDefinition }).items;
27
+ zodType = items ? z.array(typeToZod(items)) : z.array(z.unknown());
28
+ } else {
29
+ zodType =
30
+ attr.type === String
31
+ ? z.string()
32
+ : attr.type === Number
33
+ ? z.number()
34
+ : attr.type === Boolean
35
+ ? z.boolean()
36
+ : attr.type === Date
37
+ ? z.date()
38
+ : z.unknown();
39
+ }
21
40
 
22
41
  return attr.required ? zodType : zodType.optional();
23
42
  };
@@ -32,5 +51,5 @@ export const modelToZod = (model: ModelDefinition): ZodObject<any> => {
32
51
  for (const [key, attr] of Object.entries(model.attributes)) {
33
52
  shape[key] = typeToZod(attr);
34
53
  }
35
- return z.object(shape).passthrough();
54
+ return z.looseObject(shape);
36
55
  };