UNPKG

fortify-schema

Version:

TypeScript interface-like schema validation system that's easier to use than Zod

385 lines (339 loc) 9.3 kB
/** * Test script for Interface-based Schema system * Demonstrates TypeScript interface-like schema definitions */ import { Interface, FieldTypes, QuickSchemas, Make, } from "../core/schema/mode/interfaces/Interface"; console.log("=== FORTIFYJS INTERFACE SCHEMA TESTS ===\n"); // ===== BASIC INTERFACE SYNTAX ===== console.log("1. Basic Interface Syntax:"); // Simple user schema - looks like TypeScript interface! const UserSchema = Interface({ id: "number", email: "email", name: "string", age: "number?", // Optional isActive: "boolean?", // Optional with smart conversion tags: "string[]?", // Optional array role: Make.const("admin"), // Safe constant value }); console.log( "✅ Valid user:", UserSchema.safeParse({ id: 1, email: "john@example.com", name: "John Doe", age: 30, role: "admin", }) ); console.log( "❌ Invalid user:", UserSchema.safeParse({ id: "not-a-number", email: "invalid-email", name: "J", }) ); // ===== NESTED OBJECTS ===== console.log("\n2. Nested Objects:"); const OrderSchema = Interface({ id: "number", status: "pending", // Constant customer: { // Nested object name: "string", email: "email", address: { // Deeply nested street: "string", city: "string", zipCode: "string", }, }, items: [ { // Array of objects name: "string", price: "number", quantity: "int", }, ], total: "number", createdAt: "date?", }); const validOrder = { id: 123, status: "pending", customer: { name: "Jane Doe", email: "jane@example.com", address: { street: "123 Main St", city: "New York", zipCode: "10001", }, }, items: [ { name: "Product 1", price: 29.99, quantity: 2 }, { name: "Product 2", price: 15.5, quantity: 1 }, ], total: 75.48, }; console.log("✅ Valid order:", OrderSchema.safeParse(validOrder)); // ===== ARRAY TYPES ===== console.log("\n3. Array Types:"); const BlogPostSchema = Interface({ title: "string", content: "string", tags: "string[]", // Required array categories: "string[]?", // Optional array authors: "email[]", // Array of emails ratings: "number[]?", // Optional number array published: "boolean", }); console.log( "✅ Valid blog post:", BlogPostSchema.safeParse({ title: "My Blog Post", content: "This is the content...", tags: ["javascript", "typescript"], authors: ["author1@example.com", "author2@example.com"], ratings: [4.5, 5.0, 4.8], published: true, }) ); // ===== FORMAT VALIDATION ===== console.log("\n4. Format Validation:"); const ContactSchema = Interface({ name: "string", email: "email", // Email format website: "url?", // Optional URL phone: "phone?", // Optional phone userId: "uuid", // UUID format username: "username", // Username format slug: "slug", // URL slug format }); console.log( "✅ Valid contact:", ContactSchema.safeParse({ name: "John Doe", email: "john@example.com", website: "https://johndoe.com", phone: "+1234567890", userId: "123e4567-e89b-12d3-a456-426614174000", username: "john_doe", slug: "john-doe-profile", }) ); console.log( "❌ Invalid formats:", ContactSchema.safeParse({ name: "John Doe", email: "invalid-email", website: "not-a-url", phone: "123", userId: "not-a-uuid", username: "invalid username!", slug: "Invalid Slug!", }) ); // ===== NUMBER TYPES ===== console.log("\n5. Number Types:"); const ProductSchema = Interface({ id: "int", // Integer name: "string", price: "number", // Any number stock: "positive", // Positive integer rating: "float?", // Optional float discount: "number?", // Optional number categoryId: "int", }); console.log( "✅ Valid product:", ProductSchema.safeParse({ id: 1, name: "Laptop", price: 999.99, stock: 50, rating: 4.5, categoryId: 2, }) ); console.log( "❌ Invalid numbers:", ProductSchema.safeParse({ id: 1.5, // Should be integer name: "Laptop", price: "not-a-number", stock: -5, // Should be positive categoryId: "not-int", }) ); // ===== CONSTANTS AND MIXED TYPES ===== console.log("\n6. Constants and Mixed Types:"); const APIResponseSchema = Interface({ version: "1.0", // Constant string status: 200, // Constant number success: "boolean", data: { users: "any[]", // Array of any type pagination: { page: "int", total: "int", }, }, metadata: "any?", // Optional any type errors: "string[]?", // Optional string array }); console.log( "✅ Valid API response:", APIResponseSchema.safeParse({ version: "1.0", status: 200, success: true, data: { users: [ { id: 1, name: "John" }, { id: 2, name: "Jane" }, ], pagination: { page: 1, total: 100, }, }, }) ); // ===== STRICT MODE ===== console.log("\n7. Strict Mode:"); const StrictUserSchema = Interface({ id: "number", name: "string", }).strict(); console.log( "❌ Strict mode rejects extra properties:", StrictUserSchema.safeParse({ id: 1, name: "John", extraField: "not allowed", }) ); // ===== QUICK SCHEMAS ===== console.log("\n8. Quick Schemas:"); console.log( "✅ Quick User schema:", QuickSchemas.User.safeParse({ id: 1, email: "user@example.com", name: "User Name", }) ); console.log( "✅ Quick API Response:", QuickSchemas.APIResponse.safeParse({ success: true, data: { message: "Hello World" }, }) ); // ===== TYPE CONVERSION ===== console.log("\n9. Smart Type Conversion:"); const FlexibleSchema = Interface({ id: "number", // Converts string numbers active: "boolean", // Converts string booleans count: "int", // Converts string integers }); console.log( "✅ Type conversion:", FlexibleSchema.safeParse({ id: "123", // String to number active: "true", // String to boolean count: "456", // String to integer }) ); // ===== ERROR HANDLING ===== console.log("\n10. Error Handling:"); try { UserSchema.parse({ invalid: "data" }); } catch (error) { console.log("❌ Parse error:", (error as Error).message); } const result = UserSchema.safeParse({ invalid: "data" }); if (!result.success) { console.log("❌ SafeParse errors:", result.errors); } // ===== NEW SAFER SYNTAX ===== console.log("\n11. New Safer Syntax for Constants and Unions:"); // Safe constant values using Make.const() const SafeAPIResponseSchema = Interface({ version: Make.const("1.0"), // Safe constant string status: Make.const(200), // Safe constant number success: Make.const(true), // Safe constant boolean data: "any", }); console.log( "✅ Safe constants:", SafeAPIResponseSchema.safeParse({ version: "1.0", status: 200, success: true, data: { message: "Hello" }, }) ); console.log( "❌ Wrong constant:", SafeAPIResponseSchema.safeParse({ version: "2.0", // Wrong version status: 200, success: true, data: {}, }) ); // Union types using Make.union() const OrderWithUnionsSchema = Interface({ id: "number", status: Make.union( "pending", "processing", "shipped", "delivered" ), priority: Make.union("low", "medium", "high"), type: Make.unionOptional("standard", "express", "overnight"), }); console.log( "✅ Valid order with union:", OrderWithUnionsSchema.safeParse({ id: 123, status: "pending", priority: "high", }) ); console.log( "❌ Invalid union value:", OrderWithUnionsSchema.safeParse({ id: 123, status: "invalid_status", // Not in union priority: "high", }) ); console.log("\n=== INTERFACE SCHEMA TESTS COMPLETED ==="); console.log("🎉 TypeScript interface-like schemas working perfectly!"); console.log("📚 Features demonstrated:"); console.log(" - TypeScript interface-like syntax"); console.log(' - Optional fields with "?" suffix'); console.log(' - Array types with "[]" suffix'); console.log(" - Constant values (strings, numbers, etc.)"); console.log(" - Nested objects and arrays"); console.log(" - Format validation (email, url, uuid, phone, etc.)"); console.log(" - Number types (int, positive, float)"); console.log(" - Smart type conversion"); console.log(" - Strict mode for extra property control"); console.log(" - Quick schema templates"); console.log(" - Clean, readable schema definitions!"); console.log( " - Much easier than Zod and more intuitive than traditional schemas!" );