UNPKG

graplix

Version:

Authorization framework for implementing Relation-based Access Control (ReBAC) with the Resolver (Inspired by [GraphQL](https://graphql.org))

1,572 lines (1,548 loc) 190 kB
import { EventEmitter } from "events"; import * as R from "remeda"; import { transformer } from "@openfga/syntax-transformer"; //#region rolldown:runtime 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() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion //#region ../../node_modules/obliterator/iterator.js var require_iterator = __commonJS({ "../../node_modules/obliterator/iterator.js"(exports, module) { /** * Obliterator Iterator Class * =========================== * * Simple class representing the library's iterators. */ /** * Iterator class. * * @constructor * @param {function} next - Next function. */ function Iterator$11(next) { if (typeof next !== "function") throw new Error("obliterator/iterator: expecting a function!"); this.next = next; } /** * If symbols are supported, we add `next` to `Symbol.iterator`. */ if (typeof Symbol !== "undefined") Iterator$11.prototype[Symbol.iterator] = function() { return this; }; /** * Returning an iterator of the given values. * * @param {any...} values - Values. * @return {Iterator} */ Iterator$11.of = function() { var args = arguments, l = args.length, i = 0; return new Iterator$11(function() { if (i >= l) return { done: true }; return { done: false, value: args[i++] }; }); }; /** * Returning an empty iterator. * * @return {Iterator} */ Iterator$11.empty = function() { var iterator = new Iterator$11(function() { return { done: true }; }); return iterator; }; /** * Returning an iterator over the given indexed sequence. * * @param {string|Array} sequence - Target sequence. * @return {Iterator} */ Iterator$11.fromSequence = function(sequence) { var i = 0, l = sequence.length; return new Iterator$11(function() { if (i >= l) return { done: true }; return { done: false, value: sequence[i++] }; }); }; /** * Returning whether the given value is an iterator. * * @param {any} value - Value. * @return {boolean} */ Iterator$11.is = function(value) { if (value instanceof Iterator$11) return true; return typeof value === "object" && value !== null && typeof value.next === "function"; }; /** * Exporting. */ module.exports = Iterator$11; } }); //#endregion //#region ../../node_modules/obliterator/support.js var require_support = __commonJS({ "../../node_modules/obliterator/support.js"(exports) { exports.ARRAY_BUFFER_SUPPORT = typeof ArrayBuffer !== "undefined"; exports.SYMBOL_SUPPORT = typeof Symbol !== "undefined"; } }); //#endregion //#region ../../node_modules/obliterator/iter.js var require_iter = __commonJS({ "../../node_modules/obliterator/iter.js"(exports, module) { /** * Obliterator Iter Function * ========================== * * Function coercing values to an iterator. It can be quite useful when needing * to handle iterables and iterators the same way. */ var Iterator$10 = require_iterator(); var support$2 = require_support(); var ARRAY_BUFFER_SUPPORT$2 = support$2.ARRAY_BUFFER_SUPPORT; var SYMBOL_SUPPORT$2 = support$2.SYMBOL_SUPPORT; function iterOrNull(target) { if (typeof target === "string" || Array.isArray(target) || ARRAY_BUFFER_SUPPORT$2 && ArrayBuffer.isView(target)) return Iterator$10.fromSequence(target); if (typeof target !== "object" || target === null) return null; if (SYMBOL_SUPPORT$2 && typeof target[Symbol.iterator] === "function") return target[Symbol.iterator](); if (typeof target.next === "function") return target; return null; } module.exports = function iter$9(target) { var iterator = iterOrNull(target); if (!iterator) throw new Error("obliterator: target is not iterable nor a valid iterator."); return iterator; }; } }); //#endregion //#region ../../node_modules/obliterator/chain.js var require_chain = __commonJS({ "../../node_modules/obliterator/chain.js"(exports, module) { /** * Obliterator Chain Function * =========================== * * Variadic function combining the given iterables. */ var Iterator$9 = require_iterator(); var iter$8 = require_iter(); /** * Chain. * * @param {...Iterator} iterables - Target iterables. * @return {Iterator} */ module.exports = function chain$2() { var iterables = arguments; var current = null; var i = -1; return new Iterator$9(function next() { var step = null; do { if (current === null) { i++; if (i >= iterables.length) return { done: true }; current = iter$8(iterables[i]); } step = current.next(); if (step.done === true) { current = null; continue; } break; } while (true); return step; }); }; } }); //#endregion //#region ../../node_modules/obliterator/combinations.js var require_combinations = __commonJS({ "../../node_modules/obliterator/combinations.js"(exports, module) { /** * Obliterator Combinations Function * ================================== * * Iterator returning combinations of the given array. */ var Iterator$8 = require_iterator(); /** * Helper mapping indices to items. */ function indicesToItems$1(target, items, indices, r) { for (var i = 0; i < r; i++) target[i] = items[indices[i]]; } /** * Combinations. * * @param {array} array - Target array. * @param {number} r - Size of the subsequences. * @return {Iterator} */ module.exports = function combinations$1(array, r) { if (!Array.isArray(array)) throw new Error("obliterator/combinations: first argument should be an array."); var n = array.length; if (typeof r !== "number") throw new Error("obliterator/combinations: second argument should be omitted or a number."); if (r > n) throw new Error("obliterator/combinations: the size of the subsequences should not exceed the length of the array."); if (r === n) return Iterator$8.of(array.slice()); var indices = new Array(r), subsequence = new Array(r), first = true, i; for (i = 0; i < r; i++) indices[i] = i; return new Iterator$8(function next() { if (first) { first = false; indicesToItems$1(subsequence, array, indices, r); return { value: subsequence, done: false }; } if (indices[r - 1]++ < n - 1) { indicesToItems$1(subsequence, array, indices, r); return { value: subsequence, done: false }; } i = r - 2; while (i >= 0 && indices[i] >= n - (r - i)) --i; if (i < 0) return { done: true }; indices[i]++; while (++i < r) indices[i] = indices[i - 1] + 1; indicesToItems$1(subsequence, array, indices, r); return { value: subsequence, done: false }; }); }; } }); //#endregion //#region ../../node_modules/obliterator/consume.js var require_consume = __commonJS({ "../../node_modules/obliterator/consume.js"(exports, module) { /** * Obliterator Consume Function * ============================= * * Function consuming the given iterator for n or every steps. */ /** * Consume. * * @param {Iterator} iterator - Target iterator. * @param {number} [steps] - Optional steps. */ module.exports = function consume(iterator, steps) { var step, l = arguments.length > 1 ? steps : Infinity, i = 0; while (true) { if (i === l) return; step = iterator.next(); if (step.done) return; i++; } }; } }); //#endregion //#region ../../node_modules/obliterator/every.js var require_every = __commonJS({ "../../node_modules/obliterator/every.js"(exports, module) { /** * Obliterator Every Function * ========================== * * Function taking an iterable and a predicate and returning whether all * its items match the given predicate. */ var iter$7 = require_iter(); /** * Every. * * @param {Iterable} iterable - Target iterable. * @param {function} predicate - Predicate function. * @return {boolean} */ module.exports = function every(iterable, predicate) { var iterator = iter$7(iterable); var step; while (step = iterator.next(), !step.done) if (!predicate(step.value)) return false; return true; }; } }); //#endregion //#region ../../node_modules/obliterator/filter.js var require_filter = __commonJS({ "../../node_modules/obliterator/filter.js"(exports, module) { /** * Obliterator Filter Function * =========================== * * Function returning a iterator filtering the given iterator. */ var Iterator$7 = require_iterator(); var iter$6 = require_iter(); /** * Filter. * * @param {Iterable} target - Target iterable. * @param {function} predicate - Predicate function. * @return {Iterator} */ module.exports = function filter(target, predicate) { var iterator = iter$6(target); var step; return new Iterator$7(function() { do step = iterator.next(); while (!step.done && !predicate(step.value)); return step; }); }; } }); //#endregion //#region ../../node_modules/obliterator/find.js var require_find = __commonJS({ "../../node_modules/obliterator/find.js"(exports, module) { /** * Obliterator Find Function * ========================== * * Function taking an iterable and a predicate and returning the first item * matching the given predicate. */ var iter$5 = require_iter(); /** * Find. * * @param {Iterable} iterable - Target iterable. * @param {function} predicate - Predicate function. * @return {boolean} */ module.exports = function find(iterable, predicate) { var iterator = iter$5(iterable); var step; while (step = iterator.next(), !step.done) if (predicate(step.value)) return step.value; return; }; } }); //#endregion //#region ../../node_modules/obliterator/foreach.js var require_foreach = __commonJS({ "../../node_modules/obliterator/foreach.js"(exports, module) { /** * Obliterator ForEach Function * ============================= * * Helper function used to easily iterate over mixed values. */ var support$1 = require_support(); var ARRAY_BUFFER_SUPPORT$1 = support$1.ARRAY_BUFFER_SUPPORT; var SYMBOL_SUPPORT$1 = support$1.SYMBOL_SUPPORT; /** * Function able to iterate over almost any iterable JS value. * * @param {any} iterable - Iterable value. * @param {function} callback - Callback function. */ module.exports = function forEach(iterable, callback) { var iterator, k, i, l, s; if (!iterable) throw new Error("obliterator/forEach: invalid iterable."); if (typeof callback !== "function") throw new Error("obliterator/forEach: expecting a callback."); if (Array.isArray(iterable) || ARRAY_BUFFER_SUPPORT$1 && ArrayBuffer.isView(iterable) || typeof iterable === "string" || iterable.toString() === "[object Arguments]") { for (i = 0, l = iterable.length; i < l; i++) callback(iterable[i], i); return; } if (typeof iterable.forEach === "function") { iterable.forEach(callback); return; } if (SYMBOL_SUPPORT$1 && Symbol.iterator in iterable && typeof iterable.next !== "function") iterable = iterable[Symbol.iterator](); if (typeof iterable.next === "function") { iterator = iterable; i = 0; while (s = iterator.next(), s.done !== true) { callback(s.value, i); i++; } return; } for (k in iterable) if (iterable.hasOwnProperty(k)) callback(iterable[k], k); return; }; } }); //#endregion //#region ../../node_modules/obliterator/foreach-with-null-keys.js var require_foreach_with_null_keys = __commonJS({ "../../node_modules/obliterator/foreach-with-null-keys.js"(exports, module) { /** * Obliterator ForEachWithNullKeys Function * ========================================= * * Helper function used to easily iterate over mixed values. */ var support = require_support(); var ARRAY_BUFFER_SUPPORT = support.ARRAY_BUFFER_SUPPORT; var SYMBOL_SUPPORT = support.SYMBOL_SUPPORT; /** * Same function as the `forEach` but will yield `null` when the target * does not have keys. * * @param {any} iterable - Iterable value. * @param {function} callback - Callback function. */ module.exports = function forEachWithNullKeys(iterable, callback) { var iterator, k, i, l, s; if (!iterable) throw new Error("obliterator/forEachWithNullKeys: invalid iterable."); if (typeof callback !== "function") throw new Error("obliterator/forEachWithNullKeys: expecting a callback."); if (Array.isArray(iterable) || ARRAY_BUFFER_SUPPORT && ArrayBuffer.isView(iterable) || typeof iterable === "string" || iterable.toString() === "[object Arguments]") { for (i = 0, l = iterable.length; i < l; i++) callback(iterable[i], null); return; } if (iterable instanceof Set) { iterable.forEach(function(value) { callback(value, null); }); return; } if (iterable instanceof Map) { iterable.forEach(function(value, key) { callback(value, key); }); return; } if (SYMBOL_SUPPORT && Symbol.iterator in iterable && typeof iterable.next !== "function") iterable = iterable[Symbol.iterator](); if (typeof iterable.next === "function") { iterator = iterable; i = 0; while (s = iterator.next(), s.done !== true) { callback(s.value, null); i++; } return; } if (typeof iterable.forEach === "function") { iterable.forEach(callback); return; } for (k in iterable) if (iterable.hasOwnProperty(k)) callback(iterable[k], k); return; }; } }); //#endregion //#region ../../node_modules/obliterator/includes.js var require_includes = __commonJS({ "../../node_modules/obliterator/includes.js"(exports, module) { /** * Obliterator Includes Function * ============================== * * Function taking an iterable and returning whether the given item can be * found in it. */ var iter$4 = require_iter(); /** * Includes. * * @param {Iterable} iterable - Target iterable. * @param {function} value - Searched value. * @return {boolean} */ module.exports = function includes(iterable, value) { var iterator = iter$4(iterable); var step; while (step = iterator.next(), !step.done) if (step.value === value) return true; return false; }; } }); //#endregion //#region ../../node_modules/obliterator/map.js var require_map = __commonJS({ "../../node_modules/obliterator/map.js"(exports, module) { /** * Obliterator Map Function * =========================== * * Function returning a iterator mapping the given iterator's values. */ var Iterator$6 = require_iterator(); var iter$3 = require_iter(); /** * Map. * * @param {Iterator} target - Target iterable. * @param {function} mapper - Map function. * @return {Iterator} */ module.exports = function map(target, mapper) { var iterator = iter$3(target); return new Iterator$6(function next() { var step = iterator.next(); if (step.done) return step; return { value: mapper(step.value) }; }); }; } }); //#endregion //#region ../../node_modules/obliterator/match.js var require_match = __commonJS({ "../../node_modules/obliterator/match.js"(exports, module) { /** * Obliterator Match Function * =========================== * * Function returning an iterator over the matches of the given regex on the * target string. */ var Iterator$5 = require_iterator(); /** * Match. * * @param {RegExp} pattern - Regular expression to use. * @param {string} string - Target string. * @return {Iterator} */ module.exports = function match(pattern, string) { var executed = false; if (!(pattern instanceof RegExp)) throw new Error("obliterator/match: invalid pattern. Expecting a regular expression."); if (typeof string !== "string") throw new Error("obliterator/match: invalid target. Expecting a string."); return new Iterator$5(function() { if (executed && !pattern.global) { pattern.lastIndex = 0; return { done: true }; } executed = true; var m = pattern.exec(string); if (m) return { value: m }; pattern.lastIndex = 0; return { done: true }; }); }; } }); //#endregion //#region ../../node_modules/obliterator/permutations.js var require_permutations = __commonJS({ "../../node_modules/obliterator/permutations.js"(exports, module) { /** * Obliterator Permutations Function * ================================== * * Iterator returning permutations of the given array. */ var Iterator$4 = require_iterator(); /** * Helper mapping indices to items. */ function indicesToItems(target, items, indices, r) { for (var i = 0; i < r; i++) target[i] = items[indices[i]]; } /** * Permutations. * * @param {array} array - Target array. * @param {number} r - Size of the subsequences. * @return {Iterator} */ module.exports = function permutations(array, r) { if (!Array.isArray(array)) throw new Error("obliterator/permutations: first argument should be an array."); var n = array.length; if (arguments.length < 2) r = n; if (typeof r !== "number") throw new Error("obliterator/permutations: second argument should be omitted or a number."); if (r > n) throw new Error("obliterator/permutations: the size of the subsequences should not exceed the length of the array."); var indices = new Uint32Array(n), subsequence = new Array(r), cycles = new Uint32Array(r), first = true, i; for (i = 0; i < n; i++) { indices[i] = i; if (i < r) cycles[i] = n - i; } i = r; return new Iterator$4(function next() { if (first) { first = false; indicesToItems(subsequence, array, indices, r); return { value: subsequence, done: false }; } var tmp, j; i--; if (i < 0) return { done: true }; cycles[i]--; if (cycles[i] === 0) { tmp = indices[i]; for (j = i; j < n - 1; j++) indices[j] = indices[j + 1]; indices[n - 1] = tmp; cycles[i] = n - i; return next(); } else { j = cycles[i]; tmp = indices[i]; indices[i] = indices[n - j]; indices[n - j] = tmp; i = r; indicesToItems(subsequence, array, indices, r); return { value: subsequence, done: false }; } }); }; } }); //#endregion //#region ../../node_modules/obliterator/power-set.js var require_power_set = __commonJS({ "../../node_modules/obliterator/power-set.js"(exports, module) { /** * Obliterator Power Set Function * =============================== * * Iterator returning the power set of the given array. */ var Iterator$3 = require_iterator(), combinations = require_combinations(), chain$1 = require_chain(); /** * Power set. * * @param {array} array - Target array. * @return {Iterator} */ module.exports = function powerSet(array) { var n = array.length; var iterators = new Array(n + 1); iterators[0] = Iterator$3.of([]); for (var i = 1; i < n + 1; i++) iterators[i] = combinations(array, i); return chain$1.apply(null, iterators); }; } }); //#endregion //#region ../../node_modules/obliterator/range.js var require_range = __commonJS({ "../../node_modules/obliterator/range.js"(exports, module) { /** * Obliterator Range Function * =========================== * * Function returning a range iterator. */ var Iterator$2 = require_iterator(); /** * Range. * * @param {number} start - Start. * @param {number} end - End. * @param {number} step - Step. * @return {Iterator} */ module.exports = function range(start, end, step) { if (arguments.length === 1) { end = start; start = 0; } if (arguments.length < 3) step = 1; var i = start; var iterator = new Iterator$2(function() { if (i < end) { var value = i; i += step; return { value, done: false }; } return { done: true }; }); iterator.start = start; iterator.end = end; iterator.step = step; return iterator; }; } }); //#endregion //#region ../../node_modules/obliterator/some.js var require_some = __commonJS({ "../../node_modules/obliterator/some.js"(exports, module) { /** * Obliterator Some Function * ========================== * * Function taking an iterable and a predicate and returning whether a * matching item can be found. */ var iter$2 = require_iter(); /** * Some. * * @param {Iterable} iterable - Target iterable. * @param {function} predicate - Predicate function. * @return {boolean} */ module.exports = function some(iterable, predicate) { var iterator = iter$2(iterable); var step; while (step = iterator.next(), !step.done) if (predicate(step.value)) return true; return false; }; } }); //#endregion //#region ../../node_modules/obliterator/split.js var require_split = __commonJS({ "../../node_modules/obliterator/split.js"(exports, module) { /** * Obliterator Split Function * =========================== * * Function returning an iterator over the pieces of a regex split. */ var Iterator$1 = require_iterator(); /** * Function used to make the given pattern global. * * @param {RegExp} pattern - Regular expression to make global. * @return {RegExp} */ function makeGlobal(pattern) { var flags = "g"; if (pattern.multiline) flags += "m"; if (pattern.ignoreCase) flags += "i"; if (pattern.sticky) flags += "y"; if (pattern.unicode) flags += "u"; return new RegExp(pattern.source, flags); } /** * Split. * * @param {RegExp} pattern - Regular expression to use. * @param {string} string - Target string. * @return {Iterator} */ module.exports = function split(pattern, string) { if (!(pattern instanceof RegExp)) throw new Error("obliterator/split: invalid pattern. Expecting a regular expression."); if (typeof string !== "string") throw new Error("obliterator/split: invalid target. Expecting a string."); pattern = makeGlobal(pattern); var consumed = false, current = 0; return new Iterator$1(function() { if (consumed) return { done: true }; var match = pattern.exec(string), value, length; if (match) { length = match.index + match[0].length; value = string.slice(current, match.index); current = length; } else { consumed = true; value = string.slice(current); } return { value, done: false }; }); }; } }); //#endregion //#region ../../node_modules/obliterator/take.js var require_take = __commonJS({ "../../node_modules/obliterator/take.js"(exports, module) { /** * Obliterator Take Function * ========================== * * Function taking n or every value of the given iterator and returns them * into an array. */ var iter$1 = require_iter(); /** * Take. * * @param {Iterable} iterable - Target iterable. * @param {number} [n] - Optional number of items to take. * @return {array} */ module.exports = function take$1(iterable, n) { var l = arguments.length > 1 ? n : Infinity, array = l !== Infinity ? new Array(l) : [], step, i = 0; var iterator = iter$1(iterable); while (true) { if (i === l) return array; step = iterator.next(); if (step.done) { if (i !== n) array.length = i; return array; } array[i++] = step.value; } }; } }); //#endregion //#region ../../node_modules/obliterator/take-into.js var require_take_into = __commonJS({ "../../node_modules/obliterator/take-into.js"(exports, module) { /** * Obliterator Take Into Function * =============================== * * Same as the take function but enables the user to select an array class * in which to insert the retrieved values. */ var iter = require_iter(); /** * Take Into. * * @param {function} ArrayClass - Array class to use. * @param {Iterable} iterable - Target iterable. * @param {number} n - Number of items to take. * @return {array} */ module.exports = function takeInto(ArrayClass, iterable, n) { var array = new ArrayClass(n), step, i = 0; var iterator = iter(iterable); while (true) { if (i === n) return array; step = iterator.next(); if (step.done) { if (i !== n) return array.slice(0, i); return array; } array[i++] = step.value; } }; } }); //#endregion //#region ../../node_modules/obliterator/index.js var require_obliterator = __commonJS({ "../../node_modules/obliterator/index.js"(exports) { /** * Obliterator Library Endpoint * ============================= * * Exporting the library's functions. */ exports.Iterator = require_iterator(); exports.iter = require_iter(); exports.chain = require_chain(); exports.combinations = require_combinations(); exports.consume = require_consume(); exports.every = require_every(); exports.filter = require_filter(); exports.find = require_find(); exports.forEach = require_foreach(); exports.forEachWithNullKeys = require_foreach_with_null_keys(); exports.includes = require_includes(); exports.map = require_map(); exports.match = require_match(); exports.permutations = require_permutations(); exports.powerSet = require_power_set(); exports.range = require_range(); exports.some = require_some(); exports.split = require_split(); exports.take = require_take(); exports.takeInto = require_take_into(); } }); //#endregion //#region ../../node_modules/graphology/dist/graphology.esm.js var import_obliterator = __toESM(require_obliterator(), 1); const { Iterator, take, chain } = import_obliterator.default; /** * Graphology Utilities * ===================== * * Collection of helpful functions used by the implementation. */ /** * Object.assign-like polyfill. * * @param {object} target - First object. * @param {object} [...objects] - Objects to merge. * @return {object} */ function assignPolyfill() { const target = arguments[0]; for (let i = 1, l = arguments.length; i < l; i++) { if (!arguments[i]) continue; for (const k in arguments[i]) target[k] = arguments[i][k]; } return target; } let assign = assignPolyfill; if (typeof Object.assign === "function") assign = Object.assign; /** * Function returning the first matching edge for given path. * Note: this function does not check the existence of source & target. This * must be performed by the caller. * * @param {Graph} graph - Target graph. * @param {any} source - Source node. * @param {any} target - Target node. * @param {string} type - Type of the edge (mixed, directed or undirected). * @return {string|null} */ function getMatchingEdge(graph, source, target, type) { const sourceData = graph._nodes.get(source); let edge = null; if (!sourceData) return edge; if (type === "mixed") edge = sourceData.out && sourceData.out[target] || sourceData.undirected && sourceData.undirected[target]; else if (type === "directed") edge = sourceData.out && sourceData.out[target]; else edge = sourceData.undirected && sourceData.undirected[target]; return edge; } /** * Checks whether the given value is a plain object. * * @param {mixed} value - Target value. * @return {boolean} */ function isPlainObject(value) { return typeof value === "object" && value !== null; } /** * Checks whether the given object is empty. * * @param {object} o - Target Object. * @return {boolean} */ function isEmpty(o) { let k; for (k in o) return false; return true; } /** * Creates a "private" property for the given member name by concealing it * using the `enumerable` option. * * @param {object} target - Target object. * @param {string} name - Member name. */ function privateProperty(target, name, value) { Object.defineProperty(target, name, { enumerable: false, configurable: false, writable: true, value }); } /** * Creates a read-only property for the given member name & the given getter. * * @param {object} target - Target object. * @param {string} name - Member name. * @param {mixed} value - The attached getter or fixed value. */ function readOnlyProperty(target, name, value) { const descriptor = { enumerable: true, configurable: true }; if (typeof value === "function") descriptor.get = value; else { descriptor.value = value; descriptor.writable = false; } Object.defineProperty(target, name, descriptor); } /** * Returns whether the given object constitute valid hints. * * @param {object} hints - Target object. */ function validateHints(hints) { if (!isPlainObject(hints)) return false; if (hints.attributes && !Array.isArray(hints.attributes)) return false; return true; } /** * Creates a function generating incremental ids for edges. * * @return {function} */ function incrementalIdStartingFromRandomByte() { let i = Math.floor(Math.random() * 256) & 255; return () => { return i++; }; } /** * Graphology Custom Errors * ========================= * * Defining custom errors for ease of use & easy unit tests across * implementations (normalized typology rather than relying on error * messages to check whether the correct error was found). */ var GraphError = class extends Error { constructor(message) { super(); this.name = "GraphError"; this.message = message; } }; var InvalidArgumentsGraphError = class InvalidArgumentsGraphError extends GraphError { constructor(message) { super(message); this.name = "InvalidArgumentsGraphError"; if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(this, InvalidArgumentsGraphError.prototype.constructor); } }; var NotFoundGraphError = class NotFoundGraphError extends GraphError { constructor(message) { super(message); this.name = "NotFoundGraphError"; if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(this, NotFoundGraphError.prototype.constructor); } }; var UsageGraphError = class UsageGraphError extends GraphError { constructor(message) { super(message); this.name = "UsageGraphError"; if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(this, UsageGraphError.prototype.constructor); } }; /** * Graphology Internal Data Classes * ================================= * * Internal classes hopefully reduced to structs by engines & storing * necessary information for nodes & edges. * * Note that those classes don't rely on the `class` keyword to avoid some * cruft introduced by most of ES2015 transpilers. */ /** * MixedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function MixedNodeData(key, attributes) { this.key = key; this.attributes = attributes; this.clear(); } MixedNodeData.prototype.clear = function() { this.inDegree = 0; this.outDegree = 0; this.undirectedDegree = 0; this.undirectedLoops = 0; this.directedLoops = 0; this.in = {}; this.out = {}; this.undirected = {}; }; /** * DirectedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function DirectedNodeData(key, attributes) { this.key = key; this.attributes = attributes; this.clear(); } DirectedNodeData.prototype.clear = function() { this.inDegree = 0; this.outDegree = 0; this.directedLoops = 0; this.in = {}; this.out = {}; }; /** * UndirectedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function UndirectedNodeData(key, attributes) { this.key = key; this.attributes = attributes; this.clear(); } UndirectedNodeData.prototype.clear = function() { this.undirectedDegree = 0; this.undirectedLoops = 0; this.undirected = {}; }; /** * EdgeData class. * * @constructor * @param {boolean} undirected - Whether the edge is undirected. * @param {string} string - The edge's key. * @param {string} source - Source of the edge. * @param {string} target - Target of the edge. * @param {object} attributes - Edge's attributes. */ function EdgeData(undirected, key, source, target, attributes) { this.key = key; this.attributes = attributes; this.undirected = undirected; this.source = source; this.target = target; } EdgeData.prototype.attach = function() { let outKey = "out"; let inKey = "in"; if (this.undirected) outKey = inKey = "undirected"; const source = this.source.key; const target = this.target.key; this.source[outKey][target] = this; if (this.undirected && source === target) return; this.target[inKey][source] = this; }; EdgeData.prototype.attachMulti = function() { let outKey = "out"; let inKey = "in"; const source = this.source.key; const target = this.target.key; if (this.undirected) outKey = inKey = "undirected"; const adj = this.source[outKey]; const head = adj[target]; if (typeof head === "undefined") { adj[target] = this; if (!(this.undirected && source === target)) this.target[inKey][source] = this; return; } head.previous = this; this.next = head; adj[target] = this; this.target[inKey][source] = this; }; EdgeData.prototype.detach = function() { const source = this.source.key; const target = this.target.key; let outKey = "out"; let inKey = "in"; if (this.undirected) outKey = inKey = "undirected"; delete this.source[outKey][target]; delete this.target[inKey][source]; }; EdgeData.prototype.detachMulti = function() { const source = this.source.key; const target = this.target.key; let outKey = "out"; let inKey = "in"; if (this.undirected) outKey = inKey = "undirected"; if (this.previous === void 0) if (this.next === void 0) { delete this.source[outKey][target]; delete this.target[inKey][source]; } else { this.next.previous = void 0; this.source[outKey][target] = this.next; this.target[inKey][source] = this.next; } else { this.previous.next = this.next; if (this.next !== void 0) this.next.previous = this.previous; } }; /** * Graphology Node Attributes methods * =================================== */ const NODE = 0; const SOURCE = 1; const TARGET = 2; const OPPOSITE = 3; function findRelevantNodeData(graph, method, mode, nodeOrEdge, nameOrEdge, add1, add2) { let nodeData, edgeData, arg1, arg2; nodeOrEdge = "" + nodeOrEdge; if (mode === NODE) { nodeData = graph._nodes.get(nodeOrEdge); if (!nodeData) throw new NotFoundGraphError(`Graph.${method}: could not find the "${nodeOrEdge}" node in the graph.`); arg1 = nameOrEdge; arg2 = add1; } else if (mode === OPPOSITE) { nameOrEdge = "" + nameOrEdge; edgeData = graph._edges.get(nameOrEdge); if (!edgeData) throw new NotFoundGraphError(`Graph.${method}: could not find the "${nameOrEdge}" edge in the graph.`); const source = edgeData.source.key; const target = edgeData.target.key; if (nodeOrEdge === source) nodeData = edgeData.target; else if (nodeOrEdge === target) nodeData = edgeData.source; else throw new NotFoundGraphError(`Graph.${method}: the "${nodeOrEdge}" node is not attached to the "${nameOrEdge}" edge (${source}, ${target}).`); arg1 = add1; arg2 = add2; } else { edgeData = graph._edges.get(nodeOrEdge); if (!edgeData) throw new NotFoundGraphError(`Graph.${method}: could not find the "${nodeOrEdge}" edge in the graph.`); if (mode === SOURCE) nodeData = edgeData.source; else nodeData = edgeData.target; arg1 = nameOrEdge; arg2 = add1; } return [ nodeData, arg1, arg2 ]; } function attachNodeAttributeGetter(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, name] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); return data.attributes[name]; }; } function attachNodeAttributesGetter(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge) { const [data] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge); return data.attributes; }; } function attachNodeAttributeChecker(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, name] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); return data.attributes.hasOwnProperty(name); }; } function attachNodeAttributeSetter(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1, add2) { const [data, name, value] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1, add2); data.attributes[name] = value; this.emit("nodeAttributesUpdated", { key: data.key, type: "set", attributes: data.attributes, name }); return this; }; } function attachNodeAttributeUpdater(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1, add2) { const [data, name, updater] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1, add2); if (typeof updater !== "function") throw new InvalidArgumentsGraphError(`Graph.${method}: updater should be a function.`); const attributes = data.attributes; const value = updater(attributes[name]); attributes[name] = value; this.emit("nodeAttributesUpdated", { key: data.key, type: "set", attributes: data.attributes, name }); return this; }; } function attachNodeAttributeRemover(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, name] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); delete data.attributes[name]; this.emit("nodeAttributesUpdated", { key: data.key, type: "remove", attributes: data.attributes, name }); return this; }; } function attachNodeAttributesReplacer(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, attributes] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); if (!isPlainObject(attributes)) throw new InvalidArgumentsGraphError(`Graph.${method}: provided attributes are not a plain object.`); data.attributes = attributes; this.emit("nodeAttributesUpdated", { key: data.key, type: "replace", attributes: data.attributes }); return this; }; } function attachNodeAttributesMerger(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, attributes] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); if (!isPlainObject(attributes)) throw new InvalidArgumentsGraphError(`Graph.${method}: provided attributes are not a plain object.`); assign(data.attributes, attributes); this.emit("nodeAttributesUpdated", { key: data.key, type: "merge", attributes: data.attributes, data: attributes }); return this; }; } function attachNodeAttributesUpdater(Class, method, mode) { Class.prototype[method] = function(nodeOrEdge, nameOrEdge, add1) { const [data, updater] = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1); if (typeof updater !== "function") throw new InvalidArgumentsGraphError(`Graph.${method}: provided updater is not a function.`); data.attributes = updater(data.attributes); this.emit("nodeAttributesUpdated", { key: data.key, type: "update", attributes: data.attributes }); return this; }; } /** * List of methods to attach. */ const NODE_ATTRIBUTES_METHODS = [ { name: (element) => `get${element}Attribute`, attacher: attachNodeAttributeGetter }, { name: (element) => `get${element}Attributes`, attacher: attachNodeAttributesGetter }, { name: (element) => `has${element}Attribute`, attacher: attachNodeAttributeChecker }, { name: (element) => `set${element}Attribute`, attacher: attachNodeAttributeSetter }, { name: (element) => `update${element}Attribute`, attacher: attachNodeAttributeUpdater }, { name: (element) => `remove${element}Attribute`, attacher: attachNodeAttributeRemover }, { name: (element) => `replace${element}Attributes`, attacher: attachNodeAttributesReplacer }, { name: (element) => `merge${element}Attributes`, attacher: attachNodeAttributesMerger }, { name: (element) => `update${element}Attributes`, attacher: attachNodeAttributesUpdater } ]; /** * Attach every attributes-related methods to a Graph class. * * @param {function} Graph - Target class. */ function attachNodeAttributesMethods(Graph$1) { NODE_ATTRIBUTES_METHODS.forEach(function({ name, attacher }) { attacher(Graph$1, name("Node"), NODE); attacher(Graph$1, name("Source"), SOURCE); attacher(Graph$1, name("Target"), TARGET); attacher(Graph$1, name("Opposite"), OPPOSITE); }); } /** * Graphology Edge Attributes methods * =================================== */ /** * Attach an attribute getter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeGetter(Class, method, type) { /** * Get the desired attribute for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * * @return {mixed} - The attribute's value. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function(element, name) { let data; if (this.type !== "mixed" && type !== "mixed" && type !== this.type) throw new UsageGraphError(`Graph.${method}: cannot find this type of edges in your ${this.type} graph.`); if (arguments.length > 2) { if (this.multi) throw new UsageGraphError(`Graph.${method}: cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.`); const source = "" + element; const target = "" + name; name = arguments[2]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find an edge for the given path ("${source}" - "${target}").`); } else { if (type !== "mixed") throw new UsageGraphError(`Graph.${method}: calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.`); element = "" + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find the "${element}" edge in the graph.`); } return data.attributes[name]; }; } /** * Attach an attributes getter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributesGetter(Class, method, type) { /** * Retrieves all the target element's attributes. * * Arity 2: * @param {any} element - Target element. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * * @return {object} - The element's attributes. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function(element) { let data; if (this.type !== "mixed" && type !== "mixed" && type !== this.type) throw new UsageGraphError(`Graph.${method}: cannot find this type of edges in your ${this.type} graph.`); if (arguments.length > 1) { if (this.multi) throw new UsageGraphError(`Graph.${method}: cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.`); const source = "" + element, target = "" + arguments[1]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find an edge for the given path ("${source}" - "${target}").`); } else { if (type !== "mixed") throw new UsageGraphError(`Graph.${method}: calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.`); element = "" + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find the "${element}" edge in the graph.`); } return data.attributes; }; } /** * Attach an attribute checker method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeChecker(Class, method, type) { /** * Checks whether the desired attribute is set for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * * @return {boolean} * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function(element, name) { let data; if (this.type !== "mixed" && type !== "mixed" && type !== this.type) throw new UsageGraphError(`Graph.${method}: cannot find this type of edges in your ${this.type} graph.`); if (arguments.length > 2) { if (this.multi) throw new UsageGraphError(`Graph.${method}: cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.`); const source = "" + element; const target = "" + name; name = arguments[2]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find an edge for the given path ("${source}" - "${target}").`); } else { if (type !== "mixed") throw new UsageGraphError(`Graph.${method}: calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.`); element = "" + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find the "${element}" edge in the graph.`); } return data.attributes.hasOwnProperty(name); }; } /** * Attach an attribute setter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeSetter(Class, method, type) { /** * Set the desired attribute for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * @param {mixed} value - New attribute value. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * @param {mixed} value - New attribute value. * * @return {Graph} - Returns itself for chaining. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function(element, name, value) { let data; if (this.type !== "mixed" && type !== "mixed" && type !== this.type) throw new UsageGraphError(`Graph.${method}: cannot find this type of edges in your ${this.type} graph.`); if (arguments.length > 3) { if (this.multi) throw new UsageGraphError(`Graph.${method}: cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.`); const source = "" + element; const target = "" + name; name = arguments[2]; value = arguments[3]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find an edge for the given path ("${source}" - "${target}").`); } else { if (type !== "mixed") throw new UsageGraphError(`Graph.${method}: calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.`); element = "" + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError(`Graph.${method}: could not find the "${element}" edge in the graph.`); } data.attributes[name] = value; this.emit("edgeAttributesUpdated", { key: data.key, type: "set", attributes: data.attributes, name }); return this; }; } /** * Attach an attribute updater method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeUpdater(Class, method, type) { /** * Update the desired attribute for the given element (node or edge) using * the provided function. * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * @param {function} updater - Updater function. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * @param {function} updater - Updater function. * * @return {Graph}