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>;
|
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 *******/
|
||||||
@@_RECORDS_@@
|
@@_RECORDS_@@
|
||||||
|
|
||||||
|
|||||||
@@ -69,18 +69,26 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "file":
|
case "file":
|
||||||
// if (collectionName === "tests") console.log(`${collectionName}:`, field);
|
|
||||||
const maxSelectFile = field.maxSelect;
|
|
||||||
// TODO: implement maxSize, mimeTypes, protected, thumbs
|
// 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;
|
break;
|
||||||
|
|
||||||
case "json":
|
case "json":
|
||||||
// TODO: implement maxSize and json schema
|
schema = field.maxSize > 0 ? `pbJsonField(${field.maxSize})` : "pbJsonField()";
|
||||||
schema = "z.any()";
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
|
// if (collectionName === "testFiles") console.log(`${collectionName}:`, field);
|
||||||
const maxNumber = field.maxNumber;
|
const maxNumber = field.maxNumber;
|
||||||
const minNumber = field.minNumber;
|
const minNumber = field.minNumber;
|
||||||
const noDecimal = field.noDecimal;
|
const noDecimal = field.noDecimal;
|
||||||
@@ -119,7 +127,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "geoPoint":
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -136,7 +144,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
|
|||||||
if (!domains?.length) return "";
|
if (!domains?.length) return "";
|
||||||
const domainsList = domains.map((domain) => `"${domain}"`).join(", ");
|
const domainsList = domains.map((domain) => `"${domain}"`).join(", ");
|
||||||
const messageType = isWhitelist ? "isn't one of the allowed ones" : "is one of the disallowed ones";
|
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 {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user