@@ -20,7 +20,8 @@ const ZOD_TYPE_NAMES = [
2020 'ZodDate' ,
2121 'ZodEnum' ,
2222 'ZodArray' ,
23- 'ZodObject'
23+ 'ZodObject' ,
24+ 'ZodEffects'
2425] as const ;
2526
2627const INTERNAL_HEADERS = [ 'subjectID' , 'date' ] ;
@@ -31,7 +32,7 @@ const INTERNAL_HEADERS_SAMPLE_DATA = [MONGOLIAN_VOWEL_SEPARATOR + 'string', MONG
3132
3233type ZodTypeName = Extract < `${z . ZodFirstPartyTypeKind } `, ( typeof ZOD_TYPE_NAMES ) [ number ] > ;
3334
34- type RequiredZodTypeName = Exclude < ZodTypeName , 'ZodOptional' > ;
35+ type RequiredZodTypeName = Exclude < ZodTypeName , 'ZodEffects' | ' ZodOptional'> ;
3536
3637type ZodTypeNameResult =
3738 | {
@@ -71,8 +72,20 @@ function isZodEnumDef(def: AnyZodTypeDef): def is z.ZodEnumDef {
7172 return def . typeName === z . ZodFirstPartyTypeKind . ZodEnum ;
7273}
7374
75+ function isZodSetDef ( def : AnyZodTypeDef ) : def is z . ZodSetDef {
76+ return def . typeName === z . ZodFirstPartyTypeKind . ZodSet ;
77+ }
78+
7479function isZodArrayDef ( def : AnyZodTypeDef ) : def is z . ZodArrayDef {
75- return def . typeName === 'ZodArray' ;
80+ return def . typeName === z . ZodFirstPartyTypeKind . ZodArray ;
81+ }
82+
83+ function isZodEffectsDef ( def : AnyZodTypeDef ) : def is z . ZodEffectsDef {
84+ return def . typeName === z . ZodFirstPartyTypeKind . ZodEffects ;
85+ }
86+
87+ function isZodObjectDef ( def : AnyZodTypeDef ) : def is z . ZodObjectDef {
88+ return def . typeName === z . ZodFirstPartyTypeKind . ZodObject ;
7689}
7790
7891// TODO - fix extract set and record array functions to handle whitespace and trailing semicolon (present or included)
@@ -136,7 +149,26 @@ export function getZodTypeName(schema: z.ZodTypeAny, isOptional?: boolean): ZodT
136149 } ;
137150 } else if ( isZodArrayDef ( def ) ) {
138151 return interpretZodArray ( schema , def . typeName , isOptional ) ;
152+ } else if ( isZodSetDef ( def ) ) {
153+ const innerDef : unknown = def . valueType . _def ;
154+
155+ if ( ! isZodTypeDef ( innerDef ) ) {
156+ return {
157+ message : 'Invalid inner type: ZodSet value type must have a valid type definition' ,
158+ success : false
159+ } ;
160+ }
161+
162+ if ( isZodEnumDef ( innerDef ) ) {
163+ return {
164+ enumValues : innerDef . values ,
165+ isOptional : Boolean ( isOptional ) ,
166+ success : true ,
167+ typeName : def . typeName
168+ } ;
169+ }
139170 }
171+
140172 return {
141173 isOptional : Boolean ( isOptional ) ,
142174 success : true ,
@@ -194,7 +226,7 @@ export function interpretZodArray(
194226
195227export function interpretZodValue (
196228 entry : string ,
197- zType : Exclude < ZodTypeName , 'ZodArray' | 'ZodObject' | 'ZodOptional' > ,
229+ zType : Exclude < ZodTypeName , 'ZodArray' | 'ZodEffects' | ' ZodObject' | 'ZodOptional' > ,
198230 isOptional : boolean
199231) : UploadOperationResult < FormTypes . FieldValue > {
200232 if ( entry === '' && isOptional ) {
@@ -222,11 +254,14 @@ export function interpretZodValue(
222254 return { success : true , value : parseNumber ( entry ) } ;
223255 }
224256 return { message : `Invalid number type: ${ entry } ` , success : false } ;
225- //TODO if ZodSet has a enum see if those values can be shown in template data if possible
226257 case 'ZodSet' :
227258 if ( entry . startsWith ( 'SET(' ) ) {
228259 const setData = extractSetEntry ( entry ) ;
229- return { success : true , value : new Set ( setData . split ( ',' ) ) } ;
260+ const values = setData . split ( ',' ) . map ( ( s ) => s . trim ( ) ) . filter ( Boolean ) ;
261+ if ( values . length === 0 ) {
262+ return { message : 'Empty set is not allowed' , success : false } ;
263+ }
264+ return { success : true , value : new Set ( values ) } ;
230265 }
231266 return { message : `Invalid ZodSet: ${ entry } ` , success : false } ;
232267 case 'ZodString' :
@@ -268,7 +303,7 @@ export function interpretZodObjectValue(
268303 }
269304 for ( let i = 0 ; i < record . length ; i ++ ) {
270305 // TODO - make sure this is defined
271- const recordValue = record [ i ] ! . split ( ':' ) [ 1 ] ! ;
306+ const recordValue = record [ i ] ! . split ( ':' ) [ 1 ] ! . trim ( ) ;
272307
273308 const zListResult = zList [ i ] ! ;
274309 if ( ! ( zListResult . success && zListResult . typeName !== 'ZodArray' && zListResult . typeName !== 'ZodObject' ) ) {
@@ -285,7 +320,7 @@ export function interpretZodObjectValue(
285320 success : false
286321 } ;
287322 }
288- // TODO - how do we know that `zKeys` is the same length as record? What if the user forgets to add a element
323+
289324 recordArrayObject [ zKeys [ i ] ! ] = interpretZodValueResult . value ;
290325 }
291326 recordArray . push ( recordArrayObject ) ;
@@ -304,7 +339,7 @@ function generateSampleData({
304339 multiKeys,
305340 multiValues,
306341 typeName
307- } : Extract < ZodTypeNameResult , { success : true } > ) {
342+ } : Extract < Exclude < ZodTypeNameResult , 'ZodEffects' > , { success : true } > ) {
308343 switch ( typeName ) {
309344 case 'ZodBoolean' :
310345 return formatTypeInfo ( 'true/false' , isOptional ) ;
@@ -313,7 +348,14 @@ function generateSampleData({
313348 case 'ZodNumber' :
314349 return formatTypeInfo ( 'number' , isOptional ) ;
315350 case 'ZodSet' :
316- return formatTypeInfo ( 'SET(a,b,c)' , isOptional ) ;
351+ try {
352+ if ( enumValues ) return formatTypeInfo ( `SET(${ enumValues . join ( '/' ) } , ...)` , isOptional ) ;
353+
354+ return formatTypeInfo ( 'SET(a,b,c)' , isOptional ) ;
355+ } catch {
356+ throw new Error ( `Failed to generate sample data for ZodSet` ) ;
357+ }
358+
317359 case 'ZodString' :
318360 return formatTypeInfo ( 'string' , isOptional ) ;
319361 case 'ZodEnum' :
@@ -353,7 +395,6 @@ function generateSampleData({
353395 } catch {
354396 throw new Error ( 'Invalid Record Array Error' ) ;
355397 }
356-
357398 default :
358399 throw new Error ( `Invalid zod schema: unexpected type name '${ typeName satisfies never } '` ) ;
359400 }
@@ -363,12 +404,15 @@ export function createUploadTemplateCSV(instrument: AnyUnilingualFormInstrument)
363404 // TODO - type validationSchema as object
364405 const instrumentSchema = instrument . validationSchema as z . AnyZodObject ;
365406
407+ const instrumentSchemaDef : unknown = instrument . validationSchema . _def ;
408+
366409 let shape : { [ key : string ] : z . ZodTypeAny } = { } ;
367- // TODO - include ZodEffect as a typename like our other types
368- if ( ( instrumentSchema . _def . typeName as string ) === 'ZodEffects' ) {
369- // @ts -expect-error - TODO - find a type safe way to call this
370- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
371- shape = instrumentSchema . _def . schema . _def . shape ( ) as { [ key : string ] : z . ZodTypeAny } ;
410+
411+ if ( isZodTypeDef ( instrumentSchemaDef ) && isZodEffectsDef ( instrumentSchemaDef ) ) {
412+ const innerSchema : unknown = instrumentSchemaDef . schema . _def ;
413+ if ( isZodTypeDef ( innerSchema ) && isZodObjectDef ( innerSchema ) ) {
414+ shape = innerSchema . shape ( ) as { [ key : string ] : z . ZodTypeAny } ;
415+ }
372416 } else {
373417 shape = instrumentSchema . shape as { [ key : string ] : z . ZodTypeAny } ;
374418 }
@@ -402,17 +446,17 @@ export async function processInstrumentCSV(
402446 let shape : { [ key : string ] : z . ZodTypeAny } = { } ;
403447 let instrumentSchemaWithInternal : z . AnyZodObject ;
404448
405- if ( ( instrumentSchema . _def . typeName as string ) === 'ZodEffects' ) {
406- // @ts -expect-error - TODO - find a type safe way to call this
407- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
408- instrumentSchemaWithInternal = instrumentSchema . _def . schema . extend ( {
449+ const instrumentSchemaDef : unknown = instrumentSchema . _def ;
450+
451+ if ( isZodTypeDef ( instrumentSchemaDef ) && isZodEffectsDef ( instrumentSchemaDef ) ) {
452+ //TODO make this type safe without having to cast z.AnyZodObject
453+ instrumentSchemaWithInternal = ( instrumentSchemaDef . schema as z . AnyZodObject ) . extend ( {
409454 date : z . coerce . date ( ) ,
410455 subjectID : z . string ( )
411- } ) as z . AnyZodObject ;
456+ } ) ;
412457
413458 shape = instrumentSchemaWithInternal . _def . shape ( ) as { [ key : string ] : z . ZodTypeAny } ;
414459 } else {
415- //const shape2 = instrumentSchema.shape as { [key: string]: z.ZodTypeAny };
416460 instrumentSchemaWithInternal = instrumentSchema . extend ( {
417461 date : z . coerce . date ( ) ,
418462 subjectID : z . string ( )
0 commit comments