UNPKG

@lyxa.ai/types

Version:

Lyxa type definitions and validation schemas for both frontend and backend

239 lines 9.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaBuilder = exports.ZodValidation = void 0; exports.createSchemaBuilder = createSchemaBuilder; const zod_1 = require("zod"); const typegoose_1 = require("@typegoose/typegoose"); const libphonenumber_js_1 = require("libphonenumber-js"); class ZodValidation { static objectId(field = 'Field') { return zod_1.z.union([ zod_1.z .string({ required_error: `${field} is required` }) .refine(val => typegoose_1.mongoose.Types.ObjectId.isValid(val), { message: `${field} must be a valid ObjectID`, }) .transform(val => new typegoose_1.mongoose.Types.ObjectId(val)), zod_1.z.instanceof(typegoose_1.mongoose.Types.ObjectId, { message: `${field} must be a valid ObjectID`, }), ], { message: `${field} is required` }); } static enumType(enumObj, field = 'Field') { return zod_1.z.nativeEnum(enumObj, { message: `${field} must be one of: ${Object.values(enumObj).join(', ')}`, }); } static number(field = 'Field', options) { let schema = zod_1.z.number({ required_error: `${field} is required`, invalid_type_error: `${field} must be a number`, }); if (options?.min !== undefined) { schema = schema.gte(options?.min, { message: `${field} must be at least ${options?.min}`, }); } if (options?.max !== undefined) { schema = schema.lte(options?.max, { message: `${field} must be at most ${options?.max}`, }); } if (options?.isInt) { schema = schema.int({ message: `${field} must be an integer` }); } if (options?.isPositive) { schema = schema.positive({ message: `${field} must be positive` }); } if (options?.isNonpositive) { schema = schema.nonpositive({ message: `${field} must be non-positive` }); } if (options?.isNegative) { schema = schema.negative({ message: `${field} must be negative` }); } if (options?.isNonnegative) { schema = schema.nonnegative({ message: `${field} must be non-negative` }); } return schema; } static string(field = 'Field', options) { let schema = zod_1.z.string({ required_error: options?.requiredMessage || `${field} is required`, }); if (options?.minLength) { schema = schema.min(options.minLength, options.minMessage || `${field} must be at least ${options.minLength} characters`); } if (options?.maxLength) { schema = schema.max(options.maxLength, options.maxMessage || `${field} shouldn’t exceed ${options.maxLength} characters`); } if (options?.regex) { schema = schema.regex(options.regex.pattern, options.regex.message); } if (options?.isTrimmed) { return schema .transform(val => val?.trim()) .refine(val => val?.length && val?.length > 0, { message: options?.requiredMessage || `${field} is required`, }); } return schema; } static email(field = 'Email', options) { const schema = zod_1.z .string({ required_error: options?.requiredMessage || `${field} is required`, }) .email(options?.invalidMessage || `${field} must be a valid email address`); return schema; } static url(field = 'Field') { return zod_1.z.string({ required_error: `${field} is required` }).url(`${field} must be a valid URL`); } static phoneNumber(field = 'Field') { return zod_1.z.string({ required_error: `${field} is required` }).refine(value => { try { const phone = (0, libphonenumber_js_1.parsePhoneNumberFromString)(value); return phone?.isValid(); } catch { return false; } }, { message: `Please input a valid phone number`, }); } static date(field = 'Field', minDate, maxDate) { return zod_1.z .union([ zod_1.z .string({ required_error: `${field} is required` }) .refine(val => { const date = new Date(val); return !isNaN(date.getTime()); }, `${field} must be a valid date`) .transform(val => new Date(val)), zod_1.z.date({ required_error: `${field} is required`, invalid_type_error: `${field} must be a valid date`, }), ], { message: `${field} is required` }) .refine(val => minDate === undefined || val >= minDate, `${field} must be on or after ${minDate?.toISOString()}`) .refine(val => maxDate === undefined || val <= maxDate, `${field} must be on or before ${maxDate?.toISOString()}`); } static boolean(field = 'Field') { return zod_1.z.boolean({ required_error: `${field} is required`, invalid_type_error: `${field} must be a boolean`, }); } static array(schema, field = 'Array', minItems, maxItems) { let arraySchema = zod_1.z.array(schema, { required_error: `${field} is required`, }); if (minItems === 1) { arraySchema = arraySchema.min(1, `${field} cannot be empty`); } if (minItems !== undefined && minItems !== 1) { arraySchema = arraySchema.min(minItems, `${field} must have at least ${minItems} items`); } if (maxItems !== undefined) { arraySchema = arraySchema.max(maxItems, `${field} must have at most ${maxItems} items`); } return arraySchema; } static timestamps() { return { createdAt: zod_1.z.date().nullable().optional(), updatedAt: zod_1.z.date().nullable().optional(), deletedAt: zod_1.z.date().nullable().optional(), }; } static trackingFields() { return { createdBy: ZodValidation.objectId().optional(), updatedBy: ZodValidation.objectId().optional(), deletedBy: ZodValidation.objectId().optional(), ...ZodValidation.timestamps(), }; } } exports.ZodValidation = ZodValidation; class SchemaBuilder { includeTimestamps; includeTracking; baseSchema; constructor(baseFields, includeTimestamps = true, includeTracking = false) { this.includeTimestamps = includeTimestamps; this.includeTracking = includeTracking; this.baseSchema = this.createBaseSchema(baseFields); } createBaseSchema(baseFields) { const additionalFields = this.includeTracking ? ZodValidation.trackingFields() : this.includeTimestamps ? ZodValidation.timestamps() : {}; return zod_1.z.object({ ...baseFields, ...additionalFields, }); } getBaseSchema() { return this.baseSchema.strict(); } getEntitySchema() { return this.baseSchema .extend({ _id: ZodValidation.objectId(), }) .partial() .passthrough(); } getIdSchema() { return ZodValidation.objectId(); } getUpdateSchema(excludeFields = { createdAt: true, updatedAt: true }) { const entitySchema = this.getEntitySchema(); if (Object.keys(excludeFields).length === 0) { return entitySchema; } const shape = entitySchema._def.shape(); const filteredShape = {}; for (const key in shape) { if (!excludeFields[key]) { filteredShape[key] = shape[key]; } } return zod_1.z.object(filteredShape).partial().strict(); } getDeleteSchema() { return zod_1.z.object({ _id: ZodValidation.objectId('_id'), softDelete: ZodValidation.boolean('Soft Delete').default(true), }); } updateBaseSchema(extendFields, omitFields) { let schema = this.baseSchema; if (omitFields && omitFields.length > 0) { schema = schema.omit(omitFields.reduce((acc, key) => ({ ...acc, [key]: true }), {})); } if (extendFields) { schema = schema.extend({ _id: ZodValidation.objectId().optional(), ...extendFields }); } return schema; } getAllSchemas(excludeFromUpdate) { return { BaseSchema: this.getBaseSchema(), EntitySchema: this.getEntitySchema(), IdSchema: this.getIdSchema(), UpdateSchema: this.getUpdateSchema(excludeFromUpdate), DeleteSchema: this.getDeleteSchema(), }; } } exports.SchemaBuilder = SchemaBuilder; function createSchemaBuilder(baseFields, includeTimestamps = true, includeTracking = false) { return new SchemaBuilder(baseFields, includeTimestamps, includeTracking); } //# sourceMappingURL=global-validation.js.map