@pulzar/core
Version:
Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support
153 lines • 5.42 kB
JavaScript
import { z } from "zod";
export class OpenAPIGenerator {
config;
routes = new Map();
schemas = new Map();
constructor(config) {
this.config = config;
}
addRoute(path, method, metadata) {
const key = `${method.toUpperCase()} ${path}`;
this.routes.set(key, metadata);
}
addSchema(name, schema) {
this.schemas.set(name, schema);
}
generate() {
const spec = {
openapi: "3.1.0",
info: {
title: this.config.title,
version: this.config.version,
description: this.config.description,
...(this.config.contact && { contact: this.config.contact }),
...(this.config.license && { license: this.config.license }),
},
servers: this.config.servers || [{ url: "/" }],
paths: this.generatePaths(),
components: {
schemas: this.generateSchemas(),
securitySchemes: this.generateSecuritySchemes(),
},
security: this.config.security || [],
};
return spec;
}
generatePaths() {
const paths = {};
for (const [key, metadata] of this.routes) {
const parts = key.split(" ");
const method = parts[0];
const path = parts[1];
if (!method || !path)
continue;
const pathKey = this.normalizePath(path);
if (!paths[pathKey]) {
paths[pathKey] = {};
}
paths[pathKey][method.toLowerCase()] = {
summary: metadata.summary,
description: metadata.description,
tags: metadata.tags,
parameters: metadata.parameters?.map((param) => ({
name: param.name,
in: param.in,
required: param.required,
schema: this.zodToOpenAPI(param.schema),
description: param.description,
})),
requestBody: metadata.requestBody
? {
required: metadata.requestBody.required,
content: Object.entries(metadata.requestBody.content).reduce((acc, [contentType, content]) => {
acc[contentType] = {
schema: this.zodToOpenAPI(content.schema),
};
return acc;
}, {}),
}
: undefined,
responses: Object.entries(metadata.responses).reduce((acc, [code, response]) => {
acc[code] = {
description: response.description,
content: response.content
? Object.entries(response.content).reduce((contentAcc, [contentType, content]) => {
contentAcc[contentType] = {
schema: this.zodToOpenAPI(content.schema),
};
return contentAcc;
}, {})
: undefined,
};
return acc;
}, {}),
security: metadata.security,
};
}
return paths;
}
generateSchemas() {
const schemas = {};
for (const [name, schema] of this.schemas) {
schemas[name] = this.zodToOpenAPI(schema);
}
return schemas;
}
generateSecuritySchemes() {
return {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
apiKeyAuth: {
type: "apiKey",
in: "header",
name: "X-API-Key",
},
};
}
zodToOpenAPI(schema) {
// This is a simplified conversion
// In a real implementation, you'd need a more comprehensive zod-to-openapi converter
if (schema instanceof z.ZodString) {
return { type: "string" };
}
if (schema instanceof z.ZodNumber) {
return { type: "number" };
}
if (schema instanceof z.ZodBoolean) {
return { type: "boolean" };
}
if (schema instanceof z.ZodArray) {
return {
type: "array",
items: this.zodToOpenAPI(schema.element),
};
}
if (schema instanceof z.ZodObject) {
return {
type: "object",
properties: Object.entries(schema.shape).reduce((acc, [key, value]) => {
acc[key] = this.zodToOpenAPI(value);
return acc;
}, {}),
};
}
return { type: "string" };
}
normalizePath(path) {
return path.replace(/\[([^\]]+)\]/g, "{$1}");
}
generateJSON() {
return JSON.stringify(this.generate(), null, 2);
}
generateYAML() {
// In a real implementation, you'd use a YAML library
return JSON.stringify(this.generate(), null, 2);
}
}
export function createOpenAPIGenerator(config) {
return new OpenAPIGenerator(config);
}
//# sourceMappingURL=generator.js.map