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

130 lines (129 loc) 5.53 kB
"use strict"; /** * Core schema generation functionality */ Object.defineProperty(exports, "__esModule", { value: true }); exports.classToJsonSchema = classToJsonSchema; const class_validator_integration_1 = require("./class-validator-integration"); const types_1 = require("./types"); const utils_1 = require("./utils"); /** * Enriches schema properties with class-validator inferred constraints. * Called at schema generation time when all decorators have run and metadata is available. */ function enrichWithClassValidatorMetadata(properties, target) { for (const propName of Object.keys(properties)) { const inferred = (0, class_validator_integration_1.inferClassValidatorProperties)(target.prototype, propName); const prop = properties[propName]; if (!prop || Object.keys(inferred).length === 0) continue; if (inferred.maxItems !== undefined && prop.maxItems === undefined) { prop.maxItems = inferred.maxItems; } if (inferred.minItems !== undefined && prop.minItems === undefined) { prop.minItems = inferred.minItems; } if (inferred.uniqueItems !== undefined && prop.uniqueItems === undefined) { prop.uniqueItems = inferred.uniqueItems; } if (inferred.maximum !== undefined && prop.maximum === undefined) { prop.maximum = inferred.maximum; } if (inferred.minimum !== undefined && prop.minimum === undefined) { prop.minimum = inferred.minimum; } if (inferred.minLength !== undefined && prop.minLength === undefined) { prop.minLength = inferred.minLength; } if (inferred.maxLength !== undefined && prop.maxLength === undefined) { prop.maxLength = inferred.maxLength; } if (inferred.format !== undefined && prop.format === undefined) { prop.format = inferred.format; } if (inferred.type !== undefined && prop.type === undefined) { prop.type = inferred.type; } // Merge inferred items into array items (e.g. from each: true decorators) if (prop.type === 'array' && prop.items && inferred.items && Object.keys(inferred.items).length > 0) { // Merge inferred items into existing items // preserving explicit properties but filling in missing ones const mergedItems = { ...prop.items }; // Only add inferred properties that don't exist in explicit items if (inferred.items.format !== undefined && mergedItems.format === undefined) { mergedItems.format = inferred.items.format; } if (inferred.items.type !== undefined && mergedItems.type === undefined) { mergedItems.type = inferred.items.type; } if (inferred.items.minimum !== undefined && mergedItems.minimum === undefined) { mergedItems.minimum = inferred.items.minimum; } if (inferred.items.maximum !== undefined && mergedItems.maximum === undefined) { mergedItems.maximum = inferred.items.maximum; } if (inferred.items.minLength !== undefined && mergedItems.minLength === undefined) { mergedItems.minLength = inferred.items.minLength; } if (inferred.items.maxLength !== undefined && mergedItems.maxLength === undefined) { mergedItems.maxLength = inferred.items.maxLength; } if (inferred.items.enum !== undefined && mergedItems.enum === undefined) { mergedItems.enum = inferred.items.enum; } prop.items = mergedItems; } } } /** * Converts a TypeScript class to a JSON Schema * * @param target The class to convert * @param options Options for schema generation * @returns JSON Schema representation of the class * * @example * // Basic usage * const schema = classToJsonSchema(User); * * // With options * const schema = classToJsonSchema(User, { * forStructuredOutput: true, * propertyOverrides: { * 'username': { description: 'Custom description' } * }, * structuredOutputFormat: 'openai', // unused * }); */ function classToJsonSchema(target, options) { const properties = (0, utils_1.cloneMetadata)(Reflect.getMetadata(types_1.JSON_SCHEMA_METADATA_KEY, target.prototype) || {}); const requiredProps = [ ...(Reflect.getMetadata(types_1.REQUIRED_PROPS_METADATA_KEY, target.prototype) || []), ]; // Enrich with class-validator metadata (decorators have all run by schema generation time) enrichWithClassValidatorMetadata(properties, target); if (options?.propertyOverrides) { Object.entries(options.propertyOverrides).forEach(([path, updates]) => { const paths = path.split('.'); (0, decorators_1.applyPropertyUpdates)(properties, paths, updates, target); }); } const schema = { type: 'object', properties, }; if (requiredProps.length > 0) { schema.required = requiredProps; } // Handle structured output formatting based on provider if (options?.forStructuredOutput) { // For backward compatibility, use OpenAI format return (0, utils_1.prepareForOpenAIStructuredOutput)(schema, true); } return schema; } // Import here to handle circular dependencies const decorators_1 = require("./decorators");