diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f023f4c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,25 @@ +// eslint-disable-next-line no-undef +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/no-empty-function": 0, + "@typescript-eslint/no-this-alias": 0, + "@typescript-eslint/no-unused-vars":0, + "no-prototype-builtins": 0, + } +} diff --git a/.gitignore b/.gitignore index a65d046..8f22694 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ __pycache__/ *.py[cod] *$py.class +node_modules + + # C extensions *.so diff --git a/README.md b/README.md index dda44c8..20d64f5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,116 @@ + # cti-stix-visualization +## Installation + +D3 is required as a peer dependency. + +TODO POSSIBLY UPDATE + +``` +npm i cti-stix-to-vis +``` + +## Basic Use + + +```js +import d3 from "d3"; +import initViz from "cti-stix-to-viz"; + + +const Viz = initViz(d3); + +const vizInstance = new Viz(document.getElementById("my-svg"), { + id: "my-svg" +}); + +vizInstance.vizStix(testData, { + //config +}); + +``` + + +## Example usage with React: + + +TODO probably a good idea to add a link to code-sandbox + +```jsx +import React, { useEffect, useRef } from 'react'; +import './App.css'; + +import { testData } from './testData'; +import d3 from "d3"; + + +//TODO UPDATE THIS +import initViz from "cti-stix-visualization-dwj"; + + +const Viz = initViz(d3); + + +function App() { + + const svgRef = useRef(null); + useEffect(() => { + + console.log(svgRef); + if (svgRef.current) { + const vizInstance = new Viz(svgRef.current, { + id: 'foo', + + }); + + vizInstance.vizStix(testData, {}, (...args) => { + console.log("vizCallBack", args); + }, (...args) => { + console.log("errorCallback", args) + }); + } + }, [svgRef]); + return ( +
+ +
+ ); +} + +export default App; + +``` + + +## Local use + + +Install dependencies + +``` +yarn +``` + +Build the compiled code (required for local server) + +``` +yarn build +``` + +Start local server + +``` +yarn start +``` + +Open your browser to http://localhost:3000 + + + + + + *This is an [OASIS TC Open Repository](https://www.oasis-open.org/resources/open-repositories/). See the [Governance](#governance) section for more information.* The STIX visualization is meant to provide producers and consumers of STIX content with a rapid way to visualize the objects in a STIX JSON file, and the relationships between those objects. The visualization is implemented in HTML, CSS, and JavaScript (using the [D3.js](https://d3js.org/) library), and is suitable for standalone use — either on a hosted server or as a local file — or embedded into other applications. Regardless of how deployed, the JavaScript code in this repository does not transmit STIX data to any server; it is strictly processed within the browser in which the code is running, so it is suitable for data which the user does not wish to share. diff --git a/application.js b/application.js index 1f2d937..3fff754 100644 --- a/application.js +++ b/application.js @@ -16,9 +16,7 @@ require.config({ } }); -require(["domReady!", "stix2viz/stix2viz/stix2viz"], function (document, stix2viz) { - - +require(["domReady!", "stix2viz/stix2viz/stix2viz", "lib/stix2vizcore"], function (document, stix2viz) { // Init some stuff // For optimization purposes, look into moving these to local variables var visualizer; diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ee0974 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "cti-stix-visualization-dwj", + "version": "0.0.1", + "main": "lib/stix2vizcore.js", + "repository": "git@github.com:dwjohnston/cti-stix-visualization.git", + "author": "David Johnston ", + "license": "SEE LICNESE", + "scripts": { + "clean": "rm -rf lib", + "build": "tsc", + "start": "serve .", + "lint": "eslint ./stix2viz/stix2viz/stix2viz.js" + }, + "peerDependencies": { + "d3": "^3.5.14" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.17.0", + "@typescript-eslint/parser": "^5.17.0", + "d3": "^3.5.14", + "eslint": "^8.12.0", + "serve": "^13.0.2", + "typescript": "^4.6.3" + } +} diff --git a/stix2viz/stix2viz/stix2viz.js b/stix2viz/stix2viz/stix2viz.js index 870fda7..7df8ba0 100644 --- a/stix2viz/stix2viz/stix2viz.js +++ b/stix2viz/stix2viz/stix2viz.js @@ -1,818 +1,5 @@ -define(["nbextensions/stix2viz/d3"], function(d3) { - refRegex = /_refs*$/; - /* ****************************************************** - * Viz class constructor. - * - * Parameters: - * - canvas: element which will contain the graph - * - config: object containing options for the graph: - * - color: a d3 color scale - * - nodeSize: size of graph nodes, in pixels - * - iconSize: size of icon, in pixels - * - linkMultiplier: multiplier that affects the length of links between nodes - * - width: width of the svg containing the graph - * - height: height of the svg containing the graph - * - iconDir: directory in which the STIX 2 icons are located - * - legendCallback: function that takes an array of type names and create a legend for the graph - * - selectedCallback: function that acts on the data of a node when it is selected - * ******************************************************/ - function Viz(canvas, config, legendCb, selectedCb) { - // Init some stuff - this.d3Config; - this.customConfig; - this.legendCallback; - this.selectedCallback; - this.force; // Determines the "float and repel" behavior of the nodes - this.labelForce; // Determines the "float and repel" behavior of the text labels - this.svgTop; - this.svg; - this.typeGroups = {}; - this.typeIndex = 0; - - this.currentGraph = { - nodes: [], - edges: [] - }; - this.labelGraph = { - nodes: [], - edges: [] - }; - - this.idCache = {}; - // Set defaults for config if needed - this.d3Config = {}; - if (typeof config === 'undefined') config = {}; - if ('color' in config) { this.d3Config.color = config.color; } - else { this.d3Config.color = d3.scale.category20(); } - if ('nodeSize' in config) { this.d3Config.nodeSize = config.nodeSize; } - else { this.d3Config.nodeSize = 17.5; } - if ('iconSize' in config) { this.d3Config.iconSize = config.iconSize; } - else { this.d3Config.iconSize = 37; } - if ('linkMultiplier' in config) { this.d3Config.linkMultiplier = config.linkMultiplier; } - else { this.d3Config.linkMultiplier = 20; } - if ('width' in config) { this.d3Config.width = config.width; } - else { this.d3Config.width = 900; } - if ('height' in config) { this.d3Config.height = config.height; } - else { this.d3Config.height = 450; } - if ('iconDir' in config) { this.d3Config.iconDir = config.iconDir; } - else { this.d3Config.iconDir = "icons"; } - // To differentiate multiple graphs on same page - if ('id' in config) { this.id = config.id; } - else { this.id = 0; } - - if (typeof legendCb === 'undefined') { this.legendCallback = function(){}; } - else { this.legendCallback = legendCb; } - if (typeof selectedCb === 'undefined') { this.selectedCallback = function(){}; } - else { this.selectedCallback = selectedCb; } - - // keys are the name of the _ref/s property, values are the name of the - // relationship and whether the object with that property should be the - // source_ref in the relationship - this.refsMapping = { - created_by_ref: ["created-by", true], - object_marking_refs: ["applies-to", false], - object_refs: ["refers-to", true], - sighting_of_ref: ["sighting-of", true], - observed_data_refs: ["observed", true], - where_sighted_refs: ["saw", false], - object_ref: ["applies-to", true], - sample_refs: ["sample-of", false], - analysis_sco_refs: ["captured-by", false], - contains_refs: ["contains", true], - resolves_to_refs: ["resolves-to", true], - belongs_to_ref: ["belongs-to", true], - from_ref: ["from", true], - sender_ref: ["sent-by", true], - to_refs: ["to", true], - cc_refs: ["cc", true], - bcc_refs: ["bcc", true], - raw_email_ref: ["raw-binary-of", false], - parent_directory_ref: ["parent-of", false], - content_ref: ["contents-of", false], - src_ref: ["source-of", false], - dst_ref: ["destination-of", false], - src_payload_ref: ["source-payload-of", false], - dst_payload_ref: ["destination-payload-of", false], - encapsulates_refs: ["encapsulated-by", false], - encapsulated_by_ref: ["encapsulated-by", true], - opened_connection_refs: ["opened-by", false], - creator_user_ref: ["created-by", true], - image_ref: ["image-of", false], - parent_ref: ["parent-of", false] - } - - canvas.style.width = this.d3Config.width; - canvas.style.height = this.d3Config.height; - this.force = d3.layout.force().charge(-400).linkDistance(this.d3Config.linkMultiplier * this.d3Config.nodeSize).size([this.d3Config.width, this.d3Config.height]); - this.labelForce = d3.layout.force().gravity(0).linkDistance(25).linkStrength(8).charge(-120).size([this.d3Config.width, this.d3Config.height]); - this.svgTop = d3.select('#' + canvas.id); - this.svg = this.svgTop.append("g"); - }; - - /* ****************************************************** - * Attempts to build and display the graph from an - * arbitrary input string. If parsing the string does not - * produce valid JSON, fails gracefully and alerts the user. - * - * Parameters: - * - content: string of valid STIX 2 content - * - config: - * - callback: optional function to call after building the graph - * - onError: optional function to call if an error is encountered while parsing input - * ******************************************************/ - Viz.prototype.vizStix = function(content, config, callback, onError) { - try { - // Saving this to a variable stops the rest of the function from executing on parse failure - parsed = this.parseContent(content); - } - catch (err) { - alert("Something went wrong!\n\nError:\n" + err); - if (typeof onError !== 'undefined') onError(); - return; - } - - if (config) { - try { - if (typeof config === 'string' || config instanceof String) { - this.customConfig = JSON.parse(config); - } else { - this.customConfig = config; - } - } catch (err) { - alert("Something went wrong!\nThe custom config does not seem to be proper JSON.\nPlease fix or remove it and try again.\n\nError:\n" + err); - if (typeof onError !== 'undefined') onError(); - return; - } - } - - this.buildNodes(parsed); - this.initGraph(); - if (typeof callback !== 'undefined') callback(); - }; - - Viz.prototype.parseContent = function(content) { - if (typeof content === 'string' || content instanceof String) { - return this.parseContent(JSON.parse(content)); - } - else if (content.constructor === Array) { - if (this.arrHasAllStixObjs(content)) { - return { - "objects": content - }; - } - else { - throw "Input contains one or more invalid STIX objects"; - } - } - else if (this.isStixObj(content)) { - if (content.type == "bundle") { - return content; - } else { - return { - "objects": [content] - }; - } - } - else { - throw "Input is neither parseable JSON nor a STIX object"; - } - }; - - /* ****************************************************** - * Returns true if the JavaScript object passed in has - * properties required by all STIX objects. - * ******************************************************/ - Viz.prototype.isStixObj = function(obj) { - if ('type' in obj && 'id' in obj) { - return true; - } else { - return false; - } - }; - - /* ****************************************************** - * Returns true if the JavaScript array passed in has - * only objects such that each object has properties - * required by all STIX objects. - * ******************************************************/ - Viz.prototype.arrHasAllStixObjs = function(arr) { - return arr.reduce((accumulator, currentObj) => { - return accumulator && (this.isStixObj(currentObj)); - }, true); - }; - - /* ****************************************************** - * Generates the components on the chart from the JSON data - * ******************************************************/ - Viz.prototype.initGraph = function() { - var _this = this; - this.force.nodes(this.currentGraph.nodes).links(this.currentGraph.edges).start(); - this.labelForce.nodes(this.labelGraph.nodes).links(this.labelGraph.edges).start(); - - // create filter with id #drop-shadow - // height=130% so that the shadow is not clipped - var filter = this.svg.append("svg:defs").append("filter") - .attr("id", "drop-shadow") - .attr("height", "200%") - .attr("width", "200%") - .attr("x", "-50%") // x and y have to have negative offsets to - .attr("y", "-50%"); // stop the edges from getting cut off - // translate output of Gaussian blur to the right and downwards with 2px - // store result in offsetBlur - filter.append("feOffset") - .attr("in", "SourceAlpha") - .attr("dx", 0) - .attr("dy", 0) - .attr("result", "offOut"); - // SourceAlpha refers to opacity of graphic that this filter will be applied to - // convolve that with a Gaussian with standard deviation 3 and store result - // in blur - filter.append("feGaussianBlur") - .attr("in", "offOut") - .attr("stdDeviation", 7) - .attr("result", "blurOut"); - filter.append("feBlend") - .attr("in", "SourceGraphic") - .attr("in2", "blurOut") - .attr("mode", "normal"); - - // Adds style directly because it wasn't getting picked up by the style sheet - var link = this.svg.selectAll('path.link').data(this.currentGraph.edges).enter().append('path') - .attr('class', 'link') - .style("stroke", "#aaa") - .style('fill', "#aaa") - .style("stroke-width", "3px") - .attr('id', function(d, i) { return "link" + _this.id + "_" + i; }) - .on('click', function(d, i) { handleSelected(d, this); }); - - // Create the text labels that will be attatched to the paths - var linktext = this.svg.append("svg:g").selectAll("g.linklabelholder").data(this.currentGraph.edges); - linktext.enter().append("g").attr("class", "linklabelholder") - .append("text") - .attr("class", "linklabel") - .style("font-size", "13px") - .attr("text-anchor", "start") - .style("fill","#000") - .append("textPath") - .attr("xlink:href",function(d,i) { return "#link" + _this.id + "_" + i;}) - .attr("startOffset", "20%") - .text(function(d) { - return d.label; - }); - var linklabels = this.svg.selectAll('.linklabel'); - - var node = this.svg.selectAll("g.node") - .data(this.currentGraph.nodes) - .enter().append("g") - .attr("class", "node") - .call(this.force.drag); // <-- What does the "call()" function do? - node.append("circle") - .attr("r", this.d3Config.nodeSize) - .style("fill", function(d) { return _this.d3Config.color(d.typeGroup); }); - var nodeIcon = node.append("image") - .attr("x", "-" + (this.d3Config.nodeSize + 0.5) + "px") - .attr("y", "-" + (this.d3Config.nodeSize + 1.5) + "px") - .attr("width", this.d3Config.iconSize + "px") - .attr("height", this.d3Config.iconSize + "px"); - nodeIcon.each(function(d) { - _this.setNodeIcon(d3.select(this), d.type); - }); - node.on('click', function(d, i) { _this.handleSelected(d, this); }); // If they're holding shift, release - - // Fix on click/drag, unfix on double click - this.force.drag().on('dragstart', function(d, i) { - d3.event.sourceEvent.stopPropagation(); // silence other listeners - _this.handlePin(d, this, true); - });//d.fixed = true }); - node.on('dblclick', function(d, i) { _this.handlePin(d, this, false); });//d.fixed = false }); - - // Right click will greatly dim the node and associated edges - // >>>>>>> Does not currently work <<<<<<< - node.on('contextmenu', function(d) { - if(d.dimmed) { - d.dimmed = false; // <-- What is this? Where is this set? How does this work? - d.attr("class", "node"); - } else { - d.dimmed = true; - d.attr("class", "node dimmed"); - } - }); - - var anchorNode = this.svg.selectAll("g.anchorNode").data(this.labelForce.nodes()).enter().append("svg:g").attr("class", "anchorNode"); - anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF"); - anchorNode.append("svg:text").text(function(d, i) { - return i % 2 === 0 ? "" : _this.nameFor(d.node, _this.customConfig); - }).style("fill", "#555").style("font-family", "Arial").style("font-size", 12); - - // Code in the "tick" function determines where the elements - // should be redrawn every cycle (essentially, it allows the - // elements to be animated) - this.force.on("tick", function() { - - link.attr("d", function(d) { return _this.drawArrow(d); }); - - node.call(function() { - this.attr("transform", function(d) { - return "translate(" + d.x + "," + d.y + ")"; - }); - }); - - anchorNode.each(function(d, i) { - _this.labelForce.start(); - if(i % 2 === 0) { - d.x = d.node.x; - d.y = d.node.y; - } else { - var b = this.childNodes[1].getBBox(); - - var diffX = d.x - d.node.x; - var diffY = d.y - d.node.y; - - var dist = Math.sqrt(diffX * diffX + diffY * diffY); - - var shiftX = b.width * (diffX - dist) / (dist * 2); - shiftX = Math.max(-b.width, Math.min(0, shiftX)); - var shiftY = 5; - this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")"); - } - }); - - anchorNode.call(function() { - this.attr("transform", function(d) { - return "translate(" + d.x + "," + d.y + ")"; - }); - }); - - linklabels.attr('transform',function(d,i) { - if (d.target.x < d.source.x) { - bbox = this.getBBox(); - rx = bbox.x+bbox.width/2; - ry = bbox.y+bbox.height/2; - return 'rotate(180 '+rx+' '+ry+')'; - } - else { - return 'rotate(0)'; - } - }); - }); - - // Code to handle zooming and dragging the viewing area - this.svgTop.call(d3.behavior.zoom() - .scaleExtent([0.25, 5]) - .on("zoom", function() { - _this.svg.attr("transform", - "translate(" + d3.event.translate + ") " + - "scale(" + d3.event.scale + ")" - ); - }) - ) - .on("dblclick.zoom", null); - }; - - /* ****************************************************** - * Draws an arrow between two points. - * ******************************************************/ - Viz.prototype.drawArrow = function(d) { - return this.drawLine(d) + this.drawArrowHead(d); - }; - - /* ****************************************************** - * Draws a line between two points - * ******************************************************/ - Viz.prototype.drawLine = function(d) { - return this.startAt(d.source) + this.lineTo(d.target); - }; - - /* ****************************************************** - * Draws an arrow head. - * ******************************************************/ - Viz.prototype.drawArrowHead = function(d) { - var arrowTipPoint = this.calculateArrowTipPoint(d); - return this.startAt(arrowTipPoint) - + this.lineTo(this.calculateArrowBaseRightCornerPoint(d, arrowTipPoint)) - + this.lineTo(this.calculateArrowBaseLeftCornerPoint(d, arrowTipPoint)) - + this.lineTo(arrowTipPoint) - + this.closePath(); - }; - - /* ****************************************************** - * Creates the SVG for a starting point. - * ******************************************************/ - Viz.prototype.startAt = function(startPoint) { - return 'M' + startPoint.x + ',' + startPoint.y; - }; - - /* ****************************************************** - * Creates the SVG for line to a point. - * ******************************************************/ - Viz.prototype.lineTo = function(endPoint) { - return 'L' + endPoint.x + ',' + endPoint.y; - }; - - /* ****************************************************** - * Calculates the point at which the arrow tip should be. - * ******************************************************/ - Viz.prototype.calculateArrowTipPoint = function(d) { - var nodeRadius = Math.max(this.d3Config.iconSize, this.d3Config.nodeSize) / 2; - return this.translatePoint(d.target, this.calculateUnitVectorAlongLine(d), -(this.d3Config.nodeSize + 3)); - }; - - /* ****************************************************** - * Calculates the point at which the right corner of the - * base of the arrow head should be. - * ******************************************************/ - Viz.prototype.calculateArrowBaseRightCornerPoint = function(d, arrowTipPoint) { - var arrowBaseWidth = 13; - var unitVector = this.calculateUnitVectorAlongLine(d); - var arrowBasePoint = this.calculateArrowBaseCentrePoint(d, arrowTipPoint); - return this.translatePoint(arrowBasePoint, this.calculateNormal(unitVector), -arrowBaseWidth / 2); - }; - - /* ****************************************************** - * Calculates the point at which the left corner of the - * base of the arrow head should be. - * ******************************************************/ - Viz.prototype.calculateArrowBaseLeftCornerPoint = function(d, arrowTipPoint) { - var arrowBaseWidth = 13; - var unitVector = this.calculateUnitVectorAlongLine(d); - var arrowBasePoint = this.calculateArrowBaseCentrePoint(d, arrowTipPoint); - return this.translatePoint(arrowBasePoint, this.calculateNormal(unitVector), arrowBaseWidth / 2); - }; - - /* ****************************************************** - * Calculates the point at the centre of the base of the - * arrow head. - * ******************************************************/ - Viz.prototype.calculateArrowBaseCentrePoint = function(d, arrowTipPoint) { - var arrowHeadLength = 13; - return this.translatePoint(arrowTipPoint, this.calculateUnitVectorAlongLine(d), -arrowHeadLength); - }; - - /* ****************************************************** - * Translates a point. - * ******************************************************/ - Viz.prototype.translatePoint = function(startPoint, directionUnitVector, distance) { - return { x: startPoint.x + distance * directionUnitVector.x, y: startPoint.y + distance * directionUnitVector.y }; - }; - - /* ****************************************************** - * Calculates a unit vector along a particular line. - * ******************************************************/ - Viz.prototype.calculateUnitVectorAlongLine = function(d) { - var dx = d.target.x - d.source.x; - var dy = d.target.y - d.source.y; - var dr = Math.sqrt(dx * dx + dy * dy); - return { x: dx / dr, y: dy / dr }; - }; - - /* ****************************************************** - * Calculates a normal to a unit vector. - * ******************************************************/ - Viz.prototype.calculateNormal = function(unitVector) { - return { x: -unitVector.y, y: unitVector.x }; - }; - - /* ****************************************************** - * Closes an SVG path. - * ******************************************************/ - Viz.prototype.closePath = function() { - return 'Z'; - }; - - /* ****************************************************** - * Screens out D3 chart data from the presentation. - * Also makes values more readable. - * Called as the 2nd parameter to JSON.stringify(). - * ******************************************************/ - function replacer(key, value) { - var blacklist = ["typeGroup", "index", "weight", "x", "y", "px", "py", "fixed", "dimmed"]; - if (blacklist.indexOf(key) >= 0) { - return undefined; - } - // Some of the potential values are not very readable (IDs - // and object references). Let's see if we can fix that. - // Lots of assumptions being made about the structure of the JSON here... - var dictlist = ['definition', 'objects']; - if (Array.isArray(value)) { - if (key === 'kill_chain_phases') { - var newValue = []; - value.forEach(function (item) { - newValue.push(item.phase_name) - }); - return newValue; - } else if (key === 'granular_markings' || key === 'external_references') { - var newValue = []; - value.forEach(function (item) { - newValue.push(JSON.stringify(item)); - }); - return newValue.join(", "); - } else { - return value.join(", "); - } - } else if (/--/.exec(value) && !(key === "id")) { - if (!(this.idCache[value] === null || this.idCache[value] === undefined)) { - // IDs are gross, so let's display something more readable if we can - // (unless it's actually the node id) - return this.currentGraph.nodes[this.idCache[value]].name; - } - } else if (dictlist.indexOf(key) >= 0) { - return JSON.stringify(value); - } - return value; - }; - - /* ****************************************************** - * Adds class "selected" to last graph element clicked - * and removes it from all other elements. - * - * Takes datum and element as input. - * ******************************************************/ - Viz.prototype.handleSelected = function(d, el) { - var selectedReplacer = replacer.bind(this); - jsonString = JSON.stringify(d, selectedReplacer, 2); // get only the STIX values - purified = JSON.parse(jsonString); // make a new JSON object from the STIX values - - // Pretty up the keys - for (var key in purified) { - if (d.hasOwnProperty(key)) { - var keyString = key; - if (refRegex.exec(key)) { // key is "created_by_ref"... let's pretty that up - keyString = key.replace(/_(refs*)?/g, " ").trim(); - } else { - keyString = keyString.replace(/_/g, ' '); - } - keyString = keyString.charAt(0).toUpperCase() + keyString.substr(1).toLowerCase() // Capitalize it - keyString += ":"; - - purified[keyString] = purified[key]; - delete purified[key]; - } - } - - this.selectedCallback(purified); - d3.select('.selected').classed('selected', false); - d3.select(el).classed('selected', true); - }; - - /* ****************************************************** - * Handles pinning and unpinning of nodes. - * - * Takes datum, element, and boolean as input. - * ******************************************************/ - Viz.prototype.handlePin = function(d, el, pinBool) { - d.fixed = pinBool; - d3.select(el).classed("pinned", pinBool); - }; - - /* ****************************************************** - * Parses the JSON input and builds the arrays used by - * initGraph(). - * - * Takes a JSON object as input. - * ******************************************************/ - Viz.prototype.buildNodes = function(package) { - var _this = this; - var relationships = []; - if(package.hasOwnProperty('objects')) { - this.parseSDOs(package['objects']); - - // Get embedded relationships - package['objects'].forEach(function(item) { - if (item['type'] === 'relationship') { - relationships.push(item); - return; - } - Object.keys(item).forEach(function(key, index) { - if (key.endsWith("_ref") && _this.refsMapping.hasOwnProperty(key)) { - var source = (_this.refsMapping[key][1] === true) ? item["id"] : item[key]; - var target = (_this.refsMapping[key][1] === true) ? item[key] : item["id"]; - var relType = _this.refsMapping[key][0]; - relationships.push({'source_ref': source, - 'target_ref': target, - 'relationship_type': relType}); - } - else if (key.endsWith("_refs") && _this.refsMapping.hasOwnProperty(key)) { - item[key].forEach(function(refID) { - var source = (_this.refsMapping[key][1] === true) ? item["id"] : refID; - var target = (_this.refsMapping[key][1] === true) ? refID : item["id"]; - var relType = _this.refsMapping[key][0]; - relationships.push({'source_ref': source, - 'target_ref': target, - 'relationship_type': relType}); - }); - } - }); - }); - }; - - this.addRelationships(relationships); - - // Add the legend so we know what's what - this.legendCallback(Object.keys(this.typeGroups)); - }; - - /* ****************************************************** - * Uses regex to check whether the specified value for - * display_icon in customConfig is a valid URL. - * - * Note: The protocol MUST be supplied in the image URL - * (e.g. https) - * - * The regex expression below is based on: - * https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url - * ******************************************************/ - Viz.prototype.validUrl = function(imageUrl) { - var pattern = new RegExp('^(https?:\\/\\/)'+ // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|'+ // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ //port - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string - '(\\#[-a-z\\d_]*)?$','i'); - return pattern.test(imageUrl); - }; - - /* ****************************************************** - * Returns the name to use for an SDO Node - * - * Determines what name to use in the following order: - * 1) A user-chosen ID-specific label via customConfig.userLabels. - * 2) The value of a user-chosen type-specific SDO property given via - * customConfig..display_property - * 3) The SDO's "name" property - * 4) The SDO's "value" property - * 5) The SDO's "type" property - * ******************************************************/ - Viz.prototype.nameFor = function(sdo) { - - let name = null; - - if (this.customConfig !== undefined) { - if ("userLabels" in this.customConfig && - sdo.id in this.customConfig.userLabels) - name = this.customConfig.userLabels[sdo.id]; - else if (sdo.type in this.customConfig) - name = sdo[this.customConfig[sdo.type].display_property]; - - if (name && name.length > 100) - name = name.substr(0,100) + '...'; // For space-saving - } - - if (!name) { - if (sdo.name !== undefined) { - name = sdo.name; - } else if (sdo.value !== undefined) { - name = sdo.value; - } else if (sdo.path !== undefined) { - name = sdo.path; - } else { - name = sdo.type; - } - } - - return name; - }; - - /* ****************************************************** - * Returns the icon to use for an SDO Node - * - * Determines which icon to use in the following order: - * 1) A display_icon set in the config (must be in the icon directory) - * 2) A default icon for the SDO type, bundled with this library - * ******************************************************/ - Viz.prototype.iconFor = function(typeName) { - if (this.customConfig !== undefined && typeName in this.customConfig) { - let customIcon = this.customConfig[typeName].display_icon; - if (customIcon !== undefined) { - if (this.validUrl(customIcon)) { - return customIcon; - } else { - typeIcon = this.d3Config.iconDir + '/' + customIcon; - return typeIcon; - } - } - } - if (typeName !== undefined) { - typeIcon = this.d3Config.iconDir + "/stix2_" + typeName.replace(/\-/g, '_') + "_icon_tiny_round_v1.png"; - return typeIcon; - } - }; - - /* ****************************************************** - * Sets the icon on a STIX object node - * - * If the image doesn't load properly, a default 'custom object' - * icon will be used instead - * ******************************************************/ - Viz.prototype.setNodeIcon = function(node, stixType) { - var _this = this; - var tmpImg = new Image(); - tmpImg.onload = function() { - // set the node's icon to this image if it loaded properly - node.attr("xlink:href", tmpImg.src); - } - tmpImg.onerror = function() { - // set the node's icon to the default if this image could not load - node.attr("xlink:href", _this.d3Config.iconDir + "/stix2_custom_object_icon_tiny_round_v1.svg") - } - tmpImg.src = _this.iconFor(stixType, _this.customConfig); - }; - - /* ****************************************************** - * Parses valid SDOs from an array of potential SDO - * objects (ideally from the data object) - * - * Takes an array of objects as input. - * ******************************************************/ - Viz.prototype.parseSDOs = function(container) { - var cap = container.length; - for(var i = 0; i < cap; i++) { - // So, in theory, each of these should be an SDO. To be sure, we'll check to make sure it has an `id` and `type`. If not, raise an error and ignore it. - var maybeSdo = container[i]; - if(maybeSdo.id === undefined || maybeSdo.type === undefined) { - console.error("Should this be an SDO???", maybeSdo); - } else { - this.addSdo(maybeSdo); - } - } - }; - - /* ****************************************************** - * Adds an SDO node to the graph - * - * Takes a valid SDO object as input. - * ******************************************************/ - Viz.prototype.addSdo = function(sdo) { - if(this.idCache[sdo.id]) { - console.log("Skipping already added object!", sdo); - } else if(sdo.type === 'relationship') { - console.log("Skipping relationship object!", sdo); - } else { - if(this.typeGroups[sdo.type] === undefined) { - this.typeGroups[sdo.type] = this.typeIndex++; - } - sdo.typeGroup = this.typeGroups[sdo.type]; - - this.idCache[sdo.id] = this.currentGraph.nodes.length; // Edges reference nodes by their array index, so cache the current length. When we add, it will be correct - this.currentGraph.nodes.push(sdo); - - this.labelGraph.nodes.push({node: sdo}); // Two labels will orbit the node, we display the less crowded one and hide the more crowded one. - this.labelGraph.nodes.push({node: sdo}); - - this.labelGraph.edges.push({ - source : (this.labelGraph.nodes.length - 2), - target : (this.labelGraph.nodes.length - 1), - weight: 1 - }); - } - }; - - /* ****************************************************** - * Adds relationships to the graph based on the array of - * relationships contained in the data. - * - * Takes an array as input. - * ******************************************************/ - Viz.prototype.addRelationships = function(relationships) { - for(var i = 0; i < relationships.length; i++) { - var rel = relationships[i]; - if(this.idCache[rel.source_ref] === null || this.idCache[rel.source_ref] === undefined) { - console.error("Couldn't find source!", rel); - } else if (this.idCache[rel.target_ref] === null || this.idCache[rel.target_ref] === undefined) { - console.error("Couldn't find target!", rel); - } else { - this.currentGraph.edges.push({source: this.idCache[rel.source_ref], target: this.idCache[rel.target_ref], label: rel.relationship_type}); - } - } - }; - - /* ****************************************************** - * Resets the graph so it can be rebuilt - * *****************************************************/ - Viz.prototype.vizReset = function() { - this.typeGroups = {}; - this.typeIndex = 0; - - this.currentGraph = { - nodes: [], - edges: [] - }; - this.labelGraph = { - nodes: [], - edges: [] - }; - - this.idCache = {}; - - this.force.stop(); - this.labelForce.stop(); - this.svg.remove(); - }; - - module = { - "Viz": Viz - }; - - return module; -}); +define(["nbextensions/stix2viz/d3", "../../lib/stix2vizcore"], function(d3, initVis) { + return {Viz: initVis.default(d3)}; +}); \ No newline at end of file diff --git a/stix2viz/stix2viz/stix2vizcore.js b/stix2viz/stix2viz/stix2vizcore.js new file mode 100644 index 0000000..38d5889 --- /dev/null +++ b/stix2viz/stix2viz/stix2vizcore.js @@ -0,0 +1,827 @@ + +const refRegex = /_refs*$/; + +/* ****************************************************** + * Viz class constructor. + * + * Parameters: + * - canvas: element which will contain the graph + * - config: object containing options for the graph: + * - color: a d3 color scale + * - nodeSize: size of graph nodes, in pixels + * - iconSize: size of icon, in pixels + * - linkMultiplier: multiplier that affects the length of links between nodes + * - width: width of the svg containing the graph + * - height: height of the svg containing the graph + * - iconDir: directory in which the STIX 2 icons are located + * - legendCallback: function that takes an array of type names and create a legend for the graph + * - selectedCallback: function that acts on the data of a node when it is selected + * ******************************************************/ + + + +export default (d3) => { + function Viz(canvas, config, legendCb, selectedCb) { + // Init some stuff + this.d3Config; + this.customConfig; + this.legendCallback; + this.selectedCallback; + this.force; // Determines the "float and repel" behavior of the nodes + this.labelForce; // Determines the "float and repel" behavior of the text labels + this.svgTop; + this.svg; + this.typeGroups = {}; + this.typeIndex = 0; + + this.currentGraph = { + nodes: [], + edges: [] + }; + this.labelGraph = { + nodes: [], + edges: [] + }; + + this.idCache = {}; + // Set defaults for config if needed + this.d3Config = {}; + if (typeof config === 'undefined') config = {}; + if ('color' in config) { this.d3Config.color = config.color; } + else { this.d3Config.color = d3.scale.category20(); } + if ('nodeSize' in config) { this.d3Config.nodeSize = config.nodeSize; } + else { this.d3Config.nodeSize = 17.5; } + if ('iconSize' in config) { this.d3Config.iconSize = config.iconSize; } + else { this.d3Config.iconSize = 37; } + if ('linkMultiplier' in config) { this.d3Config.linkMultiplier = config.linkMultiplier; } + else { this.d3Config.linkMultiplier = 20; } + if ('width' in config) { this.d3Config.width = config.width; } + else { this.d3Config.width = 900; } + if ('height' in config) { this.d3Config.height = config.height; } + else { this.d3Config.height = 450; } + if ('iconDir' in config) { this.d3Config.iconDir = config.iconDir; } + else { this.d3Config.iconDir = "icons"; } + // To differentiate multiple graphs on same page + if ('id' in config) { this.id = config.id; } + else { this.id = 0; } + + if (typeof legendCb === 'undefined') { this.legendCallback = function () { }; } + else { this.legendCallback = legendCb; } + if (typeof selectedCb === 'undefined') { this.selectedCallback = function () { }; } + else { this.selectedCallback = selectedCb; } + + // keys are the name of the _ref/s property, values are the name of the + // relationship and whether the object with that property should be the + // source_ref in the relationship + this.refsMapping = { + created_by_ref: ["created-by", true], + object_marking_refs: ["applies-to", false], + object_refs: ["refers-to", true], + sighting_of_ref: ["sighting-of", true], + observed_data_refs: ["observed", true], + where_sighted_refs: ["saw", false], + object_ref: ["applies-to", true], + sample_refs: ["sample-of", false], + analysis_sco_refs: ["captured-by", false], + contains_refs: ["contains", true], + resolves_to_refs: ["resolves-to", true], + belongs_to_ref: ["belongs-to", true], + from_ref: ["from", true], + sender_ref: ["sent-by", true], + to_refs: ["to", true], + cc_refs: ["cc", true], + bcc_refs: ["bcc", true], + raw_email_ref: ["raw-binary-of", false], + parent_directory_ref: ["parent-of", false], + content_ref: ["contents-of", false], + src_ref: ["source-of", false], + dst_ref: ["destination-of", false], + src_payload_ref: ["source-payload-of", false], + dst_payload_ref: ["destination-payload-of", false], + encapsulates_refs: ["encapsulated-by", false], + encapsulated_by_ref: ["encapsulated-by", true], + opened_connection_refs: ["opened-by", false], + creator_user_ref: ["created-by", true], + image_ref: ["image-of", false], + parent_ref: ["parent-of", false] + } + + canvas.style.width = this.d3Config.width; + canvas.style.height = this.d3Config.height; + this.force = d3.layout.force().charge(-400).linkDistance(this.d3Config.linkMultiplier * this.d3Config.nodeSize).size([this.d3Config.width, this.d3Config.height]); + this.labelForce = d3.layout.force().gravity(0).linkDistance(25).linkStrength(8).charge(-120).size([this.d3Config.width, this.d3Config.height]); + this.svgTop = d3.select('#' + canvas.id); + this.svg = this.svgTop.append("g"); + } + + /* ****************************************************** + * Attempts to build and display the graph from an + * arbitrary input string. If parsing the string does not + * produce valid JSON, fails gracefully and alerts the user. + * + * Parameters: + * - content: string of valid STIX 2 content + * - config: + * - callback: optional function to call after building the graph + * - onError: optional function to call if an error is encountered while parsing input + * ******************************************************/ + Viz.prototype.vizStix = function (content, config, callback, onError) { + let parsed; + try { + // Saving this to a variable stops the rest of the function from executing on parse failure + parsed = this.parseContent(content); + } + catch (err) { + alert("Something went wrong!\n\nError:\n" + err); + if (typeof onError !== 'undefined') onError(); + return; + } + + if (config) { + try { + if (typeof config === 'string' || config instanceof String) { + this.customConfig = JSON.parse(config); + } else { + this.customConfig = config; + } + } catch (err) { + alert("Something went wrong!\nThe custom config does not seem to be proper JSON.\nPlease fix or remove it and try again.\n\nError:\n" + err); + if (typeof onError !== 'undefined') onError(); + return; + } + } + + this.buildNodes(parsed); + this.initGraph(); + if (typeof callback !== 'undefined') callback(); + }; + + Viz.prototype.parseContent = function (content) { + if (typeof content === 'string' || content instanceof String) { + return this.parseContent(JSON.parse(content)); + } + else if (content.constructor === Array) { + if (this.arrHasAllStixObjs(content)) { + return { + "objects": content + }; + } + else { + throw "Input contains one or more invalid STIX objects"; + } + } + else if (this.isStixObj(content)) { + if (content.type == "bundle") { + return content; + } else { + return { + "objects": [content] + }; + } + } + else { + throw "Input is neither parseable JSON nor a STIX object"; + } + }; + + /* ****************************************************** + * Returns true if the JavaScript object passed in has + * properties required by all STIX objects. + * ******************************************************/ + Viz.prototype.isStixObj = function (obj) { + if ('type' in obj && 'id' in obj) { + return true; + } else { + return false; + } + }; + + /* ****************************************************** + * Returns true if the JavaScript array passed in has + * only objects such that each object has properties + * required by all STIX objects. + * ******************************************************/ + Viz.prototype.arrHasAllStixObjs = function (arr) { + return arr.reduce((accumulator, currentObj) => { + return accumulator && (this.isStixObj(currentObj)); + }, true); + }; + + /* ****************************************************** + * Generates the components on the chart from the JSON data + * ******************************************************/ + Viz.prototype.initGraph = function () { + var _this = this; + this.force.nodes(this.currentGraph.nodes).links(this.currentGraph.edges).start(); + this.labelForce.nodes(this.labelGraph.nodes).links(this.labelGraph.edges).start(); + + // create filter with id #drop-shadow + // height=130% so that the shadow is not clipped + var filter = this.svg.append("svg:defs").append("filter") + .attr("id", "drop-shadow") + .attr("height", "200%") + .attr("width", "200%") + .attr("x", "-50%") // x and y have to have negative offsets to + .attr("y", "-50%"); // stop the edges from getting cut off + // translate output of Gaussian blur to the right and downwards with 2px + // store result in offsetBlur + filter.append("feOffset") + .attr("in", "SourceAlpha") + .attr("dx", 0) + .attr("dy", 0) + .attr("result", "offOut"); + // SourceAlpha refers to opacity of graphic that this filter will be applied to + // convolve that with a Gaussian with standard deviation 3 and store result + // in blur + filter.append("feGaussianBlur") + .attr("in", "offOut") + .attr("stdDeviation", 7) + .attr("result", "blurOut"); + filter.append("feBlend") + .attr("in", "SourceGraphic") + .attr("in2", "blurOut") + .attr("mode", "normal"); + + // Adds style directly because it wasn't getting picked up by the style sheet + var link = this.svg.selectAll('path.link').data(this.currentGraph.edges).enter().append('path') + .attr('class', 'link') + .style("stroke", "#aaa") + .style('fill', "#aaa") + .style("stroke-width", "3px") + .attr('id', function (d, i) { return "link" + _this.id + "_" + i; }) + + // TODO: I definitely can't see how this is not causing issues. + .on('click', function (d, i) { handleSelected(d, this); }); + + // Create the text labels that will be attatched to the paths + var linktext = this.svg.append("svg:g").selectAll("g.linklabelholder").data(this.currentGraph.edges); + linktext.enter().append("g").attr("class", "linklabelholder") + .append("text") + .attr("class", "linklabel") + .style("font-size", "13px") + .attr("text-anchor", "start") + .style("fill", "#000") + .append("textPath") + .attr("xlink:href", function (d, i) { return "#link" + _this.id + "_" + i; }) + .attr("startOffset", "20%") + .text(function (d) { + return d.label; + }); + var linklabels = this.svg.selectAll('.linklabel'); + + var node = this.svg.selectAll("g.node") + .data(this.currentGraph.nodes) + .enter().append("g") + .attr("class", "node") + .call(this.force.drag); // <-- What does the "call()" function do? + node.append("circle") + .attr("r", this.d3Config.nodeSize) + .style("fill", function (d) { return _this.d3Config.color(d.typeGroup); }); + var nodeIcon = node.append("image") + .attr("x", "-" + (this.d3Config.nodeSize + 0.5) + "px") + .attr("y", "-" + (this.d3Config.nodeSize + 1.5) + "px") + .attr("width", this.d3Config.iconSize + "px") + .attr("height", this.d3Config.iconSize + "px"); + nodeIcon.each(function (d) { + _this.setNodeIcon(d3.select(this), d.type); + }); + node.on('click', function (d, i) { _this.handleSelected(d, this); }); // If they're holding shift, release + + // Fix on click/drag, unfix on double click + this.force.drag().on('dragstart', function (d, i) { + d3.event.sourceEvent.stopPropagation(); // silence other listeners + _this.handlePin(d, this, true); + });//d.fixed = true }); + node.on('dblclick', function (d, i) { _this.handlePin(d, this, false); });//d.fixed = false }); + + // Right click will greatly dim the node and associated edges + // >>>>>>> Does not currently work <<<<<<< + node.on('contextmenu', function (d) { + if (d.dimmed) { + d.dimmed = false; // <-- What is this? Where is this set? How does this work? + d.attr("class", "node"); + } else { + d.dimmed = true; + d.attr("class", "node dimmed"); + } + }); + + var anchorNode = this.svg.selectAll("g.anchorNode").data(this.labelForce.nodes()).enter().append("svg:g").attr("class", "anchorNode"); + anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF"); + anchorNode.append("svg:text").text(function (d, i) { + return i % 2 === 0 ? "" : _this.nameFor(d.node, _this.customConfig); + }).style("fill", "#555").style("font-family", "Arial").style("font-size", 12); + + // Code in the "tick" function determines where the elements + // should be redrawn every cycle (essentially, it allows the + // elements to be animated) + this.force.on("tick", function () { + + link.attr("d", function (d) { return _this.drawArrow(d); }); + + node.call(function () { + this.attr("transform", function (d) { + return "translate(" + d.x + "," + d.y + ")"; + }); + }); + + anchorNode.each(function (d, i) { + _this.labelForce.start(); + if (i % 2 === 0) { + d.x = d.node.x; + d.y = d.node.y; + } else { + var b = this.childNodes[1].getBBox(); + + var diffX = d.x - d.node.x; + var diffY = d.y - d.node.y; + + var dist = Math.sqrt(diffX * diffX + diffY * diffY); + + var shiftX = b.width * (diffX - dist) / (dist * 2); + shiftX = Math.max(-b.width, Math.min(0, shiftX)); + var shiftY = 5; + this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")"); + } + }); + + anchorNode.call(function () { + this.attr("transform", function (d) { + return "translate(" + d.x + "," + d.y + ")"; + }); + }); + + linklabels.attr('transform', function (d, i) { + if (d.target.x < d.source.x) { + const bbox = this.getBBox(); + const rx = bbox.x + bbox.width / 2; + const ry = bbox.y + bbox.height / 2; + return 'rotate(180 ' + rx + ' ' + ry + ')'; + } + else { + return 'rotate(0)'; + } + }); + }); + + // Code to handle zooming and dragging the viewing area + this.svgTop.call(d3.behavior.zoom() + .scaleExtent([0.25, 5]) + .on("zoom", function () { + _this.svg.attr("transform", + "translate(" + d3.event.translate + ") " + + "scale(" + d3.event.scale + ")" + ); + }) + ) + .on("dblclick.zoom", null); + }; + + /* ****************************************************** + * Draws an arrow between two points. + * ******************************************************/ + Viz.prototype.drawArrow = function (d) { + return this.drawLine(d) + this.drawArrowHead(d); + }; + + /* ****************************************************** + * Draws a line between two points + * ******************************************************/ + Viz.prototype.drawLine = function (d) { + return this.startAt(d.source) + this.lineTo(d.target); + }; + + /* ****************************************************** + * Draws an arrow head. + * ******************************************************/ + Viz.prototype.drawArrowHead = function (d) { + var arrowTipPoint = this.calculateArrowTipPoint(d); + return this.startAt(arrowTipPoint) + + this.lineTo(this.calculateArrowBaseRightCornerPoint(d, arrowTipPoint)) + + this.lineTo(this.calculateArrowBaseLeftCornerPoint(d, arrowTipPoint)) + + this.lineTo(arrowTipPoint) + + this.closePath(); + }; + + /* ****************************************************** + * Creates the SVG for a starting point. + * ******************************************************/ + Viz.prototype.startAt = function (startPoint) { + return 'M' + startPoint.x + ',' + startPoint.y; + }; + + /* ****************************************************** + * Creates the SVG for line to a point. + * ******************************************************/ + Viz.prototype.lineTo = function (endPoint) { + return 'L' + endPoint.x + ',' + endPoint.y; + }; + + /* ****************************************************** + * Calculates the point at which the arrow tip should be. + * ******************************************************/ + Viz.prototype.calculateArrowTipPoint = function (d) { + var nodeRadius = Math.max(this.d3Config.iconSize, this.d3Config.nodeSize) / 2; + return this.translatePoint(d.target, this.calculateUnitVectorAlongLine(d), -(this.d3Config.nodeSize + 3)); + }; + + /* ****************************************************** + * Calculates the point at which the right corner of the + * base of the arrow head should be. + * ******************************************************/ + Viz.prototype.calculateArrowBaseRightCornerPoint = function (d, arrowTipPoint) { + var arrowBaseWidth = 13; + var unitVector = this.calculateUnitVectorAlongLine(d); + var arrowBasePoint = this.calculateArrowBaseCentrePoint(d, arrowTipPoint); + return this.translatePoint(arrowBasePoint, this.calculateNormal(unitVector), -arrowBaseWidth / 2); + }; + + /* ****************************************************** + * Calculates the point at which the left corner of the + * base of the arrow head should be. + * ******************************************************/ + Viz.prototype.calculateArrowBaseLeftCornerPoint = function (d, arrowTipPoint) { + var arrowBaseWidth = 13; + var unitVector = this.calculateUnitVectorAlongLine(d); + var arrowBasePoint = this.calculateArrowBaseCentrePoint(d, arrowTipPoint); + return this.translatePoint(arrowBasePoint, this.calculateNormal(unitVector), arrowBaseWidth / 2); + }; + + /* ****************************************************** + * Calculates the point at the centre of the base of the + * arrow head. + * ******************************************************/ + Viz.prototype.calculateArrowBaseCentrePoint = function (d, arrowTipPoint) { + var arrowHeadLength = 13; + return this.translatePoint(arrowTipPoint, this.calculateUnitVectorAlongLine(d), -arrowHeadLength); + }; + + /* ****************************************************** + * Translates a point. + * ******************************************************/ + Viz.prototype.translatePoint = function (startPoint, directionUnitVector, distance) { + return { x: startPoint.x + distance * directionUnitVector.x, y: startPoint.y + distance * directionUnitVector.y }; + }; + + /* ****************************************************** + * Calculates a unit vector along a particular line. + * ******************************************************/ + Viz.prototype.calculateUnitVectorAlongLine = function (d) { + var dx = d.target.x - d.source.x; + var dy = d.target.y - d.source.y; + var dr = Math.sqrt(dx * dx + dy * dy); + return { x: dx / dr, y: dy / dr }; + }; + + /* ****************************************************** + * Calculates a normal to a unit vector. + * ******************************************************/ + Viz.prototype.calculateNormal = function (unitVector) { + return { x: -unitVector.y, y: unitVector.x }; + }; + + /* ****************************************************** + * Closes an SVG path. + * ******************************************************/ + Viz.prototype.closePath = function () { + return 'Z'; + }; + + /* ****************************************************** + * Screens out D3 chart data from the presentation. + * Also makes values more readable. + * Called as the 2nd parameter to JSON.stringify(). + * ******************************************************/ + function replacer(key, value) { + var blacklist = ["typeGroup", "index", "weight", "x", "y", "px", "py", "fixed", "dimmed"]; + if (blacklist.indexOf(key) >= 0) { + return undefined; + } + // Some of the potential values are not very readable (IDs + // and object references). Let's see if we can fix that. + // Lots of assumptions being made about the structure of the JSON here... + var dictlist = ['definition', 'objects']; + if (Array.isArray(value)) { + if (key === 'kill_chain_phases') { + let newValue = []; + value.forEach(function (item) { + newValue.push(item.phase_name) + }); + return newValue; + } else if (key === 'granular_markings' || key === 'external_references') { + let newValue = []; + value.forEach(function (item) { + newValue.push(JSON.stringify(item)); + }); + return newValue.join(", "); + } else { + return value.join(", "); + } + } else if (/--/.exec(value) && !(key === "id")) { + if (!(this.idCache[value] === null || this.idCache[value] === undefined)) { + // IDs are gross, so let's display something more readable if we can + // (unless it's actually the node id) + return this.currentGraph.nodes[this.idCache[value]].name; + } + } else if (dictlist.indexOf(key) >= 0) { + return JSON.stringify(value); + } + return value; + } + + /* ****************************************************** + * Adds class "selected" to last graph element clicked + * and removes it from all other elements. + * + * Takes datum and element as input. + * ******************************************************/ + Viz.prototype.handleSelected = function (d, el) { + var selectedReplacer = replacer.bind(this); + const jsonString = JSON.stringify(d, selectedReplacer, 2); // get only the STIX values + const purified = JSON.parse(jsonString); // make a new JSON object from the STIX values + + // Pretty up the keys + for (var key in purified) { + if (d.hasOwnProperty(key)) { + var keyString = key; + if (refRegex.exec(key)) { // key is "created_by_ref"... let's pretty that up + keyString = key.replace(/_(refs*)?/g, " ").trim(); + } else { + keyString = keyString.replace(/_/g, ' '); + } + keyString = keyString.charAt(0).toUpperCase() + keyString.substr(1).toLowerCase() // Capitalize it + keyString += ":"; + + purified[keyString] = purified[key]; + delete purified[key]; + } + } + + this.selectedCallback(purified); + d3.select('.selected').classed('selected', false); + d3.select(el).classed('selected', true); + }; + + /* ****************************************************** + * Handles pinning and unpinning of nodes. + * + * Takes datum, element, and boolean as input. + * ******************************************************/ + Viz.prototype.handlePin = function (d, el, pinBool) { + d.fixed = pinBool; + d3.select(el).classed("pinned", pinBool); + }; + + /* ****************************************************** + * Parses the JSON input and builds the arrays used by + * initGraph(). + * + * Takes a JSON object as input. + * ******************************************************/ + Viz.prototype.buildNodes = function (pkg) { + var _this = this; + var relationships = []; + if (pkg.hasOwnProperty('objects')) { + this.parseSDOs(pkg['objects']); + + // Get embedded relationships + pkg['objects'].forEach(function (item) { + if (item['type'] === 'relationship') { + relationships.push(item); + return; + } + Object.keys(item).forEach(function (key, index) { + if (key.endsWith("_ref") && _this.refsMapping.hasOwnProperty(key)) { + var source = (_this.refsMapping[key][1] === true) ? item["id"] : item[key]; + var target = (_this.refsMapping[key][1] === true) ? item[key] : item["id"]; + var relType = _this.refsMapping[key][0]; + relationships.push({ + 'source_ref': source, + 'target_ref': target, + 'relationship_type': relType + }); + } + else if (key.endsWith("_refs") && _this.refsMapping.hasOwnProperty(key)) { + item[key].forEach(function (refID) { + var source = (_this.refsMapping[key][1] === true) ? item["id"] : refID; + var target = (_this.refsMapping[key][1] === true) ? refID : item["id"]; + var relType = _this.refsMapping[key][0]; + relationships.push({ + 'source_ref': source, + 'target_ref': target, + 'relationship_type': relType + }); + }); + } + }); + }); + } + + this.addRelationships(relationships); + + // Add the legend so we know what's what + this.legendCallback(Object.keys(this.typeGroups)); + }; + + /* ****************************************************** + * Uses regex to check whether the specified value for + * display_icon in customConfig is a valid URL. + * + * Note: The protocol MUST be supplied in the image URL + * (e.g. https) + * + * The regex expression below is based on: + * https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url + * ******************************************************/ + Viz.prototype.validUrl = function (imageUrl) { + var pattern = new RegExp('^(https?:\\/\\/)' + // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + //port + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d_]*)?$', 'i'); + return pattern.test(imageUrl); + }; + + /* ****************************************************** + * Returns the name to use for an SDO Node + * + * Determines what name to use in the following order: + * 1) A user-chosen ID-specific label via customConfig.userLabels. + * 2) The value of a user-chosen type-specific SDO property given via + * customConfig..display_property + * 3) The SDO's "name" property + * 4) The SDO's "value" property + * 5) The SDO's "type" property + * ******************************************************/ + Viz.prototype.nameFor = function (sdo) { + + let name = null; + + if (this.customConfig !== undefined) { + if ("userLabels" in this.customConfig && + sdo.id in this.customConfig.userLabels) + name = this.customConfig.userLabels[sdo.id]; + else if (sdo.type in this.customConfig) + name = sdo[this.customConfig[sdo.type].display_property]; + + if (name && name.length > 100) + name = name.substr(0, 100) + '...'; // For space-saving + } + + if (!name) { + if (sdo.name !== undefined) { + name = sdo.name; + } else if (sdo.value !== undefined) { + name = sdo.value; + } else if (sdo.path !== undefined) { + name = sdo.path; + } else { + name = sdo.type; + } + } + + return name; + }; + + /* ****************************************************** + * Returns the icon to use for an SDO Node + * + * Determines which icon to use in the following order: + * 1) A display_icon set in the config (must be in the icon directory) + * 2) A default icon for the SDO type, bundled with this library + * ******************************************************/ + Viz.prototype.iconFor = function (typeName) { + + let typeIcon; + if (this.customConfig !== undefined && typeName in this.customConfig) { + let customIcon = this.customConfig[typeName].display_icon; + if (customIcon !== undefined) { + if (this.validUrl(customIcon)) { + return customIcon; + } else { + typeIcon = this.d3Config.iconDir + '/' + customIcon; + return typeIcon; + } + } + } + if (typeName !== undefined) { + typeIcon = this.d3Config.iconDir + "/stix2_" + typeName.replace(/-/g, '_') + "_icon_tiny_round_v1.png"; + return typeIcon; + } + }; + + /* ****************************************************** + * Sets the icon on a STIX object node + * + * If the image doesn't load properly, a default 'custom object' + * icon will be used instead + * ******************************************************/ + Viz.prototype.setNodeIcon = function (node, stixType) { + var _this = this; + var tmpImg = new Image(); + tmpImg.onload = function () { + // set the node's icon to this image if it loaded properly + node.attr("xlink:href", tmpImg.src); + } + tmpImg.onerror = function () { + // set the node's icon to the default if this image could not load + node.attr("xlink:href", _this.d3Config.iconDir + "/stix2_custom_object_icon_tiny_round_v1.svg") + } + tmpImg.src = _this.iconFor(stixType, _this.customConfig); + }; + + /* ****************************************************** + * Parses valid SDOs from an array of potential SDO + * objects (ideally from the data object) + * + * Takes an array of objects as input. + * ******************************************************/ + Viz.prototype.parseSDOs = function (container) { + var cap = container.length; + for (var i = 0; i < cap; i++) { + // So, in theory, each of these should be an SDO. To be sure, we'll check to make sure it has an `id` and `type`. If not, raise an error and ignore it. + var maybeSdo = container[i]; + if (maybeSdo.id === undefined || maybeSdo.type === undefined) { + console.error("Should this be an SDO???", maybeSdo); + } else { + this.addSdo(maybeSdo); + } + } + }; + + /* ****************************************************** + * Adds an SDO node to the graph + * + * Takes a valid SDO object as input. + * ******************************************************/ + Viz.prototype.addSdo = function (sdo) { + if (this.idCache[sdo.id]) { + console.log("Skipping already added object!", sdo); + } else if (sdo.type === 'relationship') { + console.log("Skipping relationship object!", sdo); + } else { + if (this.typeGroups[sdo.type] === undefined) { + this.typeGroups[sdo.type] = this.typeIndex++; + } + sdo.typeGroup = this.typeGroups[sdo.type]; + + this.idCache[sdo.id] = this.currentGraph.nodes.length; // Edges reference nodes by their array index, so cache the current length. When we add, it will be correct + this.currentGraph.nodes.push(sdo); + + this.labelGraph.nodes.push({ node: sdo }); // Two labels will orbit the node, we display the less crowded one and hide the more crowded one. + this.labelGraph.nodes.push({ node: sdo }); + + this.labelGraph.edges.push({ + source: (this.labelGraph.nodes.length - 2), + target: (this.labelGraph.nodes.length - 1), + weight: 1 + }); + } + }; + + /* ****************************************************** + * Adds relationships to the graph based on the array of + * relationships contained in the data. + * + * Takes an array as input. + * ******************************************************/ + Viz.prototype.addRelationships = function (relationships) { + for (var i = 0; i < relationships.length; i++) { + var rel = relationships[i]; + if (this.idCache[rel.source_ref] === null || this.idCache[rel.source_ref] === undefined) { + console.error("Couldn't find source!", rel); + } else if (this.idCache[rel.target_ref] === null || this.idCache[rel.target_ref] === undefined) { + console.error("Couldn't find target!", rel); + } else { + this.currentGraph.edges.push({ source: this.idCache[rel.source_ref], target: this.idCache[rel.target_ref], label: rel.relationship_type }); + } + } + }; + + /* ****************************************************** + * Resets the graph so it can be rebuilt + * *****************************************************/ + Viz.prototype.vizReset = function () { + this.typeGroups = {}; + this.typeIndex = 0; + + this.currentGraph = { + nodes: [], + edges: [] + }; + this.labelGraph = { + nodes: [], + edges: [] + }; + + this.idCache = {}; + + this.force.stop(); + this.labelForce.stop(); + this.svg.remove(); + }; + + + return Viz; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d741a30 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,104 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + + /* Modules */ + "module": "AMD", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + "checkJs": false, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./lib", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": false, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + "noImplicitUseStrict": true, + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + + "include": ["./stix2viz/stix2viz/stix2vizcore.js"] + +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..a3752bd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1304 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@eslint/eslintrc@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@typescript-eslint/eslint-plugin@^5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz" + integrity sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ== + dependencies: + "@typescript-eslint/scope-manager" "5.17.0" + "@typescript-eslint/type-utils" "5.17.0" + "@typescript-eslint/utils" "5.17.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz" + integrity sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig== + dependencies: + "@typescript-eslint/scope-manager" "5.17.0" + "@typescript-eslint/types" "5.17.0" + "@typescript-eslint/typescript-estree" "5.17.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz" + integrity sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w== + dependencies: + "@typescript-eslint/types" "5.17.0" + "@typescript-eslint/visitor-keys" "5.17.0" + +"@typescript-eslint/type-utils@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz" + integrity sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg== + dependencies: + "@typescript-eslint/utils" "5.17.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz" + integrity sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw== + +"@typescript-eslint/typescript-estree@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz" + integrity sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg== + dependencies: + "@typescript-eslint/types" "5.17.0" + "@typescript-eslint/visitor-keys" "5.17.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz" + integrity sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.17.0" + "@typescript-eslint/types" "5.17.0" + "@typescript-eslint/typescript-estree" "5.17.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.17.0": + version "5.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz" + integrity sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA== + dependencies: + "@typescript-eslint/types" "5.17.0" + eslint-visitor-keys "^3.0.0" + +"@zeit/schemas@2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz" + integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== + +accepts@~1.3.5: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.7.0: + version "8.7.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + +ajv@6.12.6, ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +arch@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +arg@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz" + integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +boxen@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz" + integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +clipboardy@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz" + integrity sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ== + dependencies: + arch "^2.1.1" + execa "^1.0.0" + is-wsl "^2.1.1" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +compressible@~2.0.14: + version "2.0.18" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@1.7.3: + version "1.7.3" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz" + integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +d3@^3.5.14: + version "3.5.17" + resolved "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz" + integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g= + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.1, debug@^4.3.2: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.12.0: + version "8.12.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz" + integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q== + dependencies: + "@eslint/eslintrc" "^1.2.1" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" + integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + dependencies: + punycode "^1.3.2" + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.5" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.6.0, globals@^13.9.0: + version "13.13.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== + dependencies: + type-fest "^0.20.2" + +globby@^11.0.4: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.1.8, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0: + version "1.2.6" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +on-headers@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +rc@^1.0.1, rc@^1.1.6: + version "1.2.8" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz" + integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz" + integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= + dependencies: + rc "^1.0.1" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^5.5.0: + version "5.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +serve-handler@6.1.3: + version "6.1.3" + resolved "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz" + integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.0.4" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve@^13.0.2: + version "13.0.2" + resolved "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz" + integrity sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ== + dependencies: + "@zeit/schemas" "2.6.0" + ajv "6.12.6" + arg "2.0.0" + boxen "5.1.2" + chalk "2.4.1" + clipboardy "2.3.0" + compression "1.7.3" + serve-handler "6.1.3" + update-check "1.5.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.0: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typescript@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== + +update-check@1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz" + integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ== + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==