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.

176 lines (152 loc) 5.64 kB
import { OpenAPIOptions, OpenAPISpec } from "../../../../types/extension.type"; import { SchemaInterface } from "../../../../types/SchemaValidator.type"; /** * OpenAPI generator */ export class OpenAPIGenerator { constructor( private schema: SchemaInterface, private options: OpenAPIOptions ) {} generate(): OpenAPISpec { return { openapi: "3.0.0", info: { title: this.options.title, version: this.options.version }, servers: this.options.servers?.map(url => ({ url })) || [], components: { schemas: { [this.options.title]: this.convertSchema() } } }; } private convertSchema(): any { const properties: any = {}; const required: string[] = []; Object.entries(this.schema).forEach(([fieldName, fieldType]) => { const openApiProperty = this.convertFieldToOpenAPIProperty(fieldName, fieldType); properties[fieldName] = openApiProperty.schema; if (openApiProperty.required) { required.push(fieldName); } }); return { type: "object", properties, required: required.length > 0 ? required : undefined }; } private convertFieldToOpenAPIProperty(_fieldName: string, fieldType: any): { schema: any; required: boolean } { if (typeof fieldType === "string") { const isOptional = fieldType.includes("?"); const cleanType = fieldType.replace("?", ""); // Handle string types with constraints if (cleanType.startsWith("string")) { const constraints = this.parseStringConstraints(cleanType); return { schema: { type: "string", ...constraints }, required: !isOptional }; } // Handle specific string formats if (cleanType === "email") { return { schema: { type: "string", format: "email" }, required: !isOptional }; } if (cleanType === "url") { return { schema: { type: "string", format: "uri" }, required: !isOptional }; } if (cleanType === "uuid") { return { schema: { type: "string", format: "uuid" }, required: !isOptional }; } if (cleanType === "date") { return { schema: { type: "string", format: "date-time" }, required: !isOptional }; } // Handle number types if (cleanType === "number" || cleanType === "positive" || cleanType === "int") { const schema: any = { type: cleanType === "int" ? "integer" : "number" }; if (cleanType === "positive") { schema.minimum = 0; schema.exclusiveMinimum = true; } return { schema, required: !isOptional }; } // Handle boolean if (cleanType === "boolean") { return { schema: { type: "boolean" }, required: !isOptional }; } // Handle arrays if (cleanType.includes("[]")) { const itemType = cleanType.replace("[]", ""); const itemSchema = this.convertFieldToOpenAPIProperty("item", itemType).schema; return { schema: { type: "array", items: itemSchema }, required: !isOptional }; } // Default to string return { schema: { type: "string" }, required: !isOptional }; } // Handle object types if (typeof fieldType === "object" && !Array.isArray(fieldType)) { const nestedProperties: any = {}; const nestedRequired: string[] = []; Object.entries(fieldType).forEach(([nestedField, nestedType]) => { const nestedProperty = this.convertFieldToOpenAPIProperty(nestedField, nestedType); nestedProperties[nestedField] = nestedProperty.schema; if (nestedProperty.required) { nestedRequired.push(nestedField); } }); return { schema: { type: "object", properties: nestedProperties, required: nestedRequired.length > 0 ? nestedRequired : undefined }, required: true }; } // Fallback return { schema: { type: "string" }, required: true }; } private parseStringConstraints(stringType: string): any { const constraints: any = {}; // Parse string(min,max) format const match = stringType.match(/string\((\d+)?,?(\d+)?\)/); if (match) { if (match[1]) constraints.minLength = parseInt(match[1]); if (match[2]) constraints.maxLength = parseInt(match[2]); } return constraints; } }