/* eslint-disable no-unused-vars */ "use strict"; const $Refs = require("./refs"); const _parse = require("./parse"); const normalizeArgs = require("./normalize-args"); const resolveExternal = require("./resolve-external"); const _bundle = require("./bundle"); const _dereference = require("./dereference"); const url = require("./util/url"); const { JSONParserError, InvalidPointerError, MissingPointerError, ResolverError, ParserError, UnmatchedParserError, UnmatchedResolverError, isHandledError, JSONParserErrorGroup } = require("./util/errors"); const maybe = require("call-me-maybe"); const { ono } = require("@jsdevtools/ono"); module.exports = $RefParser; module.exports.default = $RefParser; module.exports.JSONParserError = JSONParserError; module.exports.InvalidPointerError = InvalidPointerError; module.exports.MissingPointerError = MissingPointerError; module.exports.ResolverError = ResolverError; module.exports.ParserError = ParserError; module.exports.UnmatchedParserError = UnmatchedParserError; module.exports.UnmatchedResolverError = UnmatchedResolverError; /** * This class parses a JSON schema, builds a map of its JSON references and their resolved values, * and provides methods for traversing, manipulating, and dereferencing those references. * * @constructor */ function $RefParser () { /** * The parsed (and possibly dereferenced) JSON schema object * * @type {object} * @readonly */ this.schema = null; /** * The resolved JSON references * * @type {$Refs} * @readonly */ this.$refs = new $Refs(); } /** * Parses the given JSON schema. * This method does not resolve any JSON references. * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed * @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object. * @returns {Promise} - The returned promise resolves with the parsed JSON schema object. */ $RefParser.parse = function parse (path, schema, options, callback) { let Class = this; // eslint-disable-line consistent-this let instance = new Class(); return instance.parse.apply(instance, arguments); }; /** * Parses the given JSON schema. * This method does not resolve any JSON references. * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed * @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object. * @returns {Promise} - The returned promise resolves with the parsed JSON schema object. */ $RefParser.prototype.parse = async function parse (path, schema, options, callback) { let args = normalizeArgs(arguments); let promise; if (!args.path && !args.schema) { let err = ono(`Expected a file path, URL, or object. Got ${args.path || args.schema}`); return maybe(args.callback, Promise.reject(err)); } // Reset everything this.schema = null; this.$refs = new $Refs(); // If the path is a filesystem path, then convert it to a URL. // NOTE: According to the JSON Reference spec, these should already be URLs, // but, in practice, many people use local filesystem paths instead. // So we're being generous here and doing the conversion automatically. // This is not intended to be a 100% bulletproof solution. // If it doesn't work for your use-case, then use a URL instead. let pathType = "http"; if (url.isFileSystemPath(args.path)) { args.path = url.fromFileSystemPath(args.path); pathType = "file"; } // Resolve the absolute path of the schema args.path = url.resolve(url.cwd(), args.path); if (args.schema && typeof args.schema === "object") { // A schema object was passed-in. // So immediately add a new $Ref with the schema object as its value let $ref = this.$refs._add(args.path); $ref.value = args.schema; $ref.pathType = pathType; promise = Promise.resolve(args.schema); } else { // Parse the schema file/url promise = _parse(args.path, this.$refs, args.options); } let me = this; try { let result = await promise; if (result !== null && typeof result === "object" && !Buffer.isBuffer(result)) { me.schema = result; return maybe(args.callback, Promise.resolve(me.schema)); } else if (args.options.continueOnError) { me.schema = null; // it's already set to null at line 79, but let's set it again for the sake of readability return maybe(args.callback, Promise.resolve(me.schema)); } else { throw ono.syntax(`"${me.$refs._root$Ref.path || result}" is not a valid JSON Schema`); } } catch (err) { if (!args.options.continueOnError || !isHandledError(err)) { return maybe(args.callback, Promise.reject(err)); } if (this.$refs._$refs[url.stripHash(args.path)]) { this.$refs._$refs[url.stripHash(args.path)].addError(err); } return maybe(args.callback, Promise.resolve(null)); } }; /** * Parses the given JSON schema and resolves any JSON references, including references in * externally-referenced files. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved * @param {function} [callback] * - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references * * @returns {Promise} * The returned promise resolves with a {@link $Refs} object containing the resolved JSON references */ $RefParser.resolve = function resolve (path, schema, options, callback) { let Class = this; // eslint-disable-line consistent-this let instance = new Class(); return instance.resolve.apply(instance, arguments); }; /** * Parses the given JSON schema and resolves any JSON references, including references in * externally-referenced files. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved * @param {function} [callback] * - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references * * @returns {Promise} * The returned promise resolves with a {@link $Refs} object containing the resolved JSON references */ $RefParser.prototype.resolve = async function resolve (path, schema, options, callback) { let me = this; let args = normalizeArgs(arguments); try { await this.parse(args.path, args.schema, args.options); await resolveExternal(me, args.options); finalize(me); return maybe(args.callback, Promise.resolve(me.$refs)); } catch (err) { return maybe(args.callback, Promise.reject(err)); } }; /** * Parses the given JSON schema, resolves any JSON references, and bundles all external references * into the main JSON schema. This produces a JSON schema that only has *internal* references, * not any *external* references. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced * @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object * @returns {Promise} - The returned promise resolves with the bundled JSON schema object. */ $RefParser.bundle = function bundle (path, schema, options, callback) { let Class = this; // eslint-disable-line consistent-this let instance = new Class(); return instance.bundle.apply(instance, arguments); }; /** * Parses the given JSON schema, resolves any JSON references, and bundles all external references * into the main JSON schema. This produces a JSON schema that only has *internal* references, * not any *external* references. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced * @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object * @returns {Promise} - The returned promise resolves with the bundled JSON schema object. */ $RefParser.prototype.bundle = async function bundle (path, schema, options, callback) { let me = this; let args = normalizeArgs(arguments); try { await this.resolve(args.path, args.schema, args.options); _bundle(me, args.options); finalize(me); return maybe(args.callback, Promise.resolve(me.schema)); } catch (err) { return maybe(args.callback, Promise.reject(err)); } }; /** * Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema. * That is, all JSON references are replaced with their resolved values. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced * @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object * @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object. */ $RefParser.dereference = function dereference (path, schema, options, callback) { let Class = this; // eslint-disable-line consistent-this let instance = new Class(); return instance.dereference.apply(instance, arguments); }; /** * Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema. * That is, all JSON references are replaced with their resolved values. * * @param {string} [path] - The file path or URL of the JSON schema * @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`. * @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced * @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object * @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object. */ $RefParser.prototype.dereference = async function dereference (path, schema, options, callback) { let me = this; let args = normalizeArgs(arguments); try { await this.resolve(args.path, args.schema, args.options); _dereference(me, args.options); finalize(me); return maybe(args.callback, Promise.resolve(me.schema)); } catch (err) { return maybe(args.callback, Promise.reject(err)); } }; function finalize (parser) { const errors = JSONParserErrorGroup.getParserErrors(parser); if (errors.length > 0) { throw new JSONParserErrorGroup(parser); } }