fortify-schema
Version:
TypeScript interface-like schema validation system that's easier to use than Zod
203 lines (188 loc) • 4.77 kB
text/typescript
import { StringSchema } from "./StringSchema";
import { NumberSchema } from "./NumberSchema";
import { BooleanSchema } from "./BooleanSchema";
import { ArraySchema } from "./ArraySchema";
import { ObjectSchema } from "./ObjectSchema";
import { BaseSchema } from "./BaseSchema";
/**
* Main Schema factory - TypeScript-like schema validation
*
* Provides a clean, modular API for creating validation schemas without
* mixing validation logic with schema definition logic.
*
* @example
* ```typescript
* import { Schema } from "fortify-schema";
*
* // Define a user schema
* const UserSchema = Schema.object({
* id: Schema.number().int().positive(),
* email: Schema.string().email(),
* name: Schema.string().min(2).max(50),
* age: Schema.number().int().min(0).max(120).optional(),
* isActive: Schema.boolean().default(true),
* tags: Schema.array(Schema.string()).max(10).optional()
* });
*
* // Validate data
* const result = UserSchema.safeParse({
* id: 1,
* email: 'user@example.com',
* name: 'John Doe',
* isActive: true
* });
*
* if (result.success) {
* console.log('Valid user:', result.data);
* } else {
* console.log('Validation errors:', result.errors);
* }
* ```
*/
export const Schema = {
/**
* Create a string schema
*
* @example
* ```typescript
* const nameSchema = Schema.string().min(2).max(50);
* const emailSchema = Schema.string().email();
* const slugSchema = Schema.string().slug();
* ```
*/
string(): StringSchema {
return new StringSchema();
},
/**
* Create a number schema
*
* @example
* ```typescript
* const ageSchema = Schema.number().int().min(0).max(120);
* const priceSchema = Schema.number().positive().precision(2);
* const idSchema = Schema.number().int().positive();
* ```
*/
number(): NumberSchema {
return new NumberSchema();
},
/**
* Create a boolean schema
*
* @example
* ```typescript
* const activeSchema = Schema.boolean().default(true);
* const strictBoolSchema = Schema.boolean().strict();
* ```
*/
boolean(): BooleanSchema {
return new BooleanSchema();
},
/**
* Create an array schema
*
* @example
* ```typescript
* const tagsSchema = Schema.array(Schema.string()).min(1).max(10);
* const numbersSchema = Schema.array(Schema.number()).unique();
* const usersSchema = Schema.array(UserSchema);
* ```
*/
array<T>(elementSchema: BaseSchema<T>): ArraySchema<T> {
return new ArraySchema(elementSchema);
},
/**
* Create an object schema
*
* @example
* ```typescript
* const userSchema = Schema.object({
* id: Schema.number().int(),
* name: Schema.string(),
* email: Schema.string().email()
* });
*
* const strictSchema = Schema.object({
* id: Schema.number()
* }).strict(); // No extra properties
* ```
*/
object<T extends Record<string, any>>(shape: {
[K in keyof T]: BaseSchema<T[K]>;
}): ObjectSchema<T> {
return new ObjectSchema(shape);
},
/**
* Create a custom schema with validation function
*
* @example
* ```typescript
* const customSchema = Schema.custom<string>((value) => {
* if (typeof value !== 'string') {
* throw new Error('Expected string');
* }
* if (!value.startsWith('custom_')) {
* throw new Error('Must start with custom_');
* }
* return value;
* });
* ```
*/
custom<T>(validator: (value: any) => T): BaseSchema<T> {
return new CustomSchema(validator);
},
};
/**
* Custom schema implementation
*/
class CustomSchema<T> extends BaseSchema<T> {
constructor(private validator: (value: any) => T) {
super();
}
validate(value: any) {
// Handle common validation (undefined/null)
const commonResult = this.handleCommonValidation(value);
if (commonResult) {
return commonResult;
}
const result = {
success: true,
errors: [] as string[],
warnings: [] as string[],
data: undefined as T | undefined,
};
try {
result.data = this.validator(value);
} catch (error) {
result.success = false;
result.errors.push((error as Error).message);
}
return result;
}
getConfig() {
return {
type: "custom",
optional: this._optional,
nullable: this._nullable,
default: this._default,
};
}
}
// Re-export all schema types for convenience
export {
BaseSchema,
StringSchema,
NumberSchema,
BooleanSchema,
ArraySchema,
ObjectSchema,
};
// Re-export types
export type {
SchemaValidationResult,
StringSchemaOptions,
NumberSchemaOptions,
BooleanSchemaOptions,
ArraySchemaOptions,
ObjectSchemaOptions,
} from "../../../types/types";