openapi-typescript
Version:
Convert OpenAPI 3.0 & 3.1 schemas to TypeScript
1,358 lines (1,343 loc) • 75 kB
JavaScript
"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 __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
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 __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
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// ../../node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/symbols.js
var require_symbols = __commonJS({
"../../node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/symbols.js"(exports, module2) {
"use strict";
var isHyper = typeof process !== "undefined" && process.env.TERM_PROGRAM === "Hyper";
var isWindows = typeof process !== "undefined" && process.platform === "win32";
var isLinux = typeof process !== "undefined" && process.platform === "linux";
var common = {
ballotDisabled: "\u2612",
ballotOff: "\u2610",
ballotOn: "\u2611",
bullet: "\u2022",
bulletWhite: "\u25E6",
fullBlock: "\u2588",
heart: "\u2764",
identicalTo: "\u2261",
line: "\u2500",
mark: "\u203B",
middot: "\xB7",
minus: "\uFF0D",
multiplication: "\xD7",
obelus: "\xF7",
pencilDownRight: "\u270E",
pencilRight: "\u270F",
pencilUpRight: "\u2710",
percent: "%",
pilcrow2: "\u2761",
pilcrow: "\xB6",
plusMinus: "\xB1",
question: "?",
section: "\xA7",
starsOff: "\u2606",
starsOn: "\u2605",
upDownArrow: "\u2195"
};
var windows = Object.assign({}, common, {
check: "\u221A",
cross: "\xD7",
ellipsisLarge: "...",
ellipsis: "...",
info: "i",
questionSmall: "?",
pointer: ">",
pointerSmall: "\xBB",
radioOff: "( )",
radioOn: "(*)",
warning: "\u203C"
});
var other = Object.assign({}, common, {
ballotCross: "\u2718",
check: "\u2714",
cross: "\u2716",
ellipsisLarge: "\u22EF",
ellipsis: "\u2026",
info: "\u2139",
questionFull: "\uFF1F",
questionSmall: "\uFE56",
pointer: isLinux ? "\u25B8" : "\u276F",
pointerSmall: isLinux ? "\u2023" : "\u203A",
radioOff: "\u25EF",
radioOn: "\u25C9",
warning: "\u26A0"
});
module2.exports = isWindows && !isHyper ? windows : other;
Reflect.defineProperty(module2.exports, "common", { enumerable: false, value: common });
Reflect.defineProperty(module2.exports, "windows", { enumerable: false, value: windows });
Reflect.defineProperty(module2.exports, "other", { enumerable: false, value: other });
}
});
// ../../node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/index.js
var require_ansi_colors = __commonJS({
"../../node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/index.js"(exports, module2) {
"use strict";
var isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
var ANSI_REGEX = /[\u001b\u009b][[\]#;?()]*(?:(?:(?:[^\W_]*;?[^\W_]*)\u0007)|(?:(?:[0-9]{1,4}(;[0-9]{0,4})*)?[~0-9=<>cf-nqrtyA-PRZ]))/g;
var hasColor = () => {
if (typeof process !== "undefined") {
return process.env.FORCE_COLOR !== "0";
}
return false;
};
var create = () => {
const colors = {
enabled: hasColor(),
visible: true,
styles: {},
keys: {}
};
const ansi = (style2) => {
let open = style2.open = `\x1B[${style2.codes[0]}m`;
let close = style2.close = `\x1B[${style2.codes[1]}m`;
let regex = style2.regex = new RegExp(`\\u001b\\[${style2.codes[1]}m`, "g");
style2.wrap = (input, newline) => {
if (input.includes(close))
input = input.replace(regex, close + open);
let output = open + input + close;
return newline ? output.replace(/\r*\n/g, `${close}$&${open}`) : output;
};
return style2;
};
const wrap = (style2, input, newline) => {
return typeof style2 === "function" ? style2(input) : style2.wrap(input, newline);
};
const style = (input, stack) => {
if (input === "" || input == null)
return "";
if (colors.enabled === false)
return input;
if (colors.visible === false)
return "";
let str = "" + input;
let nl = str.includes("\n");
let n = stack.length;
if (n > 0 && stack.includes("unstyle")) {
stack = [.../* @__PURE__ */ new Set(["unstyle", ...stack])].reverse();
}
while (n-- > 0)
str = wrap(colors.styles[stack[n]], str, nl);
return str;
};
const define = (name, codes, type) => {
colors.styles[name] = ansi({ name, codes });
let keys = colors.keys[type] || (colors.keys[type] = []);
keys.push(name);
Reflect.defineProperty(colors, name, {
configurable: true,
enumerable: true,
set(value) {
colors.alias(name, value);
},
get() {
let color = (input) => style(input, color.stack);
Reflect.setPrototypeOf(color, colors);
color.stack = this.stack ? this.stack.concat(name) : [name];
return color;
}
});
};
define("reset", [0, 0], "modifier");
define("bold", [1, 22], "modifier");
define("dim", [2, 22], "modifier");
define("italic", [3, 23], "modifier");
define("underline", [4, 24], "modifier");
define("inverse", [7, 27], "modifier");
define("hidden", [8, 28], "modifier");
define("strikethrough", [9, 29], "modifier");
define("black", [30, 39], "color");
define("red", [31, 39], "color");
define("green", [32, 39], "color");
define("yellow", [33, 39], "color");
define("blue", [34, 39], "color");
define("magenta", [35, 39], "color");
define("cyan", [36, 39], "color");
define("white", [37, 39], "color");
define("gray", [90, 39], "color");
define("grey", [90, 39], "color");
define("bgBlack", [40, 49], "bg");
define("bgRed", [41, 49], "bg");
define("bgGreen", [42, 49], "bg");
define("bgYellow", [43, 49], "bg");
define("bgBlue", [44, 49], "bg");
define("bgMagenta", [45, 49], "bg");
define("bgCyan", [46, 49], "bg");
define("bgWhite", [47, 49], "bg");
define("blackBright", [90, 39], "bright");
define("redBright", [91, 39], "bright");
define("greenBright", [92, 39], "bright");
define("yellowBright", [93, 39], "bright");
define("blueBright", [94, 39], "bright");
define("magentaBright", [95, 39], "bright");
define("cyanBright", [96, 39], "bright");
define("whiteBright", [97, 39], "bright");
define("bgBlackBright", [100, 49], "bgBright");
define("bgRedBright", [101, 49], "bgBright");
define("bgGreenBright", [102, 49], "bgBright");
define("bgYellowBright", [103, 49], "bgBright");
define("bgBlueBright", [104, 49], "bgBright");
define("bgMagentaBright", [105, 49], "bgBright");
define("bgCyanBright", [106, 49], "bgBright");
define("bgWhiteBright", [107, 49], "bgBright");
colors.ansiRegex = ANSI_REGEX;
colors.hasColor = colors.hasAnsi = (str) => {
colors.ansiRegex.lastIndex = 0;
return typeof str === "string" && str !== "" && colors.ansiRegex.test(str);
};
colors.alias = (name, color) => {
let fn = typeof color === "string" ? colors[color] : color;
if (typeof fn !== "function") {
throw new TypeError("Expected alias to be the name of an existing color (string) or a function");
}
if (!fn.stack) {
Reflect.defineProperty(fn, "name", { value: name });
colors.styles[name] = fn;
fn.stack = [name];
}
Reflect.defineProperty(colors, name, {
configurable: true,
enumerable: true,
set(value) {
colors.alias(name, value);
},
get() {
let color2 = (input) => style(input, color2.stack);
Reflect.setPrototypeOf(color2, colors);
color2.stack = this.stack ? this.stack.concat(fn.stack) : fn.stack;
return color2;
}
});
};
colors.theme = (custom) => {
if (!isObject(custom))
throw new TypeError("Expected theme to be an object");
for (let name of Object.keys(custom)) {
colors.alias(name, custom[name]);
}
return colors;
};
colors.alias("unstyle", (str) => {
if (typeof str === "string" && str !== "") {
colors.ansiRegex.lastIndex = 0;
return str.replace(colors.ansiRegex, "");
}
return "";
});
colors.alias("noop", (str) => str);
colors.none = colors.clear = colors.noop;
colors.stripColor = colors.unstyle;
colors.symbols = require_symbols();
colors.define = define;
return colors;
};
module2.exports = create();
module2.exports.create = create;
}
});
// src/index.ts
var src_exports = {};
__export(src_exports, {
COMMENT_HEADER: () => COMMENT_HEADER,
default: () => src_default
});
module.exports = __toCommonJS(src_exports);
var import_node_url2 = require("url");
// src/load.ts
var import_node_fs = __toESM(require("fs"), 1);
var import_node_path2 = __toESM(require("path"), 1);
var import_node_stream = require("stream");
var import_node_url = require("url");
var import_js_yaml = __toESM(require("js-yaml"), 1);
// src/utils.ts
var import_ansi_colors = __toESM(require_ansi_colors(), 1);
var import_node_path = require("path");
// ../../node_modules/.pnpm/supports-color@9.4.0/node_modules/supports-color/index.js
var import_node_process = __toESM(require("process"), 1);
var import_node_os = __toESM(require("os"), 1);
var import_node_tty = __toESM(require("tty"), 1);
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) {
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
const position = argv.indexOf(prefix + flag);
const terminatorPosition = argv.indexOf("--");
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
}
var { env } = import_node_process.default;
var flagForceColor;
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
flagForceColor = 0;
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
flagForceColor = 1;
}
function envForceColor() {
if ("FORCE_COLOR" in env) {
if (env.FORCE_COLOR === "true") {
return 1;
}
if (env.FORCE_COLOR === "false") {
return 0;
}
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
}
}
function translateLevel(level) {
if (level === 0) {
return false;
}
return {
level,
hasBasic: true,
has256: level >= 2,
has16m: level >= 3
};
}
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
const noFlagForceColor = envForceColor();
if (noFlagForceColor !== void 0) {
flagForceColor = noFlagForceColor;
}
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
if (forceColor === 0) {
return 0;
}
if (sniffFlags) {
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
return 3;
}
if (hasFlag("color=256")) {
return 2;
}
}
if ("TF_BUILD" in env && "AGENT_NAME" in env) {
return 1;
}
if (haveStream && !streamIsTTY && forceColor === void 0) {
return 0;
}
const min = forceColor || 0;
if (env.TERM === "dumb") {
return min;
}
if (import_node_process.default.platform === "win32") {
const osRelease = import_node_os.default.release().split(".");
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
return Number(osRelease[2]) >= 14931 ? 3 : 2;
}
return 1;
}
if ("CI" in env) {
if ("GITHUB_ACTIONS" in env || "GITEA_ACTIONS" in env) {
return 3;
}
if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
return 1;
}
return min;
}
if ("TEAMCITY_VERSION" in env) {
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
}
if (env.COLORTERM === "truecolor") {
return 3;
}
if (env.TERM === "xterm-kitty") {
return 3;
}
if ("TERM_PROGRAM" in env) {
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
switch (env.TERM_PROGRAM) {
case "iTerm.app": {
return version >= 3 ? 3 : 2;
}
case "Apple_Terminal": {
return 2;
}
}
}
if (/-256(color)?$/i.test(env.TERM)) {
return 2;
}
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
return 1;
}
if ("COLORTERM" in env) {
return 1;
}
return min;
}
function createSupportsColor(stream, options = {}) {
const level = _supportsColor(stream, {
streamIsTTY: stream && stream.isTTY,
...options
});
return translateLevel(level);
}
var supportsColor = {
stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }),
stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) })
};
var supports_color_default = supportsColor;
// src/utils.ts
var import_undici = require("undici");
if (!supports_color_default.stdout || supports_color_default.stdout.hasBasic === false)
import_ansi_colors.default.enabled = false;
var COMMENT_RE = /\*\//g;
var LB_RE = /\r?\n/g;
var DOUBLE_QUOTE_RE = /(?<!\\)"/g;
var ESC_0_RE = /~0/g;
var ESC_1_RE = /~1/g;
var TS_UNION_INTERSECTION_RE = /[&|]/;
var TS_READONLY_RE = /^readonly\s+/;
var JS_OBJ_KEY = /^(\d+|[A-Za-z_$][A-Za-z0-9_$]*)$/;
function walk(obj, cb, path2 = []) {
if (!obj || typeof obj !== "object")
return;
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++)
walk(obj[i], cb, path2.concat(i));
return;
}
cb(obj, path2);
for (const k of Object.keys(obj))
walk(obj[k], cb, path2.concat(k));
}
function getSchemaObjectComment(v, indentLv) {
if (!v || typeof v !== "object")
return;
const output = [];
if (v.title)
output.push(v.title);
if (v.summary)
output.push(v.summary);
if (v.format)
output.push(`Format: ${v.format}`);
if (v.deprecated)
output.push("@deprecated");
const supportedJsDocTags = ["description", "default", "example"];
for (const field of supportedJsDocTags) {
const allowEmptyString = field === "default" || field === "example";
if (v[field] === void 0) {
continue;
}
if (v[field] === "" && !allowEmptyString) {
continue;
}
const serialized = typeof v[field] === "object" ? JSON.stringify(v[field], null, 2) : v[field];
output.push(`@${field} ${serialized}`);
}
if ("const" in v)
output.push("@constant");
if (v.enum) {
let type = "unknown";
if (Array.isArray(v.type))
type = v.type.join("|");
else if (typeof v.type === "string")
type = v.type;
output.push(`@enum {${type}${v.nullable ? `|null` : ""}}`);
}
return output.length ? comment(output.join("\n"), indentLv) : void 0;
}
function comment(text, indentLv) {
const commentText = text.trim().replace(COMMENT_RE, "*\\/");
if (!commentText.includes("\n"))
return `/** ${commentText} */`;
const star = indent(" *", indentLv != null ? indentLv : 0);
const body = commentText.split(LB_RE).map((ln) => {
ln = ln.trimEnd();
return ln.length > 0 ? `${star} ${ln}` : star;
});
return ["/**", body.join("\n"), indent(" */", indentLv != null ? indentLv : 0)].join("\n");
}
function parseRef(ref) {
if (typeof ref !== "string")
return { filename: ".", path: [] };
if (ref.includes("#/")) {
const [filename, pathStr] = ref.split("#");
const parts = pathStr.split("/");
const path2 = [];
for (const part of parts) {
if (!part || part === "properties")
continue;
path2.push(decodeRef(part));
}
return { filename: filename || ".", path: path2 };
} else if (ref.includes('["')) {
const parts = ref.split('["');
const path2 = [];
for (const part of parts) {
const sanitized = part.replace('"]', "").trim();
if (!sanitized || sanitized === "properties")
continue;
path2.push(sanitized);
}
return { filename: ".", path: path2 };
}
return { filename: ref, path: [] };
}
function makeTSIndex(parts) {
return `${parts[0]}[${parts.slice(1).map(escStr).join("][")}]`;
}
function decodeRef(ref) {
return ref.replace(ESC_0_RE, "~").replace(ESC_1_RE, "/").replace(DOUBLE_QUOTE_RE, '\\"');
}
function parenthesise(type) {
return TS_UNION_INTERSECTION_RE.test(type) || TS_READONLY_RE.test(type) ? `(${type})` : type;
}
function tsArrayOf(type) {
return `${parenthesise(type)}[]`;
}
function tsIntersectionOf(...types) {
types = types.filter((t) => t !== "unknown");
if (types.length === 0)
return "unknown";
if (types.length === 1)
return String(types[0]);
return types.map((t) => parenthesise(t)).join(" & ");
}
function tsOneOf(...types) {
if (types.length === 1) {
return types[0];
} else if (types.length > 5) {
return tsUnionOf(...types);
}
return `OneOf<[${types.join(", ")}]>`;
}
function tsOmit(root, keys) {
return `Omit<${root}, ${tsUnionOf(...keys.map(escStr))}>`;
}
function tsWithRequired(root, keys) {
return `WithRequired<${root}, ${tsUnionOf(...keys.map(escStr))}>`;
}
function tsOptionalProperty(key) {
return `${key}?`;
}
function tsReadonly(type) {
return `readonly ${type}`;
}
function tsTupleOf(...types) {
return `[${types.join(", ")}]`;
}
function tsUnionOf(...types) {
if (types.length === 0)
return "never";
const members = /* @__PURE__ */ new Set();
for (const t of types) {
if (t === "unknown")
return "unknown";
members.add(String(t));
}
if (members.has("never") && members.size === 1)
return "never";
members.delete("never");
const memberArr = Array.from(members);
if (members.size === 1)
return memberArr[0];
let out = "";
for (let i = 0; i < memberArr.length; i++) {
const t = memberArr[i];
out += parenthesise(t);
if (i !== memberArr.length - 1)
out += " | ";
}
return out;
}
function escStr(input) {
if (typeof input !== "string")
return JSON.stringify(input);
return `"${input.replace(LB_RE, "").replace(DOUBLE_QUOTE_RE, '\\"')}"`;
}
function escObjKey(input) {
return JS_OBJ_KEY.test(input) ? input : escStr(input);
}
function indent(input, level) {
if (level > 0) {
return " ".repeat(level).concat(input);
} else {
return input;
}
}
function getEntries(obj, alphabetize, excludeDeprecated) {
let entries = Object.entries(obj);
if (alphabetize)
entries.sort(([a], [b]) => a.localeCompare(b, "en", { numeric: true }));
if (excludeDeprecated)
entries = entries.filter(([, v]) => !(v && typeof v === "object" && "deprecated" in v && v.deprecated));
return entries;
}
function error(msg) {
console.error(import_ansi_colors.default.red(` \u2718 ${msg}`));
}
function isRemoteURL(url) {
return url.startsWith("https://") || url.startsWith("//") || url.startsWith("http://");
}
function isFilepath(url) {
return url.startsWith("file://") || (0, import_node_path.isAbsolute)(url);
}
function getDefaultFetch() {
const globalFetch = globalThis.fetch;
if (typeof globalFetch === "undefined") {
return import_undici.fetch;
}
return globalFetch;
}
// src/load.ts
var EXT_RE = /\.(yaml|yml|json)#?\/?/i;
var VIRTUAL_JSON_URL = `file:///_json`;
function parseSchema(source) {
return source.trim().startsWith("{") ? JSON.parse(source) : import_js_yaml.default.load(source);
}
function resolveSchema(filename) {
if (isRemoteURL(filename))
return new import_node_url.URL(filename.startsWith("//") ? `https:${filename}` : filename);
const localPath = import_node_path2.default.isAbsolute(filename) ? new import_node_url.URL(`file://${filename}`) : new import_node_url.URL(filename, `file://${process.cwd()}/`);
if (!import_node_fs.default.existsSync(localPath)) {
error(`Could not locate ${filename}`);
process.exit(1);
} else if (import_node_fs.default.statSync(localPath).isDirectory()) {
error(`${localPath} is a directory not a file`);
process.exit(1);
}
return localPath;
}
function parseHttpHeaders(httpHeaders) {
const finalHeaders = {};
for (const [k, v] of Object.entries(httpHeaders)) {
if (typeof v === "string") {
finalHeaders[k] = v;
} else {
try {
const stringVal = JSON.stringify(v);
finalHeaders[k] = stringVal;
} catch (err) {
error(`Can\u2019t parse key: ${k} into JSON format. Continuing with the next HTTP header that is specified`);
}
}
}
return finalHeaders;
}
async function load(schema, options) {
var _a;
let schemaID = ".";
if (schema instanceof import_node_url.URL) {
const hint = (_a = options.hint) != null ? _a : "OpenAPI3";
if (schema.href !== options.rootURL.href) {
schemaID = relativePath(options.rootURL, schema);
}
if (options.urlCache.has(schemaID)) {
return options.schemas;
}
options.urlCache.add(schemaID);
if (schema.protocol.startsWith("http")) {
const headers = { "User-Agent": "openapi-typescript" };
if (options.auth)
headers.Authorization = options.auth;
if (options.httpHeaders) {
const parsedHeaders = parseHttpHeaders(options.httpHeaders);
for (const [k, v] of Object.entries(parsedHeaders)) {
headers[k] = v;
}
}
const res = await options.fetch(schema, {
method: options.httpMethod || "GET",
headers
});
const contents = await res.text();
options.schemas[schemaID] = { hint, schema: parseSchema(contents) };
} else {
const contents = import_node_fs.default.readFileSync(schema, "utf8");
options.schemas[schemaID] = { hint, schema: parseSchema(contents) };
}
} else if (schema instanceof import_node_stream.Readable) {
const readable = schema;
const contents = await new Promise((resolve) => {
readable.resume();
readable.setEncoding("utf8");
let content = "";
readable.on("data", (chunk) => {
content += chunk;
});
readable.on("end", () => {
resolve(content.trim());
});
});
options.schemas[schemaID] = { hint: "OpenAPI3", schema: parseSchema(contents) };
} else if (typeof schema === "object") {
options.schemas[schemaID] = {
hint: "OpenAPI3",
schema: JSON.parse(JSON.stringify(schema))
// create deep clone of inline schema (don’t mutate)
};
} else {
error(`Invalid schema`);
process.exit(1);
}
const currentSchema = options.schemas[schemaID].schema;
if (options.schemas[schemaID].hint === "OpenAPI3") {
if ("components" in currentSchema && currentSchema.components && "examples" in currentSchema.components)
delete currentSchema.components.examples;
}
const refPromises = [];
walk(currentSchema, (rawNode, nodePath) => {
for (const k of ["allOf", "anyOf", "oneOf"]) {
if (Array.isArray(rawNode[k])) {
rawNode[k] = rawNode[k].filter((o) => {
if (!o || typeof o !== "object" || Array.isArray(o))
throw new Error(`${nodePath}.${k}: Expected array of objects. Is your schema valid?`);
if (!("$ref" in o) || typeof o.$ref !== "string")
return true;
const ref2 = parseRef(o.$ref);
return !ref2.path.some((i) => i.startsWith("x-"));
});
}
}
if (!rawNode || typeof rawNode !== "object" || Array.isArray(rawNode))
throw new Error(`${nodePath}: Expected object, got ${Array.isArray(rawNode) ? "array" : typeof rawNode}. Is your schema valid?`);
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string")
return;
const node = rawNode;
const ref = parseRef(node.$ref);
if (ref.filename === ".") {
return;
}
if (ref.path.some((i) => i.startsWith("x-"))) {
delete node.$ref;
return;
}
const isRemoteFullSchema = ref.path[0] === "paths" || ref.path[0] === "components";
const hintPath = [...nodePath];
if (ref.filename)
hintPath.push(ref.filename);
hintPath.push(...ref.path);
const hint = isRemoteFullSchema ? "OpenAPI3" : getHint({ path: hintPath, external: !!ref.filename, startFrom: options.hint });
if (isRemoteURL(ref.filename) || isFilepath(ref.filename)) {
const nextURL2 = new import_node_url.URL(ref.filename.startsWith("//") ? `https://${ref.filename}` : ref.filename);
refPromises.push(load(nextURL2, { ...options, hint }));
node.$ref = node.$ref.replace(ref.filename, nextURL2.href);
return;
}
if (options.rootURL.href === VIRTUAL_JSON_URL) {
error(`Can\u2019t resolve "${ref.filename}" from dynamic JSON. Either load this schema from a filepath/URL, or set the \`cwd\` option: \`openapiTS(schema, { cwd: '/path/to/cwd' })\`.`);
process.exit(1);
}
const nextURL = new import_node_url.URL(ref.filename, schema instanceof import_node_url.URL ? schema : options.rootURL);
const nextID = relativePath(schema instanceof import_node_url.URL ? schema : options.rootURL, nextURL);
refPromises.push(load(nextURL, { ...options, hint }));
node.$ref = node.$ref.replace(ref.filename, nextID);
});
await Promise.all(refPromises);
if (schemaID === ".") {
for (const subschemaID of Object.keys(options.schemas)) {
walk(options.schemas[subschemaID].schema, (rawNode) => {
if (!("$ref" in rawNode) || typeof rawNode.$ref !== "string")
return;
const node = rawNode;
const ref = parseRef(node.$ref);
if (ref.filename === ".") {
if (subschemaID === "." || ref.path[0] === "external") {
node.$ref = makeTSIndex(ref.path);
} else {
node.$ref = makeTSIndex(["external", subschemaID, ...ref.path]);
}
} else {
const refURL = new import_node_url.URL(ref.filename, new import_node_url.URL(subschemaID, options.rootURL));
node.$ref = makeTSIndex(["external", relativePath(options.rootURL, refURL), ...ref.path]);
}
});
}
}
for (const k of Object.keys(options.schemas)) {
walk(options.schemas[k].schema, (rawNode, nodePath) => {
if (typeof rawNode === "object" && "in" in rawNode) {
const key = k === "." ? makeTSIndex(nodePath) : makeTSIndex(["external", k, ...nodePath]);
options.parameters[key] = rawNode;
}
});
}
for (const k of Object.keys(options.schemas)) {
if (JSON.stringify(options.schemas[k].schema).includes('"discriminator"')) {
walk(options.schemas[k].schema, (rawNode, nodePath) => {
const node = rawNode;
if (!node.discriminator)
return;
const discriminator = { ...node.discriminator };
const oneOf = [];
if (Array.isArray(node.oneOf)) {
for (const child of node.oneOf) {
if (!child || typeof child !== "object" || !("$ref" in child))
continue;
oneOf.push(child.$ref);
}
}
if (oneOf.length)
discriminator.oneOf = oneOf;
options.discriminators[schemaID === "." ? makeTSIndex(nodePath) : makeTSIndex(["external", k, ...nodePath])] = discriminator;
});
}
}
return options.schemas;
}
function relativePath(src, dest) {
const isSameOrigin = dest.protocol.startsWith("http") && src.protocol.startsWith("http") && dest.origin === src.origin;
const isSameDisk = dest.protocol === "file:" && src.protocol === "file:";
if (isSameOrigin || isSameDisk) {
return import_node_path2.default.posix.relative(import_node_path2.default.posix.dirname(src.pathname), dest.pathname);
}
return dest.href;
}
function getHint({ path: path2, external, startFrom }) {
if (startFrom && startFrom !== "OpenAPI3") {
switch (startFrom) {
case "OperationObject":
return getHintFromOperationObject(path2, external);
case "RequestBodyObject":
return getHintFromRequestBodyObject(path2, external);
case "ResponseObject":
return getHintFromResponseObject(path2, external);
case "SchemaMap":
return "SchemaObject";
default:
return startFrom;
}
}
switch (path2[0]) {
case "paths": {
if (EXT_RE.test(path2[2])) {
return "SchemaMap";
}
return getHintFromPathItemObject(path2.slice(2), external);
}
case "components":
return getHintFromComponentsObject(path2.slice(1), external);
}
return void 0;
}
function getHintFromComponentsObject(path2, external) {
switch (path2[0]) {
case "schemas":
case "headers":
return getHintFromSchemaObject(path2.slice(2), external);
case "parameters":
return getHintFromParameterObject(path2.slice(2), external);
case "responses":
return getHintFromResponseObject(path2.slice(2), external);
case "requestBodies":
return getHintFromRequestBodyObject(path2.slice(2), external);
case "pathItems":
return getHintFromPathItemObject(path2.slice(2), external);
}
return "SchemaObject";
}
function getHintFromMediaTypeObject(path2, external) {
switch (path2[0]) {
case "schema":
return getHintFromSchemaObject(path2.slice(1), external);
}
return "MediaTypeObject";
}
function getHintFromOperationObject(path2, external) {
switch (path2[0]) {
case "parameters":
return "ParameterObject[]";
case "requestBody":
return getHintFromRequestBodyObject(path2.slice(1), external);
case "responses":
return getHintFromResponseObject(path2.slice(2), external);
}
return "OperationObject";
}
function getHintFromParameterObject(path2, external) {
switch (path2[0]) {
case "content":
return getHintFromMediaTypeObject(path2.slice(2), external);
case "schema":
return getHintFromSchemaObject(path2.slice(1), external);
}
return "ParameterObject";
}
function getHintFromPathItemObject(path2, external) {
switch (path2[0]) {
case "parameters": {
if (typeof path2[1] === "number") {
return "ParameterObject[]";
}
return getHintFromParameterObject(path2.slice(1), external);
}
default:
return getHintFromOperationObject(path2.slice(1), external);
}
}
function getHintFromRequestBodyObject(path2, external) {
switch (path2[0]) {
case "content":
return getHintFromMediaTypeObject(path2.slice(2), external);
}
return "RequestBodyObject";
}
function getHintFromResponseObject(path2, external) {
switch (path2[0]) {
case "headers":
return getHintFromSchemaObject(path2.slice(2), external);
case "content":
return getHintFromMediaTypeObject(path2.slice(2), external);
}
return "ResponseObject";
}
function getHintFromSchemaObject(path2, external) {
switch (path2[0]) {
case "allOf":
case "anyOf":
case "oneOf":
return "SchemaMap";
}
if (path2.length >= 2 && external) {
return "SchemaMap";
}
return "SchemaObject";
}
// src/transform/parameter-object.ts
function transformParameterObject(parameterObject, { path: path2, ctx }) {
return parameterObject.schema ? transformSchemaObject(parameterObject.schema, { path: path2, ctx }) : "string";
}
// src/transform/request-body-object.ts
function transformRequestBodyObject(requestBodyObject, { path: path2, ctx }) {
let { indentLv } = ctx;
const output = ["{"];
indentLv++;
output.push(indent(ctx.immutableTypes ? tsReadonly("content: {") : "content: {", indentLv));
indentLv++;
if (!Object.keys(requestBodyObject.content).length) {
output.push(indent(`${escStr("*/*")}: never;`, indentLv));
}
for (const [contentType, mediaTypeObject] of getEntries(requestBodyObject.content, ctx.alphabetize, ctx.excludeDeprecated)) {
const c2 = getSchemaObjectComment(mediaTypeObject, indentLv);
if (c2)
output.push(indent(c2, indentLv));
let key = escStr(contentType);
if (ctx.immutableTypes)
key = tsReadonly(key);
if ("$ref" in mediaTypeObject) {
output.push(
indent(
`${key}: ${transformSchemaObject(mediaTypeObject, {
path: `${path2}/${contentType}`,
ctx: { ...ctx, indentLv }
})};`,
indentLv
)
);
} else {
const mediaType = transformMediaTypeObject(mediaTypeObject, {
path: `${path2}/${contentType}`,
ctx: { ...ctx, indentLv }
});
output.push(indent(`${key}: ${mediaType};`, indentLv));
}
}
indentLv--;
output.push(indent("};", indentLv));
indentLv--;
output.push(indent("}", indentLv));
return output.join("\n");
}
// src/transform/response-object.ts
function transformResponseObject(responseObject, { path: path2, ctx }) {
const output = ["{"];
let { indentLv } = ctx;
if (responseObject.headers) {
indentLv++;
output.push(indent(`headers: {`, indentLv));
indentLv++;
for (const [name, headerObject] of getEntries(responseObject.headers, ctx.alphabetize, ctx.excludeDeprecated)) {
const c2 = getSchemaObjectComment(headerObject, indentLv);
if (c2)
output.push(indent(c2, indentLv));
let key = escObjKey(name);
if (ctx.immutableTypes)
key = tsReadonly(key);
if ("$ref" in headerObject) {
output.push(indent(`${key}: ${headerObject.$ref};`, indentLv));
} else {
if (!headerObject.required)
key = tsOptionalProperty(key);
output.push(
indent(
`${key}: ${transformHeaderObject(headerObject, {
path: `${path2}/headers/${name}`,
ctx: { ...ctx, indentLv }
})};`,
indentLv
)
);
}
}
indentLv--;
output.push(indent(`};`, indentLv));
indentLv--;
}
if (responseObject.content) {
indentLv++;
output.push(indent("content: {", indentLv));
indentLv++;
for (const [contentType, mediaTypeObject] of getEntries(responseObject.content, ctx.alphabetize, ctx.excludeDeprecated)) {
let key = escStr(contentType);
if (ctx.immutableTypes)
key = tsReadonly(key);
output.push(
indent(
`${key}: ${transformMediaTypeObject(mediaTypeObject, {
path: `${path2}/content/${contentType}`,
ctx: { ...ctx, indentLv }
})};`,
indentLv
)
);
}
indentLv--;
output.push(indent("};", indentLv));
indentLv--;
} else {
indentLv++;
output.push(indent("content: never;", indentLv));
indentLv--;
}
output.push(indent("}", indentLv));
return output.join("\n");
}
// src/transform/operation-object.ts
function transformOperationObject(operationObject, { path: path2, ctx, wrapObject = true }) {
var _a;
let { indentLv } = ctx;
const output = wrapObject ? ["{"] : [];
indentLv++;
{
if (operationObject.parameters) {
const parameterOutput = [];
indentLv++;
for (const paramIn of ["query", "header", "path", "cookie"]) {
const paramInternalOutput = [];
indentLv++;
let allOptional = true;
for (const param of (_a = operationObject.parameters) != null ? _a : []) {
const node = "$ref" in param ? ctx.parameters[param.$ref] : param;
if ((node == null ? void 0 : node.in) !== paramIn)
continue;
let key = escObjKey(node.name);
if (paramIn !== "path" && !node.required) {
key = tsOptionalProperty(key);
} else {
allOptional = false;
}
const c2 = getSchemaObjectComment(param, indentLv);
if (c2)
paramInternalOutput.push(indent(c2, indentLv));
const parameterType = "$ref" in param ? param.$ref : transformParameterObject(param, {
path: `${path2}/parameters/${param.name}`,
ctx: { ...ctx, indentLv }
});
paramInternalOutput.push(indent(`${key}: ${parameterType};`, indentLv));
}
indentLv--;
if (paramInternalOutput.length) {
const key = allOptional ? tsOptionalProperty(paramIn) : paramIn;
parameterOutput.push(indent(`${key}: {`, indentLv));
parameterOutput.push(...paramInternalOutput);
parameterOutput.push(indent(`};`, indentLv));
}
}
indentLv--;
if (parameterOutput.length) {
output.push(indent(`parameters: {`, indentLv));
output.push(parameterOutput.join("\n"));
output.push(indent("};", indentLv));
}
}
}
{
if (operationObject.requestBody) {
const c2 = getSchemaObjectComment(operationObject.requestBody, indentLv);
if (c2)
output.push(indent(c2, indentLv));
let key = "requestBody";
if (ctx.immutableTypes)
key = tsReadonly(key);
if ("$ref" in operationObject.requestBody) {
output.push(indent(`${key}: ${transformSchemaObject(operationObject.requestBody, { path: path2, ctx })};`, indentLv));
} else {
if (!operationObject.requestBody.required)
key = tsOptionalProperty(key);
const requestBody = transformRequestBodyObject(operationObject.requestBody, {
path: `${path2}/requestBody`,
ctx: { ...ctx, indentLv }
});
output.push(indent(`${key}: ${requestBody};`, indentLv));
}
}
}
{
if (operationObject.responses) {
output.push(indent(`responses: {`, indentLv));
indentLv++;
for (const [responseCode, responseObject] of getEntries(operationObject.responses, ctx.alphabetize, ctx.excludeDeprecated)) {
const key = escObjKey(responseCode);
const c2 = getSchemaObjectComment(responseObject, indentLv);
if (c2)
output.push(indent(c2, indentLv));
if ("$ref" in responseObject) {
output.push(
indent(
`${key}: ${transformSchemaObject(responseObject, {
path: `${path2}/responses/${responseCode}`,
ctx
})};`,
indentLv
)
);
} else {
const responseType = transformResponseObject(responseObject, {
path: `${path2}/responses/${responseCode}`,
ctx: { ...ctx, indentLv }
});
output.push(indent(`${key}: ${responseType};`, indentLv));
}
}
indentLv--;
output.push(indent(`};`, indentLv));
}
}
indentLv--;
if (wrapObject) {
output.push(indent("}", indentLv));
}
return output.join("\n");
}
// src/transform/path-item-object.ts
function transformPathItemObject(pathItem, { path: path2, ctx }) {
var _a, _b, _c;
let { indentLv } = ctx;
const output = [];
output.push("{");
indentLv++;
for (const method of ["get", "put", "post", "delete", "options", "head", "patch", "trace"]) {
const operationObject = pathItem[method];
if (!operationObject)
continue;
const c2 = getSchemaObjectComment(operationObject, indentLv);
if (c2)
output.push(indent(c2, indentLv));
const keyedParameters = {};
if (!("$ref" in operationObject)) {
for (const parameter of [...(_a = pathItem.parameters) != null ? _a : [], ...(_b = operationObject.parameters) != null ? _b : []]) {
keyedParameters["$ref" in parameter ? parameter.$ref : parameter.name] = parameter;
}
}
if ("$ref" in operationObject) {
output.push(indent(`${method}: ${operationObject.$ref}`, indentLv));
} else if (operationObject.operationId) {
const operationType = transformOperationObject({ ...operationObject, parameters: Object.values(keyedParameters) }, { path: path2, ctx: { ...ctx, indentLv: 1 } });
ctx.operations[operationObject.operationId] = {
operationType,
comment: getSchemaObjectComment(operationObject, 1)
};
output.push(indent(`${method}: operations[${escStr(operationObject.operationId)}];`, indentLv));
} else {
const operationType = transformOperationObject({ ...operationObject, parameters: Object.values(keyedParameters) }, { path: path2, ctx: { ...ctx, indentLv } });
output.push(indent(`${method}: ${operationType};`, indentLv));
}
}
if ((_c = pathItem.parameters) == null ? void 0 : _c.length) {
output.push(indent(transformOperationObject({ parameters: pathItem.parameters }, { path: path2, ctx, wrapObject: false }).trim(), indentLv));
}
indentLv--;
output.push(indent("}", indentLv));
return output.join("\n");
}
// src/transform/schema-object-map.ts
function transformSchemaObjectMap(schemaObjMap, { path: path2, ctx }) {
if ("type" in schemaObjMap && typeof schemaObjMap.type === "string" || "allOf" in schemaObjMap && Array.isArray(schemaObjMap.allOf) || "oneOf" in schemaObjMap && Array.isArray(schemaObjMap.oneOf) || "anyOf" in schemaObjMap && Array.isArray(schemaObjMap.anyOf)) {
return transformSchemaObject(schemaObjMap, { path: path2, ctx });
}
let { indentLv } = ctx;
const output = ["{"];
indentLv++;
outer:
for (const [name, schemaObject] of getEntries(schemaObjMap, ctx.alphabetize, ctx.excludeDeprecated)) {
if (!schemaObject || typeof schemaObject !== "object")
continue;
const c2 = getSchemaObjectComment(schemaObject, indentLv);
if (c2)
output.push(indent(c2, indentLv));
let key = escObjKey(name);
if (ctx.immutableTypes || schemaObject.readOnly)
key = tsReadonly(key);
if (!("type" in schemaObject) && !("$ref" in schemaObject)) {
for (const method of ["get", "put", "post", "delete", "options", "head", "patch", "trace"]) {
if (method in schemaObject) {
output.push(indent(`${key}: ${transformPathItemObject(schemaObject, { path: `${path2}${name}`, ctx: { ...ctx, indentLv } })};`, indentLv));
continue outer;
}
}
}
if ("in" in schemaObject) {
output.push(indent(`${key}: ${transformParameterObject(schemaObject, { path: `${path2}${name}`, ctx: { ...ctx, indentLv } })};`, indentLv));
continue;
}
output.push(indent(`${key}: ${transformSchemaObject(schemaObject, { path: `${path2}${name}`, ctx: { ...ctx, indentLv } })};`, indentLv));
}
indentLv--;
output.push(indent("}", indentLv));
return output.join("\n");
}
// src/transform/schema-object.ts
function transformSchemaObject(schemaObject, options) {
const result = defaultSchemaObjectTransform(schemaObject, options);
if (typeof options.ctx.postTransform === "function") {
const postResult = options.ctx.postTransform(result, options);
if (postResult)
return postResult;
}
return result;
}
function defaultSchemaObjectTransform(schemaObject, { path: path2, ctx }) {
var _a, _b, _c;
let { indentLv } = ctx;
if (typeof schemaObject === "boolean") {
return schemaObject ? "unknown" : "never";
}
if (!schemaObject || typeof schemaObject !== "object")
return schemaObject;
if (Array.isArray(schemaObject)) {
const finalType2 = tsTupleOf(...schemaObject);
return ctx.immutableTypes ? tsReadonly(finalType2) : finalType2;
}
if ("$ref" in schemaObject) {
return schemaObject.$ref;
}
if (typeof ctx.transform === "function") {
const result = ctx.transform(schemaObject, { path: path2, ctx });
if (result)
return result;
}
if (schemaObject.const !== null && schemaObject.const !== void 0) {
return transformSchemaObject(escStr(schemaObject.const), {
path: path2,
ctx: { ...ctx, immutableTypes: false, indentLv: indentLv + 1 }
// note: guarantee readonly happens once, here
});
}
if (typeof schemaObject === "object" && !!schemaObject.enum && schemaObject.type !== "object") {
let items = schemaObject.enum;
if ("type" in schemaObject) {
if (schemaObject.type === "string" || Array.isArray(schemaObject.type) && schemaObject.type.includes("string")) {
items = items.map((t) => escStr(t));
}
} else {
items = items.map((t) => escStr(t || ""));
}
return tsUnionOf(...items, ...schemaObject.nullable ? ["null"] : []);
}
const oneOf = typeof schemaObject === "object" && !schemaObject.discriminator && schemaObject.oneOf || schemaObject.enum || void 0;
if (oneOf && !oneOf.some((t) => "$ref" in t && ctx.discriminators[t.$ref])) {
const oneOfNormalized = oneOf.map((item) => transformSchemaObject(item, { path: path2, ctx }));
if (schemaObject.nullable)
oneOfNormalized.push("null");
if ("type" in schemaObject && Array.isArray(schemaObject.type)) {
const coreTypes = schemaObject.type.map((t) => transformSchemaObject({ ...schemaObject, oneOf: void 0, type: t }, { path: path2, ctx }));
return tsUnionOf(...oneOfNormalized, ...coreTypes);
}
const oneOfTypes = oneOfNormalized.some((t) => typeof t === "string" && t.includes("{")) ? tsOneOf(...oneOfNormalized) : tsUnionOf(...oneOfNormalized);
if ("type" in schemaObject && schemaObject.type === "object" && (schemaObject.properties || schemaObject.additionalProperties)) {
return tsIntersectionOf(transformSchemaObject({ ...schemaObject, oneOf: void 0, enum: void 0 }, { path: path2, ctx }), oneOfTypes);
}
return oneOfTypes;
}
if ("type" in schemaObject) {
if (schemaObject.type === "null")
return "null";
if (schemaObject.type === "string" || schemaObject.type === "boolean") {
return schemaObject.nullable ? tsUnionOf(schemaObject.type, "null") : schemaObject.type;
}
if (schemaObject.type === "number" || schemaObject.type === "integer") {
return schemaObject.nullable ? tsUnionOf("number", "null") : "number";
}
if (schemaObject.type === "array") {
indentLv++;
let itemType = "unknown";
let isTupleType = false;
if (schemaObject.prefixItems || Array.isArray(schemaObject.items)) {
isTupleType = true;
const result = [];
for (const item of (_a = schemaObject.prefixItems) != null ? _a : schemaObject.items) {
result.push(transformSchemaObject(item, { path: path2, ctx: { ...ctx, indentLv } }));
}
itemType = `[${result.join(", ")}]`;
} else if (schemaObject.items) {
itemType = transformSchemaObject(schemaObject.items, { path: path2, ctx: { ...ctx, indentLv } });
}
const min = typeof schemaObject.minItems === "number" && schemaObject.minItems >= 0 ? schemaObject.minItems : 0;
const max = typeof schemaObject.maxItems === "number" && schemaObject.maxItems >= 0 && min <= schemaObject.maxItems ? schemaObject.maxItems : void 0;
const estimateCodeSize = typeof max !== "number" ? min : (max * (max + 1) - min * (min - 1)) / 2;
if (ctx.supportArrayLength && (min !== 0 || max !== void 0) && estimateCodeSize < 30) {
if (typeof schemaObject.maxItems !== "number") {
itemType = tsTupleOf(...Array.from({ length: min }).map(() => itemType), `...${tsArrayOf(itemType)}`);
return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType;
} else {
return tsUnionOf(
...Array.from({ length: (max != null ? max : 0) - min + 1 }).map((_, i) => i + min).map((n) => {
const t = tsTupleOf(...Array.from({ length: n }).map(() => itemType));
return ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(t) : t;
})
);
}
}
if (!isTupleType) {
itemType = tsArrayOf(itemType);
}
itemType = ctx.immutableTypes || schemaObject.readOnly ? tsReadonly(itemType) : itemType;
return schemaObject.nullable ? tsUnionOf(itemType, "null") : itemType;
}
if (Array.isArray(schemaObject.type)) {
return tsUnionOf(...schemaObject.type.map((t) => transformSchemaObject({ ...schemaObject, type: t }, { path: path2, ctx })));
}
}
const coreType = [];
for (const k of ["oneOf", "allOf", "anyOf"]) {
if (!schemaObject[k])
continue;
const discriminatorRef = schemaObject[k].find(
(t) => "$ref" in t && (ctx.discriminators[t.$ref] || // explicit allOf from this node
Object.values(ctx.discriminators).find((d) => {
var _a2;
return (_a2 = d.oneOf) == null ? void 0 : _a2.includes(path2);
}))
// implicit oneOf from parent
);
if (discriminatorRef && ctx.d