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
JavaScript
'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