ts-typed-api 0.1.21 → 0.1.22

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.
@@ -166,10 +166,47 @@ class SchemaRegistry {
166
166
  };
167
167
  }
168
168
  if (zodSchema instanceof zod_1.ZodArray) {
169
- const itemType = getZodType(zodSchema);
169
+ // Try multiple ways to get the array item type
170
+ let itemType;
171
+ // Method 1: Try _def.element (this is the correct property for ZodArray)
172
+ try {
173
+ const def = getZodDef(zodSchema);
174
+ if (def && def.element && typeof def.element === 'object' && def.element.constructor) {
175
+ itemType = def.element;
176
+ }
177
+ }
178
+ catch (error) {
179
+ // Continue to next method
180
+ }
181
+ // Method 2: Try getZodType if method 1 failed
182
+ if (!itemType) {
183
+ const typeResult = getZodType(zodSchema);
184
+ if (typeResult && typeof typeResult === 'object' && typeResult.constructor) {
185
+ itemType = typeResult;
186
+ }
187
+ }
188
+ // Method 3: Direct access to _def.element as fallback
189
+ if (!itemType) {
190
+ try {
191
+ const directElement = zodSchema._def?.element;
192
+ if (directElement && typeof directElement === 'object' && directElement.constructor) {
193
+ itemType = directElement;
194
+ }
195
+ }
196
+ catch (error) {
197
+ // Continue to fallback
198
+ }
199
+ }
200
+ if (itemType) {
201
+ return {
202
+ type: 'array',
203
+ items: this.zodToOpenAPI(itemType, shouldRegister)
204
+ };
205
+ }
206
+ // Ultimate fallback
170
207
  return {
171
208
  type: 'array',
172
- items: itemType ? this.zodToOpenAPI(itemType, shouldRegister) : { type: 'string' }
209
+ items: { type: 'string' }
173
210
  };
174
211
  }
175
212
  if (zodSchema instanceof zod_1.ZodObject) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-typed-api",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "A lightweight, type-safe RPC library for TypeScript with Zod validation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -270,10 +270,50 @@ class SchemaRegistry {
270
270
  }
271
271
 
272
272
  if (zodSchema instanceof ZodArray) {
273
- const itemType = getZodType(zodSchema);
273
+ // Try multiple ways to get the array item type
274
+ let itemType: ZodTypeAny | undefined;
275
+
276
+ // Method 1: Try _def.element (this is the correct property for ZodArray)
277
+ try {
278
+ const def = getZodDef(zodSchema);
279
+ if (def && def.element && typeof def.element === 'object' && def.element.constructor) {
280
+ itemType = def.element;
281
+ }
282
+ } catch (error) {
283
+ // Continue to next method
284
+ }
285
+
286
+ // Method 2: Try getZodType if method 1 failed
287
+ if (!itemType) {
288
+ const typeResult = getZodType(zodSchema);
289
+ if (typeResult && typeof typeResult === 'object' && typeResult.constructor) {
290
+ itemType = typeResult;
291
+ }
292
+ }
293
+
294
+ // Method 3: Direct access to _def.element as fallback
295
+ if (!itemType) {
296
+ try {
297
+ const directElement = (zodSchema as any)._def?.element;
298
+ if (directElement && typeof directElement === 'object' && directElement.constructor) {
299
+ itemType = directElement;
300
+ }
301
+ } catch (error) {
302
+ // Continue to fallback
303
+ }
304
+ }
305
+
306
+ if (itemType) {
307
+ return {
308
+ type: 'array',
309
+ items: this.zodToOpenAPI(itemType, shouldRegister)
310
+ };
311
+ }
312
+
313
+ // Ultimate fallback
274
314
  return {
275
315
  type: 'array',
276
- items: itemType ? this.zodToOpenAPI(itemType, shouldRegister) : { type: 'string' }
316
+ items: { type: 'string' }
277
317
  };
278
318
  }
279
319
 
@@ -186,4 +186,134 @@ describe('OpenAPI Specification Generation', () => {
186
186
  // The enum should either have values or be a basic string type (no empty enum arrays)
187
187
  expect(resultProperty.enum.length).toBeGreaterThan(0);
188
188
  });
