@lyxa.ai/types
Version:
Lyxa type definitions and validation schemas for both frontend and backend
239 lines • 9.29 kB
JavaScript
;
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