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.
228 lines (225 loc) • 7.05 kB
JavaScript
;
/**
* OpenAPI Converter - Convert schemas to OpenAPI specifications
*
* This module provides utilities to convert Fortify Schema definitions
* to OpenAPI 3.0 specifications for API documentation.
*/
/**
* OpenAPI converter for schema definitions
*/
class OpenAPIConverter {
/**
* Convert a schema to OpenAPI format
*/
static convertSchema(schema) {
const properties = {};
const required = [];
Object.entries(schema).forEach(([fieldName, fieldType]) => {
const openApiProperty = this.convertField(fieldType);
properties[fieldName] = openApiProperty.schema;
if (openApiProperty.required) {
required.push(fieldName);
}
});
return {
type: "object",
properties,
required: required.length > 0 ? required : undefined
};
}
/**
* Convert a single field to OpenAPI property
*/
static convertField(fieldType) {
if (typeof fieldType === "string") {
return this.convertStringField(fieldType);
}
if (typeof fieldType === "object" && !Array.isArray(fieldType)) {
return this.convertObjectField(fieldType);
}
// Fallback
return {
schema: { type: "string" },
required: true
};
}
/**
* Convert string-based field definitions
*/
static convertStringField(fieldType) {
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
const formatMappings = {
email: { type: "string", format: "email" },
url: { type: "string", format: "uri" },
uuid: { type: "string", format: "uuid" },
date: { type: "string", format: "date-time" },
phone: { type: "string", pattern: "^\\+?[1-9]\\d{1,14}$" }
};
if (formatMappings[cleanType]) {
return {
schema: formatMappings[cleanType],
required: !isOptional
};
}
// Handle number types
if (cleanType === "number") {
return {
schema: { type: "number" },
required: !isOptional
};
}
if (cleanType === "positive") {
return {
schema: {
type: "number",
minimum: 0,
exclusiveMinimum: true
},
required: !isOptional
};
}
if (cleanType === "int") {
return {
schema: { type: "integer" },
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.convertField(itemType).schema;
return {
schema: {
type: "array",
items: itemSchema
},
required: !isOptional
};
}
// Default to string
return {
schema: { type: "string" },
required: !isOptional
};
}
/**
* Convert object field definitions
*/
static convertObjectField(fieldType) {
const nestedProperties = {};
const nestedRequired = [];
Object.entries(fieldType).forEach(([nestedField, nestedType]) => {
const nestedProperty = this.convertField(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
};
}
/**
* Parse string constraints from string(min,max) format
*/
static parseStringConstraints(stringType) {
const constraints = {};
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;
}
/**
* Generate complete OpenAPI specification
*/
static generateOpenAPISpec(schema, options) {
const schemaObject = this.convertSchema(schema);
return {
openapi: "3.0.0",
info: {
title: options.title,
version: options.version,
description: options.description
},
servers: options.servers?.map(url => ({ url })) || [],
components: {
schemas: {
[options.schemaName || options.title]: schemaObject
}
},
paths: options.paths || {}
};
}
/**
* Generate schema reference for use in OpenAPI paths
*/
static generateSchemaReference(schemaName) {
return {
$ref: `#/components/schemas/${schemaName}`
};
}
/**
* Generate request body specification
*/
static generateRequestBody(schema, options = {}) {
const schemaObject = this.convertSchema(schema);
return {
description: options.description || "Request body",
required: options.required !== false,
content: {
"application/json": {
schema: schemaObject,
examples: options.examples
}
}
};
}
/**
* Generate response specification
*/
static generateResponse(schema, options = {}) {
const schemaObject = this.convertSchema(schema);
return {
description: options.description || "Successful response",
content: {
"application/json": {
schema: schemaObject,
examples: options.examples
}
},
headers: options.headers
};
}
}
exports.OpenAPIConverter = OpenAPIConverter;
//# sourceMappingURL=openapi-converter.js.map