openapi-resolver
Version:
Open API Resolver - Javascript library to connect to parse and resolve openapi-enabled APIs via browser or nodejs
1,643 lines (1,500 loc) • 755 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["OpenApiResolver"] = factory();
else
root["OpenApiResolver"] = factory();
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 8575:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
const Options = __webpack_require__(8316);
module.exports = normalizeArgs;
/**
* Normalizes the given arguments, accounting for optional args.
*
* @param {Arguments} args
* @returns {object}
*/
function normalizeArgs(args) {
let path, schema, options, callback;
args = Array.prototype.slice.call(args);
if (typeof args[args.length - 1] === "function") {
// The last parameter is a callback function
callback = args.pop();
}
if (typeof args[0] === "string") {
// The first parameter is the path
path = args[0];
if (typeof args[2] === "object") {
// The second parameter is the schema, and the third parameter is the options
schema = args[1];
options = args[2];
} else {
// The second parameter is the options
schema = undefined;
options = args[1];
}
} else {
// The first parameter is the schema
path = "";
schema = args[0];
options = args[1];
}
if (!(options instanceof Options)) {
options = new Options(options);
}
return {
path,
schema,
options,
callback
};
}
/***/ }),
/***/ 8316:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* eslint lines-around-comment: [2, {beforeBlockComment: false}] */
const jsonParser = __webpack_require__(9324);
const yamlParser = __webpack_require__(8015);
const textParser = __webpack_require__(4844);
const binaryParser = __webpack_require__(4486);
const fileResolver = __webpack_require__(9571);
const httpResolver = __webpack_require__(6796);
module.exports = $RefParserOptions;
/**
* Options that determine how JSON schemas are parsed, resolved, and dereferenced.
*
* @param {object|$RefParserOptions} [options] - Overridden options
* @constructor
*/
function $RefParserOptions(options) {
merge(this, $RefParserOptions.defaults);
merge(this, options);
}
$RefParserOptions.defaults = {
/**
* Determines how different types of files will be parsed.
*
* You can add additional parsers of your own, replace an existing one with
* your own implementation, or disable any parser by setting it to false.
*/
parse: {
json: jsonParser,
yaml: yamlParser,
text: textParser,
binary: binaryParser
},
/**
* Determines how JSON References will be resolved.
*
* You can add additional resolvers of your own, replace an existing one with
* your own implementation, or disable any resolver by setting it to false.
*/
resolve: {
file: fileResolver,
http: httpResolver,
/**
* Determines whether external $ref pointers will be resolved.
* If this option is disabled, then none of above resolvers will be called.
* Instead, external $ref pointers will simply be ignored.
*
* @type {boolean}
*/
external: true
},
/**
* By default, JSON Schema $Ref Parser throws the first error it encounters. Setting `continueOnError` to `true`
* causes it to keep processing as much as possible and then throw a single error that contains all errors
* that were encountered.
*/
continueOnError: false,
/**
* Determines the types of JSON references that are allowed.
*/
dereference: {
/**
* Dereference circular (recursive) JSON references?
* If false, then a {@link ReferenceError} will be thrown if a circular reference is found.
* If "ignore", then circular references will not be dereferenced.
*
* @type {boolean|string}
*/
circular: true,
/**
* A function, called for each path, which can return true to stop this path and all
* subpaths from being dereferenced further. This is useful in schemas where some
* subpaths contain literal $ref keys that should not be dereferenced.
*
* @type {function}
*/
excludedPathMatcher: () => false
}
};
/**
* Merges the properties of the source object into the target object.
*
* @param {object} target - The object that we're populating
* @param {?object} source - The options that are being merged
* @returns {object}
*/
function merge(target, source) {
if (isMergeable(source)) {
let keys = Object.keys(source);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let sourceSetting = source[key];
let targetSetting = target[key];
if (isMergeable(sourceSetting)) {
// It's a nested object, so merge it recursively
target[key] = merge(targetSetting || {}, sourceSetting);
} else if (sourceSetting !== undefined) {
// It's a scalar value, function, or array. No merging necessary. Just overwrite the target value.
target[key] = sourceSetting;
}
}
}
return target;
}
/**
* Determines whether the given value can be merged,
* or if it is a scalar value that should just override the target value.
*
* @param {*} val
* @returns {Boolean}
*/
function isMergeable(val) {
return val && typeof val === "object" && !Array.isArray(val) && !(val instanceof RegExp) && !(val instanceof Date);
}
/***/ }),
/***/ 3265:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
const {
ono
} = __webpack_require__(9978);
const url = __webpack_require__(6930);
const plugins = __webpack_require__(6250);
const {
ResolverError,
ParserError,
UnmatchedParserError,
UnmatchedResolverError,
isHandledError
} = __webpack_require__(5554);
module.exports = parse;
/**
* Reads and parses the specified file path or URL.
*
* @param {string} path - This path MUST already be resolved, since `read` doesn't know the resolution context
* @param {$Refs} $refs
* @param {$RefParserOptions} options
*
* @returns {Promise}
* The promise resolves with the parsed file contents, NOT the raw (Buffer) contents.
*/
async function parse(path, $refs, options) {
// Remove the URL fragment, if any
path = url.stripHash(path);
// Add a new $Ref for this file, even though we don't have the value yet.
// This ensures that we don't simultaneously read & parse the same file multiple times
let $ref = $refs._add(path);
// This "file object" will be passed to all resolvers and parsers.
let file = {
url: path,
extension: url.getExtension(path)
};
// Read the file and then parse the data
try {
const resolver = await readFile(file, options, $refs);
$ref.pathType = resolver.plugin.name;
file.data = resolver.result;
const parser = await parseFile(file, options, $refs);
$ref.value = parser.result;
return parser.result;
} catch (err) {
if (isHandledError(err)) {
$ref.value = err;
}
throw err;
}
}
/**
* Reads the given file, using the configured resolver plugins
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {$RefParserOptions} options
*
* @returns {Promise}
* The promise resolves with the raw file contents and the resolver that was used.
*/
function readFile(file, options, $refs) {
return new Promise((resolve, reject) => {
// console.log('Reading %s', file.url);
// Find the resolvers that can read this file
let resolvers = plugins.all(options.resolve);
resolvers = plugins.filter(resolvers, "canRead", file);
// Run the resolvers, in order, until one of them succeeds
plugins.sort(resolvers);
plugins.run(resolvers, "read", file, $refs).then(resolve, onError);
function onError(err) {
if (!err && options.continueOnError) {
// No resolver could be matched
reject(new UnmatchedResolverError(file.url));
} else if (!err || !("error" in err)) {
// Throw a generic, friendly error.
reject(ono.syntax(`Unable to resolve $ref pointer "${file.url}"`));
}
// Throw the original error, if it's one of our own (user-friendly) errors.
else if (err.error instanceof ResolverError) {
reject(err.error);
} else {
reject(new ResolverError(err, file.url));
}
}
});
}
/**
* Parses the given file's contents, using the configured parser plugins.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @param {$RefParserOptions} options
*
* @returns {Promise}
* The promise resolves with the parsed file contents and the parser that was used.
*/
function parseFile(file, options, $refs) {
return new Promise((resolve, reject) => {
// console.log('Parsing %s', file.url);
// Find the parsers that can read this file type.
// If none of the parsers are an exact match for this file, then we'll try ALL of them.
// This handles situations where the file IS a supported type, just with an unknown extension.
let allParsers = plugins.all(options.parse);
let filteredParsers = plugins.filter(allParsers, "canParse", file);
let parsers = filteredParsers.length > 0 ? filteredParsers : allParsers;
// Run the parsers, in order, until one of them succeeds
plugins.sort(parsers);
plugins.run(parsers, "parse", file, $refs).then(onParsed, onError);
function onParsed(parser) {
if (!parser.plugin.allowEmpty && isEmpty(parser.result)) {
reject(ono.syntax(`Error parsing "${file.url}" as ${parser.plugin.name}. \nParsed value is empty`));
} else {
resolve(parser);
}
}
function onError(err) {
if (!err && options.continueOnError) {
// No resolver could be matched
reject(new UnmatchedParserError(file.url));
} else if (!err || !("error" in err)) {
reject(ono.syntax(`Unable to parse ${file.url}`));
} else if (err.error instanceof ParserError) {
reject(err.error);
} else {
reject(new ParserError(err.error.message, file.url));
}
}
});
}
/**
* Determines whether the parsed value is "empty".
*
* @param {*} value
* @returns {boolean}
*/
function isEmpty(value) {
return value === undefined || typeof value === "object" && Object.keys(value).length === 0 || typeof value === "string" && value.trim().length === 0 || Buffer.isBuffer(value) && value.length === 0;
}
/***/ }),
/***/ 4486:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
let BINARY_REGEXP = /\.(jpeg|jpg|gif|png|bmp|ico)$/i;
module.exports = {
/**
* The order that this parser will run, in relation to other parsers.
*
* @type {number}
*/
order: 400,
/**
* Whether to allow "empty" files (zero bytes).
*
* @type {boolean}
*/
allowEmpty: true,
/**
* Determines whether this parser can parse a given file reference.
* Parsers that return true will be tried, in order, until one successfully parses the file.
* Parsers that return false will be skipped, UNLESS all parsers returned false, in which case
* every parser will be tried.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {boolean}
*/
canParse(file) {
// Use this parser if the file is a Buffer, and has a known binary extension
return Buffer.isBuffer(file.data) && BINARY_REGEXP.test(file.url);
},
/**
* Parses the given data as a Buffer (byte array).
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {Buffer}
*/
parse(file) {
if (Buffer.isBuffer(file.data)) {
return file.data;
} else {
// This will reject if data is anything other than a string or typed array
return Buffer.from(file.data);
}
}
};
/***/ }),
/***/ 9324:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
const {
ParserError
} = __webpack_require__(5554);
module.exports = {
/**
* The order that this parser will run, in relation to other parsers.
*
* @type {number}
*/
order: 100,
/**
* Whether to allow "empty" files. This includes zero-byte files, as well as empty JSON objects.
*
* @type {boolean}
*/
allowEmpty: true,
/**
* Determines whether this parser can parse a given file reference.
* Parsers that match will be tried, in order, until one successfully parses the file.
* Parsers that don't match will be skipped, UNLESS none of the parsers match, in which case
* every parser will be tried.
*
* @type {RegExp|string|string[]|function}
*/
canParse: ".json",
/**
* Parses the given file as JSON
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {Promise}
*/
async parse(file) {
// eslint-disable-line require-await
let data = file.data;
if (Buffer.isBuffer(data)) {
data = data.toString();
}
if (typeof data === "string") {
if (data.trim().length === 0) {
return; // This mirrors the YAML behavior
} else {
try {
return JSON.parse(data);
} catch (e) {
throw new ParserError(e.message, file.url);
}
}
} else {
// data is already a JavaScript value (object, array, number, null, NaN, etc.)
return data;
}
}
};
/***/ }),
/***/ 4844:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
const {
ParserError
} = __webpack_require__(5554);
let TEXT_REGEXP = /\.(txt|htm|html|md|xml|js|min|map|css|scss|less|svg)$/i;
module.exports = {
/**
* The order that this parser will run, in relation to other parsers.
*
* @type {number}
*/
order: 300,
/**
* Whether to allow "empty" files (zero bytes).
*
* @type {boolean}
*/
allowEmpty: true,
/**
* The encoding that the text is expected to be in.
*
* @type {string}
*/
encoding: "utf8",
/**
* Determines whether this parser can parse a given file reference.
* Parsers that return true will be tried, in order, until one successfully parses the file.
* Parsers that return false will be skipped, UNLESS all parsers returned false, in which case
* every parser will be tried.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {boolean}
*/
canParse(file) {
// Use this parser if the file is a string or Buffer, and has a known text-based extension
return (typeof file.data === "string" || Buffer.isBuffer(file.data)) && TEXT_REGEXP.test(file.url);
},
/**
* Parses the given file as text
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {string}
*/
parse(file) {
if (typeof file.data === "string") {
return file.data;
} else if (Buffer.isBuffer(file.data)) {
return file.data.toString(this.encoding);
} else {
throw new ParserError("data is not text", file.url);
}
}
};
/***/ }),
/***/ 8015:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
const {
ParserError
} = __webpack_require__(5554);
const yaml = __webpack_require__(7949);
const {
JSON_SCHEMA
} = __webpack_require__(7949);
module.exports = {
/**
* The order that this parser will run, in relation to other parsers.
*
* @type {number}
*/
order: 200,
/**
* Whether to allow "empty" files. This includes zero-byte files, as well as empty JSON objects.
*
* @type {boolean}
*/
allowEmpty: true,
/**
* Determines whether this parser can parse a given file reference.
* Parsers that match will be tried, in order, until one successfully parses the file.
* Parsers that don't match will be skipped, UNLESS none of the parsers match, in which case
* every parser will be tried.
*
* @type {RegExp|string[]|function}
*/
canParse: [".yaml", ".yml", ".json"],
// JSON is valid YAML
/**
* Parses the given file as YAML
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @param {*} file.data - The file contents. This will be whatever data type was returned by the resolver
* @returns {Promise}
*/
async parse(file) {
// eslint-disable-line require-await
let data = file.data;
if (Buffer.isBuffer(data)) {
data = data.toString();
}
if (typeof data === "string") {
try {
return yaml.load(data, {
schema: JSON_SCHEMA
});
} catch (e) {
throw new ParserError(e.message, file.url);
}
} else {
// data is already a JavaScript value (object, array, number, null, NaN, etc.)
return data;
}
}
};
/***/ }),
/***/ 5860:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
module.exports = Pointer;
const $Ref = __webpack_require__(6654);
const url = __webpack_require__(6930);
const {
JSONParserError,
InvalidPointerError,
MissingPointerError,
isHandledError
} = __webpack_require__(5554);
const slashes = /\//g;
const tildes = /~/g;
const escapedSlash = /~1/g;
const escapedTilde = /~0/g;
/**
* This class represents a single JSON pointer and its resolved value.
*
* @param {$Ref} $ref
* @param {string} path
* @param {string} [friendlyPath] - The original user-specified path (used for error messages)
* @constructor
*/
function Pointer($ref, path, friendlyPath) {
/**
* The {@link $Ref} object that contains this {@link Pointer} object.
* @type {$Ref}
*/
this.$ref = $ref;
/**
* The file path or URL, containing the JSON pointer in the hash.
* This path is relative to the path of the main JSON schema file.
* @type {string}
*/
this.path = path;
/**
* The original path or URL, used for error messages.
* @type {string}
*/
this.originalPath = friendlyPath || path;
/**
* The value of the JSON pointer.
* Can be any JSON type, not just objects. Unknown file types are represented as Buffers (byte arrays).
* @type {?*}
*/
this.value = undefined;
/**
* Indicates whether the pointer references itself.
* @type {boolean}
*/
this.circular = false;
/**
* The number of indirect references that were traversed to resolve the value.
* Resolving a single pointer may require resolving multiple $Refs.
* @type {number}
*/
this.indirections = 0;
}
/**
* Resolves the value of a nested property within the given object.
*
* @param {*} obj - The object that will be crawled
* @param {$RefParserOptions} options
* @param {string} pathFromRoot - the path of place that initiated resolving
*
* @returns {Pointer}
* Returns a JSON pointer whose {@link Pointer#value} is the resolved value.
* If resolving this value required resolving other JSON references, then
* the {@link Pointer#$ref} and {@link Pointer#path} will reflect the resolution path
* of the resolved value.
*/
Pointer.prototype.resolve = function (obj, options, pathFromRoot) {
let tokens = Pointer.parse(this.path, this.originalPath);
// Crawl the object, one token at a time
this.value = unwrapOrThrow(obj);
for (let i = 0; i < tokens.length; i++) {
if (resolveIf$Ref(this, options)) {
// The $ref path has changed, so append the remaining tokens to the path
this.path = Pointer.join(this.path, tokens.slice(i));
}
if (typeof this.value === "object" && this.value !== null && "$ref" in this.value) {
return this;
}
let token = tokens[i];
if (this.value[token] === undefined || this.value[token] === null) {
this.value = null;
throw new MissingPointerError(token, decodeURI(this.originalPath));
} else {
this.value = this.value[token];
}
}
// Resolve the final value
if (!this.value || this.value.$ref && url.resolve(this.path, this.value.$ref) !== pathFromRoot) {
resolveIf$Ref(this, options);
}
return this;
};
/**
* Sets the value of a nested property within the given object.
*
* @param {*} obj - The object that will be crawled
* @param {*} value - the value to assign
* @param {$RefParserOptions} options
*
* @returns {*}
* Returns the modified object, or an entirely new object if the entire object is overwritten.
*/
Pointer.prototype.set = function (obj, value, options) {
let tokens = Pointer.parse(this.path);
let token;
if (tokens.length === 0) {
// There are no tokens, replace the entire object with the new value
this.value = value;
return value;
}
// Crawl the object, one token at a time
this.value = unwrapOrThrow(obj);
for (let i = 0; i < tokens.length - 1; i++) {
resolveIf$Ref(this, options);
token = tokens[i];
if (this.value && this.value[token] !== undefined) {
// The token exists
this.value = this.value[token];
} else {
// The token doesn't exist, so create it
this.value = setValue(this, token, {});
}
}
// Set the value of the final token
resolveIf$Ref(this, options);
token = tokens[tokens.length - 1];
setValue(this, token, value);
// Return the updated object
return obj;
};
/**
* Parses a JSON pointer (or a path containing a JSON pointer in the hash)
* and returns an array of the pointer's tokens.
* (e.g. "schema.json#/definitions/person/name" => ["definitions", "person", "name"])
*
* The pointer is parsed according to RFC 6901
* {@link https://tools.ietf.org/html/rfc6901#section-3}
*
* @param {string} path
* @param {string} [originalPath]
* @returns {string[]}
*/
Pointer.parse = function (path, originalPath) {
// Get the JSON pointer from the path's hash
let pointer = url.getHash(path).substr(1);
// If there's no pointer, then there are no tokens,
// so return an empty array
if (!pointer) {
return [];
}
// Split into an array
pointer = pointer.split("/");
// Decode each part, according to RFC 6901
for (let i = 0; i < pointer.length; i++) {
pointer[i] = decodeURIComponent(pointer[i].replace(escapedSlash, "/").replace(escapedTilde, "~"));
}
if (pointer[0] !== "") {
throw new InvalidPointerError(pointer, originalPath === undefined ? path : originalPath);
}
return pointer.slice(1);
};
/**
* Creates a JSON pointer path, by joining one or more tokens to a base path.
*
* @param {string} base - The base path (e.g. "schema.json#/definitions/person")
* @param {string|string[]} tokens - The token(s) to append (e.g. ["name", "first"])
* @returns {string}
*/
Pointer.join = function (base, tokens) {
// Ensure that the base path contains a hash
if (base.indexOf("#") === -1) {
base += "#";
}
// Append each token to the base path
tokens = Array.isArray(tokens) ? tokens : [tokens];
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
// Encode the token, according to RFC 6901
base += "/" + encodeURIComponent(token.replace(tildes, "~0").replace(slashes, "~1"));
}
return base;
};
/**
* If the given pointer's {@link Pointer#value} is a JSON reference,
* then the reference is resolved and {@link Pointer#value} is replaced with the resolved value.
* In addition, {@link Pointer#path} and {@link Pointer#$ref} are updated to reflect the
* resolution path of the new value.
*
* @param {Pointer} pointer
* @param {$RefParserOptions} options
* @returns {boolean} - Returns `true` if the resolution path changed
*/
function resolveIf$Ref(pointer, options) {
// Is the value a JSON reference? (and allowed?)
if ($Ref.isAllowed$Ref(pointer.value, options)) {
let $refPath = url.resolve(pointer.path, pointer.value.$ref);
if ($refPath === pointer.path) {
// The value is a reference to itself, so there's nothing to do.
pointer.circular = true;
} else {
let resolved = pointer.$ref.$refs._resolve($refPath, pointer.path, options);
if (resolved === null) {
return false;
}
pointer.indirections += resolved.indirections + 1;
if ($Ref.isExtended$Ref(pointer.value)) {
// This JSON reference "extends" the resolved value, rather than simply pointing to it.
// So the resolved path does NOT change. Just the value does.
pointer.value = $Ref.dereference(pointer.value, resolved.value);
return false;
} else {
// Resolve the reference
pointer.$ref = resolved.$ref;
pointer.path = resolved.path;
pointer.value = resolved.value;
}
return true;
}
}
}
/**
* Sets the specified token value of the {@link Pointer#value}.
*
* The token is evaluated according to RFC 6901.
* {@link https://tools.ietf.org/html/rfc6901#section-4}
*
* @param {Pointer} pointer - The JSON Pointer whose value will be modified
* @param {string} token - A JSON Pointer token that indicates how to modify `obj`
* @param {*} value - The value to assign
* @returns {*} - Returns the assigned value
*/
function setValue(pointer, token, value) {
if (pointer.value && typeof pointer.value === "object") {
if (token === "-" && Array.isArray(pointer.value)) {
pointer.value.push(value);
} else {
pointer.value[token] = value;
}
} else {
throw new JSONParserError(`Error assigning $ref pointer "${pointer.path}". \nCannot set "${token}" of a non-object.`);
}
return value;
}
function unwrapOrThrow(value) {
if (isHandledError(value)) {
throw value;
}
return value;
}
/***/ }),
/***/ 6654:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
module.exports = $Ref;
const Pointer = __webpack_require__(5860);
const {
InvalidPointerError,
isHandledError,
normalizeError
} = __webpack_require__(5554);
const {
safePointerToPath,
stripHash,
getHash
} = __webpack_require__(6930);
/**
* This class represents a single JSON reference and its resolved value.
*
* @class
*/
function $Ref() {
/**
* The file path or URL of the referenced file.
* This path is relative to the path of the main JSON schema file.
*
* This path does NOT contain document fragments (JSON pointers). It always references an ENTIRE file.
* Use methods such as {@link $Ref#get}, {@link $Ref#resolve}, and {@link $Ref#exists} to get
* specific JSON pointers within the file.
*
* @type {string}
*/
this.path = undefined;
/**
* The resolved value of the JSON reference.
* Can be any JSON type, not just objects. Unknown file types are represented as Buffers (byte arrays).
*
* @type {?*}
*/
this.value = undefined;
/**
* The {@link $Refs} object that contains this {@link $Ref} object.
*
* @type {$Refs}
*/
this.$refs = undefined;
/**
* Indicates the type of {@link $Ref#path} (e.g. "file", "http", etc.)
*
* @type {?string}
*/
this.pathType = undefined;
/**
* List of all errors. Undefined if no errors.
*
* @type {Array<JSONParserError | ResolverError | ParserError | MissingPointerError>}
*/
this.errors = undefined;
}
/**
* Pushes an error to errors array.
*
* @param {Array<JSONParserError | JSONParserErrorGroup>} err - The error to be pushed
* @returns {void}
*/
$Ref.prototype.addError = function (err) {
if (this.errors === undefined) {
this.errors = [];
}
const existingErrors = this.errors.map(({
footprint
}) => footprint);
// the path has been almost certainly set at this point,
// but just in case something went wrong, normalizeError injects path if necessary
// moreover, certain errors might point at the same spot, so filter them out to reduce noise
if (Array.isArray(err.errors)) {
this.errors.push(...err.errors.map(normalizeError).filter(({
footprint
}) => !existingErrors.includes(footprint)));
} else if (!existingErrors.includes(err.footprint)) {
this.errors.push(normalizeError(err));
}
};
/**
* Determines whether the given JSON reference exists within this {@link $Ref#value}.
*
* @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
* @param {$RefParserOptions} options
* @returns {boolean}
*/
$Ref.prototype.exists = function (path, options) {
try {
this.resolve(path, options);
return true;
} catch (e) {
return false;
}
};
/**
* Resolves the given JSON reference within this {@link $Ref#value} and returns the resolved value.
*
* @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
* @param {$RefParserOptions} options
* @returns {*} - Returns the resolved value
*/
$Ref.prototype.get = function (path, options) {
return this.resolve(path, options).value;
};
/**
* Resolves the given JSON reference within this {@link $Ref#value}.
*
* @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
* @param {$RefParserOptions} options
* @param {string} friendlyPath - The original user-specified path (used for error messages)
* @param {string} pathFromRoot - The path of `obj` from the schema root
* @returns {Pointer | null}
*/
$Ref.prototype.resolve = function (path, options, friendlyPath, pathFromRoot) {
let pointer = new Pointer(this, path, friendlyPath);
try {
return pointer.resolve(this.value, options, pathFromRoot);
} catch (err) {
if (!options || !options.continueOnError || !isHandledError(err)) {
throw err;
}
if (err.path === null) {
err.path = safePointerToPath(getHash(pathFromRoot));
}
if (err instanceof InvalidPointerError) {
// this is a special case - InvalidPointerError is thrown when dereferencing external file,
// but the issue is caused by the source file that referenced the file that undergoes dereferencing
err.source = decodeURI(stripHash(pathFromRoot));
}
this.addError(err);
return null;
}
};
/**
* Sets the value of a nested property within this {@link $Ref#value}.
* If the property, or any of its parents don't exist, they will be created.
*
* @param {string} path - The full path of the property to set, optionally with a JSON pointer in the hash
* @param {*} value - The value to assign
*/
$Ref.prototype.set = function (path, value) {
let pointer = new Pointer(this, path);
this.value = pointer.set(this.value, value);
};
/**
* Determines whether the given value is a JSON reference.
*
* @param {*} value - The value to inspect
* @returns {boolean}
*/
$Ref.is$Ref = function (value) {
return value && typeof value === "object" && typeof value.$ref === "string" && value.$ref.length > 0;
};
/**
* Determines whether the given value is an external JSON reference.
*
* @param {*} value - The value to inspect
* @returns {boolean}
*/
$Ref.isExternal$Ref = function (value) {
return $Ref.is$Ref(value) && value.$ref[0] !== "#";
};
/**
* Determines whether the given value is a JSON reference, and whether it is allowed by the options.
* For example, if it references an external file, then options.resolve.external must be true.
*
* @param {*} value - The value to inspect
* @param {$RefParserOptions} options
* @returns {boolean}
*/
$Ref.isAllowed$Ref = function (value, options) {
if ($Ref.is$Ref(value)) {
if (value.$ref.substr(0, 2) === "#/" || value.$ref === "#") {
// It's a JSON Pointer reference, which is always allowed
return true;
} else if (value.$ref[0] !== "#" && (!options || options.resolve.external)) {
// It's an external reference, which is allowed by the options
return true;
}
}
};
/**
* Determines whether the given value is a JSON reference that "extends" its resolved value.
* That is, it has extra properties (in addition to "$ref"), so rather than simply pointing to
* an existing value, this $ref actually creates a NEW value that is a shallow copy of the resolved
* value, plus the extra properties.
*
* @example:
* {
* person: {
* properties: {
* firstName: { type: string }
* lastName: { type: string }
* }
* }
* employee: {
* properties: {
* $ref: #/person/properties
* salary: { type: number }
* }
* }
* }
*
* In this example, "employee" is an extended $ref, since it extends "person" with an additional
* property (salary). The result is a NEW value that looks like this:
*
* {
* properties: {
* firstName: { type: string }
* lastName: { type: string }
* salary: { type: number }
* }
* }
*
* @param {*} value - The value to inspect
* @returns {boolean}
*/
$Ref.isExtended$Ref = function (value) {
return $Ref.is$Ref(value) && Object.keys(value).length > 1;
};
/**
* Returns the resolved value of a JSON Reference.
* If necessary, the resolved value is merged with the JSON Reference to create a new object
*
* @example:
* {
* person: {
* properties: {
* firstName: { type: string }
* lastName: { type: string }
* }
* }
* employee: {
* properties: {
* $ref: #/person/properties
* salary: { type: number }
* }
* }
* }
*
* When "person" and "employee" are merged, you end up with the following object:
*
* {
* properties: {
* firstName: { type: string }
* lastName: { type: string }
* salary: { type: number }
* }
* }
*
* @param {object} $ref - The JSON reference object (the one with the "$ref" property)
* @param {*} resolvedValue - The resolved value, which can be any type
* @returns {*} - Returns the dereferenced value
*/
$Ref.dereference = function ($ref, resolvedValue) {
if (resolvedValue && typeof resolvedValue === "object" && $Ref.isExtended$Ref($ref)) {
let merged = {};
for (let key of Object.keys($ref)) {
if (key !== "$ref") {
merged[key] = $ref[key];
}
}
for (let key of Object.keys(resolvedValue)) {
if (!(key in merged)) {
merged[key] = resolvedValue[key];
}
}
return merged;
} else {
// Completely replace the original reference with the resolved value
return resolvedValue;
}
};
/***/ }),
/***/ 8031:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
const {
ono
} = __webpack_require__(9978);
const $Ref = __webpack_require__(6654);
const url = __webpack_require__(6930);
module.exports = $Refs;
/**
* This class is a map of JSON references and their resolved values.
*/
function $Refs() {
/**
* Indicates whether the schema contains any circular references.
*
* @type {boolean}
*/
this.circular = false;
/**
* A map of paths/urls to {@link $Ref} objects
*
* @type {object}
* @protected
*/
this._$refs = {};
/**
* The {@link $Ref} object that is the root of the JSON schema.
*
* @type {$Ref}
* @protected
*/
this._root$Ref = null;
}
/**
* Returns the paths of all the files/URLs that are referenced by the JSON schema,
* including the schema itself.
*
* @param {...string|string[]} [types] - Only return paths of the given types ("file", "http", etc.)
* @returns {string[]}
*/
$Refs.prototype.paths = function (types) {
// eslint-disable-line no-unused-vars
let paths = getPaths(this._$refs, arguments);
return paths.map(path => {
return path.decoded;
});
};
/**
* Returns the map of JSON references and their resolved values.
*
* @param {...string|string[]} [types] - Only return references of the given types ("file", "http", etc.)
* @returns {object}
*/
$Refs.prototype.values = function (types) {
// eslint-disable-line no-unused-vars
let $refs = this._$refs;
let paths = getPaths($refs, arguments);
return paths.reduce((obj, path) => {
obj[path.decoded] = $refs[path.encoded].value;
return obj;
}, {});
};
/**
* Returns a POJO (plain old JavaScript object) for serialization as JSON.
*
* @returns {object}
*/
$Refs.prototype.toJSON = $Refs.prototype.values;
/**
* Determines whether the given JSON reference exists.
*
* @param {string} path - The path being resolved, optionally with a JSON pointer in the hash
* @param {$RefParserOptions} [options]
* @returns {boolean}
*/
$Refs.prototype.exists = function (path, options) {
try {
this._resolve(path, "", options);
return true;
} catch (e) {
return false;
}
};
/**
* Resolves the given JSON reference and returns the resolved value.
*
* @param {string} path - The path being resolved, with a JSON pointer in the hash
* @param {$RefParserOptions} [options]
* @returns {*} - Returns the resolved value
*/
$Refs.prototype.get = function (path, options) {
return this._resolve(path, "", options).value;
};
/**
* Sets the value of a nested property within this {@link $Ref#value}.
* If the property, or any of its parents don't exist, they will be created.
*
* @param {string} path - The path of the property to set, optionally with a JSON pointer in the hash
* @param {*} value - The value to assign
*/
$Refs.prototype.set = function (path, value) {
let absPath = url.resolve(this._root$Ref.path, path);
let withoutHash = url.stripHash(absPath);
let $ref = this._$refs[withoutHash];
if (!$ref) {
throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
}
$ref.set(absPath, value);
};
/**
* Creates a new {@link $Ref} object and adds it to this {@link $Refs} object.
*
* @param {string} path - The file path or URL of the referenced file
*/
$Refs.prototype._add = function (path) {
let withoutHash = url.stripHash(path);
let $ref = new $Ref();
$ref.path = withoutHash;
$ref.$refs = this;
this._$refs[withoutHash] = $ref;
this._root$Ref = this._root$Ref || $ref;
return $ref;
};
/**
* Resolves the given JSON reference.
*
* @param {string} path - The path being resolved, optionally with a JSON pointer in the hash
* @param {string} pathFromRoot - The path of `obj` from the schema root
* @param {$RefParserOptions} [options]
* @returns {Pointer}
* @protected
*/
$Refs.prototype._resolve = function (path, pathFromRoot, options) {
let absPath = url.resolve(this._root$Ref.path, path);
let withoutHash = url.stripHash(absPath);
let $ref = this._$refs[withoutHash];
if (!$ref) {
throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
}
return $ref.resolve(absPath, options, path, pathFromRoot);
};
/**
* Returns the specified {@link $Ref} object, or undefined.
*
* @param {string} path - The path being resolved, optionally with a JSON pointer in the hash
* @returns {$Ref|undefined}
* @protected
*/
$Refs.prototype._get$Ref = function (path) {
path = url.resolve(this._root$Ref.path, path);
let withoutHash = url.stripHash(path);
return this._$refs[withoutHash];
};
/**
* Returns the encoded and decoded paths keys of the given object.
*
* @param {object} $refs - The object whose keys are URL-encoded paths
* @param {...string|string[]} [types] - Only return paths of the given types ("file", "http", etc.)
* @returns {object[]}
*/
function getPaths($refs, types) {
let paths = Object.keys($refs);
// Filter the paths by type
types = Array.isArray(types[0]) ? types[0] : Array.prototype.slice.call(types);
if (types.length > 0 && types[0]) {
paths = paths.filter(key => {
return types.indexOf($refs[key].pathType) !== -1;
});
}
// Decode local filesystem paths
return paths.map(path => {
return {
encoded: path,
decoded: $refs[path].pathType === "file" ? url.toFileSystemPath(path, true) : path
};
});
}
/***/ }),
/***/ 3747:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
const $Ref = __webpack_require__(6654);
const Pointer = __webpack_require__(5860);
const parse = __webpack_require__(3265);
const url = __webpack_require__(6930);
const {
isHandledError
} = __webpack_require__(5554);
module.exports = resolveExternal;
/**
* Crawls the JSON schema, finds all external JSON references, and resolves their values.
* This method does not mutate the JSON schema. The resolved values are added to {@link $RefParser#$refs}.
*
* NOTE: We only care about EXTERNAL references here. INTERNAL references are only relevant when dereferencing.
*
* @param {$RefParser} parser
* @param {$RefParserOptions} options
*
* @returns {Promise}
* The promise resolves once all JSON references in the schema have been resolved,
* including nested references that are contained in externally-referenced files.
*/
function resolveExternal(parser, options) {
if (!options.resolve.external) {
// Nothing to resolve, so exit early
return Promise.resolve();
}
try {
// console.log('Resolving $ref pointers in %s', parser.$refs._root$Ref.path);
let promises = crawl(parser.schema, parser.$refs._root$Ref.path + "#", parser.$refs, options);
return Promise.all(promises);
} catch (e) {
return Promise.reject(e);
}
}
/**
* Recursively crawls the given value, and resolves any external JSON references.
*
* @param {*} obj - The value to crawl. If it's not an object or array, it will be ignored.
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
* @param {$Refs} $refs
* @param {$RefParserOptions} options
* @param {Set} seen - Internal.
*
* @returns {Promise[]}
* Returns an array of promises. There will be one promise for each JSON reference in `obj`.
* If `obj` does not contain any JSON references, then the array will be empty.
* If any of the JSON references point to files that contain additional JSON references,
* then the corresponding promise will internally reference an array of promises.
*/
function crawl(obj, path, $refs, options, seen) {
seen = seen || new Set();
let promises = [];
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj) && !seen.has(obj)) {
seen.add(obj); // Track previously seen objects to avoid infinite recursion
if ($Ref.isExternal$Ref(obj)) {
promises.push(resolve$Ref(obj, path, $refs, options));
} else {
for (let key of Object.keys(obj)) {
let keyPath = Pointer.join(path, key);
let value = obj[key];
if ($Ref.isExternal$Ref(value)) {
promises.push(resolve$Ref(value, keyPath, $refs, options));
} else {
promises = promises.concat(crawl(value, keyPath, $refs, options, seen));
}
}
}
}
return promises;
}
/**
* Resolves the given JSON Reference, and then crawls the resulting value.
*
* @param {{$ref: string}} $ref - The JSON Reference to resolve
* @param {string} path - The full path of `$ref`, possibly with a JSON Pointer in the hash
* @param {$Refs} $refs
* @param {$RefParserOptions} options
*
* @returns {Promise}
* The promise resolves once all JSON references in the object have been resolved,
* including nested references that are contained in externally-referenced files.
*/
async function resolve$Ref($ref, path, $refs, options) {
// console.log('Resolving $ref pointer "%s" at %s', $ref.$ref, path);
let resolvedPath = url.resolve(path, $ref.$ref);
let withoutHash = url.stripHash(resolvedPath);
// Do we already have this $ref?
$ref = $refs._$refs[withoutHash];
if ($ref) {
// We've already parsed this $ref, so use the existing value
return Promise.resolve($ref.value);
}
// Parse the $referenced file/url
try {
const result = await parse(resolvedPath, $refs, options);
// Crawl the parsed value
// console.log('Resolving $ref pointers in %s', withoutHash);
let promises = crawl(result, withoutHash + "#", $refs, options);
return Promise.all(promises);
} catch (err) {
if (!options.continueOnError || !isHandledError(err)) {
throw err;
}
if ($refs._$refs[withoutHash]) {
err.source = decodeURI(url.stripHash(path));
err.path = url.safePointerToPath(url.getHash(path));
}
return [];
}
}
/***/ }),
/***/ 9571:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
const fs = __webpack_require__(3471);
const {
ono
} = __webpack_require__(9978);
const url = __webpack_require__(6930);
const {
ResolverError
} = __webpack_require__(5554);
module.exports = {
/**
* The order that this resolver will run, in relation to other resolvers.
*
* @type {number}
*/
order: 100,
/**
* Determines whether this resolver can read a given file reference.
* Resolvers that return true will be tried, in order, until one successfully resolves the file.
* Resolvers that return false will not be given a chance to resolve the file.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @returns {boolean}
*/
canRead(file) {
return url.isFileSystemPath(file.url);
},
/**
* Reads the given file and returns its raw contents as a Buffer.
*
* @param {object} file - An object containing information about the referenced file
* @param {string} file.url - The full URL of the referenced file
* @param {string} file.extension - The lowercased file extension (e.g. ".txt", ".html", etc.)
* @returns {Promise<Buffer>}
*/
read(file) {
return new Promise((resolve, reject) => {
let path;
try {
path = url.toFileSystemPath(file.url);
} catch (err) {
reject(new ResolverError(ono.uri(err, `Malformed URI: ${file.url}`), file.url));
}
// console.log('Opening file: %s', path);
try {
fs.readFile(path, (err, data) => {
if (err) {
reject(new ResolverError(ono(err, `Error opening file "${path}"`), path));
} else {
resolve(data);
}
});
} catch (err) {
reject(new ResolverError(ono(err, `Error opening file "${path}"`), path));
}
});
}
};
/***/ }),
/***/ 6796:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = __webpack_require__(2790);
/* provided dependency */ var Buffer = __webpack_require__(5851)["Buffer"];
const http = __webpack_require__(5262);
const https = __webpack_require__(9804);
const {
ono
} = __webpack_require__(9978);
const url = __webpack_require__(6930);
const {
ResolverError
} = __webpack_require__(5554);
module.exports = {
/**
* The order that this resolver will run, in relation to other resolvers.
*
* @type {number}
*/
order: 200,
/**
* HTTP headers to send when downloading files.
*
* @example:
* {
* "User-Agent": "JSON Schema $Ref Parser",
* Accept: "application/json"
* }
*
* @type {object}
*/
headers: null,
/**
* HTTP request timeout (in milliseconds).
*
* @type {number}
*/
timeout: 5000,
// 5 seconds
/**
* The maximum number of HTTP redirects to follow.
* To disable automatic following of redirects, set this to zero.
*
* @type {number}
*/
redirects: 5,
/**
* The `withCredentials` option of XMLHttpRequest.
* Set this to `true`