UNPKG

@firefliesai/schema-forge

Version:

Transform TypeScript classes into JSON Schema definitions with automatic support for OpenAI, Anthropic, and Google Gemini function calling (tool) formats

215 lines (214 loc) 8.25 kB
"use strict"; /** * Class-validator integration for schema-forge * * This module provides utilities to infer JSON Schema properties from class-validator decorators. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.inferClassValidatorProperties = inferClassValidatorProperties; /** * Validation type names used by class-validator */ const VALIDATION_TYPES = { ARRAY_MAX_SIZE: 'arrayMaxSize', ARRAY_MIN_SIZE: 'arrayMinSize', ARRAY_UNIQUE: 'arrayUnique', ARRAY_NOT_EMPTY: 'arrayNotEmpty', ARRAY_CONTAINS: 'arrayContains', IS_ARRAY: 'isArray', MAX: 'max', MIN: 'min', IS_INT: 'isInt', MIN_LENGTH: 'minLength', MAX_LENGTH: 'maxLength', IS_URL: 'isUrl', IS_EMAIL: 'isEmail', IS_POSITIVE: 'isPositive', }; /** * Tries to get the class-validator metadata storage if available */ function getClassValidatorMetadataStorage() { const getGlobalStorage = () => { const g = globalThis; return g.classValidatorMetadataStorage || null; }; try { // Prefer getMetadataStorage() if class-validator is loaded - ensures same instance // eslint-disable-next-line @typescript-eslint/no-require-imports const cv = require('class-validator'); if (typeof cv.getMetadataStorage === 'function') { return cv.getMetadataStorage(); } return getGlobalStorage(); } catch { return getGlobalStorage(); } } /** * Gets validation metadata for a specific property from class-validator */ function getPropertyValidationMetadata(target, propertyName) { const storage = getClassValidatorMetadataStorage(); if (!storage) { return []; } try { // Get all validation metadata for the target class const allMetadata = storage.getTargetValidationMetadatas(target, '', false, false, undefined); // Filter for the specific property return allMetadata.filter((metadata) => metadata.propertyName === propertyName); } catch { return []; } } /** * Infers JSON Schema properties from class-validator decorators * * Supported decorators: * - IsArray -> isArray: true, type: 'array' * - ArrayMaxSize -> maxItems * - ArrayMinSize -> minItems * - ArrayUnique -> uniqueItems: true * - ArrayNotEmpty -> minItems: 1 * - ArrayContains -> items.enum (when values are primitive) * - Max -> maximum (for numbers) or items.maximum (with each: true) * - Min -> minimum (for numbers) or items.minimum (with each: true) * - IsInt -> type: 'integer' or items.type (with each: true) * - MinLength -> minLength (for strings) or items.minLength (with each: true) * - MaxLength -> maxLength (for strings) or items.maxLength (with each: true) * - IsUrl -> format: 'uri' or items.format (with each: true) * - IsEmail -> format: 'email' or items.format (with each: true) * - IsPositive -> minimum: 1 or items.minimum (with each: true) */ function inferClassValidatorProperties(target, propertyName) { const metadata = getPropertyValidationMetadata(target.constructor, propertyName); const inferred = {}; const items = {}; for (const meta of metadata) { const appliesToItems = meta.each === true; // class-validator may use 'name' or 'type' for validation type const metaType = meta.name || meta.type; switch (metaType) { case VALIDATION_TYPES.IS_ARRAY: inferred.isArray = true; inferred.type = 'array'; break; case VALIDATION_TYPES.ARRAY_MAX_SIZE: inferred.isArray = true; if (meta.constraints && meta.constraints[0] !== undefined) { inferred.maxItems = meta.constraints[0]; } break; case VALIDATION_TYPES.ARRAY_MIN_SIZE: inferred.isArray = true; if (meta.constraints && meta.constraints[0] !== undefined) { inferred.minItems = meta.constraints[0]; } break; case VALIDATION_TYPES.ARRAY_UNIQUE: inferred.isArray = true; inferred.uniqueItems = true; break; case VALIDATION_TYPES.ARRAY_NOT_EMPTY: inferred.isArray = true; inferred.minItems = 1; break; case VALIDATION_TYPES.ARRAY_CONTAINS: inferred.isArray = true; if (meta.constraints && Array.isArray(meta.constraints[0])) { const values = meta.constraints[0]; const allPrimitive = values.every((v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean'); if (allPrimitive && values.length > 0) { const allSameType = values.every((v) => typeof v === typeof values[0]); if (allSameType) { const firstType = typeof values[0]; items.type = firstType === 'string' ? 'string' : firstType === 'number' ? 'number' : 'boolean'; } items.enum = values; } } break; case VALIDATION_TYPES.MAX: if (appliesToItems) { if (meta.constraints && meta.constraints[0] !== undefined) { items.maximum = meta.constraints[0]; } } else if (meta.constraints && meta.constraints[0] !== undefined) { inferred.maximum = meta.constraints[0]; } break; case VALIDATION_TYPES.MIN: if (appliesToItems) { if (meta.constraints && meta.constraints[0] !== undefined) { items.minimum = meta.constraints[0]; } } else if (meta.constraints && meta.constraints[0] !== undefined) { inferred.minimum = meta.constraints[0]; } break; case VALIDATION_TYPES.IS_INT: if (appliesToItems) { items.type = 'integer'; } else { inferred.type = 'integer'; } break; case VALIDATION_TYPES.MIN_LENGTH: if (appliesToItems) { if (meta.constraints && meta.constraints[0] !== undefined) { items.minLength = meta.constraints[0]; } } else if (meta.constraints && meta.constraints[0] !== undefined) { inferred.minLength = meta.constraints[0]; } break; case VALIDATION_TYPES.MAX_LENGTH: if (appliesToItems) { if (meta.constraints && meta.constraints[0] !== undefined) { items.maxLength = meta.constraints[0]; } } else if (meta.constraints && meta.constraints[0] !== undefined) { inferred.maxLength = meta.constraints[0]; } break; case VALIDATION_TYPES.IS_URL: if (appliesToItems) { items.format = 'uri'; } else { inferred.format = 'uri'; } break; case VALIDATION_TYPES.IS_EMAIL: if (appliesToItems) { items.format = 'email'; } else { inferred.format = 'email'; } break; case VALIDATION_TYPES.IS_POSITIVE: if (appliesToItems) { items.minimum = 1; } else { inferred.minimum = 1; } break; } } if (Object.keys(items).length > 0) { inferred.items = items; inferred.isArray = true; } return inferred; }