🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@cerios/openapi-to-typescript

Package Overview
Dependencies
Maintainers
2
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cerios/openapi-to-typescript - npm Package Compare versions

Comparing version
0.0.1
to
1.0.0
+1
dist/cli.d.mts
#!/usr/bin/env node
#!/usr/bin/env node
#!/usr/bin/env node
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/cli.ts
var import_node_fs2 = require("fs");
var import_openapi_core5 = require("@cerios/openapi-core");
var import_commander = require("commander");
var import_prompts = __toESM(require("prompts"));
// src/typescript-generator.ts
var import_node_fs = require("fs");
var import_node_path = require("path");
var import_openapi_core3 = require("@cerios/openapi-core");
// src/generators/enum-generator.ts
var import_openapi_core = require("@cerios/openapi-core");
function generateEnum(name, values, options) {
const { format, prefix, suffix, nullable } = options;
const typeName = (0, import_openapi_core.applyFormatting)(name, prefix, suffix);
let result;
switch (format) {
case "enum":
result = generateTsEnum(typeName, values);
break;
case "union":
result = generateUnion(typeName, values);
break;
case "const-object":
result = generateConstObject(typeName, values);
break;
default:
result = generateUnion(typeName, values);
}
if (nullable) {
result.code = addNullableToTypeCode(result.code, typeName);
}
return result;
}
function addNullableToTypeCode(code, typeName) {
const unionPattern = new RegExp(`(export type ${typeName} = )([^;]+)(;)`);
if (unionPattern.test(code)) {
return code.replace(unionPattern, `$1$2 | null$3`);
}
const constObjectTypePattern = new RegExp(
`(export type ${typeName} = \\(typeof ${typeName}\\)\\[keyof typeof ${typeName}\\])(;)`
);
if (constObjectTypePattern.test(code)) {
return code.replace(constObjectTypePattern, `$1 | null$2`);
}
const enumPattern = /^export enum /;
if (enumPattern.test(code)) {
return `${code}
/** Nullable version of ${typeName} enum */
export type ${typeName}Nullable = ${typeName} | null;`;
}
return code;
}
function generateTsEnum(typeName, values) {
const enumType = (0, import_openapi_core.classifyEnumType)(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = (0, import_openapi_core.stringToEnumMember)(value, usedKeys);
return ` ${memberName} = "${value}"`;
}
if (typeof value === "number") {
const memberName = (0, import_openapi_core.numericToEnumMember)(value, usedKeys);
return ` ${memberName} = ${value}`;
}
return ` ${String(value)} = ${value}`;
});
const code = `export enum ${typeName} {
${members.join(",\n")},
}`;
return { code, typeName };
}
function generateUnion(typeName, values) {
const literals = values.map((value) => {
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "boolean") {
return String(value);
}
return String(value);
});
const code = `export type ${typeName} = ${literals.join(" | ")};`;
return { code, typeName };
}
function generateConstObject(typeName, values) {
const enumType = (0, import_openapi_core.classifyEnumType)(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = (0, import_openapi_core.stringToEnumMember)(value, usedKeys);
return ` ${memberName}: "${value}"`;
}
if (typeof value === "number") {
const memberName = (0, import_openapi_core.numericToEnumMember)(value, usedKeys);
return ` ${memberName}: ${value}`;
}
return ` ${String(value)}: ${value}`;
});
const objectCode = `export const ${typeName} = {
${members.join(",\n")},
} as const;`;
const typeCode = `export type ${typeName} = (typeof ${typeName})[keyof typeof ${typeName}];`;
const code = `${objectCode}
${typeCode}`;
return { code, typeName };
}
// src/generators/type-generator.ts
var import_openapi_core2 = require("@cerios/openapi-core");
function generateTypeDeclaration(name, properties, options) {
const { prefix, suffix } = options;
const typeName = (0, import_openapi_core2.applyFormatting)(name, prefix, suffix);
const code = `export type ${typeName} = {
${properties.join(";\n ")};
};`;
return { code, typeName };
}
function formatTypeProperty(name, type, required) {
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
const optional = required ? "" : "?";
return `${safeName}${optional}: ${type}`;
}
// src/typescript-generator.ts
function stripSchemaPrefix(name, prefixes) {
if (!prefixes) return name;
const prefixArray = Array.isArray(prefixes) ? prefixes : [prefixes];
let result = name;
for (const prefix of prefixArray) {
result = (0, import_openapi_core3.stripPrefix)(result, prefix);
}
return result;
}
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
function isRecord(value) {
return typeof value === "object" && value !== null;
}
function getOperation(pathItem, method) {
if (!isRecord(pathItem)) {
return void 0;
}
const operation = pathItem[method];
return isRecord(operation) ? operation : void 0;
}
function getParameters(value) {
if (!isRecord(value)) {
return void 0;
}
const parameters = value.parameters;
return Array.isArray(parameters) ? parameters : void 0;
}
function getOperationId(operation) {
const operationId = operation.operationId;
return typeof operationId === "string" ? operationId : void 0;
}
function toParameterLike(value) {
if (!isRecord(value)) {
return void 0;
}
const param = {};
if (typeof value.in === "string") {
param.in = value.in;
}
if (typeof value.name === "string") {
param.name = value.name;
}
if (typeof value.description === "string") {
param.description = value.description;
}
if (typeof value.required === "boolean") {
param.required = value.required;
}
if (isRecord(value.schema)) {
param.schema = value.schema;
}
return param;
}
function getRequestBodyContent(operation) {
const requestBody = operation.requestBody;
if (!isRecord(requestBody)) {
return void 0;
}
const content = requestBody.content;
return isRecord(content) ? content : void 0;
}
function getResponseContent(response) {
if (!isRecord(response)) {
return void 0;
}
const content = response.content;
return isRecord(content) ? content : void 0;
}
function getMediaSchema(mediaType) {
if (!isRecord(mediaType)) {
return void 0;
}
const schema = mediaType.schema;
return isRecord(schema) ? schema : void 0;
}
var TypeScriptGenerator = class {
constructor(options) {
this.generatedTypes = /* @__PURE__ */ new Map();
this.schemaDependencies = /* @__PURE__ */ new Map();
this.circularSchemas = /* @__PURE__ */ new Set();
this.schemaUsageMap = /* @__PURE__ */ new Map();
this.filterStats = (0, import_openapi_core3.createFilterStatistics)();
var _a, _b, _c, _d, _e, _f;
if (!options.input) {
throw new import_openapi_core3.ConfigurationError("Input path is required", { providedOptions: options });
}
this.options = {
input: options.input,
outputTypes: options.outputTypes,
enumFormat: (_a = options.enumFormat) != null ? _a : "const-object",
includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
prefix: options.prefix,
suffix: options.suffix,
stripSchemaPrefix: options.stripSchemaPrefix,
stripPathPrefix: options.stripPathPrefix,
useOperationId: (_d = options.useOperationId) != null ? _d : true,
operationFilters: options.operationFilters,
showStats: (_e = options.showStats) != null ? _e : true,
batchSize: (_f = options.batchSize) != null ? _f : 10
};
this.spec = (0, import_openapi_core3.loadOpenAPISpec)(this.options.input);
this.validateSpec();
this.circularSchemas = (0, import_openapi_core3.detectCircularReferences)(this.spec);
if (this.options.operationFilters) {
this.initializeSchemaUsage();
}
}
/**
* Validate the OpenAPI specification
*/
validateSpec() {
var _a, _b;
if (!this.spec) {
throw new import_openapi_core3.SpecValidationError("Empty or invalid OpenAPI specification", {
filePath: this.options.input
});
}
if (!((_a = this.spec.openapi) == null ? void 0 : _a.startsWith("3."))) {
throw new import_openapi_core3.SpecValidationError("Only OpenAPI 3.x specifications are supported", {
filePath: this.options.input,
version: this.spec.openapi
});
}
if (!((_b = this.spec.components) == null ? void 0 : _b.schemas)) {
throw new import_openapi_core3.SpecValidationError("No schemas found in OpenAPI spec", {
filePath: this.options.input
});
}
}
/**
* Initialize schema usage map with operation filtering
*/
initializeSchemaUsage() {
if (!this.options.operationFilters || !this.spec.paths) {
return;
}
const requestSchemas = /* @__PURE__ */ new Set();
const responseSchemas = /* @__PURE__ */ new Set();
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
this.filterStats.totalOperations++;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters, this.filterStats)) {
continue;
}
this.filterStats.includedOperations++;
const op = operation;
if (op.requestBody && isRecord(op.requestBody)) {
const reqBody = op.requestBody;
if (isRecord(reqBody.content)) {
for (const mediaType of Object.values(reqBody.content)) {
if (isRecord(mediaType) && mediaType.schema) {
(0, import_openapi_core3.extractSchemaRefs)(mediaType.schema, requestSchemas);
}
}
}
}
if (isRecord(op.responses)) {
for (const response of Object.values(op.responses)) {
if (isRecord(response) && isRecord(response.content)) {
for (const mediaType of Object.values(response.content)) {
if (isRecord(mediaType) && mediaType.schema) {
(0, import_openapi_core3.extractSchemaRefs)(mediaType.schema, responseSchemas);
}
}
}
}
}
if (op.parameters && Array.isArray(op.parameters)) {
for (const param of op.parameters) {
if (isRecord(param) && param.schema) {
(0, import_openapi_core3.extractSchemaRefs)(param.schema, requestSchemas);
}
}
}
}
}
(0, import_openapi_core3.expandTransitiveReferences)(requestSchemas, this.spec);
(0, import_openapi_core3.expandTransitiveReferences)(responseSchemas, this.spec);
for (const name of requestSchemas) {
const inResponse = responseSchemas.has(name);
this.schemaUsageMap.set(name, inResponse ? "both" : "request");
}
for (const name of responseSchemas) {
if (!this.schemaUsageMap.has(name)) {
this.schemaUsageMap.set(name, "response");
}
}
}
/**
* Check if a schema should be included based on filters
*/
shouldIncludeSchema(name) {
if (this.options.operationFilters && this.schemaUsageMap.size > 0) {
return this.schemaUsageMap.has(name);
}
return true;
}
/**
* Generate TypeScript type for a schema
*/
generateSchemaType(name, schema) {
var _a, _b;
const strippedName = stripSchemaPrefix(name, this.options.stripSchemaPrefix);
const typeName = (0, import_openapi_core3.applyFormatting)(strippedName, this.options.prefix, this.options.suffix);
if (!this.schemaDependencies.has(name)) {
this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
}
const deps = (_a = this.schemaDependencies.get(name)) != null ? _a : /* @__PURE__ */ new Set();
if (schema.enum && Array.isArray(schema.enum)) {
const { code: code2 } = generateEnum(strippedName, schema.enum, {
format: (_b = this.options.enumFormat) != null ? _b : "union",
prefix: this.options.prefix,
suffix: this.options.suffix,
nullable: schema.nullable === true
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = [];
for (const subSchema of schema.allOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else if (subSchema.properties) {
const props = this.generateProperties(name, subSchema, deps);
parts.push(`{ ${props.join("; ")} }`);
}
}
const typeStr2 = parts.join(" & ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = [];
for (const subSchema of schema.oneOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = [];
for (const subSchema of schema.anyOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "object" || schema.properties) {
const properties = this.generateProperties(name, schema, deps);
const { code: code2 } = generateTypeDeclaration(strippedName, properties, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
let arrayType = `${itemType}[]`;
if (schema.nullable) {
arrayType = `${arrayType} | null`;
}
const code2 = `export type ${typeName} = ${arrayType};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
let typeStr = this.primitiveToTypeString(schema);
if (schema.nullable) {
typeStr = `${typeStr} | null`;
}
const code = `export type ${typeName} = ${typeStr};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code}`;
}
return code;
}
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
generatePropertyJSDoc(schema) {
const hasDescription = this.options.includeDescriptions && schema.description;
const hasDeprecated = schema.deprecated === true;
if (!hasDescription && !hasDeprecated) {
return "";
}
const parts = [];
if (hasDescription && schema.description) {
parts.push((0, import_openapi_core3.escapeJSDoc)(schema.description));
}
if (hasDeprecated) {
parts.push("@deprecated");
}
return `/** ${parts.join(" ")} */
`;
}
/**
* Generate properties for an object schema
*/
generateProperties(_schemaName, schema, deps) {
const properties = [];
if (!schema.properties) {
return properties;
}
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
const prop = formatTypeProperty(propName, typeStr, isRequired);
const jsdoc = this.generatePropertyJSDoc(propSchema);
properties.push(jsdoc ? `${jsdoc} ${prop}` : prop);
}
return properties;
}
/**
* Convert a schema to a TypeScript type string
*/
schemaToTypeString(schema, deps) {
if (Array.isArray(schema.type)) {
const types = schema.type;
const hasNull = types.includes("null");
const nonNullTypes = types.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
return "null";
}
const typeStrings = nonNullTypes.map((t) => {
const tempSchema = { ...schema, type: t };
return this.schemaToTypeString(tempSchema, deps);
});
const baseType = typeStrings.join(" | ");
return hasNull ? `${baseType} | null` : baseType;
}
if (schema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(schema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const typeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
if (schema.nullable) {
return `${typeName} | null`;
}
return typeName;
}
if (schema.enum && Array.isArray(schema.enum)) {
const enumType = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
if (schema.nullable) {
return `${enumType} | null`;
}
return enumType;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
const arrayType = `${itemType}[]`;
if (schema.nullable) {
return `${arrayType} | null`;
}
return arrayType;
}
if (schema.type === "object" || schema.properties) {
let objectType;
if (schema.properties) {
const props = [];
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
props.push(formatTypeProperty(propName, typeStr, isRequired));
}
objectType = `{ ${props.join("; ")} }`;
} else if (schema.additionalProperties) {
if (typeof schema.additionalProperties === "boolean") {
objectType = "Record<string, unknown>";
} else {
const valueType = this.schemaToTypeString(schema.additionalProperties, deps);
objectType = `Record<string, ${valueType}>`;
}
} else {
objectType = "Record<string, unknown>";
}
if (schema.nullable) {
return `${objectType} | null`;
}
return objectType;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = schema.allOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" & ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = schema.oneOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = schema.anyOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
const primitiveType = this.primitiveToTypeString(schema);
if (schema.nullable) {
return `${primitiveType} | null`;
}
return primitiveType;
}
/**
* Convert a primitive schema to a TypeScript type string
*/
primitiveToTypeString(schema) {
if (schema.type === "string") {
return "string";
}
if (schema.type === "integer" || schema.type === "number") {
return "number";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "null") {
return "null";
}
return "unknown";
}
/**
* Generate statistics
*/
generateStats() {
const stats = [];
stats.push("/**");
stats.push(" * TypeScript Types Generation Statistics");
stats.push(` * Generated from: ${this.options.input}`);
stats.push(` * Total schemas: ${this.generatedTypes.size}`);
if (this.circularSchemas.size > 0) {
stats.push(` * Circular references detected: ${Array.from(this.circularSchemas).join(", ")}`);
}
if (this.options.operationFilters) {
const filterStatsStr = (0, import_openapi_core3.formatFilterStatistics)(this.filterStats);
if (filterStatsStr) {
stats.push(...filterStatsStr.split("\n").map((s) => ` * ${s}`));
}
}
stats.push(" */");
return stats;
}
/**
* Topologically sort schemas
*/
topologicalSort() {
return (0, import_openapi_core3.topologicalSortSchemas)(this.schemaDependencies, this.circularSchemas);
}
/**
* Generate query parameter types for each operation
*/
generateQueryParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = (0, import_openapi_core3.mergeParameters)(getParameters(pathItem), getParameters(operation), this.spec);
const queryParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "query");
if (queryParams.length === 0) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateQueryParamsTypeName)(operationName);
const props = [];
for (const param of queryParams) {
const paramSchema = param.schema;
if (!paramSchema || !param.name) continue;
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(paramSchema, deps);
const isRequired = param.required === true;
let propDef = formatTypeProperty(param.name, typeStr, isRequired);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Query parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`QueryParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate header parameter types for each operation
*/
generateHeaderParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = (0, import_openapi_core3.mergeParameters)(getParameters(pathItem), getParameters(operation), this.spec);
const headerParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "header");
if (headerParams.length === 0) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateHeaderParamsTypeName)(operationName);
const props = [];
for (const param of headerParams) {
if (!param.name) continue;
let propDef = formatTypeProperty(param.name, "string", false);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Header parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`HeaderParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate inline request body types for each operation
*/
generateInlineRequestTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const requestBodyContent = getRequestBodyContent(operation);
if (!requestBodyContent) continue;
const contentTypes = Object.keys(requestBodyContent);
const hasMultipleContentTypes = contentTypes.length > 1;
for (const [contentType, mediaType] of Object.entries(requestBodyContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateInlineRequestTypeName)(operationName, contentType, hasMultipleContentTypes);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Request body for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`Request:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
/**
* Generate inline response types for each operation
*/
generateInlineResponseTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const responsesValue = operation.responses;
if (!isRecord(responsesValue)) continue;
const responses = responsesValue;
const successStatusCodes = Object.keys(responses).filter((code) => {
const codeNum = Number.parseInt(code, 10);
return codeNum >= 200 && codeNum < 300;
});
const hasMultipleStatuses = successStatusCodes.length > 1;
for (const [statusCode, response] of Object.entries(responses)) {
const responseContent = getResponseContent(response);
if (!responseContent) continue;
for (const [_contentType, mediaType] of Object.entries(responseContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateInlineResponseTypeName)(operationName, statusCode, hasMultipleStatuses);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Response for ${jsdocOperationName} (${statusCode})
*/
`;
this.generatedTypes.set(`Response:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
}
/**
* Generate schemas as a string (without writing to file)
*/
generateString() {
var _a;
if ((_a = this.spec.components) == null ? void 0 : _a.schemas) {
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
if (!this.shouldIncludeSchema(name)) {
continue;
}
try {
const typeCode = this.generateSchemaType(name, schema);
this.generatedTypes.set(name, typeCode);
} catch (error) {
throw new import_openapi_core3.SchemaGenerationError(`Failed to generate TypeScript type for schema: ${name}`, name, {
cause: error instanceof Error ? error.message : String(error)
});
}
}
}
this.generateQueryParamTypes();
this.generateHeaderParamTypes();
this.generateInlineRequestTypes();
this.generateInlineResponseTypes();
(0, import_openapi_core3.validateFilters)(this.filterStats, this.options.operationFilters);
const orderedSchemaNames = this.topologicalSort();
const output = [
"// Auto-generated by @cerios/openapi-to-typescript",
"// Do not edit this file manually",
""
];
if (this.options.showStats === true) {
output.push(...this.generateStats());
output.push("");
}
for (const name of orderedSchemaNames) {
const typeCode = this.generatedTypes.get(name);
if (typeCode) {
output.push(typeCode);
output.push("");
}
}
const inlineTypes = [];
for (const [key, typeCode] of this.generatedTypes.entries()) {
if (key.startsWith("QueryParams:") || key.startsWith("HeaderParams:") || key.startsWith("Request:") || key.startsWith("Response:")) {
inlineTypes.push(typeCode);
}
}
if (inlineTypes.length > 0) {
output.push("// Operation Types (Query Params, Headers, Request Bodies, Responses)");
output.push("");
for (const typeCode of inlineTypes) {
output.push(typeCode);
output.push("");
}
}
return output.join("\n");
}
/**
* Ensure directory exists for a file path
*/
ensureDirectoryExists(filePath) {
const normalizedPath = (0, import_node_path.normalize)(filePath);
const dir = (0, import_node_path.dirname)(normalizedPath);
if (!(0, import_node_fs.existsSync)(dir)) {
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
}
}
/**
* Generate and write to file
*/
generate() {
if (!this.options.outputTypes) {
throw new import_openapi_core3.ConfigurationError("Output path is required for generate()", {
suggestion: "Use generateString() to get output without writing to file"
});
}
const output = this.generateString();
const normalizedOutput = (0, import_node_path.normalize)(this.options.outputTypes);
this.ensureDirectoryExists(normalizedOutput);
(0, import_node_fs.writeFileSync)(normalizedOutput, output);
console.log(` \u2713 Generated ${normalizedOutput}`);
}
};
// src/utils/config-loader.ts
var import_openapi_core4 = require("@cerios/openapi-core");
var import_zod = require("zod");
var TypeScriptSpecificOptionsSchema = import_zod.z.strictObject({
enumFormat: import_zod.z.enum(["enum", "union", "const-object"]).optional(),
includeSchemas: import_zod.z.array(import_zod.z.string()).optional(),
excludeSchemas: import_zod.z.array(import_zod.z.string()).optional()
});
var TypeScriptGeneratorOptionsSchema = import_openapi_core4.BaseGeneratorOptionsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape,
outputTypes: import_zod.z.string()
// Make outputTypes required for TypeScript generator
});
var TypeScriptDefaultsSchema = import_openapi_core4.BaseDefaultsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape
});
var ConfigFileSchema = import_zod.z.strictObject({
defaults: TypeScriptDefaultsSchema.optional(),
specs: import_zod.z.array(TypeScriptGeneratorOptionsSchema).min(1, {
message: "Configuration must include at least one specification. Each specification should have 'input' and 'outputTypes' paths."
}),
executionMode: import_openapi_core4.ExecutionModeSchema.optional()
});
var errorMessages = {
missingFieldMessages: {
input: "Each spec must specify the path to your OpenAPI specification file.",
outputTypes: "Each spec must specify an output file path for generated TypeScript types."
},
unrecognizedKeyMessages: {
output: "Did you mean 'outputTypes'? The 'output' field was renamed to 'outputTypes'."
},
requiredFieldsHelp: "All required fields are present (specs array with input/outputTypes)"
};
var configLoader = (0, import_openapi_core4.createConfigLoader)(
{
packageName: "openapi-to-typescript",
errorMessages
},
ConfigFileSchema
);
var loadConfig = configLoader.loadConfig;
function mergeConfigWithDefaults(config) {
if (!(config == null ? void 0 : config.specs) || !Array.isArray(config.specs)) {
throw new Error("Invalid config: specs array is required");
}
const defaults = config.defaults || {};
return config.specs.map((spec) => {
const merged = {
// Apply defaults first
enumFormat: defaults.enumFormat,
includeDescriptions: defaults.includeDescriptions,
defaultNullable: defaults.defaultNullable,
useOperationId: defaults.useOperationId,
prefix: defaults.prefix,
suffix: defaults.suffix,
stripSchemaPrefix: defaults.stripSchemaPrefix,
stripPathPrefix: defaults.stripPathPrefix,
operationFilters: defaults.operationFilters,
showStats: defaults.showStats,
batchSize: defaults.batchSize,
// Override with spec-specific values (including required input/output)
...spec
};
return merged;
});
}
// src/cli.ts
var program = new import_commander.Command();
function isRecord2(value) {
return typeof value === "object" && value !== null;
}
function getStringField(value, field) {
if (!isRecord2(value)) {
return void 0;
}
const fieldValue = value[field];
return typeof fieldValue === "string" ? fieldValue : void 0;
}
function getBooleanField(value, field) {
if (!isRecord2(value)) {
return void 0;
}
const fieldValue = value[field];
return typeof fieldValue === "boolean" ? fieldValue : void 0;
}
program.name("openapi-to-typescript").description("Generate TypeScript types from OpenAPI specifications").version("1.0.0").option("-c, --config <path>", "Path to config file (openapi-to-typescript.config.{ts,json})").addHelpText(
"after",
`
Examples:
# Create a new config file
$ openapi-to-typescript init
# Generate with auto-discovered config
$ openapi-to-typescript
# Generate with custom config path
$ openapi-to-typescript --config custom.config.ts
`
).action(async (options) => {
try {
await executeConfigMode(options);
} catch (error) {
if (error instanceof import_openapi_core5.CliOptionsError || error instanceof import_openapi_core5.ConfigValidationError) {
console.error(error.message);
process.exit(1);
}
console.error("Error:", error instanceof Error ? error.message : String(error));
if (error instanceof Error && error.stack) {
console.error("\nStack trace:", error.stack);
}
process.exit(1);
}
});
program.command("init").description("Initialize a new openapi-to-typescript configuration file").action(async () => {
try {
await initConfigFile();
} catch (error) {
console.error("Error:", error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
void program.parseAsync(process.argv);
async function executeConfigMode(options) {
var _a, _b;
const config = await loadConfig(options.config);
const specs = mergeConfigWithDefaults(config);
const executionMode = config.executionMode || "parallel";
const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;
const summary = await (0, import_openapi_core5.executeBatch)(specs, executionMode, (spec) => new TypeScriptGenerator(spec), batchSize);
const exitCode = (0, import_openapi_core5.getBatchExitCode)(summary);
if (exitCode !== 0) {
process.exit(exitCode);
}
}
async function initConfigFile() {
var _a, _b;
console.log("Welcome to openapi-to-typescript configuration setup!\n");
const configFiles = ["openapi-to-typescript.config.ts", "openapi-to-typescript.config.json"];
const existingConfig = configFiles.find((f) => (0, import_node_fs2.existsSync)(f));
if (existingConfig) {
const overwriteResponse = await (0, import_prompts.default)({
type: "confirm",
name: "overwrite",
message: `Config file '${existingConfig}' already exists. Overwrite?`,
initial: false
});
const overwrite = getBooleanField(overwriteResponse, "overwrite") === true;
if (!overwrite) {
console.log("Initialization cancelled.");
return;
}
}
const { files, totalCount } = (0, import_openapi_core5.findSpecFiles)();
if (totalCount > 20) {
console.log(`Showing first 20 of ${totalCount} files found. Use manual entry to specify others.
`);
}
let inputPath;
if (files.length > 0) {
const choices = [
...files.map((f) => ({ title: `${f.path} (${f.size})`, value: f.path })),
{ title: "\u2192 Enter manually...", value: "__MANUAL__" }
];
const inputResponse = await (0, import_prompts.default)({
type: "select",
name: "input",
message: "Select OpenAPI spec file (YAML or JSON):",
choices
});
const selectedInput = getStringField(inputResponse, "input");
if (!selectedInput) {
console.log("\nInitialization cancelled.");
return;
}
if (selectedInput === "__MANUAL__") {
const manualResponse = await (0, import_prompts.default)({
type: "text",
name: "input",
message: "Input OpenAPI file path (YAML or JSON):",
initial: "openapi.{yaml,yml,json}",
validate: (value) => {
if (value.length === 0) return "Input path is required";
if (!(0, import_node_fs2.existsSync)(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
return true;
}
});
const manualInput = getStringField(manualResponse, "input");
if (!manualInput) {
console.log("\nInitialization cancelled.");
return;
}
inputPath = manualInput;
} else {
inputPath = selectedInput;
}
} else {
const manualResponse = await (0, import_prompts.default)({
type: "text",
name: "input",
message: "Input OpenAPI file path (YAML or JSON):",
initial: "openapi.{yaml,yml,json}",
validate: (value) => {
if (value.length === 0) return "Input path is required";
if (!(0, import_node_fs2.existsSync)(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
return true;
}
});
const manualInput = getStringField(manualResponse, "input");
if (!manualInput) {
console.log("\nInitialization cancelled.");
return;
}
inputPath = manualInput;
}
const response = await (0, import_prompts.default)([
{
type: "text",
name: "output",
message: "Output TypeScript file path:",
initial: "src/types.ts",
validate: (value) => value.length > 0 || "Output path is required"
},
{
type: "select",
name: "enumFormat",
message: "Enum format:",
choices: [
{ title: "Union type (e.g., 'a' | 'b')", value: "union" },
{ title: "TypeScript enum", value: "enum" },
{ title: "Const object", value: "const-object" }
],
initial: 0
},
{
type: "select",
name: "format",
message: "Config file format:",
choices: [
{ title: "TypeScript (recommended)", value: "ts" },
{ title: "JSON", value: "json" }
],
initial: 0
},
{
type: "confirm",
name: "includeDefaults",
message: "Include commonly-used recommended defaults?",
initial: true
}
]);
const output = getStringField(response, "output");
const enumFormat = (_a = getStringField(response, "enumFormat")) != null ? _a : "union";
const format = getStringField(response, "format");
const includeDefaults = (_b = getBooleanField(response, "includeDefaults")) != null ? _b : true;
if (!output || !format) {
console.log("\nInitialization cancelled.");
return;
}
const input = inputPath;
let configContent;
let configFilename;
if (format === "ts") {
configFilename = "openapi-to-typescript.config.ts";
if (includeDefaults) {
configContent = `import { defineConfig } from '@cerios/openapi-to-typescript';
export default defineConfig({
defaults: {
enumFormat: '${enumFormat}',
includeDescriptions: true,
showStats: true,
},
specs: [
{
input: '${input}',
outputTypes: '${output}',
},
],
});
`;
} else {
configContent = `import { defineConfig } from '@cerios/openapi-to-typescript';
export default defineConfig({
specs: [
{
input: '${input}',
outputTypes: '${output}',
enumFormat: '${enumFormat}',
},
],
});
`;
}
} else {
configFilename = "openapi-to-typescript.config.json";
const jsonConfig = {
specs: [
{
input,
outputTypes: output,
enumFormat
}
]
};
if (includeDefaults) {
jsonConfig.defaults = {
includeDescriptions: true,
showStats: true
};
}
configContent = `${JSON.stringify(jsonConfig, null, 2)}
`;
}
(0, import_node_fs2.writeFileSync)(configFilename, configContent, "utf-8");
console.log(`
\u2713 Created ${configFilename}`);
console.log("\nNext steps:");
console.log(" 1. Review and customize your config file if needed");
console.log(" 2. Run 'openapi-to-typescript' to generate types\n");
console.log(`${(0, import_openapi_core5.getRandomCeriosMessage)()}
`);
}
//# sourceMappingURL=cli.js.map

Sorry, the diff of this file is too big to display

#!/usr/bin/env node
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
import path from "path";
import { fileURLToPath } from "url";
var init_esm_shims = __esm({
"../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// src/generators/enum-generator.ts
import { applyFormatting, classifyEnumType, numericToEnumMember, stringToEnumMember } from "@cerios/openapi-core";
function generateEnum(name, values, options) {
const { format, prefix, suffix, nullable } = options;
const typeName = applyFormatting(name, prefix, suffix);
let result;
switch (format) {
case "enum":
result = generateTsEnum(typeName, values);
break;
case "union":
result = generateUnion(typeName, values);
break;
case "const-object":
result = generateConstObject(typeName, values);
break;
default:
result = generateUnion(typeName, values);
}
if (nullable) {
result.code = addNullableToTypeCode(result.code, typeName);
}
return result;
}
function addNullableToTypeCode(code, typeName) {
const unionPattern = new RegExp(`(export type ${typeName} = )([^;]+)(;)`);
if (unionPattern.test(code)) {
return code.replace(unionPattern, `$1$2 | null$3`);
}
const constObjectTypePattern = new RegExp(
`(export type ${typeName} = \\(typeof ${typeName}\\)\\[keyof typeof ${typeName}\\])(;)`
);
if (constObjectTypePattern.test(code)) {
return code.replace(constObjectTypePattern, `$1 | null$2`);
}
const enumPattern = /^export enum /;
if (enumPattern.test(code)) {
return `${code}
/** Nullable version of ${typeName} enum */
export type ${typeName}Nullable = ${typeName} | null;`;
}
return code;
}
function generateTsEnum(typeName, values) {
const enumType = classifyEnumType(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = stringToEnumMember(value, usedKeys);
return ` ${memberName} = "${value}"`;
}
if (typeof value === "number") {
const memberName = numericToEnumMember(value, usedKeys);
return ` ${memberName} = ${value}`;
}
return ` ${String(value)} = ${value}`;
});
const code = `export enum ${typeName} {
${members.join(",\n")},
}`;
return { code, typeName };
}
function generateUnion(typeName, values) {
const literals = values.map((value) => {
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "boolean") {
return String(value);
}
return String(value);
});
const code = `export type ${typeName} = ${literals.join(" | ")};`;
return { code, typeName };
}
function generateConstObject(typeName, values) {
const enumType = classifyEnumType(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = stringToEnumMember(value, usedKeys);
return ` ${memberName}: "${value}"`;
}
if (typeof value === "number") {
const memberName = numericToEnumMember(value, usedKeys);
return ` ${memberName}: ${value}`;
}
return ` ${String(value)}: ${value}`;
});
const objectCode = `export const ${typeName} = {
${members.join(",\n")},
} as const;`;
const typeCode = `export type ${typeName} = (typeof ${typeName})[keyof typeof ${typeName}];`;
const code = `${objectCode}
${typeCode}`;
return { code, typeName };
}
var init_enum_generator = __esm({
"src/generators/enum-generator.ts"() {
"use strict";
init_esm_shims();
}
});
// src/generators/type-generator.ts
import { applyFormatting as applyFormatting2 } from "@cerios/openapi-core";
function generateTypeDeclaration(name, properties, options) {
const { prefix, suffix } = options;
const typeName = applyFormatting2(name, prefix, suffix);
const code = `export type ${typeName} = {
${properties.join(";\n ")};
};`;
return { code, typeName };
}
function formatTypeProperty(name, type, required) {
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
const optional = required ? "" : "?";
return `${safeName}${optional}: ${type}`;
}
var init_type_generator = __esm({
"src/generators/type-generator.ts"() {
"use strict";
init_esm_shims();
}
});
// src/typescript-generator.ts
import { existsSync, mkdirSync, writeFileSync } from "fs";
import { dirname, normalize } from "path";
import {
applyFormatting as applyFormatting3,
ConfigurationError,
createFilterStatistics,
detectCircularReferences,
escapeJSDoc,
expandTransitiveReferences,
extractSchemaRefs,
formatFilterStatistics,
generateHeaderParamsTypeName,
generateInlineRequestTypeName,
generateInlineResponseTypeName,
generateQueryParamsTypeName,
getOperationName,
loadOpenAPISpec,
mergeParameters,
resolveRefName,
SchemaGenerationError,
SpecValidationError,
shouldIncludeOperation,
stripPathPrefix,
stripPrefix,
topologicalSortSchemas,
validateFilters
} from "@cerios/openapi-core";
function stripSchemaPrefix(name, prefixes) {
if (!prefixes) return name;
const prefixArray = Array.isArray(prefixes) ? prefixes : [prefixes];
let result = name;
for (const prefix of prefixArray) {
result = stripPrefix(result, prefix);
}
return result;
}
function isRecord(value) {
return typeof value === "object" && value !== null;
}
function getOperation(pathItem, method) {
if (!isRecord(pathItem)) {
return void 0;
}
const operation = pathItem[method];
return isRecord(operation) ? operation : void 0;
}
function getParameters(value) {
if (!isRecord(value)) {
return void 0;
}
const parameters = value.parameters;
return Array.isArray(parameters) ? parameters : void 0;
}
function getOperationId(operation) {
const operationId = operation.operationId;
return typeof operationId === "string" ? operationId : void 0;
}
function toParameterLike(value) {
if (!isRecord(value)) {
return void 0;
}
const param = {};
if (typeof value.in === "string") {
param.in = value.in;
}
if (typeof value.name === "string") {
param.name = value.name;
}
if (typeof value.description === "string") {
param.description = value.description;
}
if (typeof value.required === "boolean") {
param.required = value.required;
}
if (isRecord(value.schema)) {
param.schema = value.schema;
}
return param;
}
function getRequestBodyContent(operation) {
const requestBody = operation.requestBody;
if (!isRecord(requestBody)) {
return void 0;
}
const content = requestBody.content;
return isRecord(content) ? content : void 0;
}
function getResponseContent(response) {
if (!isRecord(response)) {
return void 0;
}
const content = response.content;
return isRecord(content) ? content : void 0;
}
function getMediaSchema(mediaType) {
if (!isRecord(mediaType)) {
return void 0;
}
const schema = mediaType.schema;
return isRecord(schema) ? schema : void 0;
}
var HTTP_METHODS, TypeScriptGenerator;
var init_typescript_generator = __esm({
"src/typescript-generator.ts"() {
"use strict";
init_esm_shims();
init_enum_generator();
init_type_generator();
HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
TypeScriptGenerator = class {
constructor(options) {
this.generatedTypes = /* @__PURE__ */ new Map();
this.schemaDependencies = /* @__PURE__ */ new Map();
this.circularSchemas = /* @__PURE__ */ new Set();
this.schemaUsageMap = /* @__PURE__ */ new Map();
this.filterStats = createFilterStatistics();
var _a, _b, _c, _d, _e, _f;
if (!options.input) {
throw new ConfigurationError("Input path is required", { providedOptions: options });
}
this.options = {
input: options.input,
outputTypes: options.outputTypes,
enumFormat: (_a = options.enumFormat) != null ? _a : "const-object",
includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
prefix: options.prefix,
suffix: options.suffix,
stripSchemaPrefix: options.stripSchemaPrefix,
stripPathPrefix: options.stripPathPrefix,
useOperationId: (_d = options.useOperationId) != null ? _d : true,
operationFilters: options.operationFilters,
showStats: (_e = options.showStats) != null ? _e : true,
batchSize: (_f = options.batchSize) != null ? _f : 10
};
this.spec = loadOpenAPISpec(this.options.input);
this.validateSpec();
this.circularSchemas = detectCircularReferences(this.spec);
if (this.options.operationFilters) {
this.initializeSchemaUsage();
}
}
/**
* Validate the OpenAPI specification
*/
validateSpec() {
var _a, _b;
if (!this.spec) {
throw new SpecValidationError("Empty or invalid OpenAPI specification", {
filePath: this.options.input
});
}
if (!((_a = this.spec.openapi) == null ? void 0 : _a.startsWith("3."))) {
throw new SpecValidationError("Only OpenAPI 3.x specifications are supported", {
filePath: this.options.input,
version: this.spec.openapi
});
}
if (!((_b = this.spec.components) == null ? void 0 : _b.schemas)) {
throw new SpecValidationError("No schemas found in OpenAPI spec", {
filePath: this.options.input
});
}
}
/**
* Initialize schema usage map with operation filtering
*/
initializeSchemaUsage() {
if (!this.options.operationFilters || !this.spec.paths) {
return;
}
const requestSchemas = /* @__PURE__ */ new Set();
const responseSchemas = /* @__PURE__ */ new Set();
for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
this.filterStats.totalOperations++;
if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters, this.filterStats)) {
continue;
}
this.filterStats.includedOperations++;
const op = operation;
if (op.requestBody && isRecord(op.requestBody)) {
const reqBody = op.requestBody;
if (isRecord(reqBody.content)) {
for (const mediaType of Object.values(reqBody.content)) {
if (isRecord(mediaType) && mediaType.schema) {
extractSchemaRefs(mediaType.schema, requestSchemas);
}
}
}
}
if (isRecord(op.responses)) {
for (const response of Object.values(op.responses)) {
if (isRecord(response) && isRecord(response.content)) {
for (const mediaType of Object.values(response.content)) {
if (isRecord(mediaType) && mediaType.schema) {
extractSchemaRefs(mediaType.schema, responseSchemas);
}
}
}
}
}
if (op.parameters && Array.isArray(op.parameters)) {
for (const param of op.parameters) {
if (isRecord(param) && param.schema) {
extractSchemaRefs(param.schema, requestSchemas);
}
}
}
}
}
expandTransitiveReferences(requestSchemas, this.spec);
expandTransitiveReferences(responseSchemas, this.spec);
for (const name of requestSchemas) {
const inResponse = responseSchemas.has(name);
this.schemaUsageMap.set(name, inResponse ? "both" : "request");
}
for (const name of responseSchemas) {
if (!this.schemaUsageMap.has(name)) {
this.schemaUsageMap.set(name, "response");
}
}
}
/**
* Check if a schema should be included based on filters
*/
shouldIncludeSchema(name) {
if (this.options.operationFilters && this.schemaUsageMap.size > 0) {
return this.schemaUsageMap.has(name);
}
return true;
}
/**
* Generate TypeScript type for a schema
*/
generateSchemaType(name, schema) {
var _a, _b;
const strippedName = stripSchemaPrefix(name, this.options.stripSchemaPrefix);
const typeName = applyFormatting3(strippedName, this.options.prefix, this.options.suffix);
if (!this.schemaDependencies.has(name)) {
this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
}
const deps = (_a = this.schemaDependencies.get(name)) != null ? _a : /* @__PURE__ */ new Set();
if (schema.enum && Array.isArray(schema.enum)) {
const { code: code2 } = generateEnum(strippedName, schema.enum, {
format: (_b = this.options.enumFormat) != null ? _b : "union",
prefix: this.options.prefix,
suffix: this.options.suffix,
nullable: schema.nullable === true
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = [];
for (const subSchema of schema.allOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else if (subSchema.properties) {
const props = this.generateProperties(name, subSchema, deps);
parts.push(`{ ${props.join("; ")} }`);
}
}
const typeStr2 = parts.join(" & ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = [];
for (const subSchema of schema.oneOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = [];
for (const subSchema of schema.anyOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "object" || schema.properties) {
const properties = this.generateProperties(name, schema, deps);
const { code: code2 } = generateTypeDeclaration(strippedName, properties, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
let arrayType = `${itemType}[]`;
if (schema.nullable) {
arrayType = `${arrayType} | null`;
}
const code2 = `export type ${typeName} = ${arrayType};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
let typeStr = this.primitiveToTypeString(schema);
if (schema.nullable) {
typeStr = `${typeStr} | null`;
}
const code = `export type ${typeName} = ${typeStr};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code}`;
}
return code;
}
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
generatePropertyJSDoc(schema) {
const hasDescription = this.options.includeDescriptions && schema.description;
const hasDeprecated = schema.deprecated === true;
if (!hasDescription && !hasDeprecated) {
return "";
}
const parts = [];
if (hasDescription && schema.description) {
parts.push(escapeJSDoc(schema.description));
}
if (hasDeprecated) {
parts.push("@deprecated");
}
return `/** ${parts.join(" ")} */
`;
}
/**
* Generate properties for an object schema
*/
generateProperties(_schemaName, schema, deps) {
const properties = [];
if (!schema.properties) {
return properties;
}
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
const prop = formatTypeProperty(propName, typeStr, isRequired);
const jsdoc = this.generatePropertyJSDoc(propSchema);
properties.push(jsdoc ? `${jsdoc} ${prop}` : prop);
}
return properties;
}
/**
* Convert a schema to a TypeScript type string
*/
schemaToTypeString(schema, deps) {
if (Array.isArray(schema.type)) {
const types = schema.type;
const hasNull = types.includes("null");
const nonNullTypes = types.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
return "null";
}
const typeStrings = nonNullTypes.map((t) => {
const tempSchema = { ...schema, type: t };
return this.schemaToTypeString(tempSchema, deps);
});
const baseType = typeStrings.join(" | ");
return hasNull ? `${baseType} | null` : baseType;
}
if (schema.$ref) {
const refName = resolveRefName(schema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const typeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
if (schema.nullable) {
return `${typeName} | null`;
}
return typeName;
}
if (schema.enum && Array.isArray(schema.enum)) {
const enumType = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
if (schema.nullable) {
return `${enumType} | null`;
}
return enumType;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
const arrayType = `${itemType}[]`;
if (schema.nullable) {
return `${arrayType} | null`;
}
return arrayType;
}
if (schema.type === "object" || schema.properties) {
let objectType;
if (schema.properties) {
const props = [];
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
props.push(formatTypeProperty(propName, typeStr, isRequired));
}
objectType = `{ ${props.join("; ")} }`;
} else if (schema.additionalProperties) {
if (typeof schema.additionalProperties === "boolean") {
objectType = "Record<string, unknown>";
} else {
const valueType = this.schemaToTypeString(schema.additionalProperties, deps);
objectType = `Record<string, ${valueType}>`;
}
} else {
objectType = "Record<string, unknown>";
}
if (schema.nullable) {
return `${objectType} | null`;
}
return objectType;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = schema.allOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" & ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = schema.oneOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = schema.anyOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
const primitiveType = this.primitiveToTypeString(schema);
if (schema.nullable) {
return `${primitiveType} | null`;
}
return primitiveType;
}
/**
* Convert a primitive schema to a TypeScript type string
*/
primitiveToTypeString(schema) {
if (schema.type === "string") {
return "string";
}
if (schema.type === "integer" || schema.type === "number") {
return "number";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "null") {
return "null";
}
return "unknown";
}
/**
* Generate statistics
*/
generateStats() {
const stats = [];
stats.push("/**");
stats.push(" * TypeScript Types Generation Statistics");
stats.push(` * Generated from: ${this.options.input}`);
stats.push(` * Total schemas: ${this.generatedTypes.size}`);
if (this.circularSchemas.size > 0) {
stats.push(` * Circular references detected: ${Array.from(this.circularSchemas).join(", ")}`);
}
if (this.options.operationFilters) {
const filterStatsStr = formatFilterStatistics(this.filterStats);
if (filterStatsStr) {
stats.push(...filterStatsStr.split("\n").map((s) => ` * ${s}`));
}
}
stats.push(" */");
return stats;
}
/**
* Topologically sort schemas
*/
topologicalSort() {
return topologicalSortSchemas(this.schemaDependencies, this.circularSchemas);
}
/**
* Generate query parameter types for each operation
*/
generateQueryParamTypes() {
if (!this.spec.paths) return;
for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
continue;
}
const allParams = mergeParameters(getParameters(pathItem), getParameters(operation), this.spec);
const queryParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "query");
if (queryParams.length === 0) continue;
const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateQueryParamsTypeName(operationName);
const props = [];
for (const param of queryParams) {
const paramSchema = param.schema;
if (!paramSchema || !param.name) continue;
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(paramSchema, deps);
const isRequired = param.required === true;
let propDef = formatTypeProperty(param.name, typeStr, isRequired);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path2}`;
const jsdoc = `/**
* Query parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`QueryParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate header parameter types for each operation
*/
generateHeaderParamTypes() {
if (!this.spec.paths) return;
for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
continue;
}
const allParams = mergeParameters(getParameters(pathItem), getParameters(operation), this.spec);
const headerParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "header");
if (headerParams.length === 0) continue;
const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateHeaderParamsTypeName(operationName);
const props = [];
for (const param of headerParams) {
if (!param.name) continue;
let propDef = formatTypeProperty(param.name, "string", false);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path2}`;
const jsdoc = `/**
* Header parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`HeaderParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate inline request body types for each operation
*/
generateInlineRequestTypes() {
if (!this.spec.paths) return;
for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
continue;
}
const requestBodyContent = getRequestBodyContent(operation);
if (!requestBodyContent) continue;
const contentTypes = Object.keys(requestBodyContent);
const hasMultipleContentTypes = contentTypes.length > 1;
for (const [contentType, mediaType] of Object.entries(requestBodyContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateInlineRequestTypeName(operationName, contentType, hasMultipleContentTypes);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path2}`;
const jsdoc = `/**
* Request body for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`Request:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
/**
* Generate inline response types for each operation
*/
generateInlineResponseTypes() {
if (!this.spec.paths) return;
for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
continue;
}
const responsesValue = operation.responses;
if (!isRecord(responsesValue)) continue;
const responses = responsesValue;
const successStatusCodes = Object.keys(responses).filter((code) => {
const codeNum = Number.parseInt(code, 10);
return codeNum >= 200 && codeNum < 300;
});
const hasMultipleStatuses = successStatusCodes.length > 1;
for (const [statusCode, response] of Object.entries(responses)) {
const responseContent = getResponseContent(response);
if (!responseContent) continue;
for (const [_contentType, mediaType] of Object.entries(responseContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateInlineResponseTypeName(operationName, statusCode, hasMultipleStatuses);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path2}`;
const jsdoc = `/**
* Response for ${jsdocOperationName} (${statusCode})
*/
`;
this.generatedTypes.set(`Response:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
}
/**
* Generate schemas as a string (without writing to file)
*/
generateString() {
var _a;
if ((_a = this.spec.components) == null ? void 0 : _a.schemas) {
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
if (!this.shouldIncludeSchema(name)) {
continue;
}
try {
const typeCode = this.generateSchemaType(name, schema);
this.generatedTypes.set(name, typeCode);
} catch (error) {
throw new SchemaGenerationError(`Failed to generate TypeScript type for schema: ${name}`, name, {
cause: error instanceof Error ? error.message : String(error)
});
}
}
}
this.generateQueryParamTypes();
this.generateHeaderParamTypes();
this.generateInlineRequestTypes();
this.generateInlineResponseTypes();
validateFilters(this.filterStats, this.options.operationFilters);
const orderedSchemaNames = this.topologicalSort();
const output = [
"// Auto-generated by @cerios/openapi-to-typescript",
"// Do not edit this file manually",
""
];
if (this.options.showStats === true) {
output.push(...this.generateStats());
output.push("");
}
for (const name of orderedSchemaNames) {
const typeCode = this.generatedTypes.get(name);
if (typeCode) {
output.push(typeCode);
output.push("");
}
}
const inlineTypes = [];
for (const [key, typeCode] of this.generatedTypes.entries()) {
if (key.startsWith("QueryParams:") || key.startsWith("HeaderParams:") || key.startsWith("Request:") || key.startsWith("Response:")) {
inlineTypes.push(typeCode);
}
}
if (inlineTypes.length > 0) {
output.push("// Operation Types (Query Params, Headers, Request Bodies, Responses)");
output.push("");
for (const typeCode of inlineTypes) {
output.push(typeCode);
output.push("");
}
}
return output.join("\n");
}
/**
* Ensure directory exists for a file path
*/
ensureDirectoryExists(filePath) {
const normalizedPath = normalize(filePath);
const dir = dirname(normalizedPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
/**
* Generate and write to file
*/
generate() {
if (!this.options.outputTypes) {
throw new ConfigurationError("Output path is required for generate()", {
suggestion: "Use generateString() to get output without writing to file"
});
}
const output = this.generateString();
const normalizedOutput = normalize(this.options.outputTypes);
this.ensureDirectoryExists(normalizedOutput);
writeFileSync(normalizedOutput, output);
console.log(` \u2713 Generated ${normalizedOutput}`);
}
};
}
});
// src/utils/config-loader.ts
import {
BaseDefaultsSchema,
BaseGeneratorOptionsSchema,
mergeCliWithConfig as coreMergeCliWithConfig,
createConfigLoader,
ExecutionModeSchema
} from "@cerios/openapi-core";
import { z } from "zod";
function mergeConfigWithDefaults(config) {
if (!(config == null ? void 0 : config.specs) || !Array.isArray(config.specs)) {
throw new Error("Invalid config: specs array is required");
}
const defaults = config.defaults || {};
return config.specs.map((spec) => {
const merged = {
// Apply defaults first
enumFormat: defaults.enumFormat,
includeDescriptions: defaults.includeDescriptions,
defaultNullable: defaults.defaultNullable,
useOperationId: defaults.useOperationId,
prefix: defaults.prefix,
suffix: defaults.suffix,
stripSchemaPrefix: defaults.stripSchemaPrefix,
stripPathPrefix: defaults.stripPathPrefix,
operationFilters: defaults.operationFilters,
showStats: defaults.showStats,
batchSize: defaults.batchSize,
// Override with spec-specific values (including required input/output)
...spec
};
return merged;
});
}
var TypeScriptSpecificOptionsSchema, TypeScriptGeneratorOptionsSchema, TypeScriptDefaultsSchema, ConfigFileSchema, errorMessages, configLoader, loadConfig;
var init_config_loader = __esm({
"src/utils/config-loader.ts"() {
"use strict";
init_esm_shims();
TypeScriptSpecificOptionsSchema = z.strictObject({
enumFormat: z.enum(["enum", "union", "const-object"]).optional(),
includeSchemas: z.array(z.string()).optional(),
excludeSchemas: z.array(z.string()).optional()
});
TypeScriptGeneratorOptionsSchema = BaseGeneratorOptionsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape,
outputTypes: z.string()
// Make outputTypes required for TypeScript generator
});
TypeScriptDefaultsSchema = BaseDefaultsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape
});
ConfigFileSchema = z.strictObject({
defaults: TypeScriptDefaultsSchema.optional(),
specs: z.array(TypeScriptGeneratorOptionsSchema).min(1, {
message: "Configuration must include at least one specification. Each specification should have 'input' and 'outputTypes' paths."
}),
executionMode: ExecutionModeSchema.optional()
});
errorMessages = {
missingFieldMessages: {
input: "Each spec must specify the path to your OpenAPI specification file.",
outputTypes: "Each spec must specify an output file path for generated TypeScript types."
},
unrecognizedKeyMessages: {
output: "Did you mean 'outputTypes'? The 'output' field was renamed to 'outputTypes'."
},
requiredFieldsHelp: "All required fields are present (specs array with input/outputTypes)"
};
configLoader = createConfigLoader(
{
packageName: "openapi-to-typescript",
errorMessages
},
ConfigFileSchema
);
loadConfig = configLoader.loadConfig;
}
});
// src/cli.ts
import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
import {
CliOptionsError,
ConfigValidationError,
executeBatch,
findSpecFiles,
getBatchExitCode,
getRandomCeriosMessage
} from "@cerios/openapi-core";
import { Command } from "commander";
import prompts from "prompts";
var require_cli = __commonJS({
"src/cli.ts"() {
init_esm_shims();
init_typescript_generator();
init_config_loader();
var program = new Command();
function isRecord2(value) {
return typeof value === "object" && value !== null;
}
function getStringField(value, field) {
if (!isRecord2(value)) {
return void 0;
}
const fieldValue = value[field];
return typeof fieldValue === "string" ? fieldValue : void 0;
}
function getBooleanField(value, field) {
if (!isRecord2(value)) {
return void 0;
}
const fieldValue = value[field];
return typeof fieldValue === "boolean" ? fieldValue : void 0;
}
program.name("openapi-to-typescript").description("Generate TypeScript types from OpenAPI specifications").version("1.0.0").option("-c, --config <path>", "Path to config file (openapi-to-typescript.config.{ts,json})").addHelpText(
"after",
`
Examples:
# Create a new config file
$ openapi-to-typescript init
# Generate with auto-discovered config
$ openapi-to-typescript
# Generate with custom config path
$ openapi-to-typescript --config custom.config.ts
`
).action(async (options) => {
try {
await executeConfigMode(options);
} catch (error) {
if (error instanceof CliOptionsError || error instanceof ConfigValidationError) {
console.error(error.message);
process.exit(1);
}
console.error("Error:", error instanceof Error ? error.message : String(error));
if (error instanceof Error && error.stack) {
console.error("\nStack trace:", error.stack);
}
process.exit(1);
}
});
program.command("init").description("Initialize a new openapi-to-typescript configuration file").action(async () => {
try {
await initConfigFile();
} catch (error) {
console.error("Error:", error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
void program.parseAsync(process.argv);
async function executeConfigMode(options) {
var _a, _b;
const config = await loadConfig(options.config);
const specs = mergeConfigWithDefaults(config);
const executionMode = config.executionMode || "parallel";
const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;
const summary = await executeBatch(specs, executionMode, (spec) => new TypeScriptGenerator(spec), batchSize);
const exitCode = getBatchExitCode(summary);
if (exitCode !== 0) {
process.exit(exitCode);
}
}
async function initConfigFile() {
var _a, _b;
console.log("Welcome to openapi-to-typescript configuration setup!\n");
const configFiles = ["openapi-to-typescript.config.ts", "openapi-to-typescript.config.json"];
const existingConfig = configFiles.find((f) => existsSync2(f));
if (existingConfig) {
const overwriteResponse = await prompts({
type: "confirm",
name: "overwrite",
message: `Config file '${existingConfig}' already exists. Overwrite?`,
initial: false
});
const overwrite = getBooleanField(overwriteResponse, "overwrite") === true;
if (!overwrite) {
console.log("Initialization cancelled.");
return;
}
}
const { files, totalCount } = findSpecFiles();
if (totalCount > 20) {
console.log(`Showing first 20 of ${totalCount} files found. Use manual entry to specify others.
`);
}
let inputPath;
if (files.length > 0) {
const choices = [
...files.map((f) => ({ title: `${f.path} (${f.size})`, value: f.path })),
{ title: "\u2192 Enter manually...", value: "__MANUAL__" }
];
const inputResponse = await prompts({
type: "select",
name: "input",
message: "Select OpenAPI spec file (YAML or JSON):",
choices
});
const selectedInput = getStringField(inputResponse, "input");
if (!selectedInput) {
console.log("\nInitialization cancelled.");
return;
}
if (selectedInput === "__MANUAL__") {
const manualResponse = await prompts({
type: "text",
name: "input",
message: "Input OpenAPI file path (YAML or JSON):",
initial: "openapi.{yaml,yml,json}",
validate: (value) => {
if (value.length === 0) return "Input path is required";
if (!existsSync2(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
return true;
}
});
const manualInput = getStringField(manualResponse, "input");
if (!manualInput) {
console.log("\nInitialization cancelled.");
return;
}
inputPath = manualInput;
} else {
inputPath = selectedInput;
}
} else {
const manualResponse = await prompts({
type: "text",
name: "input",
message: "Input OpenAPI file path (YAML or JSON):",
initial: "openapi.{yaml,yml,json}",
validate: (value) => {
if (value.length === 0) return "Input path is required";
if (!existsSync2(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
return true;
}
});
const manualInput = getStringField(manualResponse, "input");
if (!manualInput) {
console.log("\nInitialization cancelled.");
return;
}
inputPath = manualInput;
}
const response = await prompts([
{
type: "text",
name: "output",
message: "Output TypeScript file path:",
initial: "src/types.ts",
validate: (value) => value.length > 0 || "Output path is required"
},
{
type: "select",
name: "enumFormat",
message: "Enum format:",
choices: [
{ title: "Union type (e.g., 'a' | 'b')", value: "union" },
{ title: "TypeScript enum", value: "enum" },
{ title: "Const object", value: "const-object" }
],
initial: 0
},
{
type: "select",
name: "format",
message: "Config file format:",
choices: [
{ title: "TypeScript (recommended)", value: "ts" },
{ title: "JSON", value: "json" }
],
initial: 0
},
{
type: "confirm",
name: "includeDefaults",
message: "Include commonly-used recommended defaults?",
initial: true
}
]);
const output = getStringField(response, "output");
const enumFormat = (_a = getStringField(response, "enumFormat")) != null ? _a : "union";
const format = getStringField(response, "format");
const includeDefaults = (_b = getBooleanField(response, "includeDefaults")) != null ? _b : true;
if (!output || !format) {
console.log("\nInitialization cancelled.");
return;
}
const input = inputPath;
let configContent;
let configFilename;
if (format === "ts") {
configFilename = "openapi-to-typescript.config.ts";
if (includeDefaults) {
configContent = `import { defineConfig } from '@cerios/openapi-to-typescript';
export default defineConfig({
defaults: {
enumFormat: '${enumFormat}',
includeDescriptions: true,
showStats: true,
},
specs: [
{
input: '${input}',
outputTypes: '${output}',
},
],
});
`;
} else {
configContent = `import { defineConfig } from '@cerios/openapi-to-typescript';
export default defineConfig({
specs: [
{
input: '${input}',
outputTypes: '${output}',
enumFormat: '${enumFormat}',
},
],
});
`;
}
} else {
configFilename = "openapi-to-typescript.config.json";
const jsonConfig = {
specs: [
{
input,
outputTypes: output,
enumFormat
}
]
};
if (includeDefaults) {
jsonConfig.defaults = {
includeDescriptions: true,
showStats: true
};
}
configContent = `${JSON.stringify(jsonConfig, null, 2)}
`;
}
writeFileSync2(configFilename, configContent, "utf-8");
console.log(`
\u2713 Created ${configFilename}`);
console.log("\nNext steps:");
console.log(" 1. Review and customize your config file if needed");
console.log(" 2. Run 'openapi-to-typescript' to generate types\n");
console.log(`${getRandomCeriosMessage()}
`);
}
}
});
export default require_cli();
//# sourceMappingURL=cli.mjs.map

Sorry, the diff of this file is too big to display

import { OperationFilters, BaseGeneratorOptions, RequireExcept } from '@cerios/openapi-core';
export { BatchExecutionSummary, CircularReferenceError, CliOptionsError, ConfigValidationError, ConfigurationError, FileOperationError, Generator, GeneratorError, SchemaGenerationError, SpecValidationError, executeBatch, getBatchExitCode } from '@cerios/openapi-core';
import { z } from 'zod';
/**
* @cerios/openapi-to-typescript
*
* Types for TypeScript type generation from OpenAPI specifications
*/
/**
* Format for generating enums
* - 'enum': Generate TypeScript enums
* - 'union': Generate union of string literals (default)
* - 'const-object': Generate const object with derived type
*/
type EnumFormat = "enum" | "union" | "const-object";
/**
* Options for TypeScriptGenerator
*
* Extends BaseGeneratorOptions from @cerios/openapi-core with TypeScript-specific options
*/
interface TypeScriptGeneratorOptions extends BaseGeneratorOptions {
/**
* Format for generating enums
* @default 'const-object'
*/
enumFormat?: EnumFormat;
}
/**
* Resolved options with defaults applied
*/
interface ResolvedOptions extends RequireExcept<TypeScriptGeneratorOptions, "stripSchemaPrefix" | "stripPathPrefix" | "operationFilters" | "prefix" | "suffix" | "batchSize"> {
}
/**
* Execution mode for batch processing
*/
type ExecutionMode = "parallel" | "sequential";
/**
* Default options that can be shared across specs
*/
interface DefaultOptions {
enumFormat?: EnumFormat;
includeDescriptions?: boolean;
defaultNullable?: boolean;
prefix?: string;
suffix?: string;
stripSchemaPrefix?: string | string[];
stripPathPrefix?: string;
useOperationId?: boolean;
operationFilters?: OperationFilters;
showStats?: boolean;
batchSize?: number;
}
/**
* Spec options that can also include a name
*/
interface SpecOptions extends TypeScriptGeneratorOptions {
/**
* Optional name for this spec (for logging)
*/
name?: string;
}
/**
* Configuration file schema
*/
interface ConfigFile {
/**
* Default options applied to all specs
*/
defaults?: DefaultOptions;
/**
* Array of specifications to process
*/
specs: SpecOptions[];
/**
* Execution mode for batch processing
* @default 'parallel'
*/
executionMode?: ExecutionMode;
}
/**
* Define a configuration file with type checking
*/
declare function defineConfig(config: ConfigFile): ConfigFile;
/**
* Enum generator for TypeScript types
*
* Generates TypeScript enums, union types, or const objects from OpenAPI enum schemas
*/
interface EnumGeneratorOptions {
format: EnumFormat;
prefix?: string;
suffix?: string;
nullable?: boolean;
}
interface EnumGeneratorResult {
code: string;
typeName: string;
}
/**
* Generate TypeScript code for an enum schema
*/
declare function generateEnum(name: string, values: (string | number | boolean)[], options: EnumGeneratorOptions): EnumGeneratorResult;
/**
* Type generator for TypeScript types
*
* Generates TypeScript type aliases from OpenAPI object schemas
*/
interface TypeGeneratorOptions {
prefix?: string;
suffix?: string;
}
interface TypeGeneratorResult {
code: string;
typeName: string;
}
/**
* Generate a TypeScript type declaration
*/
declare function generateTypeDeclaration(name: string, properties: string[], options: TypeGeneratorOptions): TypeGeneratorResult;
/**
* Format a property for use in an interface or type
*/
declare function formatTypeProperty(name: string, type: string, required: boolean): string;
/**
* TypeScript Generator
*
* Main class for generating TypeScript types from OpenAPI specifications
*/
declare class TypeScriptGenerator {
private generatedTypes;
private schemaDependencies;
private circularSchemas;
private options;
private spec;
private schemaUsageMap;
private filterStats;
constructor(options: TypeScriptGeneratorOptions);
/**
* Validate the OpenAPI specification
*/
private validateSpec;
/**
* Initialize schema usage map with operation filtering
*/
private initializeSchemaUsage;
/**
* Check if a schema should be included based on filters
*/
private shouldIncludeSchema;
/**
* Generate TypeScript type for a schema
*/
private generateSchemaType;
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
private generatePropertyJSDoc;
/**
* Generate properties for an object schema
*/
private generateProperties;
/**
* Convert a schema to a TypeScript type string
*/
private schemaToTypeString;
/**
* Convert a primitive schema to a TypeScript type string
*/
private primitiveToTypeString;
/**
* Generate statistics
*/
private generateStats;
/**
* Topologically sort schemas
*/
private topologicalSort;
/**
* Generate query parameter types for each operation
*/
private generateQueryParamTypes;
/**
* Generate header parameter types for each operation
*/
private generateHeaderParamTypes;
/**
* Generate inline request body types for each operation
*/
private generateInlineRequestTypes;
/**
* Generate inline response types for each operation
*/
private generateInlineResponseTypes;
/**
* Generate schemas as a string (without writing to file)
*/
generateString(): string;
/**
* Ensure directory exists for a file path
*/
private ensureDirectoryExists;
/**
* Generate and write to file
*/
generate(): void;
}
/**
* Configuration loader for openapi-to-typescript
*
* Supports: openapi-to-typescript.config.{ts,json}, package.json under "openapi-to-typescript" key
*/
/**
* TypeScript-specific options schema
*/
declare const TypeScriptSpecificOptionsSchema: z.ZodObject<{
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Full generator options schema - base + TypeScript-specific
* Uses .extend() instead of deprecated .merge() for Zod v4 compatibility
*/
declare const TypeScriptGeneratorOptionsSchema: z.ZodObject<{
input: z.ZodString;
includeDescriptions: z.ZodOptional<z.ZodBoolean>;
defaultNullable: z.ZodOptional<z.ZodBoolean>;
stripSchemaPrefix: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
stripPathPrefix: z.ZodOptional<z.ZodString>;
useOperationId: z.ZodOptional<z.ZodBoolean>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
operationFilters: z.ZodOptional<z.ZodObject<{
includeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
includePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeDeprecated: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strict>>;
showStats: z.ZodOptional<z.ZodBoolean>;
cacheSize: z.ZodOptional<z.ZodNumber>;
batchSize: z.ZodOptional<z.ZodNumber>;
name: z.ZodOptional<z.ZodString>;
outputTypes: z.ZodString;
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Defaults schema - base defaults + TypeScript-specific options
* Uses .extend() instead of deprecated .merge() for Zod v4 compatibility
*/
declare const TypeScriptDefaultsSchema: z.ZodObject<{
includeDescriptions: z.ZodOptional<z.ZodBoolean>;
defaultNullable: z.ZodOptional<z.ZodBoolean>;
stripSchemaPrefix: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
stripPathPrefix: z.ZodOptional<z.ZodString>;
useOperationId: z.ZodOptional<z.ZodBoolean>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
operationFilters: z.ZodOptional<z.ZodObject<{
includeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
includePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeDeprecated: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strict>>;
showStats: z.ZodOptional<z.ZodBoolean>;
cacheSize: z.ZodOptional<z.ZodNumber>;
batchSize: z.ZodOptional<z.ZodNumber>;
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Load and validate configuration file
* Supports: openapi-to-typescript.config.{ts,json}, package.json under "openapi-to-typescript" key
*
* @param configPath - Optional explicit path to config file. If not provided, searches automatically
* @returns Validated ConfigFile object
* @throws Error if config file not found, invalid, or contains unknown properties
*/
declare const loadConfig: (configPath?: string) => Promise<ConfigFile>;
/**
* Merge global defaults with per-spec configuration
* CLI arguments have highest precedence and are merged separately in CLI layer
*
* @param config - Validated configuration file
* @returns Array of fully resolved TypeScriptGeneratorOptions objects
*/
declare function mergeConfigWithDefaults(config: ConfigFile): TypeScriptGeneratorOptions[];
/**
* Merge CLI options with config options
* CLI options have highest precedence and override both spec and default config
*
* @param specConfig - Configuration from config file (with defaults already applied)
* @param cliOptions - Options provided via CLI arguments
* @returns Merged TypeScriptGeneratorOptions with CLI taking precedence
*/
declare function mergeCliWithConfig(specConfig: TypeScriptGeneratorOptions, cliOptions: Partial<TypeScriptGeneratorOptions>): TypeScriptGeneratorOptions;
export { type ConfigFile, type DefaultOptions, type EnumFormat, type EnumGeneratorOptions, type EnumGeneratorResult, type ExecutionMode, type ResolvedOptions, type SpecOptions, type TypeGeneratorOptions, type TypeGeneratorResult, TypeScriptDefaultsSchema, TypeScriptGenerator, type TypeScriptGeneratorOptions, TypeScriptGeneratorOptionsSchema, TypeScriptSpecificOptionsSchema, defineConfig, formatTypeProperty, generateEnum, generateTypeDeclaration, loadConfig, mergeCliWithConfig, mergeConfigWithDefaults };
import { OperationFilters, BaseGeneratorOptions, RequireExcept } from '@cerios/openapi-core';
export { BatchExecutionSummary, CircularReferenceError, CliOptionsError, ConfigValidationError, ConfigurationError, FileOperationError, Generator, GeneratorError, SchemaGenerationError, SpecValidationError, executeBatch, getBatchExitCode } from '@cerios/openapi-core';
import { z } from 'zod';
/**
* @cerios/openapi-to-typescript
*
* Types for TypeScript type generation from OpenAPI specifications
*/
/**
* Format for generating enums
* - 'enum': Generate TypeScript enums
* - 'union': Generate union of string literals (default)
* - 'const-object': Generate const object with derived type
*/
type EnumFormat = "enum" | "union" | "const-object";
/**
* Options for TypeScriptGenerator
*
* Extends BaseGeneratorOptions from @cerios/openapi-core with TypeScript-specific options
*/
interface TypeScriptGeneratorOptions extends BaseGeneratorOptions {
/**
* Format for generating enums
* @default 'const-object'
*/
enumFormat?: EnumFormat;
}
/**
* Resolved options with defaults applied
*/
interface ResolvedOptions extends RequireExcept<TypeScriptGeneratorOptions, "stripSchemaPrefix" | "stripPathPrefix" | "operationFilters" | "prefix" | "suffix" | "batchSize"> {
}
/**
* Execution mode for batch processing
*/
type ExecutionMode = "parallel" | "sequential";
/**
* Default options that can be shared across specs
*/
interface DefaultOptions {
enumFormat?: EnumFormat;
includeDescriptions?: boolean;
defaultNullable?: boolean;
prefix?: string;
suffix?: string;
stripSchemaPrefix?: string | string[];
stripPathPrefix?: string;
useOperationId?: boolean;
operationFilters?: OperationFilters;
showStats?: boolean;
batchSize?: number;
}
/**
* Spec options that can also include a name
*/
interface SpecOptions extends TypeScriptGeneratorOptions {
/**
* Optional name for this spec (for logging)
*/
name?: string;
}
/**
* Configuration file schema
*/
interface ConfigFile {
/**
* Default options applied to all specs
*/
defaults?: DefaultOptions;
/**
* Array of specifications to process
*/
specs: SpecOptions[];
/**
* Execution mode for batch processing
* @default 'parallel'
*/
executionMode?: ExecutionMode;
}
/**
* Define a configuration file with type checking
*/
declare function defineConfig(config: ConfigFile): ConfigFile;
/**
* Enum generator for TypeScript types
*
* Generates TypeScript enums, union types, or const objects from OpenAPI enum schemas
*/
interface EnumGeneratorOptions {
format: EnumFormat;
prefix?: string;
suffix?: string;
nullable?: boolean;
}
interface EnumGeneratorResult {
code: string;
typeName: string;
}
/**
* Generate TypeScript code for an enum schema
*/
declare function generateEnum(name: string, values: (string | number | boolean)[], options: EnumGeneratorOptions): EnumGeneratorResult;
/**
* Type generator for TypeScript types
*
* Generates TypeScript type aliases from OpenAPI object schemas
*/
interface TypeGeneratorOptions {
prefix?: string;
suffix?: string;
}
interface TypeGeneratorResult {
code: string;
typeName: string;
}
/**
* Generate a TypeScript type declaration
*/
declare function generateTypeDeclaration(name: string, properties: string[], options: TypeGeneratorOptions): TypeGeneratorResult;
/**
* Format a property for use in an interface or type
*/
declare function formatTypeProperty(name: string, type: string, required: boolean): string;
/**
* TypeScript Generator
*
* Main class for generating TypeScript types from OpenAPI specifications
*/
declare class TypeScriptGenerator {
private generatedTypes;
private schemaDependencies;
private circularSchemas;
private options;
private spec;
private schemaUsageMap;
private filterStats;
constructor(options: TypeScriptGeneratorOptions);
/**
* Validate the OpenAPI specification
*/
private validateSpec;
/**
* Initialize schema usage map with operation filtering
*/
private initializeSchemaUsage;
/**
* Check if a schema should be included based on filters
*/
private shouldIncludeSchema;
/**
* Generate TypeScript type for a schema
*/
private generateSchemaType;
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
private generatePropertyJSDoc;
/**
* Generate properties for an object schema
*/
private generateProperties;
/**
* Convert a schema to a TypeScript type string
*/
private schemaToTypeString;
/**
* Convert a primitive schema to a TypeScript type string
*/
private primitiveToTypeString;
/**
* Generate statistics
*/
private generateStats;
/**
* Topologically sort schemas
*/
private topologicalSort;
/**
* Generate query parameter types for each operation
*/
private generateQueryParamTypes;
/**
* Generate header parameter types for each operation
*/
private generateHeaderParamTypes;
/**
* Generate inline request body types for each operation
*/
private generateInlineRequestTypes;
/**
* Generate inline response types for each operation
*/
private generateInlineResponseTypes;
/**
* Generate schemas as a string (without writing to file)
*/
generateString(): string;
/**
* Ensure directory exists for a file path
*/
private ensureDirectoryExists;
/**
* Generate and write to file
*/
generate(): void;
}
/**
* Configuration loader for openapi-to-typescript
*
* Supports: openapi-to-typescript.config.{ts,json}, package.json under "openapi-to-typescript" key
*/
/**
* TypeScript-specific options schema
*/
declare const TypeScriptSpecificOptionsSchema: z.ZodObject<{
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Full generator options schema - base + TypeScript-specific
* Uses .extend() instead of deprecated .merge() for Zod v4 compatibility
*/
declare const TypeScriptGeneratorOptionsSchema: z.ZodObject<{
input: z.ZodString;
includeDescriptions: z.ZodOptional<z.ZodBoolean>;
defaultNullable: z.ZodOptional<z.ZodBoolean>;
stripSchemaPrefix: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
stripPathPrefix: z.ZodOptional<z.ZodString>;
useOperationId: z.ZodOptional<z.ZodBoolean>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
operationFilters: z.ZodOptional<z.ZodObject<{
includeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
includePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeDeprecated: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strict>>;
showStats: z.ZodOptional<z.ZodBoolean>;
cacheSize: z.ZodOptional<z.ZodNumber>;
batchSize: z.ZodOptional<z.ZodNumber>;
name: z.ZodOptional<z.ZodString>;
outputTypes: z.ZodString;
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Defaults schema - base defaults + TypeScript-specific options
* Uses .extend() instead of deprecated .merge() for Zod v4 compatibility
*/
declare const TypeScriptDefaultsSchema: z.ZodObject<{
includeDescriptions: z.ZodOptional<z.ZodBoolean>;
defaultNullable: z.ZodOptional<z.ZodBoolean>;
stripSchemaPrefix: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
stripPathPrefix: z.ZodOptional<z.ZodString>;
useOperationId: z.ZodOptional<z.ZodBoolean>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
operationFilters: z.ZodOptional<z.ZodObject<{
includeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
includePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeMethods: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeOperationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeDeprecated: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strict>>;
showStats: z.ZodOptional<z.ZodBoolean>;
cacheSize: z.ZodOptional<z.ZodNumber>;
batchSize: z.ZodOptional<z.ZodNumber>;
enumFormat: z.ZodOptional<z.ZodEnum<{
union: "union";
enum: "enum";
"const-object": "const-object";
}>>;
includeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
excludeSchemas: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strict>;
/**
* Load and validate configuration file
* Supports: openapi-to-typescript.config.{ts,json}, package.json under "openapi-to-typescript" key
*
* @param configPath - Optional explicit path to config file. If not provided, searches automatically
* @returns Validated ConfigFile object
* @throws Error if config file not found, invalid, or contains unknown properties
*/
declare const loadConfig: (configPath?: string) => Promise<ConfigFile>;
/**
* Merge global defaults with per-spec configuration
* CLI arguments have highest precedence and are merged separately in CLI layer
*
* @param config - Validated configuration file
* @returns Array of fully resolved TypeScriptGeneratorOptions objects
*/
declare function mergeConfigWithDefaults(config: ConfigFile): TypeScriptGeneratorOptions[];
/**
* Merge CLI options with config options
* CLI options have highest precedence and override both spec and default config
*
* @param specConfig - Configuration from config file (with defaults already applied)
* @param cliOptions - Options provided via CLI arguments
* @returns Merged TypeScriptGeneratorOptions with CLI taking precedence
*/
declare function mergeCliWithConfig(specConfig: TypeScriptGeneratorOptions, cliOptions: Partial<TypeScriptGeneratorOptions>): TypeScriptGeneratorOptions;
export { type ConfigFile, type DefaultOptions, type EnumFormat, type EnumGeneratorOptions, type EnumGeneratorResult, type ExecutionMode, type ResolvedOptions, type SpecOptions, type TypeGeneratorOptions, type TypeGeneratorResult, TypeScriptDefaultsSchema, TypeScriptGenerator, type TypeScriptGeneratorOptions, TypeScriptGeneratorOptionsSchema, TypeScriptSpecificOptionsSchema, defineConfig, formatTypeProperty, generateEnum, generateTypeDeclaration, loadConfig, mergeCliWithConfig, mergeConfigWithDefaults };
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
CircularReferenceError: () => import_openapi_core5.CircularReferenceError,
CliOptionsError: () => import_openapi_core5.CliOptionsError,
ConfigValidationError: () => import_openapi_core5.ConfigValidationError,
ConfigurationError: () => import_openapi_core5.ConfigurationError,
FileOperationError: () => import_openapi_core5.FileOperationError,
GeneratorError: () => import_openapi_core5.GeneratorError,
SchemaGenerationError: () => import_openapi_core5.SchemaGenerationError,
SpecValidationError: () => import_openapi_core5.SpecValidationError,
TypeScriptDefaultsSchema: () => TypeScriptDefaultsSchema,
TypeScriptGenerator: () => TypeScriptGenerator,
TypeScriptGeneratorOptionsSchema: () => TypeScriptGeneratorOptionsSchema,
TypeScriptSpecificOptionsSchema: () => TypeScriptSpecificOptionsSchema,
defineConfig: () => defineConfig,
executeBatch: () => import_openapi_core5.executeBatch,
formatTypeProperty: () => formatTypeProperty,
generateEnum: () => generateEnum,
generateTypeDeclaration: () => generateTypeDeclaration,
getBatchExitCode: () => import_openapi_core5.getBatchExitCode,
loadConfig: () => loadConfig,
mergeCliWithConfig: () => mergeCliWithConfig,
mergeConfigWithDefaults: () => mergeConfigWithDefaults
});
module.exports = __toCommonJS(src_exports);
var import_openapi_core5 = require("@cerios/openapi-core");
// src/generators/enum-generator.ts
var import_openapi_core = require("@cerios/openapi-core");
function generateEnum(name, values, options) {
const { format, prefix, suffix, nullable } = options;
const typeName = (0, import_openapi_core.applyFormatting)(name, prefix, suffix);
let result;
switch (format) {
case "enum":
result = generateTsEnum(typeName, values);
break;
case "union":
result = generateUnion(typeName, values);
break;
case "const-object":
result = generateConstObject(typeName, values);
break;
default:
result = generateUnion(typeName, values);
}
if (nullable) {
result.code = addNullableToTypeCode(result.code, typeName);
}
return result;
}
function addNullableToTypeCode(code, typeName) {
const unionPattern = new RegExp(`(export type ${typeName} = )([^;]+)(;)`);
if (unionPattern.test(code)) {
return code.replace(unionPattern, `$1$2 | null$3`);
}
const constObjectTypePattern = new RegExp(
`(export type ${typeName} = \\(typeof ${typeName}\\)\\[keyof typeof ${typeName}\\])(;)`
);
if (constObjectTypePattern.test(code)) {
return code.replace(constObjectTypePattern, `$1 | null$2`);
}
const enumPattern = /^export enum /;
if (enumPattern.test(code)) {
return `${code}
/** Nullable version of ${typeName} enum */
export type ${typeName}Nullable = ${typeName} | null;`;
}
return code;
}
function generateTsEnum(typeName, values) {
const enumType = (0, import_openapi_core.classifyEnumType)(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = (0, import_openapi_core.stringToEnumMember)(value, usedKeys);
return ` ${memberName} = "${value}"`;
}
if (typeof value === "number") {
const memberName = (0, import_openapi_core.numericToEnumMember)(value, usedKeys);
return ` ${memberName} = ${value}`;
}
return ` ${String(value)} = ${value}`;
});
const code = `export enum ${typeName} {
${members.join(",\n")},
}`;
return { code, typeName };
}
function generateUnion(typeName, values) {
const literals = values.map((value) => {
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "boolean") {
return String(value);
}
return String(value);
});
const code = `export type ${typeName} = ${literals.join(" | ")};`;
return { code, typeName };
}
function generateConstObject(typeName, values) {
const enumType = (0, import_openapi_core.classifyEnumType)(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = (0, import_openapi_core.stringToEnumMember)(value, usedKeys);
return ` ${memberName}: "${value}"`;
}
if (typeof value === "number") {
const memberName = (0, import_openapi_core.numericToEnumMember)(value, usedKeys);
return ` ${memberName}: ${value}`;
}
return ` ${String(value)}: ${value}`;
});
const objectCode = `export const ${typeName} = {
${members.join(",\n")},
} as const;`;
const typeCode = `export type ${typeName} = (typeof ${typeName})[keyof typeof ${typeName}];`;
const code = `${objectCode}
${typeCode}`;
return { code, typeName };
}
// src/generators/type-generator.ts
var import_openapi_core2 = require("@cerios/openapi-core");
function generateTypeDeclaration(name, properties, options) {
const { prefix, suffix } = options;
const typeName = (0, import_openapi_core2.applyFormatting)(name, prefix, suffix);
const code = `export type ${typeName} = {
${properties.join(";\n ")};
};`;
return { code, typeName };
}
function formatTypeProperty(name, type, required) {
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
const optional = required ? "" : "?";
return `${safeName}${optional}: ${type}`;
}
// src/types.ts
function defineConfig(config) {
return config;
}
// src/typescript-generator.ts
var import_node_fs = require("fs");
var import_node_path = require("path");
var import_openapi_core3 = require("@cerios/openapi-core");
function stripSchemaPrefix(name, prefixes) {
if (!prefixes) return name;
const prefixArray = Array.isArray(prefixes) ? prefixes : [prefixes];
let result = name;
for (const prefix of prefixArray) {
result = (0, import_openapi_core3.stripPrefix)(result, prefix);
}
return result;
}
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
function isRecord(value) {
return typeof value === "object" && value !== null;
}
function getOperation(pathItem, method) {
if (!isRecord(pathItem)) {
return void 0;
}
const operation = pathItem[method];
return isRecord(operation) ? operation : void 0;
}
function getParameters(value) {
if (!isRecord(value)) {
return void 0;
}
const parameters = value.parameters;
return Array.isArray(parameters) ? parameters : void 0;
}
function getOperationId(operation) {
const operationId = operation.operationId;
return typeof operationId === "string" ? operationId : void 0;
}
function toParameterLike(value) {
if (!isRecord(value)) {
return void 0;
}
const param = {};
if (typeof value.in === "string") {
param.in = value.in;
}
if (typeof value.name === "string") {
param.name = value.name;
}
if (typeof value.description === "string") {
param.description = value.description;
}
if (typeof value.required === "boolean") {
param.required = value.required;
}
if (isRecord(value.schema)) {
param.schema = value.schema;
}
return param;
}
function getRequestBodyContent(operation) {
const requestBody = operation.requestBody;
if (!isRecord(requestBody)) {
return void 0;
}
const content = requestBody.content;
return isRecord(content) ? content : void 0;
}
function getResponseContent(response) {
if (!isRecord(response)) {
return void 0;
}
const content = response.content;
return isRecord(content) ? content : void 0;
}
function getMediaSchema(mediaType) {
if (!isRecord(mediaType)) {
return void 0;
}
const schema = mediaType.schema;
return isRecord(schema) ? schema : void 0;
}
var TypeScriptGenerator = class {
constructor(options) {
this.generatedTypes = /* @__PURE__ */ new Map();
this.schemaDependencies = /* @__PURE__ */ new Map();
this.circularSchemas = /* @__PURE__ */ new Set();
this.schemaUsageMap = /* @__PURE__ */ new Map();
this.filterStats = (0, import_openapi_core3.createFilterStatistics)();
var _a, _b, _c, _d, _e, _f;
if (!options.input) {
throw new import_openapi_core3.ConfigurationError("Input path is required", { providedOptions: options });
}
this.options = {
input: options.input,
outputTypes: options.outputTypes,
enumFormat: (_a = options.enumFormat) != null ? _a : "const-object",
includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
prefix: options.prefix,
suffix: options.suffix,
stripSchemaPrefix: options.stripSchemaPrefix,
stripPathPrefix: options.stripPathPrefix,
useOperationId: (_d = options.useOperationId) != null ? _d : true,
operationFilters: options.operationFilters,
showStats: (_e = options.showStats) != null ? _e : true,
batchSize: (_f = options.batchSize) != null ? _f : 10
};
this.spec = (0, import_openapi_core3.loadOpenAPISpec)(this.options.input);
this.validateSpec();
this.circularSchemas = (0, import_openapi_core3.detectCircularReferences)(this.spec);
if (this.options.operationFilters) {
this.initializeSchemaUsage();
}
}
/**
* Validate the OpenAPI specification
*/
validateSpec() {
var _a, _b;
if (!this.spec) {
throw new import_openapi_core3.SpecValidationError("Empty or invalid OpenAPI specification", {
filePath: this.options.input
});
}
if (!((_a = this.spec.openapi) == null ? void 0 : _a.startsWith("3."))) {
throw new import_openapi_core3.SpecValidationError("Only OpenAPI 3.x specifications are supported", {
filePath: this.options.input,
version: this.spec.openapi
});
}
if (!((_b = this.spec.components) == null ? void 0 : _b.schemas)) {
throw new import_openapi_core3.SpecValidationError("No schemas found in OpenAPI spec", {
filePath: this.options.input
});
}
}
/**
* Initialize schema usage map with operation filtering
*/
initializeSchemaUsage() {
if (!this.options.operationFilters || !this.spec.paths) {
return;
}
const requestSchemas = /* @__PURE__ */ new Set();
const responseSchemas = /* @__PURE__ */ new Set();
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
this.filterStats.totalOperations++;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters, this.filterStats)) {
continue;
}
this.filterStats.includedOperations++;
const op = operation;
if (op.requestBody && isRecord(op.requestBody)) {
const reqBody = op.requestBody;
if (isRecord(reqBody.content)) {
for (const mediaType of Object.values(reqBody.content)) {
if (isRecord(mediaType) && mediaType.schema) {
(0, import_openapi_core3.extractSchemaRefs)(mediaType.schema, requestSchemas);
}
}
}
}
if (isRecord(op.responses)) {
for (const response of Object.values(op.responses)) {
if (isRecord(response) && isRecord(response.content)) {
for (const mediaType of Object.values(response.content)) {
if (isRecord(mediaType) && mediaType.schema) {
(0, import_openapi_core3.extractSchemaRefs)(mediaType.schema, responseSchemas);
}
}
}
}
}
if (op.parameters && Array.isArray(op.parameters)) {
for (const param of op.parameters) {
if (isRecord(param) && param.schema) {
(0, import_openapi_core3.extractSchemaRefs)(param.schema, requestSchemas);
}
}
}
}
}
(0, import_openapi_core3.expandTransitiveReferences)(requestSchemas, this.spec);
(0, import_openapi_core3.expandTransitiveReferences)(responseSchemas, this.spec);
for (const name of requestSchemas) {
const inResponse = responseSchemas.has(name);
this.schemaUsageMap.set(name, inResponse ? "both" : "request");
}
for (const name of responseSchemas) {
if (!this.schemaUsageMap.has(name)) {
this.schemaUsageMap.set(name, "response");
}
}
}
/**
* Check if a schema should be included based on filters
*/
shouldIncludeSchema(name) {
if (this.options.operationFilters && this.schemaUsageMap.size > 0) {
return this.schemaUsageMap.has(name);
}
return true;
}
/**
* Generate TypeScript type for a schema
*/
generateSchemaType(name, schema) {
var _a, _b;
const strippedName = stripSchemaPrefix(name, this.options.stripSchemaPrefix);
const typeName = (0, import_openapi_core3.applyFormatting)(strippedName, this.options.prefix, this.options.suffix);
if (!this.schemaDependencies.has(name)) {
this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
}
const deps = (_a = this.schemaDependencies.get(name)) != null ? _a : /* @__PURE__ */ new Set();
if (schema.enum && Array.isArray(schema.enum)) {
const { code: code2 } = generateEnum(strippedName, schema.enum, {
format: (_b = this.options.enumFormat) != null ? _b : "union",
prefix: this.options.prefix,
suffix: this.options.suffix,
nullable: schema.nullable === true
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = [];
for (const subSchema of schema.allOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else if (subSchema.properties) {
const props = this.generateProperties(name, subSchema, deps);
parts.push(`{ ${props.join("; ")} }`);
}
}
const typeStr2 = parts.join(" & ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = [];
for (const subSchema of schema.oneOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = [];
for (const subSchema of schema.anyOf) {
if (subSchema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "object" || schema.properties) {
const properties = this.generateProperties(name, schema, deps);
const { code: code2 } = generateTypeDeclaration(strippedName, properties, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
let arrayType = `${itemType}[]`;
if (schema.nullable) {
arrayType = `${arrayType} | null`;
}
const code2 = `export type ${typeName} = ${arrayType};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
let typeStr = this.primitiveToTypeString(schema);
if (schema.nullable) {
typeStr = `${typeStr} | null`;
}
const code = `export type ${typeName} = ${typeStr};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code}`;
}
return code;
}
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
generatePropertyJSDoc(schema) {
const hasDescription = this.options.includeDescriptions && schema.description;
const hasDeprecated = schema.deprecated === true;
if (!hasDescription && !hasDeprecated) {
return "";
}
const parts = [];
if (hasDescription && schema.description) {
parts.push((0, import_openapi_core3.escapeJSDoc)(schema.description));
}
if (hasDeprecated) {
parts.push("@deprecated");
}
return `/** ${parts.join(" ")} */
`;
}
/**
* Generate properties for an object schema
*/
generateProperties(_schemaName, schema, deps) {
const properties = [];
if (!schema.properties) {
return properties;
}
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
const prop = formatTypeProperty(propName, typeStr, isRequired);
const jsdoc = this.generatePropertyJSDoc(propSchema);
properties.push(jsdoc ? `${jsdoc} ${prop}` : prop);
}
return properties;
}
/**
* Convert a schema to a TypeScript type string
*/
schemaToTypeString(schema, deps) {
if (Array.isArray(schema.type)) {
const types = schema.type;
const hasNull = types.includes("null");
const nonNullTypes = types.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
return "null";
}
const typeStrings = nonNullTypes.map((t) => {
const tempSchema = { ...schema, type: t };
return this.schemaToTypeString(tempSchema, deps);
});
const baseType = typeStrings.join(" | ");
return hasNull ? `${baseType} | null` : baseType;
}
if (schema.$ref) {
const refName = (0, import_openapi_core3.resolveRefName)(schema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const typeName = (0, import_openapi_core3.applyFormatting)(strippedRef, this.options.prefix, this.options.suffix);
if (schema.nullable) {
return `${typeName} | null`;
}
return typeName;
}
if (schema.enum && Array.isArray(schema.enum)) {
const enumType = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
if (schema.nullable) {
return `${enumType} | null`;
}
return enumType;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
const arrayType = `${itemType}[]`;
if (schema.nullable) {
return `${arrayType} | null`;
}
return arrayType;
}
if (schema.type === "object" || schema.properties) {
let objectType;
if (schema.properties) {
const props = [];
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
props.push(formatTypeProperty(propName, typeStr, isRequired));
}
objectType = `{ ${props.join("; ")} }`;
} else if (schema.additionalProperties) {
if (typeof schema.additionalProperties === "boolean") {
objectType = "Record<string, unknown>";
} else {
const valueType = this.schemaToTypeString(schema.additionalProperties, deps);
objectType = `Record<string, ${valueType}>`;
}
} else {
objectType = "Record<string, unknown>";
}
if (schema.nullable) {
return `${objectType} | null`;
}
return objectType;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = schema.allOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" & ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = schema.oneOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = schema.anyOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
const primitiveType = this.primitiveToTypeString(schema);
if (schema.nullable) {
return `${primitiveType} | null`;
}
return primitiveType;
}
/**
* Convert a primitive schema to a TypeScript type string
*/
primitiveToTypeString(schema) {
if (schema.type === "string") {
return "string";
}
if (schema.type === "integer" || schema.type === "number") {
return "number";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "null") {
return "null";
}
return "unknown";
}
/**
* Generate statistics
*/
generateStats() {
const stats = [];
stats.push("/**");
stats.push(" * TypeScript Types Generation Statistics");
stats.push(` * Generated from: ${this.options.input}`);
stats.push(` * Total schemas: ${this.generatedTypes.size}`);
if (this.circularSchemas.size > 0) {
stats.push(` * Circular references detected: ${Array.from(this.circularSchemas).join(", ")}`);
}
if (this.options.operationFilters) {
const filterStatsStr = (0, import_openapi_core3.formatFilterStatistics)(this.filterStats);
if (filterStatsStr) {
stats.push(...filterStatsStr.split("\n").map((s) => ` * ${s}`));
}
}
stats.push(" */");
return stats;
}
/**
* Topologically sort schemas
*/
topologicalSort() {
return (0, import_openapi_core3.topologicalSortSchemas)(this.schemaDependencies, this.circularSchemas);
}
/**
* Generate query parameter types for each operation
*/
generateQueryParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = (0, import_openapi_core3.mergeParameters)(getParameters(pathItem), getParameters(operation), this.spec);
const queryParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "query");
if (queryParams.length === 0) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateQueryParamsTypeName)(operationName);
const props = [];
for (const param of queryParams) {
const paramSchema = param.schema;
if (!paramSchema || !param.name) continue;
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(paramSchema, deps);
const isRequired = param.required === true;
let propDef = formatTypeProperty(param.name, typeStr, isRequired);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Query parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`QueryParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate header parameter types for each operation
*/
generateHeaderParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = (0, import_openapi_core3.mergeParameters)(getParameters(pathItem), getParameters(operation), this.spec);
const headerParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "header");
if (headerParams.length === 0) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateHeaderParamsTypeName)(operationName);
const props = [];
for (const param of headerParams) {
if (!param.name) continue;
let propDef = formatTypeProperty(param.name, "string", false);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Header parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`HeaderParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate inline request body types for each operation
*/
generateInlineRequestTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const requestBodyContent = getRequestBodyContent(operation);
if (!requestBodyContent) continue;
const contentTypes = Object.keys(requestBodyContent);
const hasMultipleContentTypes = contentTypes.length > 1;
for (const [contentType, mediaType] of Object.entries(requestBodyContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateInlineRequestTypeName)(operationName, contentType, hasMultipleContentTypes);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Request body for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`Request:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
/**
* Generate inline response types for each operation
*/
generateInlineResponseTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!(0, import_openapi_core3.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
continue;
}
const responsesValue = operation.responses;
if (!isRecord(responsesValue)) continue;
const responses = responsesValue;
const successStatusCodes = Object.keys(responses).filter((code) => {
const codeNum = Number.parseInt(code, 10);
return codeNum >= 200 && codeNum < 300;
});
const hasMultipleStatuses = successStatusCodes.length > 1;
for (const [statusCode, response] of Object.entries(responses)) {
const responseContent = getResponseContent(response);
if (!responseContent) continue;
for (const [_contentType, mediaType] of Object.entries(responseContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = (0, import_openapi_core3.stripPathPrefix)(path, this.options.stripPathPrefix);
const operationName = (0, import_openapi_core3.getOperationName)(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = (0, import_openapi_core3.generateInlineResponseTypeName)(operationName, statusCode, hasMultipleStatuses);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Response for ${jsdocOperationName} (${statusCode})
*/
`;
this.generatedTypes.set(`Response:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
}
/**
* Generate schemas as a string (without writing to file)
*/
generateString() {
var _a;
if ((_a = this.spec.components) == null ? void 0 : _a.schemas) {
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
if (!this.shouldIncludeSchema(name)) {
continue;
}
try {
const typeCode = this.generateSchemaType(name, schema);
this.generatedTypes.set(name, typeCode);
} catch (error) {
throw new import_openapi_core3.SchemaGenerationError(`Failed to generate TypeScript type for schema: ${name}`, name, {
cause: error instanceof Error ? error.message : String(error)
});
}
}
}
this.generateQueryParamTypes();
this.generateHeaderParamTypes();
this.generateInlineRequestTypes();
this.generateInlineResponseTypes();
(0, import_openapi_core3.validateFilters)(this.filterStats, this.options.operationFilters);
const orderedSchemaNames = this.topologicalSort();
const output = [
"// Auto-generated by @cerios/openapi-to-typescript",
"// Do not edit this file manually",
""
];
if (this.options.showStats === true) {
output.push(...this.generateStats());
output.push("");
}
for (const name of orderedSchemaNames) {
const typeCode = this.generatedTypes.get(name);
if (typeCode) {
output.push(typeCode);
output.push("");
}
}
const inlineTypes = [];
for (const [key, typeCode] of this.generatedTypes.entries()) {
if (key.startsWith("QueryParams:") || key.startsWith("HeaderParams:") || key.startsWith("Request:") || key.startsWith("Response:")) {
inlineTypes.push(typeCode);
}
}
if (inlineTypes.length > 0) {
output.push("// Operation Types (Query Params, Headers, Request Bodies, Responses)");
output.push("");
for (const typeCode of inlineTypes) {
output.push(typeCode);
output.push("");
}
}
return output.join("\n");
}
/**
* Ensure directory exists for a file path
*/
ensureDirectoryExists(filePath) {
const normalizedPath = (0, import_node_path.normalize)(filePath);
const dir = (0, import_node_path.dirname)(normalizedPath);
if (!(0, import_node_fs.existsSync)(dir)) {
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
}
}
/**
* Generate and write to file
*/
generate() {
if (!this.options.outputTypes) {
throw new import_openapi_core3.ConfigurationError("Output path is required for generate()", {
suggestion: "Use generateString() to get output without writing to file"
});
}
const output = this.generateString();
const normalizedOutput = (0, import_node_path.normalize)(this.options.outputTypes);
this.ensureDirectoryExists(normalizedOutput);
(0, import_node_fs.writeFileSync)(normalizedOutput, output);
console.log(` \u2713 Generated ${normalizedOutput}`);
}
};
// src/utils/config-loader.ts
var import_openapi_core4 = require("@cerios/openapi-core");
var import_zod = require("zod");
var TypeScriptSpecificOptionsSchema = import_zod.z.strictObject({
enumFormat: import_zod.z.enum(["enum", "union", "const-object"]).optional(),
includeSchemas: import_zod.z.array(import_zod.z.string()).optional(),
excludeSchemas: import_zod.z.array(import_zod.z.string()).optional()
});
var TypeScriptGeneratorOptionsSchema = import_openapi_core4.BaseGeneratorOptionsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape,
outputTypes: import_zod.z.string()
// Make outputTypes required for TypeScript generator
});
var TypeScriptDefaultsSchema = import_openapi_core4.BaseDefaultsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape
});
var ConfigFileSchema = import_zod.z.strictObject({
defaults: TypeScriptDefaultsSchema.optional(),
specs: import_zod.z.array(TypeScriptGeneratorOptionsSchema).min(1, {
message: "Configuration must include at least one specification. Each specification should have 'input' and 'outputTypes' paths."
}),
executionMode: import_openapi_core4.ExecutionModeSchema.optional()
});
var errorMessages = {
missingFieldMessages: {
input: "Each spec must specify the path to your OpenAPI specification file.",
outputTypes: "Each spec must specify an output file path for generated TypeScript types."
},
unrecognizedKeyMessages: {
output: "Did you mean 'outputTypes'? The 'output' field was renamed to 'outputTypes'."
},
requiredFieldsHelp: "All required fields are present (specs array with input/outputTypes)"
};
var configLoader = (0, import_openapi_core4.createConfigLoader)(
{
packageName: "openapi-to-typescript",
errorMessages
},
ConfigFileSchema
);
var loadConfig = configLoader.loadConfig;
function mergeConfigWithDefaults(config) {
if (!(config == null ? void 0 : config.specs) || !Array.isArray(config.specs)) {
throw new Error("Invalid config: specs array is required");
}
const defaults = config.defaults || {};
return config.specs.map((spec) => {
const merged = {
// Apply defaults first
enumFormat: defaults.enumFormat,
includeDescriptions: defaults.includeDescriptions,
defaultNullable: defaults.defaultNullable,
useOperationId: defaults.useOperationId,
prefix: defaults.prefix,
suffix: defaults.suffix,
stripSchemaPrefix: defaults.stripSchemaPrefix,
stripPathPrefix: defaults.stripPathPrefix,
operationFilters: defaults.operationFilters,
showStats: defaults.showStats,
batchSize: defaults.batchSize,
// Override with spec-specific values (including required input/output)
...spec
};
return merged;
});
}
function mergeCliWithConfig(specConfig, cliOptions) {
return (0, import_openapi_core4.mergeCliWithConfig)(specConfig, cliOptions);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CircularReferenceError,
CliOptionsError,
ConfigValidationError,
ConfigurationError,
FileOperationError,
GeneratorError,
SchemaGenerationError,
SpecValidationError,
TypeScriptDefaultsSchema,
TypeScriptGenerator,
TypeScriptGeneratorOptionsSchema,
TypeScriptSpecificOptionsSchema,
defineConfig,
executeBatch,
formatTypeProperty,
generateEnum,
generateTypeDeclaration,
getBatchExitCode,
loadConfig,
mergeCliWithConfig,
mergeConfigWithDefaults
});
//# sourceMappingURL=index.js.map

Sorry, the diff of this file is too big to display

// src/index.ts
import {
CircularReferenceError,
CliOptionsError,
ConfigurationError as ConfigurationError2,
ConfigValidationError,
executeBatch,
FileOperationError,
GeneratorError,
getBatchExitCode,
SchemaGenerationError as SchemaGenerationError2,
SpecValidationError as SpecValidationError2
} from "@cerios/openapi-core";
// src/generators/enum-generator.ts
import { applyFormatting, classifyEnumType, numericToEnumMember, stringToEnumMember } from "@cerios/openapi-core";
function generateEnum(name, values, options) {
const { format, prefix, suffix, nullable } = options;
const typeName = applyFormatting(name, prefix, suffix);
let result;
switch (format) {
case "enum":
result = generateTsEnum(typeName, values);
break;
case "union":
result = generateUnion(typeName, values);
break;
case "const-object":
result = generateConstObject(typeName, values);
break;
default:
result = generateUnion(typeName, values);
}
if (nullable) {
result.code = addNullableToTypeCode(result.code, typeName);
}
return result;
}
function addNullableToTypeCode(code, typeName) {
const unionPattern = new RegExp(`(export type ${typeName} = )([^;]+)(;)`);
if (unionPattern.test(code)) {
return code.replace(unionPattern, `$1$2 | null$3`);
}
const constObjectTypePattern = new RegExp(
`(export type ${typeName} = \\(typeof ${typeName}\\)\\[keyof typeof ${typeName}\\])(;)`
);
if (constObjectTypePattern.test(code)) {
return code.replace(constObjectTypePattern, `$1 | null$2`);
}
const enumPattern = /^export enum /;
if (enumPattern.test(code)) {
return `${code}
/** Nullable version of ${typeName} enum */
export type ${typeName}Nullable = ${typeName} | null;`;
}
return code;
}
function generateTsEnum(typeName, values) {
const enumType = classifyEnumType(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = stringToEnumMember(value, usedKeys);
return ` ${memberName} = "${value}"`;
}
if (typeof value === "number") {
const memberName = numericToEnumMember(value, usedKeys);
return ` ${memberName} = ${value}`;
}
return ` ${String(value)} = ${value}`;
});
const code = `export enum ${typeName} {
${members.join(",\n")},
}`;
return { code, typeName };
}
function generateUnion(typeName, values) {
const literals = values.map((value) => {
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "boolean") {
return String(value);
}
return String(value);
});
const code = `export type ${typeName} = ${literals.join(" | ")};`;
return { code, typeName };
}
function generateConstObject(typeName, values) {
const enumType = classifyEnumType(values);
if (enumType === "boolean") {
return generateUnion(typeName, values);
}
const usedKeys = /* @__PURE__ */ new Set();
const members = values.map((value) => {
if (typeof value === "string") {
const memberName = stringToEnumMember(value, usedKeys);
return ` ${memberName}: "${value}"`;
}
if (typeof value === "number") {
const memberName = numericToEnumMember(value, usedKeys);
return ` ${memberName}: ${value}`;
}
return ` ${String(value)}: ${value}`;
});
const objectCode = `export const ${typeName} = {
${members.join(",\n")},
} as const;`;
const typeCode = `export type ${typeName} = (typeof ${typeName})[keyof typeof ${typeName}];`;
const code = `${objectCode}
${typeCode}`;
return { code, typeName };
}
// src/generators/type-generator.ts
import { applyFormatting as applyFormatting2 } from "@cerios/openapi-core";
function generateTypeDeclaration(name, properties, options) {
const { prefix, suffix } = options;
const typeName = applyFormatting2(name, prefix, suffix);
const code = `export type ${typeName} = {
${properties.join(";\n ")};
};`;
return { code, typeName };
}
function formatTypeProperty(name, type, required) {
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
const optional = required ? "" : "?";
return `${safeName}${optional}: ${type}`;
}
// src/types.ts
function defineConfig(config) {
return config;
}
// src/typescript-generator.ts
import { existsSync, mkdirSync, writeFileSync } from "fs";
import { dirname, normalize } from "path";
import {
applyFormatting as applyFormatting3,
ConfigurationError,
createFilterStatistics,
detectCircularReferences,
escapeJSDoc,
expandTransitiveReferences,
extractSchemaRefs,
formatFilterStatistics,
generateHeaderParamsTypeName,
generateInlineRequestTypeName,
generateInlineResponseTypeName,
generateQueryParamsTypeName,
getOperationName,
loadOpenAPISpec,
mergeParameters,
resolveRefName,
SchemaGenerationError,
SpecValidationError,
shouldIncludeOperation,
stripPathPrefix,
stripPrefix,
topologicalSortSchemas,
validateFilters
} from "@cerios/openapi-core";
function stripSchemaPrefix(name, prefixes) {
if (!prefixes) return name;
const prefixArray = Array.isArray(prefixes) ? prefixes : [prefixes];
let result = name;
for (const prefix of prefixArray) {
result = stripPrefix(result, prefix);
}
return result;
}
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
function isRecord(value) {
return typeof value === "object" && value !== null;
}
function getOperation(pathItem, method) {
if (!isRecord(pathItem)) {
return void 0;
}
const operation = pathItem[method];
return isRecord(operation) ? operation : void 0;
}
function getParameters(value) {
if (!isRecord(value)) {
return void 0;
}
const parameters = value.parameters;
return Array.isArray(parameters) ? parameters : void 0;
}
function getOperationId(operation) {
const operationId = operation.operationId;
return typeof operationId === "string" ? operationId : void 0;
}
function toParameterLike(value) {
if (!isRecord(value)) {
return void 0;
}
const param = {};
if (typeof value.in === "string") {
param.in = value.in;
}
if (typeof value.name === "string") {
param.name = value.name;
}
if (typeof value.description === "string") {
param.description = value.description;
}
if (typeof value.required === "boolean") {
param.required = value.required;
}
if (isRecord(value.schema)) {
param.schema = value.schema;
}
return param;
}
function getRequestBodyContent(operation) {
const requestBody = operation.requestBody;
if (!isRecord(requestBody)) {
return void 0;
}
const content = requestBody.content;
return isRecord(content) ? content : void 0;
}
function getResponseContent(response) {
if (!isRecord(response)) {
return void 0;
}
const content = response.content;
return isRecord(content) ? content : void 0;
}
function getMediaSchema(mediaType) {
if (!isRecord(mediaType)) {
return void 0;
}
const schema = mediaType.schema;
return isRecord(schema) ? schema : void 0;
}
var TypeScriptGenerator = class {
constructor(options) {
this.generatedTypes = /* @__PURE__ */ new Map();
this.schemaDependencies = /* @__PURE__ */ new Map();
this.circularSchemas = /* @__PURE__ */ new Set();
this.schemaUsageMap = /* @__PURE__ */ new Map();
this.filterStats = createFilterStatistics();
var _a, _b, _c, _d, _e, _f;
if (!options.input) {
throw new ConfigurationError("Input path is required", { providedOptions: options });
}
this.options = {
input: options.input,
outputTypes: options.outputTypes,
enumFormat: (_a = options.enumFormat) != null ? _a : "const-object",
includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
prefix: options.prefix,
suffix: options.suffix,
stripSchemaPrefix: options.stripSchemaPrefix,
stripPathPrefix: options.stripPathPrefix,
useOperationId: (_d = options.useOperationId) != null ? _d : true,
operationFilters: options.operationFilters,
showStats: (_e = options.showStats) != null ? _e : true,
batchSize: (_f = options.batchSize) != null ? _f : 10
};
this.spec = loadOpenAPISpec(this.options.input);
this.validateSpec();
this.circularSchemas = detectCircularReferences(this.spec);
if (this.options.operationFilters) {
this.initializeSchemaUsage();
}
}
/**
* Validate the OpenAPI specification
*/
validateSpec() {
var _a, _b;
if (!this.spec) {
throw new SpecValidationError("Empty or invalid OpenAPI specification", {
filePath: this.options.input
});
}
if (!((_a = this.spec.openapi) == null ? void 0 : _a.startsWith("3."))) {
throw new SpecValidationError("Only OpenAPI 3.x specifications are supported", {
filePath: this.options.input,
version: this.spec.openapi
});
}
if (!((_b = this.spec.components) == null ? void 0 : _b.schemas)) {
throw new SpecValidationError("No schemas found in OpenAPI spec", {
filePath: this.options.input
});
}
}
/**
* Initialize schema usage map with operation filtering
*/
initializeSchemaUsage() {
if (!this.options.operationFilters || !this.spec.paths) {
return;
}
const requestSchemas = /* @__PURE__ */ new Set();
const responseSchemas = /* @__PURE__ */ new Set();
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
this.filterStats.totalOperations++;
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters, this.filterStats)) {
continue;
}
this.filterStats.includedOperations++;
const op = operation;
if (op.requestBody && isRecord(op.requestBody)) {
const reqBody = op.requestBody;
if (isRecord(reqBody.content)) {
for (const mediaType of Object.values(reqBody.content)) {
if (isRecord(mediaType) && mediaType.schema) {
extractSchemaRefs(mediaType.schema, requestSchemas);
}
}
}
}
if (isRecord(op.responses)) {
for (const response of Object.values(op.responses)) {
if (isRecord(response) && isRecord(response.content)) {
for (const mediaType of Object.values(response.content)) {
if (isRecord(mediaType) && mediaType.schema) {
extractSchemaRefs(mediaType.schema, responseSchemas);
}
}
}
}
}
if (op.parameters && Array.isArray(op.parameters)) {
for (const param of op.parameters) {
if (isRecord(param) && param.schema) {
extractSchemaRefs(param.schema, requestSchemas);
}
}
}
}
}
expandTransitiveReferences(requestSchemas, this.spec);
expandTransitiveReferences(responseSchemas, this.spec);
for (const name of requestSchemas) {
const inResponse = responseSchemas.has(name);
this.schemaUsageMap.set(name, inResponse ? "both" : "request");
}
for (const name of responseSchemas) {
if (!this.schemaUsageMap.has(name)) {
this.schemaUsageMap.set(name, "response");
}
}
}
/**
* Check if a schema should be included based on filters
*/
shouldIncludeSchema(name) {
if (this.options.operationFilters && this.schemaUsageMap.size > 0) {
return this.schemaUsageMap.has(name);
}
return true;
}
/**
* Generate TypeScript type for a schema
*/
generateSchemaType(name, schema) {
var _a, _b;
const strippedName = stripSchemaPrefix(name, this.options.stripSchemaPrefix);
const typeName = applyFormatting3(strippedName, this.options.prefix, this.options.suffix);
if (!this.schemaDependencies.has(name)) {
this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
}
const deps = (_a = this.schemaDependencies.get(name)) != null ? _a : /* @__PURE__ */ new Set();
if (schema.enum && Array.isArray(schema.enum)) {
const { code: code2 } = generateEnum(strippedName, schema.enum, {
format: (_b = this.options.enumFormat) != null ? _b : "union",
prefix: this.options.prefix,
suffix: this.options.suffix,
nullable: schema.nullable === true
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = [];
for (const subSchema of schema.allOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else if (subSchema.properties) {
const props = this.generateProperties(name, subSchema, deps);
parts.push(`{ ${props.join("; ")} }`);
}
}
const typeStr2 = parts.join(" & ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = [];
for (const subSchema of schema.oneOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = [];
for (const subSchema of schema.anyOf) {
if (subSchema.$ref) {
const refName = resolveRefName(subSchema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const refTypeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
parts.push(refTypeName);
} else {
parts.push(this.schemaToTypeString(subSchema, deps));
}
}
const typeStr2 = parts.join(" | ");
const code2 = `export type ${typeName} = ${typeStr2};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "object" || schema.properties) {
const properties = this.generateProperties(name, schema, deps);
const { code: code2 } = generateTypeDeclaration(strippedName, properties, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
let arrayType = `${itemType}[]`;
if (schema.nullable) {
arrayType = `${arrayType} | null`;
}
const code2 = `export type ${typeName} = ${arrayType};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code2}`;
}
return code2;
}
let typeStr = this.primitiveToTypeString(schema);
if (schema.nullable) {
typeStr = `${typeStr} | null`;
}
const code = `export type ${typeName} = ${typeStr};`;
if (this.options.includeDescriptions && schema.description) {
return `/**
* ${schema.description}
*/
${code}`;
}
return code;
}
/**
* Generate JSDoc comment for a property schema
* Handles both description and deprecated flags
*/
generatePropertyJSDoc(schema) {
const hasDescription = this.options.includeDescriptions && schema.description;
const hasDeprecated = schema.deprecated === true;
if (!hasDescription && !hasDeprecated) {
return "";
}
const parts = [];
if (hasDescription && schema.description) {
parts.push(escapeJSDoc(schema.description));
}
if (hasDeprecated) {
parts.push("@deprecated");
}
return `/** ${parts.join(" ")} */
`;
}
/**
* Generate properties for an object schema
*/
generateProperties(_schemaName, schema, deps) {
const properties = [];
if (!schema.properties) {
return properties;
}
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
const prop = formatTypeProperty(propName, typeStr, isRequired);
const jsdoc = this.generatePropertyJSDoc(propSchema);
properties.push(jsdoc ? `${jsdoc} ${prop}` : prop);
}
return properties;
}
/**
* Convert a schema to a TypeScript type string
*/
schemaToTypeString(schema, deps) {
if (Array.isArray(schema.type)) {
const types = schema.type;
const hasNull = types.includes("null");
const nonNullTypes = types.filter((t) => t !== "null");
if (nonNullTypes.length === 0) {
return "null";
}
const typeStrings = nonNullTypes.map((t) => {
const tempSchema = { ...schema, type: t };
return this.schemaToTypeString(tempSchema, deps);
});
const baseType = typeStrings.join(" | ");
return hasNull ? `${baseType} | null` : baseType;
}
if (schema.$ref) {
const refName = resolveRefName(schema.$ref);
deps.add(refName);
const strippedRef = stripSchemaPrefix(refName, this.options.stripSchemaPrefix);
const typeName = applyFormatting3(strippedRef, this.options.prefix, this.options.suffix);
if (schema.nullable) {
return `${typeName} | null`;
}
return typeName;
}
if (schema.enum && Array.isArray(schema.enum)) {
const enumType = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
if (schema.nullable) {
return `${enumType} | null`;
}
return enumType;
}
if (schema.type === "array" && schema.items) {
const itemType = this.schemaToTypeString(schema.items, deps);
const arrayType = `${itemType}[]`;
if (schema.nullable) {
return `${arrayType} | null`;
}
return arrayType;
}
if (schema.type === "object" || schema.properties) {
let objectType;
if (schema.properties) {
const props = [];
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(schema.properties)) {
const isRequired = required.has(propName);
const typeStr = this.schemaToTypeString(propSchema, deps);
props.push(formatTypeProperty(propName, typeStr, isRequired));
}
objectType = `{ ${props.join("; ")} }`;
} else if (schema.additionalProperties) {
if (typeof schema.additionalProperties === "boolean") {
objectType = "Record<string, unknown>";
} else {
const valueType = this.schemaToTypeString(schema.additionalProperties, deps);
objectType = `Record<string, ${valueType}>`;
}
} else {
objectType = "Record<string, unknown>";
}
if (schema.nullable) {
return `${objectType} | null`;
}
return objectType;
}
if (schema.allOf && Array.isArray(schema.allOf)) {
const parts = schema.allOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" & ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.oneOf && Array.isArray(schema.oneOf)) {
const parts = schema.oneOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
if (schema.anyOf && Array.isArray(schema.anyOf)) {
const parts = schema.anyOf.map((s) => this.schemaToTypeString(s, deps));
const baseType = parts.join(" | ");
if (schema.nullable) {
return `${baseType} | null`;
}
return baseType;
}
const primitiveType = this.primitiveToTypeString(schema);
if (schema.nullable) {
return `${primitiveType} | null`;
}
return primitiveType;
}
/**
* Convert a primitive schema to a TypeScript type string
*/
primitiveToTypeString(schema) {
if (schema.type === "string") {
return "string";
}
if (schema.type === "integer" || schema.type === "number") {
return "number";
}
if (schema.type === "boolean") {
return "boolean";
}
if (schema.type === "null") {
return "null";
}
return "unknown";
}
/**
* Generate statistics
*/
generateStats() {
const stats = [];
stats.push("/**");
stats.push(" * TypeScript Types Generation Statistics");
stats.push(` * Generated from: ${this.options.input}`);
stats.push(` * Total schemas: ${this.generatedTypes.size}`);
if (this.circularSchemas.size > 0) {
stats.push(` * Circular references detected: ${Array.from(this.circularSchemas).join(", ")}`);
}
if (this.options.operationFilters) {
const filterStatsStr = formatFilterStatistics(this.filterStats);
if (filterStatsStr) {
stats.push(...filterStatsStr.split("\n").map((s) => ` * ${s}`));
}
}
stats.push(" */");
return stats;
}
/**
* Topologically sort schemas
*/
topologicalSort() {
return topologicalSortSchemas(this.schemaDependencies, this.circularSchemas);
}
/**
* Generate query parameter types for each operation
*/
generateQueryParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = mergeParameters(getParameters(pathItem), getParameters(operation), this.spec);
const queryParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "query");
if (queryParams.length === 0) continue;
const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateQueryParamsTypeName(operationName);
const props = [];
for (const param of queryParams) {
const paramSchema = param.schema;
if (!paramSchema || !param.name) continue;
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(paramSchema, deps);
const isRequired = param.required === true;
let propDef = formatTypeProperty(param.name, typeStr, isRequired);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Query parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`QueryParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate header parameter types for each operation
*/
generateHeaderParamTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
continue;
}
const allParams = mergeParameters(getParameters(pathItem), getParameters(operation), this.spec);
const headerParams = allParams.map((param) => toParameterLike(param)).filter((param) => param !== void 0 && param.in === "header");
if (headerParams.length === 0) continue;
const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateHeaderParamsTypeName(operationName);
const props = [];
for (const param of headerParams) {
if (!param.name) continue;
let propDef = formatTypeProperty(param.name, "string", false);
if (this.options.includeDescriptions && param.description) {
propDef = `/** ${param.description} */
${propDef}`;
}
props.push(propDef);
}
const { code } = generateTypeDeclaration(typeName, props, {
prefix: this.options.prefix,
suffix: this.options.suffix
});
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Header parameters for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`HeaderParams:${typeName}`, `${jsdoc}${code}`);
}
}
}
/**
* Generate inline request body types for each operation
*/
generateInlineRequestTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
continue;
}
const requestBodyContent = getRequestBodyContent(operation);
if (!requestBodyContent) continue;
const contentTypes = Object.keys(requestBodyContent);
const hasMultipleContentTypes = contentTypes.length > 1;
for (const [contentType, mediaType] of Object.entries(requestBodyContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateInlineRequestTypeName(operationName, contentType, hasMultipleContentTypes);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Request body for ${jsdocOperationName}
*/
`;
this.generatedTypes.set(`Request:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
/**
* Generate inline response types for each operation
*/
generateInlineResponseTypes() {
if (!this.spec.paths) return;
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const method of HTTP_METHODS) {
const operation = getOperation(pathItem, method);
if (!operation) continue;
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
continue;
}
const responsesValue = operation.responses;
if (!isRecord(responsesValue)) continue;
const responses = responsesValue;
const successStatusCodes = Object.keys(responses).filter((code) => {
const codeNum = Number.parseInt(code, 10);
return codeNum >= 200 && codeNum < 300;
});
const hasMultipleStatuses = successStatusCodes.length > 1;
for (const [statusCode, response] of Object.entries(responses)) {
const responseContent = getResponseContent(response);
if (!responseContent) continue;
for (const [_contentType, mediaType] of Object.entries(responseContent)) {
const mediaSchema = getMediaSchema(mediaType);
if (!mediaSchema) continue;
if (mediaSchema.$ref) continue;
const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
const operationName = getOperationName(
getOperationId(operation),
method,
strippedPath,
this.options.useOperationId
);
const typeName = generateInlineResponseTypeName(operationName, statusCode, hasMultipleStatuses);
const deps = /* @__PURE__ */ new Set();
const typeStr = this.schemaToTypeString(mediaSchema, deps);
const code = `export type ${typeName} = ${typeStr};`;
const jsdocOperationName = getOperationId(operation) || `${method.toUpperCase()} ${path}`;
const jsdoc = `/**
* Response for ${jsdocOperationName} (${statusCode})
*/
`;
this.generatedTypes.set(`Response:${typeName}`, `${jsdoc}${code}`);
}
}
}
}
}
/**
* Generate schemas as a string (without writing to file)
*/
generateString() {
var _a;
if ((_a = this.spec.components) == null ? void 0 : _a.schemas) {
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
if (!this.shouldIncludeSchema(name)) {
continue;
}
try {
const typeCode = this.generateSchemaType(name, schema);
this.generatedTypes.set(name, typeCode);
} catch (error) {
throw new SchemaGenerationError(`Failed to generate TypeScript type for schema: ${name}`, name, {
cause: error instanceof Error ? error.message : String(error)
});
}
}
}
this.generateQueryParamTypes();
this.generateHeaderParamTypes();
this.generateInlineRequestTypes();
this.generateInlineResponseTypes();
validateFilters(this.filterStats, this.options.operationFilters);
const orderedSchemaNames = this.topologicalSort();
const output = [
"// Auto-generated by @cerios/openapi-to-typescript",
"// Do not edit this file manually",
""
];
if (this.options.showStats === true) {
output.push(...this.generateStats());
output.push("");
}
for (const name of orderedSchemaNames) {
const typeCode = this.generatedTypes.get(name);
if (typeCode) {
output.push(typeCode);
output.push("");
}
}
const inlineTypes = [];
for (const [key, typeCode] of this.generatedTypes.entries()) {
if (key.startsWith("QueryParams:") || key.startsWith("HeaderParams:") || key.startsWith("Request:") || key.startsWith("Response:")) {
inlineTypes.push(typeCode);
}
}
if (inlineTypes.length > 0) {
output.push("// Operation Types (Query Params, Headers, Request Bodies, Responses)");
output.push("");
for (const typeCode of inlineTypes) {
output.push(typeCode);
output.push("");
}
}
return output.join("\n");
}
/**
* Ensure directory exists for a file path
*/
ensureDirectoryExists(filePath) {
const normalizedPath = normalize(filePath);
const dir = dirname(normalizedPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
/**
* Generate and write to file
*/
generate() {
if (!this.options.outputTypes) {
throw new ConfigurationError("Output path is required for generate()", {
suggestion: "Use generateString() to get output without writing to file"
});
}
const output = this.generateString();
const normalizedOutput = normalize(this.options.outputTypes);
this.ensureDirectoryExists(normalizedOutput);
writeFileSync(normalizedOutput, output);
console.log(` \u2713 Generated ${normalizedOutput}`);
}
};
// src/utils/config-loader.ts
import {
BaseDefaultsSchema,
BaseGeneratorOptionsSchema,
mergeCliWithConfig as coreMergeCliWithConfig,
createConfigLoader,
ExecutionModeSchema
} from "@cerios/openapi-core";
import { z } from "zod";
var TypeScriptSpecificOptionsSchema = z.strictObject({
enumFormat: z.enum(["enum", "union", "const-object"]).optional(),
includeSchemas: z.array(z.string()).optional(),
excludeSchemas: z.array(z.string()).optional()
});
var TypeScriptGeneratorOptionsSchema = BaseGeneratorOptionsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape,
outputTypes: z.string()
// Make outputTypes required for TypeScript generator
});
var TypeScriptDefaultsSchema = BaseDefaultsSchema.extend({
...TypeScriptSpecificOptionsSchema.shape
});
var ConfigFileSchema = z.strictObject({
defaults: TypeScriptDefaultsSchema.optional(),
specs: z.array(TypeScriptGeneratorOptionsSchema).min(1, {
message: "Configuration must include at least one specification. Each specification should have 'input' and 'outputTypes' paths."
}),
executionMode: ExecutionModeSchema.optional()
});
var errorMessages = {
missingFieldMessages: {
input: "Each spec must specify the path to your OpenAPI specification file.",
outputTypes: "Each spec must specify an output file path for generated TypeScript types."
},
unrecognizedKeyMessages: {
output: "Did you mean 'outputTypes'? The 'output' field was renamed to 'outputTypes'."
},
requiredFieldsHelp: "All required fields are present (specs array with input/outputTypes)"
};
var configLoader = createConfigLoader(
{
packageName: "openapi-to-typescript",
errorMessages
},
ConfigFileSchema
);
var loadConfig = configLoader.loadConfig;
function mergeConfigWithDefaults(config) {
if (!(config == null ? void 0 : config.specs) || !Array.isArray(config.specs)) {
throw new Error("Invalid config: specs array is required");
}
const defaults = config.defaults || {};
return config.specs.map((spec) => {
const merged = {
// Apply defaults first
enumFormat: defaults.enumFormat,
includeDescriptions: defaults.includeDescriptions,
defaultNullable: defaults.defaultNullable,
useOperationId: defaults.useOperationId,
prefix: defaults.prefix,
suffix: defaults.suffix,
stripSchemaPrefix: defaults.stripSchemaPrefix,
stripPathPrefix: defaults.stripPathPrefix,
operationFilters: defaults.operationFilters,
showStats: defaults.showStats,
batchSize: defaults.batchSize,
// Override with spec-specific values (including required input/output)
...spec
};
return merged;
});
}
function mergeCliWithConfig(specConfig, cliOptions) {
return coreMergeCliWithConfig(specConfig, cliOptions);
}
export {
CircularReferenceError,
CliOptionsError,
ConfigValidationError,
ConfigurationError2 as ConfigurationError,
FileOperationError,
GeneratorError,
SchemaGenerationError2 as SchemaGenerationError,
SpecValidationError2 as SpecValidationError,
TypeScriptDefaultsSchema,
TypeScriptGenerator,
TypeScriptGeneratorOptionsSchema,
TypeScriptSpecificOptionsSchema,
defineConfig,
executeBatch,
formatTypeProperty,
generateEnum,
generateTypeDeclaration,
getBatchExitCode,
loadConfig,
mergeCliWithConfig,
mergeConfigWithDefaults
};
//# sourceMappingURL=index.mjs.map

Sorry, the diff of this file is too big to display

The MIT License (MIT)
Copyright © 2025 Cerios
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# @cerios/openapi-to-typescript
Generate TypeScript types from OpenAPI specifications.
## Features
- Generate TypeScript `type` declarations from OpenAPI schemas
- Generate TypeScript `enum`, `union`, or `const object` from OpenAPI enums
- Configurable output format options
- CLI and programmatic API
- Supports OpenAPI 3.0 and 3.1
## Installation
```bash
npm install @cerios/openapi-to-typescript
```
## Quick Start
### CLI Usage
```bash
# Initialize a config file
npx openapi-to-typescript init
# Generate types
npx openapi-to-typescript generate
```
### Configuration
Create a `openapi-to-typescript.config.ts` file:
```typescript
import { defineConfig } from "@cerios/openapi-to-typescript";
export default defineConfig({
input: "./openapi.yaml",
outputTypes: "./src/types.ts",
// Output format for enums: 'enum', 'union' (default), or 'const-object'
enumFormat: "union",
});
```
### Programmatic API
```typescript
import { TypeScriptGenerator } from "@cerios/openapi-to-typescript";
const generator = new TypeScriptGenerator({
input: "./openapi.yaml",
outputTypes: "./src/types.ts",
enumFormat: "union",
});
// Generate and write to file
generator.generate();
// Or generate as string
const code = generator.generateString();
```
## Configuration Options
| Option | Type | Default | Description |
| --------------------- | ------------------------------------- | --------- | --------------------------------------------------------------- |
| `input` | `string` | - | Path to OpenAPI specification (YAML or JSON) |
| `outputTypes` | `string` | - | Output file path for generated types |
| `enumFormat` | `'enum' \| 'union' \| 'const-object'` | `'union'` | How to generate enums |
| `includeDescriptions` | `boolean` | `true` | Include JSDoc comments |
| `defaultNullable` | `boolean` | `false` | Treat properties as nullable by default |
| `stripSchemaPrefix` | `string \| string[]` | - | Remove prefix from schema names (supports glob) |
| `stripPathPrefix` | `string` | - | Remove prefix from paths |
| `useOperationId` | `boolean` | `true` | Use operationId for operation-derived type names when available |
| `prefix` | `string` | - | Add prefix to generated type names |
| `suffix` | `string` | - | Add suffix to generated type names |
| `operationFilters` | `object` | - | Filter operations by tags, paths, methods |
| `showStats` | `boolean` | `true` | Include generation statistics |
| `batchSize` | `number` | `10` | Parallel processing batch size |
## Output Format Examples
### Enum Format
**`union` (default):**
```typescript
export type Status = "active" | "inactive" | "pending";
```
**`enum`:**
```typescript
export enum Status {
Active = "active",
Inactive = "inactive",
Pending = "pending",
}
```
**`const-object`:**
```typescript
export const Status = {
Active: "active",
Inactive: "inactive",
Pending: "pending",
} as const;
export type Status = (typeof Status)[keyof typeof Status];
```
## License
MIT
+77
-77
{
"name": "@cerios/openapi-to-typescript",
"version": "0.0.1",
"description": "Generate TypeScript types from OpenAPI specifications",
"keywords": [
"api",
"cerios",
"code-generation",
"codegen",
"openapi",
"swagger",
"types",
"typescript"
],
"homepage": "https://github.com/CeriosTesting/openapi-to-typescript#readme",
"bugs": {
"url": "https://github.com/CeriosTesting/openapi-to-typescript/issues"
},
"license": "MIT",
"author": "Ronald Veth - Cerios",
"repository": {
"type": "git",
"url": "git+https://github.com/CeriosTesting/openapi-to-typescript.git",
"directory": "packages/openapi-to-typescript"
},
"bin": {
"openapi-to-typescript": "./dist/cli.js"
},
"files": [
"dist"
],
"type": "commonjs",
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"provenance": false
},
"scripts": {
"build": "tsup",
"compile": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"@cerios/openapi-core": "workspace:*",
"commander": "catalog:",
"cosmiconfig": "catalog:",
"prompts": "catalog:",
"yaml": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@types/node": "catalog:",
"@types/prompts": "catalog:",
"tsup": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"engines": {
"node": ">=18"
}
}
"name": "@cerios/openapi-to-typescript",
"version": "1.0.0",
"description": "Generate TypeScript types from OpenAPI specifications",
"keywords": [
"api",
"cerios",
"code-generation",
"codegen",
"openapi",
"swagger",
"types",
"typescript"
],
"homepage": "https://github.com/CeriosTesting/openapi-codegen#readme",
"bugs": {
"url": "https://github.com/CeriosTesting/openapi-codegen/issues"
},
"license": "MIT",
"author": "Ronald Veth - Cerios",
"repository": {
"type": "git",
"url": "git+https://github.com/CeriosTesting/openapi-codegen.git",
"directory": "packages/openapi-to-typescript"
},
"bin": {
"openapi-to-typescript": "./dist/cli.js"
},
"files": [
"dist"
],
"type": "commonjs",
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"provenance": true
},
"dependencies": {
"commander": "^14.0.3",
"cosmiconfig": "^9.0.0",
"prompts": "^2.4.2",
"yaml": "^2.8.2",
"zod": "^4.3.6",
"@cerios/openapi-core": "1.0.0"
},
"devDependencies": {
"@types/node": "^25.2.3",
"@types/prompts": "^2.4.9",
"tsup": "^8.5.1",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
},
"engines": {
"node": ">=18"
},
"scripts": {
"build": "tsup",
"compile": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest"
}
}