Add JSON field parser with flexible type transformation
This commit is contained in:
		| @@ -33,6 +33,41 @@ export const RecordModel = z.object({ | ||||
| }); | ||||
| export type RecordModel = z.infer<typeof RecordModel>; | ||||
|  | ||||
| /******* JSON FIELD *******/ | ||||
| export const pbJsonField = (maxSizeInBytes: number = 1048576) => { | ||||
|   const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]); | ||||
|   const jsonSchema: z.ZodType<any> = z.lazy(() => | ||||
|     z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)]) | ||||
|   ); | ||||
|  | ||||
|   const stringTransform = z.string() | ||||
|     .max(maxSizeInBytes, `JSON field cannot exceed ${maxSizeInBytes} bytes`) | ||||
|     .transform((val) => { | ||||
|       if (val === "true") return true; | ||||
|       if (val === "false") return false; | ||||
|       if (val === "null") return null; | ||||
|  | ||||
|       if ((val.startsWith('[') && val.endsWith(']'))||(val.startsWith('{') && val.endsWith('}'))) | ||||
|         try { | ||||
|           return JSON.parse(val); | ||||
|         } catch { | ||||
|           return val; | ||||
|         } | ||||
|  | ||||
|       const num = Number(val); | ||||
|       if (!isNaN(num) && isFinite(num) && val.trim() !== '') | ||||
|         return num; | ||||
|  | ||||
|       if (val.startsWith('"') && val.endsWith('"')) | ||||
|         return val.slice(1, -1); | ||||
|  | ||||
|       return val; | ||||
|     }); | ||||
|  | ||||
|   return z.union([jsonSchema, stringTransform]); | ||||
| }; | ||||
|  | ||||
|  | ||||
| /******* RECORDS *******/ | ||||
| @@_RECORDS_@@ | ||||
|  | ||||
|   | ||||
| @@ -69,18 +69,26 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | ||||
|         break; | ||||
|  | ||||
|       case "file": | ||||
|         // if (collectionName === "tests") console.log(`${collectionName}:`, field); | ||||
|         const maxSelectFile = field.maxSelect; | ||||
|         // TODO: implement maxSize, mimeTypes, protected, thumbs | ||||
|         schema = `z.string()${maxSelectFile === 1 ? "" : `.array()${maxSelectFile ? `.max(${maxSelectFile})` : ""}`}`; | ||||
|         // NOTE: PocketBase API returns only filenames in responses, limiting validation capabilities. | ||||
|         // Full file validation (size, MIME types, etc.) should be implemented at the create record endpoint. | ||||
|         // const mimeTypesArray: string[] = field.mimeTypes || []; | ||||
|         // const protectedFile: boolean = field.protected; | ||||
|         // const thumbsFileArray: string[] = field.thumbs || []; | ||||
|         // const maxSizeFile: number = field.maxSize; | ||||
|         const maxSelectFile: number = field.maxSelect; | ||||
|         const fileFieldMaxSelect = maxSelectFile ? `.max(${maxSelectFile})` : ""; | ||||
|         const fileFieldTypeArray = maxSelectFile === 1 ? "" : `.array()${fileFieldMaxSelect}`; | ||||
|  | ||||
|         schema = `z.string()${fileFieldTypeArray}`; | ||||
|         break; | ||||
|  | ||||
|       case "json": | ||||
|         // TODO: implement maxSize and json schema | ||||
|         schema = "z.any()"; | ||||
|         schema = field.maxSize > 0 ? `pbJsonField(${field.maxSize})` : "pbJsonField()"; | ||||
|         break; | ||||
|  | ||||
|       case "number": | ||||
|         // if (collectionName === "testFiles") console.log(`${collectionName}:`, field); | ||||
|         const maxNumber = field.maxNumber; | ||||
|         const minNumber = field.minNumber; | ||||
|         const noDecimal = field.noDecimal; | ||||
| @@ -119,7 +127,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | ||||
|         break; | ||||
|  | ||||
|       case "geoPoint": | ||||
|         schema = "z.object({ lat: z.number().min(-90).max(90), lng: z.number().min(-180).max(180) })"; | ||||
|         schema = "z.object({ lat: z.number().min(-90).max(90), lon: z.number().min(-180).max(180) })"; | ||||
|         break; | ||||
|  | ||||
|       default: | ||||
| @@ -136,7 +144,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | ||||
|     if (!domains?.length) return ""; | ||||
|     const domainsList = domains.map((domain) => `"${domain}"`).join(", "); | ||||
|     const messageType = isWhitelist ? "isn't one of the allowed ones" : "is one of the disallowed ones"; | ||||
|     return `.refine((email) => { const domain = email.split("@")[1]; return domain && ${isWhitelist ? "" : "!"}[${domainsList}].includes(domain); }, { message: "Invalid email, email domain ${messageType}" })`; | ||||
|     return `.refine((email) => { const domain = email.split("@")[1]; const domainsArray = [${domainsList}]; return domain && ${isWhitelist ? "" : "!"}domainsArray.includes(domain); }, { message: "Invalid email, email domain ${messageType}" })`; | ||||
|   }; | ||||
|  | ||||
|   return { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user