189
+
190
+ test('should handle nested object arrays correctly', () => {
191
+ // Test case for nested object array structure
192
+ const s1 = z.object({
193
+ version: z.number(),
194
+ });
195
+
196
+ const s2 = z.object({
197
+ versions: z.array(s1),
198
+ });
199
+
200
+ const NestedArrayTestDefinition = CreateApiDefinition({
201
+ endpoints: {
202
+ test: {
203
+ nestedArrayEndpoint: {
204
+ method: 'POST' as const,
205
+ path: '/test/nested-array',
206
+ body: s2,
207
+ responses: {
208
+ 200: z.object({
209
+ data: s2,
210
+ success: z.boolean()
211
+ })
212
+ }
213
+ }
214
+ }
215
+ }
216
+ });
217
+
218
+ const spec = generateOpenApiSpec(NestedArrayTestDefinition);
219
+
220
+ expect(spec).toBeDefined();
221
+ expect(spec.paths).toBeDefined();
222
+ expect(spec.components?.schemas).toBeDefined();
223
+
224
+ const nestedArrayEndpoint = spec.paths['/test/nested-array'];
225
+ expect(nestedArrayEndpoint).toBeDefined();
226
+ expect(nestedArrayEndpoint.post).toBeDefined();
227
+
228
+ // Check request body schema
229
+ const requestBodySchema = nestedArrayEndpoint.post?.requestBody?.content?.['application/json']?.schema;
230
+ expect(requestBodySchema).toBeDefined();
231
+
232
+ // The schema should be a reference to components
233
+ expect(requestBodySchema?.$ref).toBeDefined();
234
+ const schemaName = requestBodySchema?.$ref?.split('/').pop();
235
+ const actualSchema = spec.components?.schemas?.[schemaName!];
236
+ expect(actualSchema).toBeDefined();
237
+
238
+ expect(actualSchema?.type).toBe('object');
239
+ expect(actualSchema?.properties).toBeDefined();
240
+
241
+ // Verify the versions property is an array
242
+ const versionsProperty = actualSchema?.properties?.versions;
243
+ expect(versionsProperty).toBeDefined();
244
+ expect(versionsProperty?.type).toBe('array');
245
+ expect(versionsProperty?.items).toBeDefined();
246
+
247
+ // The array items should properly reference the nested object schema (s1)
248
+ const arrayItemsSchema = versionsProperty?.items;
249
+ if (arrayItemsSchema?.$ref) {
250
+ // If it's a reference, resolve it and check the object structure
251
+ const itemSchemaName = arrayItemsSchema.$ref.split('/').pop();
252
+ const itemSchema = spec.components?.schemas?.[itemSchemaName!];
253
+ expect(itemSchema).toBeDefined();
254
+ expect(itemSchema?.type).toBe('object');
255
+ expect(itemSchema?.properties?.version).toBeDefined();
256
+ expect(itemSchema?.properties?.version?.type).toBe('number');
257
+ } else {
258
+ // If it's inline, it should be an object with version property
259
+ expect(arrayItemsSchema?.type).toBe('object');
260
+ expect(arrayItemsSchema?.properties?.version).toBeDefined();
261
+ expect(arrayItemsSchema?.properties?.version?.type).toBe('number');
262
+ }
263
+
264
+ // Check response schema structure
265
+ const responseSchema = nestedArrayEndpoint.post?.responses?.['200']?.content?.['application/json']?.schema;
266
+ expect(responseSchema).toBeDefined();
267
+
268
+ expect(responseSchema?.$ref).toBeDefined();
269
+ const responseSchemaName = responseSchema?.$ref?.split('/').pop();
270
+ const actualResponseSchema = spec.components?.schemas?.[responseSchemaName!];
271
+ expect(actualResponseSchema).toBeDefined();
272
+
273
+ expect(actualResponseSchema?.type).toBe('object');
274
+ expect(actualResponseSchema?.properties).toBeDefined();
275
+ expect(actualResponseSchema?.properties?.data).toBeDefined();
276
+ expect(actualResponseSchema?.properties?.success).toBeDefined();
277
+ expect(actualResponseSchema?.properties?.success?.type).toBe('boolean');
278
+
279
+ // Verify that the nested data property has the correct structure
280
+ const dataProperty = actualResponseSchema?.properties?.data;
281
+ if (dataProperty?.$ref) {
282
+ // If data is a reference, resolve it
283
+ const dataSchemaName = dataProperty.$ref.split('/').pop();
284
+ const dataSchema = spec.components?.schemas?.[dataSchemaName!];
285
+ expect(dataSchema).toBeDefined();
286
+ expect(dataSchema?.type).toBe('object');
287
+ expect(dataSchema?.properties?.versions).toBeDefined();
288
+ expect(dataSchema?.properties?.versions?.type).toBe('array');
289
+
290
+ // The versions array items should be objects with version property
291
+ const dataVersionsItems = dataSchema?.properties?.versions?.items;
292
+ if (dataVersionsItems?.$ref) {
293
+ const dataItemSchemaName = dataVersionsItems.$ref.split('/').pop();
294
+ const dataItemSchema = spec.components?.schemas?.[dataItemSchemaName!];
295
+ expect(dataItemSchema?.type).toBe('object');
296
+ expect(dataItemSchema?.properties?.version?.type).toBe('number');
297
+ } else {
298
+ expect(dataVersionsItems?.type).toBe('object');
299
+ expect(dataVersionsItems?.properties?.version?.type).toBe('number');
300
+ }
301
+ } else {
302
+ // If data is inline
303
+ expect(dataProperty?.type).toBe('object');
304
+ expect(dataProperty?.properties?.versions).toBeDefined();
305
+ expect(dataProperty?.properties?.versions?.type).toBe('array');
306
+
307
+ const dataVersionsItems = dataProperty?.properties?.versions?.items;
308
+ if (dataVersionsItems?.$ref) {
309
+ const dataItemSchemaName = dataVersionsItems.$ref.split('/').pop();
310
+ const dataItemSchema = spec.components?.schemas?.[dataItemSchemaName!];
311
+ expect(dataItemSchema?.type).toBe('object');
312
+ expect(dataItemSchema?.properties?.version?.type).toBe('number');
313
+ } else {
314
+ expect(dataVersionsItems?.type).toBe('object');
315
+ expect(dataVersionsItems?.properties?.version?.type).toBe('number');
316
+ }
317
+ }
318
+ });
189
319
  });