oas-normalize
Version:
Tooling for converting, validating, and parsing OpenAPI, Swagger, and Postman API definitions
226 lines (225 loc) • 6.98 kB
JavaScript
import {
ValidationError
} from "./chunk-KGEOPSPH.js";
import {
getAPIDefinitionType,
getType,
isOpenAPI,
isPostman,
isSwagger,
prepareURL,
stringToJSON
} from "./chunk-SZISBDLH.js";
// src/index.ts
import fs from "fs";
import { bundle, compileErrors, dereference, validate } from "@readme/openapi-parser";
import postmanToOpenAPI from "@readme/postman-to-openapi";
import converter from "swagger2openapi";
var OASNormalize = class _OASNormalize {
cache;
// biome-ignore lint/suspicious/noExplicitAny: Intentionally loose because this library supports a wide variety of inputs.
file;
opts;
type;
// biome-ignore lint/suspicious/noExplicitAny: Intentionally loose because this library supports a wide variety of inputs.
constructor(file, opts) {
this.file = file;
this.opts = {
colorizeErrors: false,
enablePaths: false,
parser: {},
...opts
};
if (!this.opts.enablePaths) {
if (!this.opts.parser) this.opts.parser = {};
if (!this.opts.parser.resolve) this.opts.parser.resolve = {};
this.opts.parser.resolve = { file: false };
}
this.type = getType(this.file);
this.cache = {
load: false,
bundle: false,
deref: false
};
}
/**
* Load and return the API definition that `oas-normalize` was initialized with.
*
*/
async load() {
if (this.cache.load) return this.cache.load;
const resolve = (obj) => {
const ret = stringToJSON(obj);
this.cache.load = ret;
return ret;
};
switch (this.type) {
case "json":
case "string-json":
case "string-yaml":
return resolve(this.file);
case "buffer":
return resolve(this.file.toString());
case "url": {
const { url, options } = prepareURL(this.file);
const resp = await fetch(url, options).then((res) => res.text());
return resolve(resp);
}
case "path": {
if (!this.opts.enablePaths) {
throw new Error("Use `opts.enablePaths` to enable accessing local files.");
}
const contents = fs.readFileSync(this.file).toString();
if (!contents.trim()) {
throw new Error("No file contents found.");
}
return resolve(contents);
}
default:
throw new Error("Could not load this file.");
}
}
static async convertPostmanToOpenAPI(schema) {
return postmanToOpenAPI(JSON.stringify(schema), void 0, {
outputFormat: "json",
replaceVars: true
}).then(JSON.parse);
}
/**
* Bundle up the given API definition, resolving any external `$ref` pointers in the process.
*
*/
async bundle() {
if (this.cache.bundle) return this.cache.bundle;
const parserOptions = this.opts.parser || {};
return this.load().then((schema) => {
if (isPostman(schema)) {
return _OASNormalize.convertPostmanToOpenAPI(schema);
}
return schema;
}).then((schema) => bundle(schema, parserOptions)).then((bundled) => {
this.cache.bundle = bundled;
return bundled;
});
}
/**
* Dereference the given API definition.
*
*/
async dereference() {
if (this.cache.deref) return this.cache.deref;
const parserOptions = this.opts.parser || {};
return this.load().then((schema) => {
if (isPostman(schema)) {
return _OASNormalize.convertPostmanToOpenAPI(schema);
}
return schema;
}).then((schema) => dereference(schema, parserOptions)).then((dereferenced) => {
this.cache.deref = dereferenced;
return dereferenced;
});
}
/**
* Dereference the given API definition.
*
* This method is deprecated in favor of `dereference`. It will be removed in a future release.
*
* @deprecated
*/
async deref() {
return this.dereference();
}
/**
* Convert a given API definition to OpenAPI if it is not already.
*
*/
async convert() {
if (this.cache.convert) return this.cache.convert;
return this.load().then(async (schema) => {
return isPostman(schema) ? _OASNormalize.convertPostmanToOpenAPI(schema) : schema;
}).then(async (schema) => {
if (!isSwagger(schema) && !isOpenAPI(schema)) {
throw new Error("The supplied API definition is unsupported.");
} else if (isOpenAPI(schema)) {
return schema;
}
const baseVersion = parseInt(schema.swagger, 10);
if (baseVersion === 1) {
throw new Error("Swagger v1.2 is unsupported.");
}
return converter.convertObj(schema, { anchors: true }).then((options) => options.openapi);
});
}
/**
* Validate a given API definition.
*
* If supplied a Postman collection it will be converted to OpenAPI first and then run through
* standard OpenAPI validation.
*
*/
async validate(opts = {}) {
const parserOptions = opts.parser || this.opts.parser || {};
if (!parserOptions.validate) parserOptions.validate = {};
if (!parserOptions.validate.errors) parserOptions.validate.errors = {};
parserOptions.validate.errors.colorize = this.opts.colorizeErrors;
return this.load().then(async (schema) => {
return isPostman(schema) ? _OASNormalize.convertPostmanToOpenAPI(schema) : schema;
}).then(async (schema) => {
if (!isSwagger(schema) && !isOpenAPI(schema)) {
throw new ValidationError("The supplied API definition is unsupported.");
} else if (isSwagger(schema)) {
const baseVersion = parseInt(schema.swagger, 10);
if (baseVersion === 1) {
throw new ValidationError("Swagger v1.2 is unsupported.");
}
}
const clonedSchema = JSON.parse(JSON.stringify(schema));
const result = await validate(clonedSchema, parserOptions);
if (!result.valid) {
throw new ValidationError(compileErrors(result));
}
return result;
});
}
/**
* Retrieve OpenAPI, Swagger, or Postman version information about the supplied API definition.
*
*/
async version() {
return this.load().then((schema) => {
switch (getAPIDefinitionType(schema)) {
case "openapi":
return {
specification: "openapi",
version: schema.openapi
};
case "postman": {
let version = "unknown";
if (schema?.info?.schema) {
const match = (schema?.info).schema.match(
/http(s?):\/\/schema.getpostman.com\/json\/collection\/v([0-9.]+)\//
);
if (match) {
version = match[2];
}
}
return {
specification: "postman",
version
};
}
case "swagger":
return {
specification: "swagger",
version: schema.swagger
};
default:
throw new Error("Unknown file detected.");
}
});
}
};
export {
OASNormalize as default
};
//# sourceMappingURL=index.js.map