Skip to content

Commit 9cac8d7

Browse files
authored
Add support for nested objects and arrays in Zod schema parser (#5098)
feat(secureZodParser): Add support for nested objects and arrays of primitives
1 parent e5381f5 commit 9cac8d7

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

packages/components/src/secureZodParser.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,28 @@ export class SecureZodSchemaParser {
135135
}
136136

137137
private static parseZodType(typeStr: string): any {
138+
// Check if this is a nested object (not in an array)
139+
if (typeStr.startsWith('z.object(') && !typeStr.startsWith('z.array(')) {
140+
// Extract object content
141+
const objectMatch = typeStr.match(/z\.object\(\s*\{([\s\S]*)\}\s*\)/)
142+
if (!objectMatch) {
143+
throw new Error('Invalid object syntax')
144+
}
145+
146+
const objectContent = objectMatch[1]
147+
const objectProperties = this.parseObjectProperties(objectContent)
148+
149+
return {
150+
isNestedObject: true,
151+
objectSchema: objectProperties
152+
}
153+
}
154+
155+
// Check if this is any kind of array
156+
if (typeStr.startsWith('z.array(')) {
157+
return this.parseArray(typeStr)
158+
}
159+
138160
const type: { base: string; modifiers: any[]; baseArgs?: any[] } = { base: '', modifiers: [] }
139161

140162
// Handle chained methods like z.string().max(500).optional()
@@ -181,6 +203,74 @@ export class SecureZodSchemaParser {
181203
return type
182204
}
183205

206+
private static parseArray(typeStr: string): any {
207+
// Extract the content inside array()
208+
const arrayContentMatch = typeStr.match(/z\.array\(\s*([\s\S]*)\s*\)$/)
209+
if (!arrayContentMatch) {
210+
throw new Error('Invalid array syntax')
211+
}
212+
213+
const arrayContent = arrayContentMatch[1].trim()
214+
215+
// Parse the object inside the array
216+
if (arrayContent.startsWith('z.object(')) {
217+
// Extract object content
218+
const objectMatch = arrayContent.match(/z\.object\(\s*\{([\s\S]*)\}\s*\)/)
219+
if (!objectMatch) {
220+
throw new Error('Invalid object syntax inside array')
221+
}
222+
223+
const objectContent = objectMatch[1]
224+
const objectProperties = this.parseObjectProperties(objectContent)
225+
226+
// Validate each property in the nested object
227+
for (const propValue of Object.values(objectProperties)) {
228+
this.validateTypeInfo(propValue)
229+
}
230+
231+
return {
232+
isArrayOfObjects: true,
233+
objectSchema: objectProperties
234+
}
235+
}
236+
237+
// Handle simple arrays (e.g., z.array(z.string()))
238+
const innerType = this.parseZodType(arrayContent)
239+
240+
return {
241+
isSimpleArray: true,
242+
innerType: innerType
243+
}
244+
}
245+
246+
private static validateTypeInfo(typeInfo: any): void {
247+
// If it's a nested object or array of objects, validate each property
248+
if (typeInfo.isNestedObject || typeInfo.isArrayOfObjects) {
249+
for (const propValue of Object.values(typeInfo.objectSchema)) {
250+
this.validateTypeInfo(propValue)
251+
}
252+
return
253+
}
254+
255+
// If it's a simple array, validate the inner type
256+
if (typeInfo.isSimpleArray) {
257+
this.validateTypeInfo(typeInfo.innerType)
258+
return
259+
}
260+
261+
// Validate base type
262+
if (!this.ALLOWED_TYPES.includes(typeInfo.base)) {
263+
throw new Error(`Unsupported type: ${typeInfo.base}`)
264+
}
265+
266+
// Validate modifiers
267+
for (const modifier of typeInfo.modifiers || []) {
268+
if (!this.ALLOWED_TYPES.includes(modifier.name)) {
269+
throw new Error(`Unsupported modifier: ${modifier.name}`)
270+
}
271+
}
272+
}
273+
184274
private static parseArguments(argsStr: string): any[] {
185275
// Remove outer parentheses
186276
const inner = argsStr.slice(1, -1).trim()
@@ -250,6 +340,23 @@ export class SecureZodSchemaParser {
250340
}
251341

252342
private static buildZodType(typeInfo: any): z.ZodTypeAny {
343+
// Special case for nested objects
344+
if (typeInfo.isNestedObject) {
345+
return this.buildZodSchema(typeInfo.objectSchema)
346+
}
347+
348+
// Special case for array of objects
349+
if (typeInfo.isArrayOfObjects) {
350+
const objectSchema = this.buildZodSchema(typeInfo.objectSchema)
351+
return z.array(objectSchema)
352+
}
353+
354+
// Special case for simple arrays
355+
if (typeInfo.isSimpleArray) {
356+
const innerZodType = this.buildZodType(typeInfo.innerType)
357+
return z.array(innerZodType)
358+
}
359+
253360
let zodType: z.ZodTypeAny
254361

255362
// Build base type

0 commit comments

Comments
 (0)