rozwiązanie kolejnych todo i pozbycie się zbędnych paczek npm.

This commit is contained in:
2025-10-10 14:45:04 +02:00
parent d4dfed9603
commit 827b53e630
5 changed files with 179 additions and 458 deletions

View File

@@ -62,25 +62,31 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
break;
case "email":
const onlyDomainsConstraint = createEmailDomainConstraint(field.onlyDomains, true);
const exceptDomainsConstraint = createEmailDomainConstraint(field.exceptDomains, false);
const onlyDomainsConstraint = createDomainConstraint(field.onlyDomains, true, "email");
const exceptDomainsConstraint = createDomainConstraint(field.exceptDomains, false, "email");
schema = `z.string().email()${onlyDomainsConstraint}${exceptDomainsConstraint}`;
break;
case "file":
// TODO: implement maxSize, mimeTypes, protected, thumbs
// 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 maxSelectFile: number = field.maxSelect;
const maxSizeFile: number = field.maxSize;
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}`;
let fileValidation = "z.instanceof(File)";
if (maxSizeFile) fileValidation += `.refine((file) => file.size <= ${maxSizeFile}, { message: "File size too large" })`;
if (mimeTypesArray.length > 0)
fileValidation += `.refine((file) => ${JSON.stringify(mimeTypesArray)}.includes(file.type), { message: "Invalid file type" })`;
const baseFileSchema = `z.union([z.string(), ${fileValidation}])`;
schema = `${baseFileSchema}${fileFieldTypeArray}`;
break;
case "json":
@@ -88,42 +94,43 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
break;
case "number":
// if (collectionName === "testFiles") console.log(`${collectionName}:`, field);
const maxNumber = field.maxNumber;
const minNumber = field.minNumber;
const noDecimal = field.noDecimal;
schema = `z.number()${noDecimal ? ".int()" : ""}${minNumber ? `.min(${minNumber})` : ""}${maxNumber ? `.max(${maxNumber})` : ""}`;
const maxNumber = field.maxNumber ? `.max(${field.maxNumber})` : "";
const minNumber = field.minNumber ? `.min(${field.minNumber})` : "";
const noDecimal = field.noDecimal ? ".int()" : "";
schema = `z.number()${noDecimal}${minNumber}${maxNumber}`;
break;
case "relation":
// TODO: implement cascadeDelete, displayFields, multiple records
const required = field.required;
const maxSelectRelation = field.maxSelect;
const minSelectRelation = field.minSelect;
const min = `.min(${minSelectRelation})`;
const max = `.max(${maxSelectRelation})`;
const multiple = maxSelectRelation === 1 ? "" : `.array()${min}${max}`;
const isOptional = required || maxSelectRelation !== 1 ? `` : `.transform((id) => id === "" ? undefined : id)`;
// TODO: implement cascadeDelete, displayFields, multiple records query
const multiple = field.maxSelect === 1 ? "" : `.array().min(${field.minSelect}).max(${field.maxSelect})`;
const isOptional = field.required || field.maxSelect !== 1 ? `` : `.transform((id: string) => id === "" ? undefined : id)`;
schema = `z.string()${isOptional}${multiple}`;
break;
case "select":
const maxSelect = field.maxSelect;
// if (collectionName === "testFiles") console.log(`${collectionName}:`, field);
// TODO: implement values
const maxSelect = field.maxSelect;
schema = `${opts.nameEnumSchema(opts.nameEnumField(collectionName, field.name))}${maxSelect === 1 ? "" : `.array().max(${maxSelect})`}`;
break;
case "text":
const maxText = field.max;
const minText = field.min;
// TODO: implement pattern
schema = `z.string()${minText ? `.min(${minText})` : ""}${maxText ? `.max(${maxText})` : ""}`;
const patternText =
field.pattern && field.pattern.trim() !== "" ? `.regex(new RegExp("${field.pattern.replace(/"/g, '\\"')}"))` : "";
const maxText = field.max ? `.max(${field.max})` : "";
const minText = field.min ? `.min(${field.min})` : "";
schema = `z.string()${minText}${maxText}${patternText}`;
break;
case "url":
// TODO: implement exceptDomains and onlyDomains
schema = "z.string().url()";
const onlyDomainsUrlConstraint = createDomainConstraint(field.onlyDomains, true, "url");
const exceptDomainsUrlConstraint = createDomainConstraint(field.exceptDomains, false, "url");
schema = `z.string().url()${onlyDomainsUrlConstraint}${exceptDomainsUrlConstraint}`;
break;
case "geoPoint":
@@ -140,11 +147,19 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
/* Helpers */
const createEmailDomainConstraint = (domains: string[], isWhitelist: boolean) => {
const createDomainConstraint = (domains: string[], isWhitelist: boolean, type: "email" | "url") => {
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]; const domainsArray = [${domainsList}]; return domain && ${isWhitelist ? "" : "!"}domainsArray.includes(domain); }, { message: "Invalid email, email domain ${messageType}" })`;
const negation = isWhitelist ? "" : "!";
const domainExtraction = type === "email" ? 'const domain = value.split("@")[1];' : "const domain = new URL(value).hostname;";
const errorHandling = type === "url" ? "try { " : "";
const errorCatch = type === "url" ? " } catch { return false; }" : "";
return `.refine((value: string) => { ${errorHandling}${domainExtraction} const domainsArray = [${domainsList}]; return domain && ${negation}domainsArray.includes(domain);${errorCatch} }, { message: "Invalid ${type}, domain ${messageType}" })`;
};
return {