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.

186 lines (167 loc) 6.62 kB
import { InferSchemaType } from "../schema/mode/interfaces/Interface"; import { InterfaceSchema } from "../schema/mode/interfaces/InterfaceSchema"; import { SchemaInterface } from "../types/SchemaValidator.type"; /** * Schema modification utilities - transform and combine schemas */ export class Mod { /** * Merge multiple schemas into a single schema * @example * ```typescript * const UserSchema = Interface({ id: "number", name: "string" }); * const ProfileSchema = Interface({ bio: "string?", avatar: "url?" }); * * const MergedSchema = Mod.merge(UserSchema, ProfileSchema); * // Result: { id: number, name: string, bio?: string, avatar?: string } * ``` */ static merge<T, U>( schema1: InterfaceSchema<T>, schema2: InterfaceSchema<U> ): InterfaceSchema<T & U> { // Get the internal definitions from both schemas const def1 = (schema1 as any).definition as SchemaInterface; const def2 = (schema2 as any).definition as SchemaInterface; // Merge the definitions const mergedDefinition: SchemaInterface = { ...def1, ...def2 }; // Get options from both schemas (schema2 options take precedence) const options1 = (schema1 as any).options || {}; const options2 = (schema2 as any).options || {}; const mergedOptions = { ...options1, ...options2 }; return new InterfaceSchema<T & U>(mergedDefinition, mergedOptions); } /** * Pick specific fields from a schema * @example * ```typescript * const UserSchema = Interface({ * id: "number", * name: "string", * email: "email", * password: "string" * }); * * const PublicUserSchema = Mod.pick(UserSchema, ["id", "name", "email"]); * // Result: { id: number, name: string, email: string } * ``` */ static pick<T, K extends keyof T>( schema: InterfaceSchema<T>, keys: K[] ): InterfaceSchema<Pick<T, K>> { const definition = (schema as any).definition as SchemaInterface; const options = (schema as any).options || {}; // Create new definition with only the picked keys const pickedDefinition: SchemaInterface = {}; for (const key of keys) { const keyStr = key as string; if (keyStr in definition) { pickedDefinition[keyStr] = definition[keyStr]; } } return new InterfaceSchema<Pick<T, K>>(pickedDefinition, options); } /** * Omit specific fields from a schema * @example * ```typescript * const UserSchema = Interface({ * id: "number", * name: "string", * email: "email", * password: "string" * }); * * const SafeUserSchema = Mod.omit(UserSchema, ["password"]); * // Result: { id: number, name: string, email: string } * ``` */ static omit<T, K extends keyof T>( schema: InterfaceSchema<T>, keys: K[] ): InterfaceSchema<Omit<T, K>> { const definition = (schema as any).definition as SchemaInterface; const options = (schema as any).options || {}; // Create new definition without the omitted keys const omittedDefinition: SchemaInterface = { ...definition }; for (const key of keys) { const keyStr = key as string; delete omittedDefinition[keyStr]; } return new InterfaceSchema<Omit<T, K>>(omittedDefinition, options); } /** * Make all fields in a schema optional * @example * ```typescript * const UserSchema = Interface({ id: "number", name: "string" }); * const PartialUserSchema = Mod.partial(UserSchema); * // Result: { id?: number, name?: string } * ``` */ static partial<T>(schema: InterfaceSchema<T>): InterfaceSchema<Partial<T>> { const definition = (schema as any).definition as SchemaInterface; const options = (schema as any).options || {}; // Convert all fields to optional by adding "?" suffix const partialDefinition: SchemaInterface = {}; for (const [key, value] of Object.entries(definition)) { if (typeof value === "string" && !value.endsWith("?")) { partialDefinition[key] = value + "?"; } else { partialDefinition[key] = value; } } return new InterfaceSchema<Partial<T>>(partialDefinition, options); } /** * Make all fields in a schema required (remove optional markers) * @example * ```typescript * const UserSchema = Interface({ id: "number", name: "string?" }); * const RequiredUserSchema = Mod.required(UserSchema); * // Result: { id: number, name: string } * ``` */ static required<T>(schema: InterfaceSchema<T>): InterfaceSchema<Required<T>> { const definition = (schema as any).definition as SchemaInterface; const options = (schema as any).options || {}; // Remove "?" suffix from all fields const requiredDefinition: SchemaInterface = {}; for (const [key, value] of Object.entries(definition)) { if (typeof value === "string" && value.endsWith("?")) { requiredDefinition[key] = value.slice(0, -1); } else { requiredDefinition[key] = value; } } return new InterfaceSchema<Required<T>>(requiredDefinition, options); } /** * Extend a schema with additional fields * @example * ```typescript * const BaseSchema = Interface({ id: "number", name: "string" }); * const ExtendedSchema = Mod.extend(BaseSchema, { * email: "email", * createdAt: "date" * }); * // Result: { id: number, name: string, email: string, createdAt: Date } * ``` */ static extend<T, U extends SchemaInterface>( schema: InterfaceSchema<T>, extension: U ): InterfaceSchema<T & InferSchemaType<U>> { const definition = (schema as any).definition as SchemaInterface; const options = (schema as any).options || {}; const extendedDefinition: SchemaInterface = { ...definition, ...extension }; return new InterfaceSchema<T & InferSchemaType<U>>(extendedDefinition, options); } }