-
Notifications
You must be signed in to change notification settings - Fork 2
Fix schema generation for embedded recursive structs #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix schema generation for embedded recursive structs #17
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: String Length Validation Now Counts Code Points
String validation methods (len, min, max, gt, gte, lt, lte) now use custom .refine() functions with [...val].length instead of Zod's native .min(), .max(), and .length() methods. This changes string length counting from UTF-16 code units to Unicode code points/graphemes, leading to different validation results for multi-byte characters and emojis. This breaking change appears to be an accidental side effect unrelated to the PR's goal of fixing recursive embedded structs.
zod.go#L939-L970
Lines 939 to 970 in 924b7cd
| case "oneof": | |
| vals := splitParamsRegex.FindAllString(part[6:], -1) | |
| for i := 0; i < len(vals); i++ { | |
| vals[i] = strings.Replace(vals[i], "'", "", -1) | |
| } | |
| if len(vals) == 0 { | |
| panic("oneof= must be followed by a list of values") | |
| } | |
| // const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]); | |
| validateStr.WriteString(fmt.Sprintf(".enum([\"%s\"] as const)", strings.Join(vals, "\", \""))) | |
| case "len": | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length === %s, 'String must contain %s character(s)')", valValue, valValue)) | |
| case "min": | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length >= %s, 'String must contain at least %s character(s)')", valValue, valValue)) | |
| case "max": | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length <= %s, 'String must contain at most %s character(s)')", valValue, valValue)) | |
| case "gt": | |
| val, err := strconv.Atoi(valValue) | |
| if err != nil { | |
| panic("gt= must be followed by a number") | |
| } | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length > %d, 'String must contain at least %d character(s)')", val, val+1)) | |
| case "gte": | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length >= %s, 'String must contain at least %s character(s)')", valValue, valValue)) | |
| case "lt": | |
| val, err := strconv.Atoi(valValue) | |
| if err != nil { | |
| panic("lt= must be followed by a number") | |
| } | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length < %d, 'String must contain at most %d character(s)')", val, val-1)) | |
| case "lte": | |
| refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length <= %s, 'String must contain at most %s character(s)')", valValue, valValue)) |
Was this report helpful? Give feedback by reacting with 👍 or 👎
This should fix #16 following colinhacks/zod#2984
Note that recursive schema handling is much simpler in v4 of zod. We won't have the need to handle recursive types differently (so a lot of code can be cleaned up) and we won't even get the current issue. Maybe we can support v4 of zod in v2 of zen