UNPKG

fortify-schema

Version:

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

201 lines (198 loc) 6.41 kB
'use strict'; /** * Smart Schema Inference - Revolutionary TypeScript type-to-schema conversion * * This module provides automatic schema generation from TypeScript types, * making schema definition even more seamless. */ /** * Smart inference utilities for automatic schema generation */ const Smart = { /** * Infer schema from TypeScript interface using runtime reflection * * @example * ```typescript * interface User { * id: number; * email: string; * name?: string; * } * * // Use with sample data that matches your interface * const UserSchema = Smart.fromType<User>({ * id: 1, * email: "user@example.com", * name: "John Doe" * }); * // Generates: Interface({ id: "positive", email: "email", name: "string?" }) * ``` */ fromType(sampleData) { if (!sampleData || typeof sampleData !== 'object') { throw new Error('Smart.fromType() requires sample data that matches your TypeScript interface'); } return Smart.fromSample(sampleData); }, /** * Infer schema from sample data with intelligent type detection * * @example * ```typescript * const sampleUser = { * id: 1, * email: "user@example.com", * name: "John Doe", * tags: ["developer", "typescript"] * }; * * const UserSchema = Smart.fromSample(sampleUser); * // Generates: Interface({ id: "positive", email: "email", name: "string", tags: "string[]" }) * ``` */ fromSample(sample) { const schema = {}; for (const [key, value] of Object.entries(sample)) { schema[key] = Smart.inferFieldType(value); } return schema; }, /** * Infer field type from value with smart detection */ inferFieldType(value) { if (value === null || value === undefined) { return "any?"; } if (typeof value === "string") { // Smart email detection if (Smart.isEmail(value)) return "email"; // Smart URL detection if (Smart.isUrl(value)) return "url"; // Smart UUID detection if (Smart.isUuid(value)) return "uuid"; // Smart phone detection if (Smart.isPhone(value)) return "phone"; return "string"; } if (typeof value === "number") { // Smart positive number detection if (value > 0 && Number.isInteger(value)) return "positive"; if (Number.isInteger(value)) return "int"; return "number"; } if (typeof value === "boolean") { return "boolean"; } if (value instanceof Date) { return "date"; } if (Array.isArray(value)) { if (value.length === 0) return "any[]"; // Detect array element type from first element const elementType = Smart.inferFieldType(value[0]); const baseType = elementType.replace("?", ""); // Remove optional marker return `${baseType}[]`; } if (typeof value === "object") { // Nested object - recursively infer const nestedSchema = {}; for (const [nestedKey, nestedValue] of Object.entries(value)) { nestedSchema[nestedKey] = Smart.inferFieldType(nestedValue); } return nestedSchema; } return "any"; }, /** * Smart format detection utilities */ isEmail(str) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str); }, isUrl(str) { try { new URL(str); return true; } catch { return false; } }, isUuid(str) { return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str); }, isPhone(str) { return /^\+?[1-9]\d{1,14}$/.test(str.replace(/[\s\-\(\)]/g, '')); }, /** * Generate schema from JSON Schema (migration helper) * * @example * ```typescript * const jsonSchema = { * type: "object", * properties: { * id: { type: "number" }, * email: { type: "string", format: "email" } * } * }; * * const schema = Smart.fromJsonSchema(jsonSchema); * ``` */ fromJsonSchema(jsonSchema) { if (jsonSchema.type === "object" && jsonSchema.properties) { const schema = {}; for (const [key, prop] of Object.entries(jsonSchema.properties)) { schema[key] = Smart.convertJsonSchemaProperty(prop); } return schema; } throw new Error("Unsupported JSON Schema format"); }, convertJsonSchemaProperty(prop) { const isOptional = !prop.required; const suffix = isOptional ? "?" : ""; switch (prop.type) { case "string": if (prop.format === "email") return `email${suffix}`; if (prop.format === "uri") return `url${suffix}`; if (prop.format === "uuid") return `uuid${suffix}`; if (prop.minLength && prop.maxLength) { return `string(${prop.minLength},${prop.maxLength})${suffix}`; } return `string${suffix}`; case "number": case "integer": if (prop.minimum && prop.maximum) { return `number(${prop.minimum},${prop.maximum})${suffix}`; } if (prop.minimum > 0) return `positive${suffix}`; return prop.type === "integer" ? `int${suffix}` : `number${suffix}`; case "boolean": return `boolean${suffix}`; case "array": const itemType = Smart.convertJsonSchemaProperty(prop.items || { type: "any" }); const baseType = itemType.replace("?", ""); return `${baseType}[]${suffix}`; default: return `any${suffix}`; } } }; exports.Smart = Smart; exports.default = Smart; //# sourceMappingURL=SmartInference.js.map