@flexbase/openapi-generator
Version:
Open API code generator
1,356 lines • 52.5 kB
JavaScript
#!/usr/bin/env node
import { ConsoleLogger } from "@flexbase/logger";
import chalk from "chalk";
import { program } from "commander";
import * as glob from "glob";
import Path from "path";
import fs from "fs-extra";
import Handlebars from "handlebars";
import { array, code, collection, comparison, date, html, i18n, inflection, object, markdown, math, misc, number, path, regex, string, url } from "useful-handlebars-helpers";
import prettier from "prettier";
import $RefParser from "@stoplight/json-schema-ref-parser";
import { OpenAPIV3 } from "openapi-types";
class ChalkLogger extends ConsoleLogger {
error(e, ...t) {
super.error(typeof e == "object" ? e : chalk.redBright(e), ...t);
}
warn(e, ...t) {
super.warn(typeof e == "object" ? e : chalk.yellow(e), ...t);
}
info(e, ...t) {
super.info(typeof e == "object" ? e : chalk.blueBright(e), ...t);
}
debug(e, ...t) {
super.debug(typeof e == "object" ? e : chalk.greenBright(e), ...t);
}
trace(e, ...t) {
super.trace(typeof e == "object" ? e : chalk.magenta(e), ...t);
}
}
const name = "@flexbase/openapi-generator", version = "3.2.1", description = "Open API code generator", pkg = {
name,
version,
description
}, references = /* @__PURE__ */ new Map(), createHandlebars = (jsonSchema) => {
const handlebars = Handlebars.create();
handlebars.Utils.createFrame = function(n) {
const e = handlebars.Utils.extend({}, n);
return e._parent = n, e.data = /* @__PURE__ */ new Map(), e;
}, [array, code, collection, comparison, date, html, i18n, inflection, object, markdown, math, misc, number, object, path, regex, string, url].forEach(
(n) => {
handlebars.registerHelper(n);
}
);
function isOptions(n) {
return typeof n == "object" && typeof n?.hash == "object";
}
function isBlock(n) {
return isOptions(n) && typeof n.fn == "function" && typeof n.inverse == "function";
}
function renderValue(n, e, t) {
return isOptions(n) ? renderValue(null, n, t) : isOptions(e) ? renderValue(n, {}, e) : isBlock(t) ? n ? t.fn(e) : t.inverse(e) : n;
}
return handlebars.registerHelper("set", function(n, e, t, r) {
console.log({ context: n, name: e, value: t, options: r });
}), handlebars.registerHelper("registerReference", function(n, e, t, r) {
references.has(e) || references.set(e, /* @__PURE__ */ new Map());
const i = references.get(e);
i.has(t) && handlebars.log(2, `Multiple references ${t} registered in ${e}, last one wins!`);
const s = typeof n == "object" && r ? r.fn(n) : n;
i.set(t, s);
}), handlebars.registerHelper("resolveReference", function(n, e, t) {
const r = references.get(e);
r || handlebars.log(1, `${e} references not found`);
const i = typeof n == "object" && t ? t.fn(n) : n, s = r.get(i);
return s || handlebars.log(1, `Reference ${i} not registered with ${e}`), s;
}), handlebars.registerHelper("clearReferences", function(n, e) {
const t = typeof n == "object" && e ? e.fn(n) : n;
if (typeof t == "string") {
const r = references.get(t);
r && r.clear();
} else
references.clear();
}), handlebars.registerHelper("jsonRef", function(n, e) {
if (typeof n == "function" && (n = n.call(handlebars)), !Handlebars.Utils.isEmpty(n)) {
const t = jsonSchema.get(n), r = e.data ? Handlebars.createFrame(e.data) : void 0;
return e.fn(t, { data: r, blockParams: [n] });
}
}), handlebars.registerHelper("hasLength", function(n, e) {
const t = n;
let r = 0;
return typeof t == "string" || Array.isArray(t) ? r = t.length : typeof t == "object" && (r = Object.keys(t).length), r === e;
}), handlebars.registerHelper("wrap", function(n, e, t, r) {
const i = r.fn(n);
if (!i)
return i;
const s = i.trim();
return e && (e = e.replaceAll("\\n", `
`)), t && (t = t.replaceAll("\\n", `
`)), s.length > 0 ? `${e}${s}${t}` : s;
}), handlebars.registerHelper("newline", function() {
return `
`;
}), handlebars.registerHelper("toRegex", function(n, e) {
return new RegExp(n, e);
}), handlebars.registerHelper("replace", function(n, e, t, r) {
const i = (a) => typeof a == "string" && a !== "", s = (a) => Object.prototype.toString.call(a) === "[object RegExp]", o = typeof n == "object" && r ? r.fn(n) : n;
return i(o) ? !i(e) && !s(e) ? o : (!i(t) && !s(t) && typeof t != "function" && (t = ""), o.replace(e, t)) : "";
}), handlebars.registerHelper("function", function(str, options) {
const _isString = (n) => typeof n == "string" && n !== "", rendered = typeof str == "object" && options ? options.fn(str) : str;
return _isString(rendered) ? eval(rendered) : "";
}), handlebars.registerHelper("extendProperty", function(n, e, t) {
let r = t;
isBlock(t) && (r = t.fn(n)), n[e] = r;
}), handlebars.registerHelper("isDefined", function(n, e) {
return renderValue(n, n, e) !== void 0;
}), handlebars.registerHelper("strJoin", function(n, ...e) {
return e.map((r) => typeof r == "string" ? r : void 0).filter((r) => r).join(n);
}), handlebars.logger.actualLogger = new ChalkLogger(), handlebars.log = (n, ...e) => {
const t = ["debug", "info", "warn", "error"], r = typeof n == "string" ? t.includes(n) ? n : "info" : t.at(n) ?? "info";
handlebars.logger.actualLogger[r](...e);
}, handlebars;
}, runPrettier = async (n, e) => await prettier.format(n, {
semi: !0,
singleQuote: !0,
arrowParens: "avoid",
tabWidth: 2,
useTabs: !1,
printWidth: 150,
parser: e
}), isReference$1 = (n) => n.type === "reference", isMediaContent = (n) => n.type === "mediaContent", isMediaType = (n) => n.type === "mediaTypeObject", isPrimative = (n) => ["string", "number", "boolean", "integer", "null"].includes(n.type), isObjectNode$1 = (n) => n.type === "object", isProperty = (n) => n.type === "property", isComposite$1 = (n) => n.type === "composite", isUnion$1 = (n) => n.type === "union", isXor$1 = (n) => n.type === "xor", isExclusion = (n) => n.type === "exclusion", isArrayNode = (n) => n.type === "array", isLink = (n) => n.type === "linkObject", isNamedLink = (n) => n.type === "link", isRequestBody = (n) => n.type === "requestBody", isResponseBody = (n) => n.type === "responseObject", isResponse = (n) => n.type === "response", isHeader = (n) => n.type === "headerObject", isNamedHeader = (n) => n.type === "header", isParameter$1 = (n) => n.type === "parameter", isCallback = (n) => n.type === "callback", isOperation = (n) => n.type === "operation", isPathItem = (n) => n.type === "pathItemObject", isReference = (n) => n.type === "reference", isObjectNode = (n) => n.type === "object", isParameter = (n) => n.type === "parameter", isParameterObject = (n) => n.type === "parameterObject", isHeaderObject = (n) => n.type === "headerObject", isUnion = (n) => n.type === "union", isComposite = (n) => n.type === "composite", isXor = (n) => n.type === "xor", murmurHash = (n, e) => {
typeof n == "string" && (n = new TextEncoder().encode(n));
let t, r, i, s;
const o = n.length & 3, a = n.length - o;
t = e;
const c = 3432918353, f = 461845907;
for (s = 0; s < a; )
i = n[s] & 255 | (n[++s] & 255) << 8 | (n[++s] & 255) << 16 | (n[++s] & 255) << 24, ++s, i = (i & 65535) * c + (((i >>> 16) * c & 65535) << 16) & 4294967295, i = i << 15 | i >>> 17, i = (i & 65535) * f + (((i >>> 16) * f & 65535) << 16) & 4294967295, t ^= i, t = t << 13 | t >>> 19, r = (t & 65535) * 5 + (((t >>> 16) * 5 & 65535) << 16) & 4294967295, t = (r & 65535) + 27492 + (((r >>> 16) + 58964 & 65535) << 16);
switch (i = 0, o) {
case 3:
i ^= (n[s + 2] & 255) << 16;
case 2:
i ^= (n[s + 1] & 255) << 8;
case 1:
i ^= n[s] & 255, i = (i & 65535) * c + (((i >>> 16) * c & 65535) << 16) & 4294967295, i = i << 15 | i >>> 17, i = (i & 65535) * f + (((i >>> 16) * f & 65535) << 16) & 4294967295, t ^= i;
break;
}
return t ^= n.length, t ^= t >>> 16, t = (t & 65535) * 2246822507 + (((t >>> 16) * 2246822507 & 65535) << 16) & 4294967295, t ^= t >>> 13, t = (t & 65535) * 3266489909 + (((t >>> 16) * 3266489909 & 65535) << 16) & 4294967295, t ^= t >>> 16, t >>> 0;
};
class Converter {
constructor(e) {
this._logger = e;
}
recursionDepth = 0;
findInSection(e, t, r) {
const i = e[r];
if (i)
try {
const s = i.filter((o) => o.referenceName === t.reference);
return s === void 0 || s.length === 0 ? void 0 : (s.length > 1 && this._logger.warn(`mulitiple references of ${r} ${t.reference} found, using first instance`), s[0]);
} catch (s) {
this._logger.error(s, { node: t, section: r });
return;
}
}
find(e, t) {
for (const r of Object.keys(e)) {
const i = this.findInSection(e, t, r);
if (i)
return { component: i, section: r };
}
this._logger.warn(`missing reference: ${t.reference}`);
}
addComponent(e, t, r, i) {
r[i] ??= {}, r[i][e] = t, r[i][e].name ??= e;
}
convertParsedNode(e, t, r) {
try {
if (this.recursionDepth += 1, this.recursionDepth > 20)
return {
type: e.type
};
if (isReference$1(e)) {
const i = this.find(t, e);
return i && this.addComponent(
i.component.name,
this.convertParsedNode(i.component.definition, t, r),
r,
i.section
), this.convertReference(e);
} else {
if (isPrimative(e))
return this.convertPrimative(e);
if (isParameter$1(e))
return this.convertParameter(e, t, r);
if (isResponse(e))
return this.convertResponse(e, t, r);
if (isResponseBody(e))
return this.convertResponseObject(e, t, r, 0);
if (isHeader(e))
return this.convertHeader(e, t, r);
if (isMediaType(e))
return this.convertMediaType(e, t, r);
if (isObjectNode$1(e))
return this.convertObject(e, t, r);
if (isArrayNode(e))
return this.convertArray(e, t, r);
if (isUnion$1(e))
return this.convertUnion(e, t, r);
if (isXor$1(e))
return this.convertXor(e, t, r);
if (isComposite$1(e))
return this.convertComposite(e, t, r);
if (isExclusion(e))
return this.convertExclusion(e, t, r);
if (isRequestBody(e))
return this.convertRequestBody(e, t, r);
}
} catch {
this._logger.error("error", { parsedNode: e });
} finally {
this.recursionDepth -= 1;
}
return {
type: e.type
};
}
convertReference(e) {
return {
type: "reference",
$ref: e.reference.replace("#/components/schemas/", "#/components/models/"),
summary: e.summary,
description: e.description
};
}
convertPrimative(e) {
return {
...e
};
}
convertObject(e, t, r) {
return {
...e,
properties: e.properties.map((i) => ({ ...i, definition: this.convertParsedNode(i.definition, t, r) })),
additionalProperty: e.additionalProperty ? this.convertParsedNode(e.additionalProperty, t, r) : void 0
};
}
compress(e, t, r) {
if (e.definitions.length === 1)
return this.convertParsedNode(e.definitions[0], t, r);
let i, s;
if ("discriminatorPropertyName" in e && e.discriminatorPropertyName && (i = e.discriminatorPropertyName, e.discriminatorMapping)) {
s = {};
for (const [o, a] of Object.entries(e.discriminatorMapping))
s[o] = this.convertParsedNode(a, t, r);
}
return {
...e,
definitions: e.definitions.map((o) => this.convertParsedNode(o, t, r)),
discriminatorPropertyName: i,
discriminatorMapping: s
};
}
convertUnion(e, t, r) {
return this.compress(e, t, r);
}
convertXor(e, t, r) {
return this.compress(e, t, r);
}
convertComposite(e, t, r) {
return this.compress(e, t, r);
}
convertExclusion(e, t, r) {
return {
...e,
definition: this.convertParsedNode(e.definition, t, r)
};
}
convertArray(e, t, r) {
return {
...e,
definition: this.convertParsedNode(e.definition, t, r)
};
}
convertParameter(e, t, r) {
let i = { type: "error" };
return e.definition ? i = this.convertParsedNode(e.definition, t, r) : e.content && (i = this.convertParsedNode(e.content[0], t, r)), {
type: "parameterObject",
name: e.name,
properties: [
{
type: "parameter",
name: e.name,
description: e.description,
required: e.required,
deprecated: e.deprecated,
allowEmptyValue: e.allowEmptyValue,
style: e.style,
explode: e.explode,
allowReserved: e.allowReserved,
extensions: e.extensions,
definition: i
}
]
};
}
convertResponse(e, t, r) {
const i = Number.isNaN(Number(e.status)) ? 200 : Number(e.status);
let s, o;
if (isReference$1(e.definition)) {
const a = this.findInSection(t, e.definition, "responses");
s = a?.name, o = a?.definition;
} else
s = e.definition.name ?? e.name, o = e.definition;
if (o) {
const a = this.convertResponseObject(o, t, r, i), c = `${a.name ?? s ?? "_" + String(murmurHash(JSON.stringify(a), 42))}${i}`, f = `#/components/responseObjects/${c}`;
return this.addComponent(c, a, r, "responseObjects"), { type: "reference", $ref: f };
} else
return { type: "responseObject", status: i, name: s, description: e.description ?? "" };
}
convertResponseObject(e, t, r, i) {
let s, o;
if (e.headers) {
const a = [];
e.headers?.forEach((c) => {
const f = this.convertParsedNode(c.definition, t, r);
a.push({
...f,
name: c.name
});
}), o = { type: "headerObject", properties: a };
}
return e.links && this._logger.warn("Response links not supported yet"), e.content?.forEach((a) => {
s ??= {}, s[a.name] = this.convertParsedNode(a.definition, t, r);
}), {
type: "responseObject",
status: i,
name: e.name,
description: e.description,
headers: o,
"content-type": s
};
}
convertRequestBody(e, t, r) {
const i = {}, s = e.content?.length === 1 ? e.name : void 0;
return e.content?.forEach((o) => {
let a = this.convertParsedNode(o.definition, t, r);
if (!isReference(a)) {
const c = `${a.name ?? s ?? "_" + String(murmurHash(JSON.stringify(a), 42))}`;
a.name = c;
const f = `#/components/requestObjects/${c}`;
this.addComponent(c, a, r, "requestObjects"), a = { type: "reference", $ref: f };
}
i[o.name] = a;
}), { ...e, name: e.name ?? "", type: "request", "content-type": i };
}
convertHeader(e, t, r) {
let i = { type: "error" };
return e.definition ? i = this.convertParsedNode(e.definition, t, r) : e.content && (i = this.convertParsedNode(e.content[0], t, r)), {
name: e.name ?? "",
type: "header",
description: e.description,
required: e.required,
deprecated: e.deprecated,
allowEmptyValue: e.allowEmptyValue,
style: e.style,
explode: e.explode,
allowReserved: e.allowReserved,
extensions: e.extensions,
definition: i
};
}
convertMediaType(e, t, r) {
return e.definition ? this.convertParsedNode(e.definition, t, r) : { type: "null" };
}
}
class OpenApiOptimizer {
constructor(e) {
this._logger = e, this._converter = new Converter(e);
}
_converter;
optimize(e) {
const t = this.optimizePaths(e);
return this.optimizeComponents(t.components), this.buildPathRegex(t), t;
}
lookupReference(e, t, r) {
const i = t[r]?.find((s) => s.referenceName === e.reference);
if (!i) {
this._logger.warn(`Unable to find ${r}: ${e.reference}`);
return;
}
return i;
}
lookupComponent(e, t, r) {
const i = e.$ref.substring(e.$ref.lastIndexOf("/") + 1), s = Object.entries(t[r] ?? {}).find((o) => o[0] === i);
if (s)
return s[1];
}
createParameterObject(e, t, r) {
const i = [];
return e.forEach((s) => {
if (isReference$1(s)) {
const o = this.lookupReference(s, t, "parameters");
o && (i.push({
type: "reference",
$ref: o.referenceName
}), this._converter.addComponent(
o.name,
this._converter.convertParsedNode(o.definition, t, r),
r,
"parameters"
));
} else s.definition && i.push(this._converter.convertParsedNode(s, t, r));
}), {
type: "composite",
definitions: i
};
}
optimizeParameters(e, t, r, i, s) {
let o;
if (r.length === 1) {
const a = this._converter.convertParsedNode(r[0], e.components, t);
isReference(a) ? o = a : (a.name = i, this._converter.addComponent(i, a, t, s), o = {
type: "reference",
$ref: `#/components/${s}/${i}`
});
} else
this._converter.addComponent(i, this.createParameterObject(r, e.components, t), t, s), o = {
type: "reference",
$ref: `#/components/${s}/${i}`
};
return o;
}
optimizeOperationParameters(e, t, r) {
let i, s, o, a;
if (r.parameters)
for (const u of r.parameters)
switch ((isReference$1(u) ? this.lookupReference(u, e.components, "parameters")?.definition : u)?.in) {
case "path":
i ??= [], i.push(u);
break;
case "header":
s ??= [], s.push(u);
break;
case "query":
o ??= [], o.push(u);
break;
case "cookie":
a ??= [], a.push(u);
break;
}
let c, f, p, l, h;
const d = `${r.operationId}`;
return i && (c = this.optimizeParameters(e, t, i, d, "pathParameters")), s && (f = this.optimizeParameters(e, t, s, d, "headerParameters")), o && (p = this.optimizeParameters(e, t, o, d, "queryParameters")), a && (l = this.optimizeParameters(e, t, a, d, "cookieParameters")), { pathParameter: c, headerParameter: f, queryParameter: p, cookieParameter: l, pathRegex: h };
}
optimizeOperationResponses(e, t, r) {
let i;
if (r.responses) {
const s = `${r.operationId}`, o = `#/components/responses/${s}`, a = { type: "response", name: s, responses: [] };
this._converter.addComponent(s, a, t, "responses"), r.responses.forEach((c) => {
const f = isReference$1(c) ? this.lookupReference(c, e.components, "responses")?.definition : c;
if (f) {
f.name ??= s;
const p = this._converter.convertParsedNode(f, e.components, t);
a.responses.push(p);
}
}), i = { type: "reference", $ref: o };
}
return i;
}
optimizeOperationRequest(e, t, r) {
let i;
if (r.requestBody) {
const s = isReference$1(r.requestBody) ? this.lookupReference(r.requestBody, e.components, "requests")?.definition : r.requestBody;
if (s) {
const o = s.name ?? `${r.operationId}`, a = `#/components/requests/${o}`, c = { ...this._converter.convertParsedNode(s, e.components, t), name: o, content: void 0 };
this._converter.addComponent(o, c, t, "requests"), i = { type: "reference", $ref: a };
}
}
return i;
}
optimizeOperation(e, t, r) {
const i = this.optimizeOperationResponses(e, t, r), s = this.optimizeOperationRequest(e, t, r), { pathParameter: o, headerParameter: a, queryParameter: c, cookieParameter: f, pathRegex: p } = this.optimizeOperationParameters(
e,
t,
r
);
return {
type: "operation",
method: r.method,
tags: r.tags,
description: r.description,
summary: r.summary,
operationId: r.operationId,
deprecated: r.deprecated,
//callbacks: operation.callbacks,
security: r.security,
extensions: r.extensions,
response: i,
request: s,
pathParameter: o,
headerParameter: a,
queryParameter: c,
cookieParameter: f,
pathRegex: p
};
}
optimizePathItem(e, t, r) {
return r.operations.map((i) => this.optimizeOperation(e, t, i));
}
optimizePaths(e) {
const t = {}, r = [];
return e.paths.forEach((i) => {
if (i.definition)
if (isReference$1(i.definition))
this._logger.warn("Path references not supported");
else {
const s = this.optimizePathItem(e, t, i.definition);
r.push({ type: "path", name: i.name, operations: s });
}
}), { ...e, paths: r, components: t };
}
compactComposite(e) {
const t = [], r = [];
for (const s of e.definitions)
isObjectNode(s) || isParameterObject(s) || isHeaderObject(s) ? t.push(s) : r.push(s);
const i = [];
return t.forEach((s) => i.push(...s.properties)), i.length > 0 && r.push({ type: "object", properties: i }), r.length > 1 ? { ...e, definitions: r } : { ...e, definitions: void 0, ...r[0] };
}
optimizeComponentRecord(e) {
const t = Object.entries(e);
for (const r of t) {
const i = r[1];
if (isComposite(i)) {
const s = this.compactComposite(i);
e[r[0]] = s;
}
}
return e;
}
optimizeComponents(e) {
e.models && (e.models = this.optimizeComponentRecord(e.models)), e.requests && (e.requests = this.optimizeComponentRecord(e.requests)), e.requestObjects && (e.requestObjects = this.optimizeComponentRecord(e.requestObjects)), e.responses && (e.responses = this.optimizeComponentRecord(e.responses)), e.responseObjects && (e.responseObjects = this.optimizeComponentRecord(e.responseObjects)), e.parameters && (e.parameters = this.optimizeComponentRecord(e.parameters)), e.headers && (e.headers = this.optimizeComponentRecord(e.headers)), e.callbacks && (e.callbacks = this.optimizeComponentRecord(e.callbacks)), e.pathItems && (e.pathItems = this.optimizeComponentRecord(e.pathItems)), e.pathParameters && (e.pathParameters = this.optimizeComponentRecord(e.pathParameters)), e.headerParameters && (e.headerParameters = this.optimizeComponentRecord(e.headerParameters)), e.queryParameters && (e.queryParameters = this.optimizeComponentRecord(e.queryParameters)), e.cookieParameters && (e.cookieParameters = this.optimizeComponentRecord(e.cookieParameters));
}
getPathParamRegex(e, t) {
let r;
const i = this.lookupComponent(e, t, "pathParameters") ?? this.lookupComponent(e, t, "parameters");
return i ? isObjectNode(i) || isParameterObject(i) ? i.properties.forEach((s) => {
if (isParameter(s)) {
const o = s.name, a = s.definition;
r = { ...r, [o]: a.format ?? a.type };
}
}) : (isComposite(i) || isUnion(i) || isXor(i)) && i.definitions?.forEach((s) => {
isReference(s) && (r = { ...r, ...this.getPathParamRegex(s, t) });
}) : this._logger.warn(`Unable to find path parameter: (${e.$ref})`), r;
}
buildPathRegex(e) {
e.paths.forEach((t) => {
t.operations.forEach((r) => {
r.pathParameter && (r.pathRegex = this.getPathParamRegex(r.pathParameter, e.components));
});
});
}
}
class Organizer {
constructor(e) {
this._logger = e;
}
organizeByTags(e) {
return e.tags.map((r) => {
const i = {
title: r.name,
apiName: e.apiName,
description: r.description,
version: e.version,
tags: [r],
paths: this.organizePathsByTags(e, r.name),
components: {}
};
return i.components = this.organizeComponentsByTags(e, i.paths), i;
});
}
organizePathsByTags(e, t) {
const r = e.paths.map((s) => {
const o = s.definition;
if (o === void 0)
return;
if (isReference$1(o))
return s;
const a = o.operations;
if (a !== void 0 && a.some((c) => c.tags?.includes(t)))
return s;
}), i = (s) => s !== void 0;
return r.filter(i);
}
organizeComponentsByTags(e, t) {
const r = {
models: [],
requests: [],
responses: [],
parameters: [],
headers: [],
securitySchemes: [],
callbacks: [],
pathItems: []
};
return t.forEach((i) => {
const s = i.definition;
s !== void 0 && this.traverseReferences(e, r, s);
}), r.models = r.models.sort((i, s) => i.name.localeCompare(s.name)), r.requests = r.requests.sort((i, s) => i.name.localeCompare(s.name)), r.responses = r.responses.sort((i, s) => i.name.localeCompare(s.name)), r.parameters = r.parameters.sort((i, s) => i.name.localeCompare(s.name)), r.headers = r.headers.sort((i, s) => i.name.localeCompare(s.name)), r.securitySchemes = r.securitySchemes.sort((i, s) => i.name.localeCompare(s.name)), r.callbacks = r.callbacks.sort((i, s) => i.name.localeCompare(s.name)), r.pathItems = r.pathItems.sort((i, s) => i.name.localeCompare(s.name)), r;
}
traverseReferences(e, t, r) {
if (isReference$1(r)) {
const { component: i, section: s } = this.find(e.components, r);
if (!i) {
this._logger.warn(`${r.reference} not found`);
return;
}
const o = this.addIfMissing(i, t, s);
o && this.traverseReferences(e, t, o.definition);
} else isPathItem(r) ? r.operations.forEach((i) => this.traverseReferences(e, t, i)) : isOperation(r) ? (r.parameters && r.parameters.forEach((i) => this.traverseReferences(e, t, i)), r.requestBody && this.traverseReferences(e, t, r.requestBody), r.responses && r.responses.forEach((i) => this.traverseReferences(e, t, i)), r.callbacks && r.callbacks.forEach((i) => this.traverseReferences(e, t, i))) : isRequestBody(r) && r.content ? r.content.forEach((i) => {
this.traverseReferences(e, t, i.definition);
}) : isMediaContent(r) ? this.traverseReferences(e, t, r.definition) : isMediaType(r) && r.definition ? this.traverseReferences(e, t, r.definition) : isObjectNode$1(r) ? (r.properties.forEach((i) => this.traverseReferences(e, t, i)), r.additionalProperty && this.traverseReferences(e, t, r.additionalProperty)) : isProperty(r) ? this.traverseReferences(e, t, r.definition) : isResponse(r) ? this.traverseReferences(e, t, r.definition) : isResponseBody(r) ? (r.content && r.content.forEach((i) => {
this.traverseReferences(e, t, i.definition);
}), r.headers && r.headers.forEach((i) => {
this.traverseReferences(e, t, i.definition);
}), r.links && r.links.forEach((i) => {
this.traverseReferences(e, t, i.definition);
})) : isNamedHeader(r) ? this.traverseReferences(e, t, r.definition) : isHeader(r) ? (r.definition && this.traverseReferences(e, t, r.definition), r.content && r.content.forEach((i) => {
this.traverseReferences(e, t, i.definition);
})) : isCallback(r) ? this.traverseReferences(e, t, r.definition) : isArrayNode(r) ? this.traverseReferences(e, t, r.definition) : isComposite$1(r) ? r.definitions.forEach((i) => {
this.traverseReferences(e, t, i);
}) : isExclusion(r) ? this.traverseReferences(e, t, r.definition) : isNamedLink(r) ? this.traverseReferences(e, t, r.definition) : isLink(r) || (isParameter$1(r) ? (r.definition && this.traverseReferences(e, t, r.definition), r.content && r.content.forEach((i) => {
this.traverseReferences(e, t, i.definition);
})) : isUnion$1(r) ? r.definitions.forEach((i) => {
this.traverseReferences(e, t, i);
}) : isXor$1(r) && r.definitions.forEach((i) => {
this.traverseReferences(e, t, i);
}));
}
findInSection(e, t, r) {
const i = e[r];
if (!i)
return;
const s = i.filter((o) => o.referenceName === t.reference);
if (!(s === void 0 || s.length === 0))
return s.length > 1 && this._logger.warn(`mulitiple references of ${r} ${t.reference} found, using first instance`), s[0];
}
find(e, t) {
for (const r of Object.keys(e)) {
const i = this.findInSection(e, t, r);
if (i)
return { component: i, section: r };
}
return {};
}
addIfMissing(e, t, r) {
const i = t[r];
if (!i.find((o) => o.referenceName === e.referenceName))
return i.push(e), e;
}
}
const childProcess = {}, registerPartials = (n, e) => {
const t = glob.sync(e);
for (const r of t) {
const i = Path.basename(r), s = Path.extname(i);
n.registerPartial(i.replace(s, ""), fs.readFileSync(r, "utf8"));
}
}, substituteParams = (n, e) => {
const t = /({.*?})/g, r = n.match(t);
if (!r) return n;
let i = n;
return r.forEach((s) => {
const o = e.get(s);
o && (i = i.replaceAll(s, o));
}), i;
}, writeFile = async (n, e, t, r) => {
const i = Path.join(n, e);
let s = JSON.stringify(t);
try {
s = await runPrettier(s, "json");
} catch (o) {
r.info(`Prettier error on ${i}`, o);
}
await fs.ensureDir(n), await fs.writeFile(i, s);
}, isEmpty = (n) => n.components.models === void 0 && n.components.responses === void 0 && n.components.requests === void 0 && n.components.pathParameters === void 0 && n.components.headerParameters === void 0 && n.components.queryParameters === void 0 && n.components.cookieParameters === void 0 && n.paths.length === 0, build = async (n, e, t) => {
if (n.generate === void 0) {
t.info("No generate config settings found");
return;
}
const r = new Organizer(t), i = new OpenApiOptimizer(t), s = /* @__PURE__ */ new Map(), o = e.title.trim(), a = /([^a-zA-Z0-9])+/g, c = o.replace(a, "-").toLocaleLowerCase();
if (s.set("{api}", c), n.debug) {
const f = substituteParams(n.debugPath, s);
await writeFile(f, `${e.title}.parsed.json`, e, t), await writeFile(f, `${e.title}.optimized.json`, i.optimize(e), t);
}
for (const f of Object.entries(n.generate)) {
const p = f[1], l = p.tags ?? n.tags, h = p.skipEmpty ?? n.skipEmpty, d = l ? r.organizeByTags(e) : [e];
for (const u of d) {
const b = u.title.trim().replace(a, "-").toLocaleLowerCase();
s.set("{name}", b);
const g = i.optimize(u);
if (!(h && isEmpty(g))) {
if (n.debug) {
const m = substituteParams(n.debugPath, s);
await writeFile(m, `${u.title}.parsed.json`, u, t), await writeFile(m, `${u.title}.optimized.json`, g, t);
}
await generate(n, p, s, g, t);
}
}
}
}, runScript = (n, e) => {
let t = !1;
const r = childProcess.fork(n);
return r.on("error", function(i) {
t || (t = !0, e(i));
}), r.on("exit", function(i) {
if (t) return;
t = !0;
const s = i === 0 ? void 0 : new Error("exit code " + i);
e(s);
}), r;
}, generate = async (n, e, t, r, i) => {
const s = [...n.sharedTemplates ?? [], ...e.additionalTemplates ?? []], o = await $RefParser.resolve(r);
if (e.script) {
const l = runScript(e.script, (h) => {
if (h) throw h;
});
l.on("message", (h) => {
h === "ready" && l.send(r);
});
return;
}
const a = createHandlebars(o);
registerPartials(a, s);
const c = await fs.readFile(e.template, "utf8"), f = a.compile(c), p = substituteParams(e.target, t);
await render(n, p, r, f, i);
}, render = async (n, e, t, r, i) => {
const s = { document: t }, o = Path.extname(e);
await fs.ensureDir(Path.dirname(e));
try {
let a = r(s, { data: /* @__PURE__ */ new Map(), allowProtoMethodsByDefault: !0 });
if (n.prettier)
try {
a = await runPrettier(a, o === ".ts" ? "typescript" : "babel");
} catch (c) {
i.info(`Prettier error on ${e}`, c);
}
await fs.writeFile(e, a, "utf8");
} catch (a) {
i.error(`Error generating ${e}`, a);
}
};
function IsDocument(n) {
return "components" in n;
}
const nonArraySchemaObjectType = ["string", "number", "boolean", "integer", "null"];
class OpenApiParser {
constructor(e) {
this._logger = e;
}
parse(e) {
const t = e.info.title, r = e.info.title, i = e.info.description, s = e.info.version, o = e.tags ? e.tags.map((f) => this.parseTag(f)) : [], a = e.components ? this.parseComponents(e.components) : {}, c = e.paths ? this.parsePaths(e.paths, a) : [];
return { title: t, apiName: r, description: i, version: s, components: a, paths: c, tags: o };
}
parseComponents(e) {
const t = {};
if (e.schemas) {
const r = Object.entries(e.schemas);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/schemas/${s}`, c = this.parseSchema(o);
t.models ??= [], t.models.push({ name: s, referenceName: a, definition: c });
}
}
if (e.securitySchemes) {
const r = Object.entries(e.securitySchemes);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/securitySchemes/${s}`, c = this.parseSecurityScheme(o);
t.securitySchemes ??= [], t.securitySchemes.push({ name: s, referenceName: a, definition: c });
}
}
if (e.parameters) {
const r = Object.entries(e.parameters);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/parameters/${s}`, c = this.parseParameter(o, t);
t.parameters ??= [], t.parameters.push({ name: s, referenceName: a, definition: c });
}
}
if (e.headers) {
const r = Object.entries(e.headers);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/headers/${s}`, c = this.parseHeaderObject(o, t);
t.headers ??= [], t.headers.push({ name: s, referenceName: a, definition: c });
}
}
if (e.requestBodies) {
const r = Object.entries(e.requestBodies);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/requestBodies/${s}`, c = this.parseRequestBody(o, t);
isRequestBody(c) && (c.name = s), t.requests ??= [], t.requests.push({ name: s, referenceName: a, definition: c });
}
}
if (e.responses) {
const r = Object.entries(e.responses);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/responses/${s}`, c = this.parseResponse(o, t);
t.responses ??= [], t.responses.push({ name: s, referenceName: a, definition: c });
}
}
if (e.callbacks) {
const r = Object.entries(e.callbacks);
for (const i of r) {
const s = i[0], o = i[1], a = `#/components/callbacks/${s}`, c = this.parseCallback(o, s, t);
t.callbacks ??= [], t.callbacks.push({ name: s, referenceName: a, definition: c });
}
}
return t;
}
parsePaths(e, t) {
const r = [], i = Object.entries(e);
for (const s of i) {
const o = s[0], a = s[1], c = a ? this.parsePathItemObject(a, t) : void 0;
r.push({
type: "pathItem",
name: o,
definition: c
});
}
return r.sort((s, o) => {
const a = (s.name.match(/{[^}]+}/g) || []).length, c = (o.name.match(/{[^}]+}/g) || []).length;
if (a === 0 && c > 0) return -1;
if (a > 0 && c === 0) return 1;
if (a !== c) return a - c;
const f = s.name.split("/").filter(Boolean).length, p = o.name.split("/").filter(Boolean).length;
return f !== p ? p - f : s.name.localeCompare(o.name);
});
}
isReferenceObject(e) {
return "$ref" in e;
}
isArraySchemaObject(e) {
return e.type === "array";
}
isNonArraySchemaObject(e) {
return !("items" in e);
}
isMixedSchemaObject(e) {
return Array.isArray(e.type);
}
isAllOf(e) {
return e.allOf !== void 0;
}
isAnyOf(e) {
return e.anyOf !== void 0;
}
isOneOf(e) {
return e.oneOf !== void 0;
}
lookupReference(e, t, r) {
const i = t[r]?.find((s) => s.referenceName === e.reference);
if (!i) {
this._logger.warn(`Unable to find ${r}: ${e.reference}`);
return;
}
return i;
}
getExtensions(e) {
const t = {}, r = Object.keys(e).filter((i) => i.startsWith("x-"));
if (r.length !== 0)
return r.forEach((i) => t[i] = e[i]), t;
}
getModifiers(e) {
return {
name: e.title,
description: e.description,
format: e.format,
default: e.default,
multipleOf: e.multipleOf,
maximum: e.maximum,
exclusiveMaximum: e.exclusiveMaximum,
minimum: e.minimum,
exclusiveMinimum: e.exclusiveMinimum,
maxLength: e.maxLength,
minLength: e.minLength,
pattern: e.pattern,
additionalProperties: e.additionalProperties,
maxItems: e.maxItems,
minItems: e.minItems,
uniqueItems: e.uniqueItems,
maxProperties: e.maxProperties,
minProperties: e.minProperties,
required: e.required,
enum: e.enum,
nullable: e.nullable,
discriminator: e.discriminator,
readOnly: e.readOnly,
writeOnly: e.writeOnly,
deprecated: e.deprecated,
extensions: this.getExtensions(e),
example: e.example
};
}
parseSchema(e, t) {
if (this.isReferenceObject(e))
return this.createReference(e);
e.type ??= t ?? e.enum ? "string" : "object";
const r = this.getModifiers(e);
if (this.isArraySchemaObject(e))
return this.parseArray(e, r);
if (this.isMixedSchemaObject(e))
return this.parseMixedSchemaObject(e, r);
if (this.isNonArraySchemaObject(e)) {
if (this.isAllOf(e))
return this.parseAllOf(e, r);
if (this.isAnyOf(e))
return this.parseAnyOf(e, r);
if (this.isOneOf(e))
return this.parseOneOf(e, r);
if (e.not)
return this.parseNotObject(e.not, r);
if (e.type === "object")
return this.parseObject(e, r);
if (e.type && nonArraySchemaObjectType.includes(e.type))
return this.createLiteral(e, r);
}
throw this._logger.error(e), new Error("Unknown schema");
}
parseMediaContent(e, t, r) {
if (!e) return;
const i = [], s = Object.entries(e);
for (const o of s) {
const a = o[0], c = o[1], f = this.parseMediaTypeObject(c, t, r);
i.push({
type: "mediaContent",
name: a,
definition: f
});
}
return i;
}
parseMediaEncoding(e, t) {
if (!e) return;
const r = [];
if (e) {
const i = Object.entries(e);
for (const s of i) {
const o = s[0], a = s[1], c = this.parseEncodingObject(a, t);
r.push({
name: o,
...c
});
}
}
return r;
}
parseHeaders(e, t) {
if (!e) return;
const r = [], i = Object.entries(e);
for (const s of i) {
const o = s[0], a = s[1], c = this.parseHeaderObject(a, t);
r.push({
type: "header",
name: o,
definition: c
});
}
return r;
}
parseLinks(e) {
if (!e) return;
const t = [], r = Object.entries(e);
for (const i of r) {
const s = i[0], o = i[1], a = this.parseLinkObject(o);
t.push({
type: "link",
name: s,
definition: a
});
}
return t;
}
parseRequestBody(e, t, r) {
if (this.isReferenceObject(e))
return this.createReference(e);
const i = e.description, s = e.required, o = this.getExtensions(e), a = this.parseMediaContent(e.content, "readOnly", t);
return {
type: "requestBody",
name: r,
description: i,
required: s,
extensions: o,
content: a
};
}
omitProperties(e, t, r) {
const i = [];
if (isObjectNode$1(e)) {
for (const s of e.properties)
if (s.definition[t] !== !0)
if (isReference$1(s.definition)) {
const o = this.lookupReference(s.definition, r, "models");
if (o) {
const a = this.createOmitDefinition(o.definition, t, r);
a ? (!isObjectNode$1(a) || a.properties.length > 0) && i.push({
...s,
definition: a
}) : i.push(s);
}
} else
i.push(s);
}
return i;
}
createOmitDefinition(e, t, r) {
if (isReference$1(e)) {
const i = this.lookupReference(e, r, "models");
if (i)
return this.createOmitDefinition(i.definition, t, r);
} else if (isObjectNode$1(e)) {
const i = this.omitProperties(e, t, r);
if (i.length !== e.properties.length)
return {
...e,
properties: i
};
} else if (isComposite$1(e)) {
const i = [...e.definitions];
let s = !1;
for (let o = 0; o < i.length; ++o) {
const a = this.createOmitDefinition(i[o], t, r);
a && (s = !0, i[o] = a);
}
if (s)
return {
...e,
definitions: i
};
}
}
parseMediaTypeObject(e, t, r) {
const i = e.schema ? this.parseSchema(e.schema) : void 0, s = this.parseMediaEncoding(e.encoding, r);
let o;
if (i)
if (isReference$1(i)) {
const a = this.lookupReference(i, r, "models");
a && (o = this.createOmitDefinition(a.definition, t, r));
} else
o = this.createOmitDefinition(i, t, r);
return {
type: "mediaTypeObject",
definition: o ?? i,
encodings: s
};
}
parseEncodingObject(e, t) {
const r = e.contentType, i = e.style, s = e.explode, o = e.allowReserved, a = this.parseHeaders(e.headers, t);
return {
type: "encoding",
contentType: r,
style: i,
explode: s,
allowReserved: o,
headers: a
};
}
parseHeaderObject(e, t) {
return this.isReferenceObject(e) ? this.createReference(e) : {
type: "headerObject",
...this.parseParameterBaseObject(e, t)
};
}
parseLinkObject(e) {
if (this.isReferenceObject(e))
return this.createReference(e);
const t = e.server, r = e.operationRef, i = e.operationId, s = e.requestBody, o = e.description, a = e.parameters;
return {
type: "linkObject",
server: t,
operationRef: r,
operationId: i,
requestBody: s,
description: o,
parameters: a
};
}
parseParameterSchema(e, t) {
const r = this.parseSchema(e);
return t && r.type !== "array" ? {
type: "array",
definition: r
} : r;
}
parseParameterBaseObject(e, t) {
const r = e.description, i = e.required, s = e.deprecated, o = e.allowEmptyValue, a = e.style, c = e.explode, f = e.allowReserved, p = e.schema ? this.parseParameterSchema(e.schema, a) : void 0, l = this.parseMediaContent(e.content, "readOnly", t);
return {
description: r,
required: i,
deprecated: s,
allowEmptyValue: o,
style: a,
explode: c,
allowReserved: f,
definition: p,
content: l
};
}
parseResponse(e, t) {
if (this.isReferenceObject(e))
return this.createReference(e);
const r = e.description, i = this.parseHeaders(e.headers, t), s = this.parseMediaContent(e.content, "writeOnly", t), o = this.parseLinks(e.links);
return {
type: "responseObject",
description: r,
headers: i,
content: s,
links: o
};
}
parseParameter(e, t) {
return this.isReferenceObject(e) ? this.createReference(e) : {
type: "parameter",
name: e.name,
in: e.in,
...this.parseParameterBaseObject(e, t)
};
}
parseSecurityScheme(e) {
if (this.isReferenceObject(e))
return this.createReference(e);
const t = "securityScheme", r = e.description;
switch (e.type) {
case "http":
return { type: t, description: r, bearerFormat: e.bearerFormat, scheme: e.scheme };
case "apiKey":
return { type: t, description: r, name: e.name, in: e.in };
case "openIdConnect":
return { type: t, description: r, openIdConnectUrl: e.openIdConnectUrl };
case "oauth2":
return { type: t, description: r, flows: e.flows };
default:
throw new Error(`Unknown security scheme ${e}`);
}
}
parseCallback(e, t, r) {
const i = this.parsePathItemObject(e, r);
return { type: "callback", name: t, definition: i };
}
parsePathItemObject(e, t) {
if (this.isReferenceObject(e))
return this.createReference(e);
const r = [];
for (const i in OpenAPIV3.HttpMethods) {
const s = e[i.toLowerCase()];
if (!s)
continue;
const o = this.parseOperationObject(s, i.toLowerCase(), e.parameters, t);
r.push(o);
}
return {
type: "pathItemObject",
operations: r
//parameters,
};
}
parseOperationObject(e, t, r, i) {
let s, o, a;
const c = e.tags, f = e.description, p = e.summary, l = e.operationId, h = e.deprecated, d = this.getExtensions(e);
(c === void 0 || c.length === 0) && this._logger.warn(`Operation ${l} does not have any tags`);
const u = [...r ?? [], ...e.parameters ?? []];
for (const m of u) {
const y = this.parseParameter(m, i);
s ??= [], s.push(y);
}
const b = e.requestBody ? this.parseRequestBody(e.requestBody, i, l) : void 0;
if (e.responses) {
o = [];
const m = Object.entries(e.responses);
for (const y of m) {
const P = y[0], v = y[1];
Number.isNaN(Number(P)) && this._logger.warn(`Operation ${l} has invalid response status: ${P}. Will use 200 instead`);
const O = this.parseResponse(v, i);
o.push({
type: "response",
status: P,
definition: O
});
}
}
if (e.callbacks) {
a = [];
const m = Object.entries(e.callbacks);
for (const y of m) {
const P = y[0], v = y[1];
a.push(this.parseCallback(v, P, i));
}
}
const g = e.security;
return {
type: "operation",
method: t,
tags: c,
description: f,
summary: p,
operationId: l,
deprecated: h,
parameters: s,
requestBody: b,
responses: o,
callbacks: a,
security: g,
extensions: d
};
}
parseTag(e) {
return { type: "tag", name: e.name, description: e.description };
}
createReference(e) {
return {
type: "reference",
reference: e.$ref,
summary: e.summary,
description: e.description
};
}
createLiteral(e, t) {
return {
type: e.type,
...t
};
}
createProperty(e, t, r) {
const i = this.parseSchema(t), s = i.description;
return i.description = void 0, {
name: e,
type: "property",
definition: i,
required: r,
description: s
};
}
parseObject(e, t) {
const r = [];
if (e.properties) {
const s = Object.entries(e.properties);
for (const o of s) {
if (o[0].length === 0) {
this._logger.warn("property is missing name");
continue;
}
const a = e.required?.find((f) => f === o[0]) !== void 0, c = this.createProperty(o[0], o[1], a);
r.push(c);
}
}
let i;
return e.additionalProperties && (typeof e.additionalProperties == "boolean" ? e.additionalProperties && (i = { type: "object", properties: [] }) : i = this.parseSchema(e.additionalProperties)), {
...t,
type: "object",
properties: r,
additionalProperty: i,
additionalProperties: void 0,
required: void 0
};
}
parseAllOf(e, t) {
const i = e.allOf.map((s) => this.parseSchema(s, e.type));
return {
type: "composite",
...t,
definitions: i
};
}
parseAnyOf(e, t) {
const i = e.anyOf.map((s) => this.parseSchema(s, e.type));
return {
type: "union",
...t,
definitions: i
};
}
parseOneOf(e, t) {
const i = e.oneOf.map((a) => this.parseSchema(a, e.type));
let s, o;
if (e.discriminator && (s = e.discriminator.propertyName, e.discriminator.mapping)) {
o = {};
for (const [a, c] of Object.entries(e.discriminator.mapping)) {
const f = this.parseDiscriminatorReference(c);
o[a] = f;
}
}
return {
type: "xor",
...t,
definitions: i,
discriminatorPropertyName: s,
discriminatorMapping: o
};
}
parseDiscriminatorReference(e) {
const t = {
$ref: e
};
return this.createReference(t);
}
parseNotObject(e, t) {
return {
type: "exclusion",
...t,
definition: this.parseSchema(e)
};
}
parseArray(e, t) {
const r = e.items !== void 0, i = e.enum !== void 0;
return {
type: "array",
...t,
enum: void 0,
definition: r ? this.parseSchema(e.items) : i ? { type: "string", enum: e.enum } : { type: "object", properties: [] }
};
}
parseMixedSchemaObject(e, t) {
if (!e.type)
return this._logger.warn("Mixed schema missing type array"), {
type: "error"
};
const r = e.type.map((i) => i === "object" ? this.parseObject({ ...e, type: i }, t) : i === "array" ? this.parseArray({ ...e, type: i }, t) : nonArraySchemaObjectType.includes(i) ? this.createLiteral({ ...e, type: i }, t) : { type: "error" });
return {
type: "union",
...t,
enum: void 0,
definitions: r
};
}
}
class OpenApiParser3 extends OpenApiParser {
constructor(e) {
super(e);
}
}
class OpenApiParser3_1 extends OpenApiParser {
constructor(e) {
super(e);
}
parseComponents(e) {
const { models: t, requests: r, responses: i, parameters: s, headers: o, securitySchemes: a, callbacks: c } = super.parseComponents(e), f = [];
if (e.pathItems) {
const p = Object.entries(e.pathItems);
for (const l of p) {
const h = l[0], d = l[1], u = `#/components/pathItems/${h}`, b = this.parsePathItemObject(d, { models: t, requests: r, responses: i, parameters: s, headers: o, securitySchemes: a, callbacks: c });
f.push({ name: h, referenceName: u, definition: b });
}
}
return { models: t, requests: r, responses: i, parameters: s, headers: o, securitySchemes: a, callbacks: c, pathItems: f };
}
}
class OpenApiParserFactor {
static isOpenAPIV2(e) {
return "swagger" in e && e.swagger === "2.0";
}
static isOpenAPIV3(e) {
return "openapi" in e && e.openapi !== "3.1.0";
}
static isOpenAPIV3_1(e) {
return "openapi" in e && e.openapi === "3.1.0";
}
static parse(e, t) {
if (this.isOpenAPIV2(e))
throw new Error("v2 not supported");
if (this.isOpenAPIV3(e))
re