UNPKG

@open-rpc/schema-utils-js

Version:

<center> <span> <img alt="CircleCI branch" src="https://img.shields.io/circleci/project/github/open-rpc/schema-utils-js/master.svg"> <img src="https://codecov.io/gh/open-rpc/schema-utils-js/branch/master/graph/badge.svg" /> <img alt="npm" sr

662 lines (661 loc) 28.7 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenRPCDocumentDereferencingError = void 0; var dereferencer_1 = __importDefault(require("@json-schema-tools/dereferencer")); var meta_schema_1 = __importDefault(require("@open-rpc/meta-schema")); var reference_resolver_1 = __importDefault(require("@json-schema-tools/reference-resolver")); var fast_safe_stringify_1 = __importDefault(require("fast-safe-stringify")); /** * Provides an error interface for OpenRPC Document dereferencing problems * * @category Errors * */ var OpenRPCDocumentDereferencingError = /** @class */ (function () { /** * @param e The error that originated from jsonSchemaRefParser */ function OpenRPCDocumentDereferencingError(e) { this.name = "OpenRPCDocumentDereferencingError"; this.message = "The json schema provided cannot be dereferenced. Received Error: \n ".concat(e); } return OpenRPCDocumentDereferencingError; }()); exports.OpenRPCDocumentDereferencingError = OpenRPCDocumentDereferencingError; var derefItem = function (item, doc, resolver) { return __awaiter(void 0, void 0, void 0, function () { var $ref, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: $ref = item.$ref; if ($ref === undefined) { return [2 /*return*/, item]; } _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, resolver.resolve($ref, doc)]; case 2: // returns resolved value of the reference // eslint-disable-next-line @typescript-eslint/no-explicit-any return [2 /*return*/, (_a.sent())]; case 3: err_1 = _a.sent(); throw new OpenRPCDocumentDereferencingError([ "unable to eval pointer against OpenRPC Document.", "error type: ".concat(err_1.name), "instance: ".concat(err_1.instance), "token: ".concat(err_1.token), "pointer: ".concat($ref), "reference object: ".concat((0, fast_safe_stringify_1.default)(item)), ].join("\n")); case 4: return [2 /*return*/]; } }); }); }; var derefItems = function (items, doc, resolver) { return __awaiter(void 0, void 0, void 0, function () { var dereffed, _i, items_1, i, _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: dereffed = []; _i = 0, items_1 = items; _c.label = 1; case 1: if (!(_i < items_1.length)) return [3 /*break*/, 4]; i = items_1[_i]; _b = (_a = dereffed).push; return [4 /*yield*/, derefItem(i, doc, resolver)]; case 2: _b.apply(_a, [_c.sent()]); _c.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, dereffed]; } }); }); }; var matchDerefItems = function (items, doc, resolver) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { if (Array.isArray(items)) { return [2 /*return*/, derefItems(items, doc, resolver)]; } return [2 /*return*/, derefItem(items, doc, resolver)]; }); }); }; var handleSchemaWithSchemaComponents = function (s, schemaComponents) { return __awaiter(void 0, void 0, void 0, function () { var dereffer, dereffed, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: if (s === true || s === false) { return [2 /*return*/, Promise.resolve(s)]; } if (schemaComponents !== undefined) { s.components = { schemas: schemaComponents }; } dereffer = new dereferencer_1.default(s); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, dereffer.resolve()]; case 2: dereffed = _a.sent(); if (dereffed !== true && dereffed !== false) { delete dereffed.components; delete s.components; } return [2 /*return*/, dereffed]; case 3: e_1 = _a.sent(); throw new OpenRPCDocumentDereferencingError([ "Unable to parse reference inside of JSONSchema", s.title ? "Schema Title: ".concat(s.title) : "", "error message: ".concat(e_1.message), "schema in question: ".concat((0, fast_safe_stringify_1.default)(s)), ].join("\n")); case 4: return [2 /*return*/]; } }); }); }; var handleSchemaComponents = function (doc) { return __awaiter(void 0, void 0, void 0, function () { var schemas, schemaKeys, _i, schemaKeys_1, k, _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (doc.components === undefined) { return [2 /*return*/, Promise.resolve(doc)]; } if (doc.components.schemas === undefined) { return [2 /*return*/, Promise.resolve(doc)]; } schemas = doc.components.schemas; schemaKeys = Object.keys(schemas); _i = 0, schemaKeys_1 = schemaKeys; _c.label = 1; case 1: if (!(_i < schemaKeys_1.length)) return [3 /*break*/, 4]; k = schemaKeys_1[_i]; _a = schemas; _b = k; return [4 /*yield*/, handleSchemaWithSchemaComponents(schemas[k], schemas)]; case 2: _a[_b] = _c.sent(); _c.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, doc]; } }); }); }; var handleSchemasInsideContentDescriptorComponents = function (doc) { return __awaiter(void 0, void 0, void 0, function () { var cds, cdsKeys, componentSchemas, _i, cdsKeys_1, cdK, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: if (doc.components === undefined) { return [2 /*return*/, Promise.resolve(doc)]; } if (doc.components.contentDescriptors === undefined) { return [2 /*return*/, Promise.resolve(doc)]; } cds = doc.components.contentDescriptors; cdsKeys = Object.keys(cds); componentSchemas = {}; if (doc.components.schemas) { componentSchemas = doc.components.schemas; } _i = 0, cdsKeys_1 = cdsKeys; _b.label = 1; case 1: if (!(_i < cdsKeys_1.length)) return [3 /*break*/, 4]; cdK = cdsKeys_1[_i]; _a = cds[cdK]; return [4 /*yield*/, handleSchemaWithSchemaComponents(cds[cdK].schema, componentSchemas)]; case 2: _a.schema = _b.sent(); _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, doc]; } }); }); }; // remap the definitions map to remove the definitions. prefix and replace it with the parent object type var remap = function (definitionsMap) { var _a, _b; var remappedDefinitions = {}; var graph = new Map(); var resolved = new Set(); // Build dependency graph for (var _i = 0, _c = Object.entries(definitionsMap); _i < _c.length; _i++) { var _d = _c[_i], key = _d[0], paths = _d[1]; graph.set(key, new Set()); for (var _e = 0, paths_1 = paths; _e < paths_1.length; _e++) { var path = paths_1[_e]; var parts = path.split("."); if (parts.length === 1) { (_a = graph.get(key)) === null || _a === void 0 ? void 0 : _a.add(path); } else if (path.startsWith("definitions.")) { parts.shift(); // Remove 'definitions' var parentType = parts[0]; if (parentType && parentType !== key) { (_b = graph.get(key)) === null || _b === void 0 ? void 0 : _b.add(parentType); } } } } // Helper to resolve a definition and its dependencies var resolveDef = function (key) { var _a; if (resolved.has(key)) return; // Resolve dependencies first (_a = graph.get(key)) === null || _a === void 0 ? void 0 : _a.forEach(function (dep) { return resolveDef(dep); }); if (!definitionsMap[key]) { return key; } var accumulatedPaths = []; definitionsMap[key].forEach(function (path) { if (!path.startsWith("definitions.")) { accumulatedPaths.push(path); return; } var parts = path.split("."); parts.shift(); // Remove 'definitions' var parentType = parts.shift(); var remainingPath = parts.join("."); if (!parentType || !remappedDefinitions[parentType]) { accumulatedPaths.push(remainingPath); return; } remappedDefinitions[parentType].forEach(function (basePath) { var newPath = basePath ? "".concat(basePath, ".").concat(remainingPath) : remainingPath; accumulatedPaths.push(newPath); }); }); remappedDefinitions[key] = accumulatedPaths; resolved.add(key); }; // Resolve all definitions Object.keys(definitionsMap).forEach(resolveDef); return remappedDefinitions; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any function createDefinitionsMap(schema, path) { if (path === void 0) { path = ""; } var definitionsMap = {}; var simplifyPath = function (path) { // Remove .items, .patternProperties, and anything after them return path.split(/\.(items|patternProperties)/)[0]; }; var addToMap = function (definitionName, currentPath) { if (definitionName && definitionName !== "referenceObject") { if (!definitionsMap[definitionName]) { definitionsMap[definitionName] = []; } var simplifiedPath = simplifyPath(currentPath); if (simplifiedPath && !definitionsMap[definitionName].includes(simplifiedPath)) { definitionsMap[definitionName].push(simplifiedPath); } } }; // Handle object properties recursively // eslint-disable-next-line @typescript-eslint/no-explicit-any var traverseObject = function (obj, currentPath) { if (!obj || typeof obj !== "object") return; // Handle direct $ref if ("$ref" in obj) { var definitionName = obj["$ref"].split("/").pop(); addToMap(definitionName, currentPath); } // Handle arrays with items if ("items" in obj) { // Direct $ref in items if (obj.items.$ref) { var definitionName = obj.items.$ref.split("/").pop(); addToMap(definitionName, "".concat(currentPath, ".items")); } // oneOf in items if (obj.items.oneOf) { // eslint-disable-next-line @typescript-eslint/no-explicit-any obj.items.oneOf.forEach(function (item) { if (item.$ref) { var definitionName = item.$ref.split("/").pop(); addToMap(definitionName, "".concat(currentPath, ".items")); } }); } } // Handle properties if ("properties" in obj) { // eslint-disable-next-line @typescript-eslint/no-explicit-any Object.entries(obj.properties).forEach(function (_a) { var key = _a[0], value = _a[1]; var newPath = currentPath ? "".concat(currentPath, ".").concat(key) : key; traverseObject(value, newPath); }); } // Handle oneOf at current level if ("oneOf" in obj) { // eslint-disable-next-line @typescript-eslint/no-explicit-any obj.oneOf.forEach(function (item) { if (item.$ref) { var definitionName = item.$ref.split("/").pop(); addToMap(definitionName, currentPath); } traverseObject(item, currentPath); }); } // Recursively traverse all other properties Object.entries(obj).forEach(function (_a) { var key = _a[0], value = _a[1]; if (value && typeof value === "object" && key !== "properties" && key !== "items" && key !== "oneOf") { var newPath = currentPath ? "".concat(currentPath, ".").concat(key) : key; traverseObject(value, newPath); } }); }; traverseObject(schema, path); return remap(definitionsMap); } function resolveDefinition(definitionsMap, definitionKey) { return definitionsMap[definitionKey] || []; } // Traverses an object based on a dot-separated path and returns all matching objects at that path // eslint-disable-next-line @typescript-eslint/no-explicit-any var getDoc = function (docName, derefDoc) { var docNames = docName.split("."); // eslint-disable-next-line @typescript-eslint/no-explicit-any var traverseObject = function (obj, pathParts) { // eslint-disable-next-line @typescript-eslint/no-explicit-any var results = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any var traverse = function (current, depth) { if (!current) return; // If we've reached our target depth, collect this object if (depth === pathParts.length) { results.push(current); return; } var part = pathParts[depth]; var next = current[part]; // Handle both arrays and objects if (Array.isArray(next)) { next.forEach(function (item) { return traverse(item, depth + 1); }); } else if (next && typeof next === "object") { traverse(next, depth + 1); } }; traverse(obj, 0); return results; }; return { items: traverseObject(derefDoc, docNames) }; }; /* eslint-disable @typescript-eslint/no-explicit-any */ var handleExtension = function (extensionOrRef, doc, resolver) { return __awaiter(void 0, void 0, void 0, function () { var componentSchemas, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!(extensionOrRef.$ref !== undefined)) return [3 /*break*/, 2]; return [4 /*yield*/, derefItem({ $ref: extensionOrRef.$ref }, doc, resolver)]; case 1: extensionOrRef = _b.sent(); _b.label = 2; case 2: componentSchemas = {}; if (doc.components && doc.components.schemas) { componentSchemas = doc.components.schemas; } if (!(extensionOrRef.schema !== undefined)) return [3 /*break*/, 4]; _a = extensionOrRef; return [4 /*yield*/, handleSchemaWithSchemaComponents(extensionOrRef.schema, componentSchemas)]; case 3: _a.schema = _b.sent(); _b.label = 4; case 4: return [2 /*return*/, extensionOrRef]; } }); }); }; /* eslint-enable @typescript-eslint/no-explicit-any */ var handleMethod = function (methodOrRef, doc, resolver) { return __awaiter(void 0, void 0, void 0, function () { var method, _a, _b, _c, _d, _i, _e, exPairing, _f, _g, _h, _j, componentSchemas, params, _k, params_1, p, _l, result, _m; return __generator(this, function (_o) { switch (_o.label) { case 0: method = methodOrRef; if (!(methodOrRef.$ref !== undefined)) return [3 /*break*/, 2]; return [4 /*yield*/, derefItem({ $ref: methodOrRef.$ref }, doc, resolver)]; case 1: method = _o.sent(); _o.label = 2; case 2: if (!(method.tags !== undefined)) return [3 /*break*/, 4]; _a = method; return [4 /*yield*/, derefItems(method.tags, doc, resolver)]; case 3: _a.tags = _o.sent(); _o.label = 4; case 4: if (!(method.errors !== undefined)) return [3 /*break*/, 6]; _b = method; return [4 /*yield*/, derefItems(method.errors, doc, resolver)]; case 5: _b.errors = _o.sent(); _o.label = 6; case 6: if (!(method.links !== undefined)) return [3 /*break*/, 8]; _c = method; return [4 /*yield*/, derefItems(method.links, doc, resolver)]; case 7: _c.links = _o.sent(); _o.label = 8; case 8: if (!(method.examples !== undefined)) return [3 /*break*/, 14]; _d = method; return [4 /*yield*/, derefItems(method.examples, doc, resolver)]; case 9: _d.examples = _o.sent(); _i = 0, _e = method.examples; _o.label = 10; case 10: if (!(_i < _e.length)) return [3 /*break*/, 14]; exPairing = _e[_i]; _f = exPairing; return [4 /*yield*/, derefItems(exPairing.params, doc, resolver)]; case 11: _f.params = _o.sent(); if (!(exPairing.result !== undefined)) return [3 /*break*/, 13]; _g = exPairing; return [4 /*yield*/, derefItem(exPairing.result, doc, resolver)]; case 12: _g.result = _o.sent(); _o.label = 13; case 13: _i++; return [3 /*break*/, 10]; case 14: _h = method; return [4 /*yield*/, derefItems(method.params, doc, resolver)]; case 15: _h.params = _o.sent(); if (!(method.result !== undefined)) return [3 /*break*/, 17]; _j = method; return [4 /*yield*/, derefItem(method.result, doc, resolver)]; case 16: _j.result = _o.sent(); _o.label = 17; case 17: componentSchemas = {}; if (doc.components && doc.components.schemas) { componentSchemas = doc.components.schemas; } params = method.params; _k = 0, params_1 = params; _o.label = 18; case 18: if (!(_k < params_1.length)) return [3 /*break*/, 21]; p = params_1[_k]; _l = p; return [4 /*yield*/, handleSchemaWithSchemaComponents(p.schema, componentSchemas)]; case 19: _l.schema = _o.sent(); _o.label = 20; case 20: _k++; return [3 /*break*/, 18]; case 21: if (!(method.result !== undefined)) return [3 /*break*/, 23]; result = method.result; _m = result; return [4 /*yield*/, handleSchemaWithSchemaComponents(result.schema, componentSchemas)]; case 22: _m.schema = _o.sent(); _o.label = 23; case 23: return [2 /*return*/, method]; } }); }); }; /** * replaces $ref's within a document and its schemas. The replaced value will be a javascript object reference to the * real schema / open-rpc component * * @param schema The OpenRPC document * * @returns The same OpenRPC Document that was passed in, but with all $ref's dereferenced. * * @throws [[OpenRPCDocumentDereferencingError]] * * @example * ```typescript * * import { OpenRPC } from "@open-rpc/meta-schema" * import { dereferenceDocument } from "@open-rpc/schema-utils-js"; * * try { * const dereffedDocument = await dereferenceDocument({ ... }) as OpenRPC; * } catch (e) { * // handle validation errors * } * ``` * */ function dereferenceDocument(openrpcDocument, resolver) { if (resolver === void 0) { resolver = reference_resolver_1.default; } return __awaiter(this, void 0, void 0, function () { var derefDoc, definitionsMap, extensions, extensionDerefs, _i, _a, extension, derefedExtension, _b, _c, def, methods, _d, _e, method, _f, _g, _h, extensionDerefs_1, extension, _j, _k, docName, items, _l, items_2, item, _m, _o; return __generator(this, function (_p) { switch (_p.label) { case 0: derefDoc = __assign({}, openrpcDocument); return [4 /*yield*/, handleSchemaComponents(derefDoc)]; case 1: derefDoc = _p.sent(); return [4 /*yield*/, handleSchemasInsideContentDescriptorComponents(derefDoc)]; case 2: derefDoc = _p.sent(); definitionsMap = createDefinitionsMap(meta_schema_1.default); extensions = []; extensionDerefs = []; if (!derefDoc["x-extensions"]) return [3 /*break*/, 7]; _i = 0, _a = derefDoc["x-extensions"]; _p.label = 3; case 3: if (!(_i < _a.length)) return [3 /*break*/, 6]; extension = _a[_i]; return [4 /*yield*/, handleExtension(extension, derefDoc, resolver)]; case 4: derefedExtension = _p.sent(); extensions.push(derefedExtension); for (_b = 0, _c = derefedExtension.restricted; _b < _c.length; _b++) { def = _c[_b]; extensionDerefs.push({ extensionName: derefedExtension.name, docNames: resolveDefinition(definitionsMap, def), }); } _p.label = 5; case 5: _i++; return [3 /*break*/, 3]; case 6: derefDoc["x-extensions"] = extensions; _p.label = 7; case 7: methods = []; _d = 0, _e = derefDoc.methods; _p.label = 8; case 8: if (!(_d < _e.length)) return [3 /*break*/, 11]; method = _e[_d]; _g = (_f = methods).push; return [4 /*yield*/, handleMethod(method, derefDoc, resolver)]; case 9: _g.apply(_f, [_p.sent()]); _p.label = 10; case 10: _d++; return [3 /*break*/, 8]; case 11: _h = 0, extensionDerefs_1 = extensionDerefs; _p.label = 12; case 12: if (!(_h < extensionDerefs_1.length)) return [3 /*break*/, 19]; extension = extensionDerefs_1[_h]; _j = 0, _k = extension.docNames; _p.label = 13; case 13: if (!(_j < _k.length)) return [3 /*break*/, 18]; docName = _k[_j]; items = getDoc(docName, derefDoc).items; _l = 0, items_2 = items; _p.label = 14; case 14: if (!(_l < items_2.length)) return [3 /*break*/, 17]; item = items_2[_l]; if (!(item && item[extension.extensionName])) return [3 /*break*/, 16]; _m = item; _o = extension.extensionName; return [4 /*yield*/, matchDerefItems(item[extension.extensionName], derefDoc, resolver)]; case 15: _m[_o] = _p.sent(); _p.label = 16; case 16: _l++; return [3 /*break*/, 14]; case 17: _j++; return [3 /*break*/, 13]; case 18: _h++; return [3 /*break*/, 12]; case 19: derefDoc.methods = methods; return [2 /*return*/, derefDoc]; } }); }); } exports.default = dereferenceDocument;