@@ -7,7 +7,7 @@ import type { Group } from '@opendatacapture/schemas/group';
77import type { UnilingualInstrumentInfo } from '@opendatacapture/schemas/instrument' ;
88import type { UploadInstrumentRecordsData } from '@opendatacapture/schemas/instrument-records' ;
99import { encodeScopedSubjectId } from '@opendatacapture/subject-utils' ;
10- import { mapValues , reject } from 'lodash-es' ;
10+ import { mapValues } from 'lodash-es' ;
1111import { parse , unparse } from 'papaparse' ;
1212import type { Merge } from 'type-fest' ;
1313import { z as z3 } from 'zod/v3' ;
@@ -19,6 +19,50 @@ const INTERNAL_HEADERS_SAMPLE_DATA = ['string', 'yyyy-mm-dd'];
1919
2020const SUBJECT_ID_REGEX = / ^ [ ^ $ \s ] + $ / ;
2121
22+ function parseBooleanEntry ( entry : string ) : boolean {
23+ if ( entry . toLowerCase ( ) === 'true' ) {
24+ return true ;
25+ } else if ( entry . toLowerCase ( ) === 'false' ) {
26+ return false ;
27+ }
28+ throw new UploadError ( {
29+ en : `Undecipherable Boolean Type: '${ entry } '` ,
30+ fr : `Type booléen indéchiffrable : '${ entry } '`
31+ } ) ;
32+ }
33+
34+ function parseDateEntry ( entry : string ) : Date {
35+ const date = new Date ( entry ) ;
36+ if ( Number . isNaN ( date . getTime ( ) ) ) {
37+ throw new UploadError ( {
38+ en : `Failed to parse date: '${ entry } '` ,
39+ fr : `Échec de l'analyse de la date : '${ entry } '`
40+ } ) ;
41+ }
42+ return date ;
43+ }
44+
45+ function parseNumberEntry ( entry : string ) : number {
46+ if ( isNumberLike ( entry ) ) {
47+ return parseNumber ( entry ) ;
48+ }
49+ throw new UploadError ( {
50+ en : `Invalid number type: '${ entry } '` ,
51+ fr : `Type de nombre invalide : '${ entry } '`
52+ } ) ;
53+ }
54+
55+ function parseSetEntry ( entry : string ) : Set < string > {
56+ const set = extractSetEntry ( entry ) ;
57+ if ( set . size === 0 ) {
58+ throw new UploadError ( {
59+ en : 'Empty set is not allowed' ,
60+ fr : "Un ensemble vide n'est pas autorisé"
61+ } ) ;
62+ }
63+ return set ;
64+ }
65+
2266const ZOD_TYPE_NAMES = [
2367 'ZodNumber' ,
2468 'ZodString' ,
@@ -333,7 +377,7 @@ export namespace Zod3 {
333377 }
334378 }
335379
336- function interpetZodConvertResult ( convertResult : ZodTypeNameResult , entry : string ) : unknown {
380+ function interpretZodConvertResult ( convertResult : ZodTypeNameResult , entry : string ) : unknown {
337381 if ( entry === '' && convertResult . isOptional ) {
338382 return undefined ;
339383 }
@@ -351,46 +395,22 @@ export namespace Zod3 {
351395 convertResult . multiKeys ! . forEach ( ( key , index ) => {
352396 const rawValue = parsedRecord [ key ] ;
353397 if ( rawValue !== undefined ) {
354- result [ key ] = interpetZodConvertResult ( convertResult . multiValues ! [ index ] ! , rawValue ) ;
398+ result [ key ] = interpretZodConvertResult ( convertResult . multiValues ! [ index ] ! , rawValue ) ;
355399 }
356400 } ) ;
357401 return result ;
358402 } ) ;
359403
360404 case 'ZodBoolean' :
361- if ( entry . toLowerCase ( ) === 'true' ) {
362- return true ;
363- } else if ( entry . toLowerCase ( ) === 'false' ) {
364- return false ;
365- }
366- throw new UploadError ( {
367- en : `Undecipherable Boolean Type: '${ entry } '` ,
368- fr : `Type booléen indéchiffrable : '${ entry } '`
369- } ) ;
370- case 'ZodDate' : {
371- const date = new Date ( entry ) ;
372- if ( Number . isNaN ( date . getTime ( ) ) ) {
373- throw new UploadError ( {
374- en : `Failed to parse date: '${ entry } '` ,
375- fr : `Échec de l'analyse de la date : '${ entry } '`
376- } ) ;
377- }
378- return date ;
379- }
405+ return parseBooleanEntry ( entry ) ;
406+ case 'ZodDate' :
407+ return parseDateEntry ( entry ) ;
380408 case 'ZodEnum' :
381409 return entry ;
382410 case 'ZodNumber' :
383- if ( isNumberLike ( entry ) ) {
384- return parseNumber ( entry ) ;
385- }
386- throw new UploadError ( { en : `Invalid number type: '${ entry } '` , fr : `Type de nombre invalide : '${ entry } '` } ) ;
387- case 'ZodSet' : {
388- const set = extractSetEntry ( entry ) ;
389- if ( set . size === 0 ) {
390- throw new UploadError ( { en : 'Empty set is not allowed' , fr : "Un ensemble vide n'est pas autorisé" } ) ;
391- }
392- return set ;
393- }
411+ return parseNumberEntry ( entry ) ;
412+ case 'ZodSet' :
413+ return parseSetEntry ( entry ) ;
394414 case 'ZodString' :
395415 return entry ;
396416 default :
@@ -505,7 +525,7 @@ export namespace Zod3 {
505525 ) ;
506526 }
507527 try {
508- const result = interpetZodConvertResult ( getZodTypeName ( shape [ key ] ) , rawValue ) ;
528+ const result = interpretZodConvertResult ( getZodTypeName ( shape [ key ] ) , rawValue ) ;
509529 jsonLine [ headers [ i ] ! ] = result ;
510530 } catch ( error : unknown ) {
511531 if ( error instanceof UploadError ) {
@@ -655,51 +675,27 @@ export namespace Zod4 {
655675 }
656676 }
657677
658- function interpetZodConvertResult ( convertResult : ZodConvertResult , entry : string ) : unknown {
678+ function interpretZodConvertResult ( convertResult : ZodConvertResult , entry : string ) : unknown {
659679 if ( entry === '' && convertResult . isOptional ) {
660680 return undefined ;
661681 }
662682 switch ( convertResult . typeName ) {
663683 case 'array' :
664684 return extractRecordArrayEntry ( entry ) . map ( ( parsedRecord ) => {
665- return mapValues ( parsedRecord , ( entry ) : unknown => interpetZodConvertResult ( convertResult . innerType , entry ) ) ;
685+ return mapValues ( parsedRecord , ( entry ) : unknown => interpretZodConvertResult ( convertResult . innerType , entry ) ) ;
666686 } ) ;
667687
668688 case 'boolean' :
669- if ( entry . toLowerCase ( ) === 'true' ) {
670- return true ;
671- } else if ( entry . toLowerCase ( ) === 'false' ) {
672- return false ;
673- }
674- throw new UploadError ( {
675- en : `Undecipherable Boolean Type: '${ entry } '` ,
676- fr : `Type booléen indéchiffrable : '${ entry } '`
677- } ) ;
678- case 'date' : {
679- const date = new Date ( entry ) ;
680- if ( Number . isNaN ( date . getTime ( ) ) ) {
681- throw new UploadError ( {
682- en : `Failed to parse date: '${ entry } '` ,
683- fr : `Échec de l'analyse de la date : '${ entry } '`
684- } ) ;
685- }
686- return date ;
687- }
689+ return parseBooleanEntry ( entry ) ;
690+ case 'date' :
691+ return parseDateEntry ( entry ) ;
688692 case 'enum' :
689693 return entry ;
690694 case 'int' :
691695 case 'number' :
692- if ( isNumberLike ( entry ) ) {
693- return parseNumber ( entry ) ;
694- }
695- throw new UploadError ( { en : `Invalid number type: '${ entry } '` , fr : `Type de nombre invalide : '${ entry } '` } ) ;
696- case 'set' : {
697- const set = extractSetEntry ( entry ) ;
698- if ( set . size === 0 ) {
699- throw new UploadError ( { en : 'Empty set is not allowed' , fr : "Un ensemble vide n'est pas autorisé" } ) ;
700- }
701- return set ;
702- }
696+ return parseNumberEntry ( entry ) ;
697+ case 'set' :
698+ return parseSetEntry ( entry ) ;
703699 case 'string' :
704700 return entry ;
705701 default :
@@ -790,7 +786,7 @@ export namespace Zod4 {
790786
791787 const shape = instrumentSchema . shape as { [ key : string ] : z4 . ZodType } ;
792788
793- return new Promise < FormTypes . Data [ ] > ( ( resolve ) => {
789+ return new Promise < FormTypes . Data [ ] > ( ( resolve , reject ) => {
794790 const reader = new FileReader ( ) ;
795791 reader . onload = ( ) => {
796792 const text = reader . result as string ;
@@ -837,7 +833,7 @@ export namespace Zod4 {
837833 ) ;
838834 }
839835 try {
840- const result = interpetZodConvertResult ( parseZodSchema ( shape [ key ] ) , rawValue ) ;
836+ const result = interpretZodConvertResult ( parseZodSchema ( shape [ key ] ) , rawValue ) ;
841837 jsonLine [ headers [ i ] ! ] = result ;
842838 } catch ( error : unknown ) {
843839 if ( error instanceof UploadError ) {
0 commit comments