|  |  |  | @@ -26,17 +26,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | 
		
	
		
			
				|  |  |  |  |     const schemaName = opts.nameRecordSchema(name); | 
		
	
		
			
				|  |  |  |  |     const typeName = opts.nameRecordType(name); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Filter out system fields that are already inherited from RecordModel or should be omitted | 
		
	
		
			
				|  |  |  |  |     const systemFields = new Set([ | 
		
	
		
			
				|  |  |  |  |       "id", | 
		
	
		
			
				|  |  |  |  |       "created", | 
		
	
		
			
				|  |  |  |  |       "updated", | 
		
	
		
			
				|  |  |  |  |       "collectionId", | 
		
	
		
			
				|  |  |  |  |       "collectionName", | 
		
	
		
			
				|  |  |  |  |       "expand", // inherited from BaseModel/RecordModel | 
		
	
		
			
				|  |  |  |  |       "password", | 
		
	
		
			
				|  |  |  |  |       "tokenKey", // should not be in response schema | 
		
	
		
			
				|  |  |  |  |     ]); | 
		
	
		
			
				|  |  |  |  |     const systemFields = new Set(["id", "created", "updated", "collectionId", "collectionName", "expand", "password", "tokenKey"]); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     const customFields = fields.filter((field) => !systemFields.has(field.name)); | 
		
	
		
			
				|  |  |  |  |     const fieldStrings = sortBy(customFields, ["name"]).map((field) => stringifyField(field, name)); | 
		
	
	
		
			
				
					
					|  |  |  | @@ -44,125 +34,6 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | 
		
	
		
			
				|  |  |  |  |     return `export const ${schemaName} = z.object({\n\t...RecordModel.omit({ expand: true }).shape,\n\tcollectionName: z.literal("${name}"),\n\t${fieldStrings.join(",\n\t")},\n});\nexport type ${typeName} = z.infer<typeof ${schemaName}>;`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyField(field: CollectionField, collectionName: string) { | 
		
	
		
			
				|  |  |  |  |     let schema: string; | 
		
	
		
			
				|  |  |  |  |     // TODO: | 
		
	
		
			
				|  |  |  |  |     console.log(`${collectionName}: ${field.type}`); | 
		
	
		
			
				|  |  |  |  |     switch (field.type) { | 
		
	
		
			
				|  |  |  |  |       case "bool": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyBoolField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "date": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyDateField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "editor": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyEditorField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "email": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyEmailField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "file": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyFileField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "json": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyJsonField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "number": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyNumberField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "relation": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyRelationField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "select": | 
		
	
		
			
				|  |  |  |  |         schema = stringifySelectField(field, collectionName); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "text": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyTextField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "url": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyUrlField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       case "geoPoint": | 
		
	
		
			
				|  |  |  |  |         schema = stringifyGeoPointField(field); | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |       default: | 
		
	
		
			
				|  |  |  |  |         console.warn(`Unknown field type "${field.type}" for field "${field.name}". Using z.any() as fallback.`); | 
		
	
		
			
				|  |  |  |  |         schema = "z.any()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     return `${field.name}: ${schema}${(field as any).required ? "" : ".optional()"}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyBoolField(_: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     return "z.boolean()"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyDateField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     // TODO: implement min and max | 
		
	
		
			
				|  |  |  |  |     return "z.string().pipe(z.coerce.date())"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyEditorField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     // TODO: implement convertUrls | 
		
	
		
			
				|  |  |  |  |     return "z.string()"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyEmailField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     // TODO: implement exceptDomains and onlyDomains | 
		
	
		
			
				|  |  |  |  |     return "z.string().email()"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyFileField(field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     const maxSelect = (field as any).options?.maxSelect; | 
		
	
		
			
				|  |  |  |  |     // TODO: implement maxSize, mimeTypes, protected, thumbs | 
		
	
		
			
				|  |  |  |  |     return `z.string()${maxSelect === 1 ? "" : `.array()${maxSelect ? `.max(${maxSelect})` : ""}`}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyJsonField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     // TODO: implement maxSize and json schema | 
		
	
		
			
				|  |  |  |  |     return "z.any()"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyNumberField(field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     const options = (field as any).options || {}; | 
		
	
		
			
				|  |  |  |  |     const { max, min, noDecimal } = options; | 
		
	
		
			
				|  |  |  |  |     return `z.number()${noDecimal ? ".int()" : ""}${min ? `.min(${min})` : ""}${max ? `.max(${max})` : ""}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyRelationField(field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     const options = (field as any).options || {}; | 
		
	
		
			
				|  |  |  |  |     const required = (field as any).required; | 
		
	
		
			
				|  |  |  |  |     const { maxSelect, minSelect } = options; | 
		
	
		
			
				|  |  |  |  |     // TODO: implement cascadeDelete, displayFields | 
		
	
		
			
				|  |  |  |  |     const min = minSelect ? `.min(${minSelect})` : ""; | 
		
	
		
			
				|  |  |  |  |     const max = maxSelect ? `.max(${maxSelect})` : ""; | 
		
	
		
			
				|  |  |  |  |     const multiple = maxSelect === 1 ? "" : `.array()${min}${max}`; | 
		
	
		
			
				|  |  |  |  |     const isOptional = required || maxSelect !== 1 ? `` : `.transform((id) => id === "" ? undefined : id)`; | 
		
	
		
			
				|  |  |  |  |     return `z.string()${isOptional}${multiple}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifySelectField(field: CollectionField, collectionName: string) { | 
		
	
		
			
				|  |  |  |  |     const maxSelect = (field as any).options?.maxSelect; | 
		
	
		
			
				|  |  |  |  |     // TODO: implement values | 
		
	
		
			
				|  |  |  |  |     return `${opts.nameEnumSchema(opts.nameEnumField(collectionName, field.name))}${maxSelect === 1 ? "" : `.array().max(${maxSelect})`}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyTextField(field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     const options = (field as any).options || {}; | 
		
	
		
			
				|  |  |  |  |     const { max, min } = options; | 
		
	
		
			
				|  |  |  |  |     // TODO: implement pattern | 
		
	
		
			
				|  |  |  |  |     return `z.string()${min ? `.min(${min})` : ""}${max ? `.max(${max})` : ""}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyUrlField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     // TODO: implement exceptDomains and onlyDomains | 
		
	
		
			
				|  |  |  |  |     return "z.string().url()"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyGeoPointField(_field: CollectionField) { | 
		
	
		
			
				|  |  |  |  |     return "z.object({ lat: z.number().min(-90).max(90), lng: z.number().min(-180).max(180) })"; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifySchemasEntry({ name }: CollectionModel) { | 
		
	
		
			
				|  |  |  |  |     return `["${name}", ${opts.nameRecordSchema(name)}]`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					
					|  |  |  | @@ -171,6 +42,90 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO | 
		
	
		
			
				|  |  |  |  |     return `\t\tcollection(idOrName: "${name}"): RecordService<z.input<typeof ${opts.nameRecordSchema(name)}>>;`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   function stringifyField(field: CollectionField, collectionName: string) { | 
		
	
		
			
				|  |  |  |  |     let schema: string; | 
		
	
		
			
				|  |  |  |  |     // if (collectionName === "relations") console.log(`${collectionName}:`, field); | 
		
	
		
			
				|  |  |  |  |     switch (field.type) { | 
		
	
		
			
				|  |  |  |  |       case "bool": | 
		
	
		
			
				|  |  |  |  |         schema = "z.boolean()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "date": | 
		
	
		
			
				|  |  |  |  |         // TODO: implement min and max | 
		
	
		
			
				|  |  |  |  |         schema = "z.string().pipe(z.coerce.date())"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "editor": | 
		
	
		
			
				|  |  |  |  |         // TODO: implement convertUrls | 
		
	
		
			
				|  |  |  |  |         schema = "z.string()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "email": | 
		
	
		
			
				|  |  |  |  |         // TODO: implement exceptDomains and onlyDomains | 
		
	
		
			
				|  |  |  |  |         schema = "z.string().email()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "file": | 
		
	
		
			
				|  |  |  |  |         const maxSelectFile = field.maxSelect; | 
		
	
		
			
				|  |  |  |  |         // TODO: implement maxSize, mimeTypes, protected, thumbs | 
		
	
		
			
				|  |  |  |  |         schema = `z.string()${maxSelectFile === 1 ? "" : `.array()${maxSelectFile ? `.max(${maxSelectFile})` : ""}`}`; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "json": | 
		
	
		
			
				|  |  |  |  |         // TODO: implement maxSize and json schema | 
		
	
		
			
				|  |  |  |  |         schema = "z.any()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "number": | 
		
	
		
			
				|  |  |  |  |         const maxNumber = field.maxNumber; | 
		
	
		
			
				|  |  |  |  |         const minNumber = field.minNumber; | 
		
	
		
			
				|  |  |  |  |         const noDecimal = field.noDecimal; | 
		
	
		
			
				|  |  |  |  |         schema = `z.number()${noDecimal ? ".int()" : ""}${minNumber ? `.min(${minNumber})` : ""}${maxNumber ? `.max(${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)`; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         schema = `z.string()${isOptional}${multiple}`; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "select": | 
		
	
		
			
				|  |  |  |  |         const maxSelect = field.maxSelect; | 
		
	
		
			
				|  |  |  |  |         // TODO: implement values | 
		
	
		
			
				|  |  |  |  |         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})` : ""}`; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "url": | 
		
	
		
			
				|  |  |  |  |         // TODO: implement exceptDomains and onlyDomains | 
		
	
		
			
				|  |  |  |  |         schema = "z.string().url()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       case "geoPoint": | 
		
	
		
			
				|  |  |  |  |         schema = "z.object({ lat: z.number().min(-90).max(90), lng: z.number().min(-180).max(180) })"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       default: | 
		
	
		
			
				|  |  |  |  |         console.warn(`Unknown field type "${field.type}" for field "${field.name}". Using z.any() as fallback.`); | 
		
	
		
			
				|  |  |  |  |         schema = "z.any()"; | 
		
	
		
			
				|  |  |  |  |         break; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     return `${field.name}: ${schema}${(field as any).required ? "" : ".optional()"}`; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   return { | 
		
	
		
			
				|  |  |  |  |     collectionNames: `[\n\t${collections.map(({ name }) => `"${name}"`).join(",\n\t")},\n]`, | 
		
	
		
			
				|  |  |  |  |     enums: getCollectionSelectFields().map(stringifyEnum).join("\n\n"), | 
		
	
	
		
			
				
					
					|  |  |  |   |