UNPKG

fortify-schema

Version:

A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.

278 lines (274 loc) 7.85 kB
'use strict'; var InterfaceSchema = require('./InterfaceSchema.js'); require('./typescript/index.js'); var TypeValidators = require('./validators/TypeValidators.js'); var ConstraintParser = require('./validators/ConstraintParser.js'); require('../../../types/ValidatorTypes.js'); require('../../../utils/UrlArgs.js'); require('./validators/UnionCache.js'); require('./errors/ErrorHandler.js'); require('./errors/types/errors.type.js'); /** * TypeScript Interface-like Schema System * * The most intuitive way to define schemas - just like TypeScript interfaces! * * @example * ```typescript * import { Interface as IF} from "fortify-schema"; * * // Define schema like a TypeScript interface * const UserSchema = IF({ * id: "number", * email: "email", * name: "string", * age: "number?", // Optional * isActive: "boolean?", // Optional * tags: "string[]?", // Optional array * role: "admin", // Constant value * profile: { // Nested object * bio: "string?", * avatar: "url?" * } * }); * * // Validate data * const result = UserSchema.safeParse(userData); * ``` */ /** * Convert Make objects to string syntax that InterfaceSchema understands */ function convertMakeObjectsToStrings(definition) { if (typeof definition !== "object" || definition === null) { return definition; } // Handle arrays if (Array.isArray(definition)) { return definition.map((item) => convertMakeObjectsToStrings(item)); } // Check if this is a Make.const() object: convert => =constVal if (typeof definition === "object" && "const" in definition && Object.keys(definition).length === 1) { // Convert Make.const() to string syntax return `=${definition.const}`; } // Check if this is a Make.union() object if (typeof definition === "object" && "union" in definition && Array.isArray(definition.union)) { // Check if it's an optional union (Make.unionOptional) if ("optional" in definition && definition.optional === true) { // Convert Make.unionOptional() to string syntax with "?" suffix return definition.union.join("|") + "?"; } else { // Convert Make.union() to string syntax return definition.union.join("|"); } } // Recursively process nested objects const result = {}; for (const [key, value] of Object.entries(definition)) { result[key] = convertMakeObjectsToStrings(value); } return result; } /** * Create a schema using TypeScript interface-like syntax with full type inference * * @param definition - Schema definition using TypeScript-like syntax * @param options - Optional validation options * @returns InterfaceSchema instance with inferred types * * @example Basic Usage * ```typescript * const UserSchema = Interface({ * id: "number", * email: "email", * name: "string", * age: "number?", * isActive: "boolean?", * tags: "string[]?" * }); * * // result is fully typed as: * // SchemaValidationResult<{ * // id: number; * // email: string; * // name: string; * // age?: number; * // isActive?: boolean; * // tags?: string[]; * // }> * const result = UserSchema.safeParse(data); * ``` * * @example With Constraints * ```typescript * const UserSchema = Interface({ * username: "string(3,20)", // 3-20 characters * age: "number(18,120)", // 18-120 years * tags: "string[](1,10)?", // 1-10 tags, optional * }); * ``` * * @example Nested Objects * ```typescript * const OrderSchema = Interface({ * id: "number", * customer: { * name: "string", * email: "email", * address: { * street: "string", * city: "string", * zipCode: "string" * } * }, * items: [{ * name: "string", * price: "number", * quantity: "int" * }] * }); * ``` */ function Interface(definition, options) { // Convert Make objects to string syntax before creating schema const processedDefinition = convertMakeObjectsToStrings(definition); // Debug: Log the conversion (disabled for now) // if (process.env.NODE_ENV !== "production") { // console.log("DEBUG: Original definition:", JSON.stringify(definition, null, 2)); // console.log("DEBUG: Processed definition:", JSON.stringify(processedDefinition, null, 2)); // } return new InterfaceSchema.InterfaceSchema(processedDefinition, options); } /** * Available field types for schema definitions */ const FieldTypes = { // Basic types STRING: "string", STRING_OPTIONAL: "string?", NUMBER: "number", NUMBER_OPTIONAL: "number?", BOOLEAN: "boolean", BOOLEAN_OPTIONAL: "boolean?", DATE: "date", DATE_OPTIONAL: "date?", ANY: "any", ANY_OPTIONAL: "any?", // String formats EMAIL: "email", EMAIL_OPTIONAL: "email?", URL: "url", URL_OPTIONAL: "url?", UUID: "uuid", UUID_OPTIONAL: "uuid?", PHONE: "phone", PHONE_OPTIONAL: "phone?", SLUG: "slug", SLUG_OPTIONAL: "slug?", USERNAME: "username", USERNAME_OPTIONAL: "username?", // Number types INT: "int", INT_OPTIONAL: "int?", POSITIVE: "positive", POSITIVE_OPTIONAL: "positive?", FLOAT: "float", FLOAT_OPTIONAL: "float?", // Array types STRING_ARRAY: "string[]", STRING_ARRAY_OPTIONAL: "string[]?", NUMBER_ARRAY: "number[]", NUMBER_ARRAY_OPTIONAL: "number[]?", BOOLEAN_ARRAY: "boolean[]", BOOLEAN_ARRAY_OPTIONAL: "boolean[]?", INT_ARRAY: "int[]", INT_ARRAY_OPTIONAL: "int[]?", EMAIL_ARRAY: "email[]", EMAIL_ARRAY_OPTIONAL: "email[]?", URL_ARRAY: "url[]", URL_ARRAY_OPTIONAL: "url[]?", // Record types RECORD_STRING_ANY: "record<string,any>", RECORD_STRING_ANY_OPTIONAL: "record<string,any>?", RECORD_STRING_STRING: "record<string,string>", RECORD_STRING_STRING_OPTIONAL: "record<string,string>?", RECORD_STRING_NUMBER: "record<string,number>", RECORD_STRING_NUMBER_OPTIONAL: "record<string,number>?", }; /** * Quick schema creation helpers */ const QuickSchemas = { /** * User schema with common fields */ User: Interface({ id: "number", email: "email", name: "string", createdAt: "date?", updatedAt: "date?", }), /** * API response schema */ APIResponse: Interface({ success: "boolean", data: "any?", errors: "string[]?", timestamp: "date?", }), /** * Pagination schema */ Pagination: Interface({ page: "int", limit: "int", total: "int", hasNext: "boolean?", hasPrev: "boolean?", }), /** * Address schema */ Address: Interface({ street: "string", city: "string", state: "string?", zipCode: "string", country: "string", }), /** * Contact info schema */ Contact: Interface({ email: "email?", phone: "phone?", website: "url?", }), }; /** * Custom error class for schema validation */ class SchemaValidationError extends Error { constructor(message, errors, warnings) { super(message); this.errors = errors; this.warnings = warnings; this.name = "SchemaValidationError"; } } exports.InterfaceSchema = InterfaceSchema.InterfaceSchema; exports.TypeValidators = TypeValidators.TypeValidators; exports.ConstraintParser = ConstraintParser.ConstraintParser; exports.FieldTypes = FieldTypes; exports.Interface = Interface; exports.QuickSchemas = QuickSchemas; exports.SchemaValidationError = SchemaValidationError; //# sourceMappingURL=Interface.js.map