├── .DS_Store ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ ├── CODEOWNERS │ └── node.js.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── benchmarks └── webpack │ ├── find-cycles.js │ ├── index.md │ └── webpack.json ├── examples ├── affected-builds │ ├── README.md │ └── index.js └── circular-dependencies │ ├── README.md │ └── index.js ├── package.json ├── pnpm-lock.yaml ├── src ├── digraph.spec.ts ├── digraph.ts ├── index.ts ├── traversal.spec.ts └── vertex.ts ├── tsconfig.build.json └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoine-coulon/digraph-js/9ebbe2be5f5280a80c0239942fbe23bb15d46827/.DS_Store -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | examples/ 3 | benchmarks/ -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": [ 6 | "@nodesecure/eslint-config", 7 | "prettier", 8 | "plugin:import/typescript" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "sourceType": "module", 13 | "project": "./tsconfig.json" 14 | }, 15 | "plugins": ["@typescript-eslint", "prettier", "import"], 16 | "settings": { 17 | "import/parsers": { 18 | "@typescript-eslint/parser": [".ts", ".tsx"] 19 | }, 20 | "import/resolver": { 21 | "typescript": { 22 | "alwaysTryTypes": true, 23 | "project": "tsconfig.json" 24 | } 25 | } 26 | }, 27 | "rules": { 28 | "indent": "off", 29 | "brace-style": "off", 30 | "no-empty": ["error", { "allowEmptyCatch": true }], 31 | "id-length": "off", 32 | "import/order": [ 33 | "error", 34 | { 35 | "newlines-between": "always", 36 | "alphabetize": { 37 | "order": "asc" 38 | } 39 | } 40 | ], 41 | "@typescript-eslint/no-unused-vars": [ 42 | "error", 43 | { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" } 44 | ], 45 | "prettier/prettier": [ 46 | "error", 47 | {}, 48 | { 49 | "usePrettierrc": true 50 | } 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @antoine-coulon -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Dag.js Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: [16.x] 14 | fail-fast: false 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | - name: Install dependencies 22 | run: npm install 23 | - name: Run tests 24 | run: npm run test 25 | - name: Build project 26 | run: npm run build 27 | - uses: NodeSecure/ci-action@v1 28 | with: 29 | warnings: off -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "singleQuote": false, 4 | "trailingComma": "none", 5 | "endOfLine": "auto" 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 Antoine Coulon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | digraph-js 3 |

4 | 5 | Make Directed Graphs traversal and construction effortless, also includes deep circular dependency detection. 6 | 7 | **digraph-js** is a lightweight library allowing you to create a Directed Acyclic Graph data structure with embedded features such as deep cycle dependency detection and graph introspection (find deeply ancestors and successors for any given vertex). 8 | It can be used to model complex dependencies systems based on graphs. 9 | 10 | ✅ Create a graph structure including edges and vertices seamlessly 11 | 12 | ✅ Traverse graph, using Depth-first or Breadth-first searchs 13 | 14 | ✅ Deeply find direct/indirect children and parent dependencies of each vertex in the graph (top-to-bottom or bottom-to-top traversals) 15 | 16 | ✅ Ensure that a given graph is Acyclic by deeply detecting circular dependencies while having the possibility to limit the search depth 17 | 18 | ✅ Find precisely all vertices involved in cycles and sub-cycles 19 | 20 | ## Installation 21 | 22 | ```bash 23 | $ npm install digraph-js 24 | ``` 25 | 26 | ## How to use it 27 | 28 | ```js 29 | import { DiGraph } from "digraph-js"; 30 | import assert from "node:assert"; 31 | 32 | const myGraph = new DiGraph(); 33 | 34 | const myDependencyA = { id: "dependencyA", adjacentTo: [], body: {} }; 35 | const myDependencyB = { id: "dependencyB", adjacentTo: [], body: {} }; 36 | const myDependencyC = { id: "dependencyC", adjacentTo: [], body: {} }; 37 | 38 | // Add vertices to the graph 39 | myGraph.addVertices(myDependencyA, myDependencyB, myDependencyC); 40 | 41 | // Link graph vertices: A ---> B link created 42 | myGraph.addEdge({ from: myDependencyA.id, to: myDependencyB.id }); 43 | 44 | // Graph traversels 45 | myGraph.addEdge({ from: myDependencyB.id, to: myDependencyC.id }); 46 | // getDeepChildren traverses the graph in a Depth-First Search fashion 47 | const deepDependenciesOfA = myGraph.getDeepChildren("dependencyA"); 48 | // deepDependenciesOfA is an iterable structure that can be lazily consumed 49 | assert.deepEqual([...deepDependenciesOfA], ["dependencyB", "dependencyC"]); 50 | 51 | // Here we voluntarily create a cyclic dependency 52 | myGraph.addEdge({ from: myDependencyB.id, to: myDependencyA.id }); 53 | // Detect if the Directed Graph is acyclic (Directed Acyclic Graph) 54 | assert.equal(myGraph.isAcyclic, false); 55 | assert.equal(myGraph.hasCycles(), true); 56 | assert.deepEqual(myGraph.findCycles().cycles, [["dependencyA", "dependencyB"]]); 57 | 58 | // Limit cycles search or dependency depth 59 | // Imagine a case where the cycle is created at depth 6 60 | assert.equal(myGraph.hasCycles({ maxDepth: 5 }), false); 61 | // Or that you want to get all children of a vertex but with a max depth of 5 62 | // meaning that you don't want dependencies going over 5 generations 63 | assert.equal(myGraph.getDeepChildren("dependencyA"), 5); 64 | 65 | 66 | // Traversals 67 | 68 | // Lazily pull vertices from the graph 69 | for(const vertex of myGraph.traverse({ traversal: "dfs" })) { 70 | console.log(vertex.id); 71 | } 72 | 73 | // Eagerly pull all the graph vertices at once 74 | const graphVertices = myGraph.traverseEager({ traversal: "dfs" }); 75 | console.log(graphVertices.length); 76 | ``` 77 | 78 | ## You already manipulate Directed Graphs without knowing it 79 | 80 |

81 | digraph 82 |

83 | 84 | Take for instance the image above with four Vertices each representing a 85 | JavaScript file. 86 | 87 | Now the question is: what are the **relationships** between these files? In all 88 | programming languages, one file might import one or multiple files. Whenever 89 | a file imports another one, an implicit relationship is created. 90 | 91 | **_hello.js_** 92 | 93 | ```js 94 | export function sayHello() {} 95 | ``` 96 | 97 | **_main.js_** 98 | 99 | ```js 100 | import { sayHello } from "hello.js"; 101 | ``` 102 | 103 | As you can see above, **main.js** imports **hello.js** to use the `sayHello` 104 | function. The static import creates an implicit relationship between both files. 105 | In the fields of graphs, this relationship can be modeled as a directed edge 106 | from **main.js** to **hello.js** (can be written as **main.js ---> hello.js**) 107 | We can also say that main.js **depends on** hello.js. 108 | 109 | We can update the graph with our edges represented: 110 | 111 |

112 | digraph 113 |

114 | 115 | Basically this graph says that: 116 | 117 | - FileA directly depends on FileD and FileB 118 | - FileA indirectly depends on FileC (through both FileD and FileB) 119 | - FileD directly depends on FileC 120 | - FileB directly depends on FileC 121 | - FileC directly depends on nothing 122 | 123 | This structure may seem simple but can in fact be used to model very complex 124 | schemas such as: 125 | 126 | - **_Static dependencies analysis_** such as cycle dependencies detection 127 | (e.g: [ESLint no-cycle plugin](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-cycle.md)) 128 | - **_Incremental/Affected tasks_** Bundlers/Monorepos tools make extensive use of it (e.g: [NX's affected build/test/lint...](https://nx.dev/using-nx/affected)) 129 | - **_Task orchestration_** using a directed acyclic graph, parallel vs sequential 130 | computations can be modeled (e.g: Continuous Integration schemas with stages, jobs, tasks) 131 | 132 | ## Further exploring with examples which recreate common features: 133 | 134 | - [affected builds (NX alike)](https://github.com/antoine-coulon/digraph-js/tree/master/examples/affected-builds) 135 | - [detect cyclic imports (eslint-plugin-import alike)](https://github.com/antoine-coulon/digraph-js/tree/master/examples/circular-dependencies) 136 | - Sequential vs Parallel execution => Work in progress 137 | -------------------------------------------------------------------------------- /benchmarks/webpack/find-cycles.js: -------------------------------------------------------------------------------- 1 | import { readFile } from "node:fs/promises"; 2 | import path from "node:path"; 3 | import { performance } from "node:perf_hooks"; 4 | import { DiGraph } from "../../dist/index.js"; 5 | 6 | { 7 | console.log("----------------------------------------"); 8 | const data = await readFile(path.join(process.cwd(), "./webpack.json")); 9 | const graph = new DiGraph(); 10 | for (const [nodeId, nodeValue] of Object.entries(JSON.parse(data))) { 11 | graph.addVertex({ id: nodeId, adjacentTo: nodeValue.adjacentTo, body: {} }); 12 | } 13 | const start = performance.now(); 14 | console.log("Started webpack benchmark with cycle detection = INFINITY"); 15 | const cycles = graph.findCycles(); 16 | const end = performance.now() - start; 17 | console.log(`${cycles.length} cycles found`); 18 | console.log( 19 | `Took ${(end / 1000).toFixed(3)} seconds to find cycles on Webpack` 20 | ); 21 | } 22 | 23 | { 24 | console.log("----------------------------------------"); 25 | const data = await readFile(path.join(process.cwd(), "./webpack.json")); 26 | const graph = new DiGraph(); 27 | for (const [nodeId, nodeValue] of Object.entries(JSON.parse(data))) { 28 | graph.addVertex({ id: nodeId, adjacentTo: nodeValue.adjacentTo, body: {} }); 29 | } 30 | const start = performance.now(); 31 | console.log("Started webpack benchmark with cycle detection = 500"); 32 | const cycles = graph.findCycles({ maxDepth: 500 }); 33 | const end = performance.now() - start; 34 | console.log(`${cycles.length} cycles found`); 35 | console.log( 36 | `Took ${(end / 1000).toFixed(3)} seconds to find cycles on Webpack` 37 | ); 38 | } 39 | 40 | { 41 | console.log("----------------------------------------"); 42 | const data = await readFile(path.join(process.cwd(), "./webpack.json")); 43 | const graph = new DiGraph(); 44 | for (const [nodeId, nodeValue] of Object.entries(JSON.parse(data))) { 45 | graph.addVertex({ id: nodeId, adjacentTo: nodeValue.adjacentTo, body: {} }); 46 | } 47 | const start = performance.now(); 48 | console.log("Started webpack benchmark with cycle detection = 100"); 49 | const cycles = graph.findCycles({ maxDepth: 100 }); 50 | const end = performance.now() - start; 51 | console.log(`${cycles.length} cycles found`); 52 | console.log( 53 | `Took ${(end / 1000).toFixed(3)} seconds to find cycles on Webpack` 54 | ); 55 | } 56 | 57 | { 58 | console.log("----------------------------------------"); 59 | const data = await readFile(path.join(process.cwd(), "./webpack.json")); 60 | const graph = new DiGraph(); 61 | for (const [nodeId, nodeValue] of Object.entries(JSON.parse(data))) { 62 | graph.addVertex({ id: nodeId, adjacentTo: nodeValue.adjacentTo, body: {} }); 63 | } 64 | const start = performance.now(); 65 | console.log("Started webpack benchmark with cycle detection = 20"); 66 | const cycles = graph.findCycles({ maxDepth: 20 }); 67 | const end = performance.now() - start; 68 | console.log(`${cycles.length} cycles found`); 69 | console.log( 70 | `Took ${(end / 1000).toFixed(3)} seconds to find cycles on Webpack` 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /benchmarks/webpack/index.md: -------------------------------------------------------------------------------- 1 | To perform benchmarks on a wide graph, we use webpack which is probably one of the largest open source Node.js codebase. `./webpack.json` is the file representing the whole webpack graph, built by [skott](https://github.com/antoine-coulon/skott). 2 | 3 | Using an Apple M1, here are the results: 4 | 5 | ``` 6 | ---------------------------------------- 7 | Started webpack benchmark with cycle detection = INFINITY 8 | 935 cycles found 9 | Took 12.144 seconds to find cycles on Webpack 10 | ---------------------------------------- 11 | Started webpack benchmark with cycle detection = 500 12 | 876 cycles found 13 | Took 9.318 seconds to find cycles on Webpack 14 | ---------------------------------------- 15 | Started webpack benchmark with cycle detection = 100 16 | 370 cycles found 17 | Took 0.320 seconds to find cycles on Webpack 18 | ---------------------------------------- 19 | Started webpack benchmark with cycle detection = 20 20 | 30 cycles found 21 | Took 0.038 seconds to find cycles on Webpack 22 | ``` -------------------------------------------------------------------------------- /examples/affected-builds/README.md: -------------------------------------------------------------------------------- 1 | # Affected behavior 2 | 3 | One of the strengths of the Directed Acyclic Graph is its ability to model dependencies 4 | between ressources. Take for instance a project with multiple libs that are independently 5 | built each time we want to ship the main app. 6 | 7 | ``` 8 | project 9 | │ 10 | └───lib1 11 | | | dist/ 12 | │ │ component1.jsx 13 | │ 14 | └───lib2 15 | | dist/ 16 | │ component2.jsx 17 | │ component3.jsx 18 | | 19 | | package.json 20 | | app.js 21 | ``` 22 | 23 | Our `app.js` represents our entry file and uses components from both `lib1` and `lib2`. 24 | Now imagine you only change the `component1.jsx` but you have to build your whole 25 | app using a script such as: 26 | 27 | ```bash 28 | # 'build' is a script which builds lib1 and lib2 each time no matter what 29 | $ npm run build 30 | ``` 31 | 32 | You might wonder what is the problem with this approach. The answer is that 33 | its probably fine most of the time for small to medium sized projects. 34 | However if your `lib1` and `lib2` end up growing (more components, but also 35 | including assets such as images, fonts etc) you might encounter performance issues 36 | for building your whole app. 37 | 38 | ## Affected projects/packages detection 39 | 40 | The whole idea of detecting affected projects/packages is to only re-build 41 | what change in your project. The tool in charge of that can introspect your 42 | project, and internally emit a Directed Acyclic Graph responsible for establishing 43 | dependencies between pieces of your project. By using the emitted graph 44 | and a persisted cache (also handle by the tool), this enables smart builds and 45 | many other tasks that depends on the state of the project (linting, testing, etc). 46 | 47 | These concepts are very widely implement in Monorepo tools such as NX, Rush, Turborepo... 48 | See more here: https://monorepo.tools/#detecting-affected-projects-packages 49 | 50 | 51 | ## Example simulating affected build for a multi-libs project 52 | 53 | See a very basic implementation of this concept in `index.js`. Be sure to 54 | build the `/dist` folder using the `build` script. 55 | 56 | ```bash 57 | $ npm run build 58 | $ node examples/affected-builds/index.js 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /examples/affected-builds/index.js: -------------------------------------------------------------------------------- 1 | import crypto from "node:crypto"; 2 | 3 | import { DiGraph } from "../../dist/index.js"; 4 | 5 | const lib1Metadata = { 6 | id: "lib1", 7 | adjacentTo: [], 8 | body: { 9 | component: `` 10 | } 11 | }; 12 | 13 | const lib2Metadata = { 14 | id: "lib2", 15 | adjacentTo: [], 16 | body: { component: `
hello lib2
` } 17 | }; 18 | 19 | const lib3Metadata = { 20 | id: "lib3", 21 | adjacentTo: [], 22 | body: { component: `hello lib3` } 23 | }; 24 | 25 | /** 26 | * Simulating a project with three distincts libraries. 27 | * lib1 depends on lib3 (via the use of lib3.MyLib3Component) while library 2 is 28 | * independent. 29 | */ 30 | const projectGraph = new DiGraph(); 31 | 32 | // Update the Graph with detected dependencies 33 | projectGraph.addVertices(lib1Metadata, lib2Metadata, lib3Metadata); 34 | 35 | // lib1 depends on lib3 so we need to express this relationship using an edge 36 | projectGraph.addEdge({ from: lib1Metadata.id, to: lib3Metadata.id }); 37 | 38 | // Simulating a simple cache, persisting an hashed value of the component 39 | const cache = { 40 | lib1: {}, 41 | lib2: {}, 42 | lib3: {} 43 | }; 44 | 45 | function isLibraryAffected(library) { 46 | const libraryHashedContent = crypto 47 | .createHash("sha1") 48 | .update(library.body.component) 49 | .digest("hex"); 50 | 51 | return libraryHashedContent !== cache[library.id].component; 52 | } 53 | 54 | function buildLibrary(library) { 55 | const libraryHashedContent = crypto 56 | .createHash("sha1") 57 | .update(library.body.component) 58 | .digest("hex"); 59 | 60 | console.log(`Building library: '${library.id}'`); 61 | // dependencyLib.buildFiles(); <= Webpack or any bundler 62 | cache[library.id].component = libraryHashedContent; 63 | } 64 | 65 | function buildAffected(library) { 66 | /** 67 | * If the component is still the same (i.e: hash data hasnt changed), we 68 | * don't want to rebuild it. If its "Affected", we must invalidate it 69 | */ 70 | if (isLibraryAffected(library)) { 71 | // Component's hash changed, meaning we must build the library 72 | buildLibrary(library); 73 | 74 | return { hasLibraryBeenRebuilt: true }; 75 | } 76 | 77 | // Lib has not changed so does not require a new build 78 | console.log(`Using cached version of '${library.id}'`); 79 | 80 | return { hasLibraryBeenRebuilt: false }; 81 | } 82 | 83 | function* buildAllRootLibraryDependencies(rootLibrary) { 84 | for (const rootLibraryDependency of projectGraph.getLowerDependencies( 85 | rootLibrary 86 | )) { 87 | /** 88 | * Recursively build affected libraries starting from the deepest dependencies 89 | * of the root library. 90 | * N.B: DiGraph could also be used in order to orchestrate parallelization. For 91 | * example, two dependencies which don't share any dependencies in common 92 | * could be built in parallel. 93 | */ 94 | yield* buildAllRootLibraryDependencies(rootLibraryDependency); 95 | } 96 | 97 | // End up by building the root library once all dependencies are up-to-date 98 | const { hasLibraryBeenRebuilt } = buildAffected(rootLibrary); 99 | 100 | yield hasLibraryBeenRebuilt; 101 | } 102 | 103 | /** 104 | * Build a library using affected mode (i.e: using DiGraph and cache to skip 105 | * unnecessary rebuild if possible) 106 | */ 107 | function buildEverythingAffectedIncludingRootLibrary(rootLibrary) { 108 | const rootLibraryDependencies = projectGraph.getLowerDependencies( 109 | rootLibrary.id 110 | ); 111 | const allRebuiltLibraries = []; 112 | 113 | for (const dependencyLibrary of rootLibraryDependencies) { 114 | allRebuiltLibraries.push([ 115 | ...buildAllRootLibraryDependencies(dependencyLibrary) 116 | ]); 117 | } 118 | 119 | /** 120 | * All root library's dependencies were re-built if necessary (i.e: affected). 121 | * However, we now need to determine if the root library has to also be 122 | * rebuilt. There are 2 conditions requiring the root library to be rebuilt: 123 | * - The root library itself changed 124 | * - Atleast one of the dependencies of the library changed 125 | */ 126 | const HAS_LIBRARY_BEEN_REBUILT = true; 127 | const atleastOneLibraryChanged = allRebuiltLibraries 128 | .flat() 129 | .includes(HAS_LIBRARY_BEEN_REBUILT); 130 | 131 | if (atleastOneLibraryChanged) { 132 | buildLibrary(rootLibrary); 133 | } else { 134 | // Check if library itself changed 135 | buildAffected(rootLibrary); 136 | } 137 | } 138 | 139 | function buildProjectUsingAffectedStrategy() { 140 | console.log("\n----STEP 1-----"); 141 | // Building for the first time 142 | buildEverythingAffectedIncludingRootLibrary(lib1Metadata); 143 | /** 144 | * Building for the second time but no dependencies of lib1 changed (neither 145 | * lib3 or lib4) so it remains unaffected (i.e: using cache) 146 | */ 147 | console.log("\n----STEP 2-----"); 148 | buildEverythingAffectedIncludingRootLibrary(lib1Metadata); 149 | 150 | console.log("\n----STEP 3-----"); 151 | /** 152 | * Let's now change the content of lib3's component. 153 | * Remember, lib1 depends on lib3 via the use of lib3.MyLib3Component. 154 | */ 155 | console.log("Changing lib3's content..."); 156 | projectGraph.updateVertexBody(lib3Metadata.id, { 157 | // lib3 component is updated 158 | component: `hello affected lib3!` 159 | }); 160 | 161 | console.log("\n----STEP 4-----"); 162 | /** 163 | * Now that lib3 (dependency of lib1) changed BOTH lib3 and lib1 are considered 164 | * affected. 165 | * It means that we must rebuild both, starting with lib3 (lib1 must build 166 | * with the latest version of lib3). 167 | */ 168 | buildEverythingAffectedIncludingRootLibrary(lib1Metadata); 169 | 170 | console.log("\n----STEP 5-----"); 171 | /** 172 | * Now that everything was rebuilt, we can easily use cached versions once 173 | * again. 174 | */ 175 | buildEverythingAffectedIncludingRootLibrary(lib1Metadata); 176 | } 177 | 178 | buildProjectUsingAffectedStrategy(); 179 | -------------------------------------------------------------------------------- /examples/circular-dependencies/README.md: -------------------------------------------------------------------------------- 1 | # Circular dependencies 2 | 3 | A Directed Graph can be used to detect cyclic dependencies. Conceptually, 4 | we have a cyclic dependency whenever there is a path from `Vertex X` -passing by 5 | one or many vertices- to `Vertex X`. 6 | 7 | Let's take a very simple example to illustrate a cycle dependency. 8 | Let's say we have three JavaScript files, **A.js**, **B.js**, **C.js**: 9 | 10 | **A.js** 11 | ```js 12 | import { doSomethingFromB } from 'B.js'; 13 | 14 | export function doSomethingFromA() {} 15 | ``` 16 | 17 | **B.js** 18 | ```js 19 | import { doSomethingFromC } from 'C.js'; 20 | 21 | export function doSomethingFromB() {} 22 | ``` 23 | 24 | **C.js** 25 | ```js 26 | import { doSomethingFromA } from 'A.js'; 27 | 28 | export function doSomethingFromC() {} 29 | ``` 30 | 31 | **Cycle spotted**: `A --> B --> C --> A`. If you don't remember the definition, 32 | there is a cycle because it exists a path from `A` leading to `A`. 33 | Even if some Node.js module systems are able to resolve cyclic imports, 34 | cycle dependencies often reveal conceptions problems. 35 | 36 | **dag.js** enables effortless cyclic dependencies detection. 37 | 38 | ## Example simulating cyclic dependencies between JavaScript files 39 | 40 | See a very basic implementation of this concept in `index.js`. Be sure to 41 | build the `/dist` folder using the `build` script. 42 | 43 | ```bash 44 | $ npm run build 45 | $ node examples/circular-dependencies/index.js 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /examples/circular-dependencies/index.js: -------------------------------------------------------------------------------- 1 | import { DiGraph } from "../../dist/index.js"; 2 | 3 | const contentFileA = ` 4 | import FunctionB from 'B.js'; 5 | `; 6 | 7 | const contentFileB = ` 8 | import FunctionC from 'C.js'; 9 | `; 10 | 11 | const contentFileC = ` 12 | import FunctionA from 'A.js'; 13 | import FunctionD from 'D.js'; 14 | `; 15 | 16 | const contentFileD = ` 17 | import FunctionC from 'c.js'; 18 | `; 19 | 20 | function detectCyclicImports() { 21 | const diGraph = new DiGraph(); 22 | 23 | const fileA = { 24 | id: "A.js", 25 | adjacentTo: [], 26 | // metadata to simulate a real use case, this is not used to detect cycles 27 | body: { fileContent: contentFileA } 28 | }; 29 | const fileB = { 30 | id: "B.js", 31 | adjacentTo: [], 32 | // metadata to simulate a real use case, this is not used to detect cycles 33 | body: { fileContent: contentFileB } 34 | }; 35 | const fileC = { 36 | id: "C.js", 37 | adjacentTo: [], 38 | // metadata to simulate a real use case, this is not used to detect cycles 39 | body: { fileContent: contentFileC } 40 | }; 41 | const fileD = { 42 | id: "D.js", 43 | adjacentTo: [], 44 | // metadata to simulate a real use case, this is not used to detect cycles 45 | body: { fileContent: contentFileD } 46 | }; 47 | 48 | diGraph.addVertices(fileA, fileB, fileC, fileD); 49 | 50 | diGraph.addEdge({ from: fileA.id, to: fileC.id }); 51 | diGraph.addEdge({ from: fileB.id, to: fileA.id }); 52 | diGraph.addEdge({ from: fileC.id, to: fileB.id }); 53 | 54 | diGraph.addEdge({ from: fileC.id, to: fileD.id }); 55 | diGraph.addEdge({ from: fileD.id, to: fileC.id }); 56 | 57 | const cycles = diGraph.findCycles(); 58 | const prettyPrintCycles = cycles 59 | .map((cycle, index) => `Cycle n°${index + 1}: [${cycle.join(" --> ")}]`) 60 | .join("\n"); 61 | 62 | console.log("Has cycle dependencies?", cycles.length > 0); 63 | console.log(prettyPrintCycles); 64 | } 65 | 66 | detectCyclicImports(); 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "digraph-js", 3 | "version": "2.2.3", 4 | "type": "module", 5 | "main": "./dist/index.js", 6 | "exports": { 7 | "import": "./dist/index.js" 8 | }, 9 | "types": "dist/index.d.ts", 10 | "typings": "dist/index.d.ts", 11 | "description": "A dependency free library to create and traverse directed graphs", 12 | "author": { 13 | "name": "Antoine Coulon", 14 | "url": "https://github.com/antoine-coulon" 15 | }, 16 | "license": "MIT", 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/antoine-coulon/digraph-js.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/antoine-coulon/digraph-js/issues" 23 | }, 24 | "homepage": "https://github.com/antoine-coulon/digraph-js#readme", 25 | "engines": { 26 | "node": ">=16.0.0" 27 | }, 28 | "files": [ 29 | "dist" 30 | ], 31 | "keywords": [ 32 | "node", 33 | "javascript", 34 | "typescript", 35 | "nodejs", 36 | "node_modules", 37 | "tree", 38 | "npm", 39 | "graph", 40 | "visualization", 41 | "dependencies", 42 | "lightweight" 43 | ], 44 | "scripts": { 45 | "prestart": "pnpm run build", 46 | "start": "node dist/index.js", 47 | "build": "rimraf ./dist && tsc --project tsconfig.build.json", 48 | "test": "vitest run", 49 | "test:watch": "vitest", 50 | "benchmarks:webpack": "pnpm run build && cd benchmarks/webpack && node find-cycles.js", 51 | "prepublishOnly": "pnpm run test && pnpm run build" 52 | }, 53 | "devDependencies": { 54 | "@nodesecure/eslint-config": "^1.3.0", 55 | "@types/lodash.isequal": "^4.5.6", 56 | "@types/lodash.uniqwith": "^4.5.7", 57 | "@types/node": "^16.11.12", 58 | "eslint": "^8.4.1", 59 | "eslint-config-prettier": "^8.3.0", 60 | "eslint-import-resolver-typescript": "^2.5.0", 61 | "eslint-plugin-import": "^2.25.4", 62 | "eslint-plugin-prettier": "^4.0.0", 63 | "mocha": "^9.1.4", 64 | "prettier": "^2.5.1", 65 | "rimraf": "^3.0.2", 66 | "typescript": "^4.5.2", 67 | "vitest": "^0.28.3" 68 | }, 69 | "dependencies": { 70 | "lodash.isequal": "^4.5.0", 71 | "lodash.uniqwith": "^4.5.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@nodesecure/eslint-config': ^1.3.0 5 | '@types/lodash.isequal': ^4.5.6 6 | '@types/lodash.uniqwith': ^4.5.7 7 | '@types/node': ^16.11.12 8 | eslint: ^8.4.1 9 | eslint-config-prettier: ^8.3.0 10 | eslint-import-resolver-typescript: ^2.5.0 11 | eslint-plugin-import: ^2.25.4 12 | eslint-plugin-prettier: ^4.0.0 13 | lodash.isequal: ^4.5.0 14 | lodash.uniqwith: ^4.5.0 15 | mocha: ^9.1.4 16 | prettier: ^2.5.1 17 | rimraf: ^3.0.2 18 | typescript: ^4.5.2 19 | vitest: ^0.28.3 20 | 21 | dependencies: 22 | lodash.isequal: 4.5.0 23 | lodash.uniqwith: 4.5.0 24 | 25 | devDependencies: 26 | '@nodesecure/eslint-config': 1.3.1_typescript@4.5.5 27 | '@types/lodash.isequal': 4.5.6 28 | '@types/lodash.uniqwith': 4.5.7 29 | '@types/node': 16.11.25 30 | eslint: 8.9.0 31 | eslint-config-prettier: 8.3.0_eslint@8.9.0 32 | eslint-import-resolver-typescript: 2.5.0_cmtdok55f7srt3k3ux6kqq5jcq 33 | eslint-plugin-import: 2.25.4_mcbq4n2nanbsepnnliqrsfzxaa 34 | eslint-plugin-prettier: 4.0.0_a62cezdlw5ot3n4rmiou7w6jsi 35 | mocha: 9.2.0 36 | prettier: 2.5.1 37 | rimraf: 3.0.2 38 | typescript: 4.5.5 39 | vitest: 0.28.3 40 | 41 | packages: 42 | 43 | /@ampproject/remapping/2.1.2: 44 | resolution: {integrity: sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==} 45 | engines: {node: '>=6.0.0'} 46 | dependencies: 47 | '@jridgewell/trace-mapping': 0.3.4 48 | dev: true 49 | 50 | /@babel/code-frame/7.16.7: 51 | resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==} 52 | engines: {node: '>=6.9.0'} 53 | dependencies: 54 | '@babel/highlight': 7.16.10 55 | dev: true 56 | 57 | /@babel/compat-data/7.17.0: 58 | resolution: {integrity: sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==} 59 | engines: {node: '>=6.9.0'} 60 | dev: true 61 | 62 | /@babel/core/7.17.5: 63 | resolution: {integrity: sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==} 64 | engines: {node: '>=6.9.0'} 65 | dependencies: 66 | '@ampproject/remapping': 2.1.2 67 | '@babel/code-frame': 7.16.7 68 | '@babel/generator': 7.17.3 69 | '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.5 70 | '@babel/helper-module-transforms': 7.16.7 71 | '@babel/helpers': 7.17.2 72 | '@babel/parser': 7.17.3 73 | '@babel/template': 7.16.7 74 | '@babel/traverse': 7.17.3 75 | '@babel/types': 7.17.0 76 | convert-source-map: 1.8.0 77 | debug: 4.3.3 78 | gensync: 1.0.0-beta.2 79 | json5: 2.2.0 80 | semver: 6.3.0 81 | transitivePeerDependencies: 82 | - supports-color 83 | dev: true 84 | 85 | /@babel/eslint-parser/7.17.0_zed727toqyqv34g6wjqlfftrje: 86 | resolution: {integrity: sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==} 87 | engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} 88 | peerDependencies: 89 | '@babel/core': '>=7.11.0' 90 | eslint: ^7.5.0 || ^8.0.0 91 | dependencies: 92 | '@babel/core': 7.17.5 93 | eslint: 8.9.0 94 | eslint-scope: 5.1.1 95 | eslint-visitor-keys: 2.1.0 96 | semver: 6.3.0 97 | dev: true 98 | 99 | /@babel/generator/7.17.3: 100 | resolution: {integrity: sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==} 101 | engines: {node: '>=6.9.0'} 102 | dependencies: 103 | '@babel/types': 7.17.0 104 | jsesc: 2.5.2 105 | source-map: 0.5.7 106 | dev: true 107 | 108 | /@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.5: 109 | resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==} 110 | engines: {node: '>=6.9.0'} 111 | peerDependencies: 112 | '@babel/core': ^7.0.0 113 | dependencies: 114 | '@babel/compat-data': 7.17.0 115 | '@babel/core': 7.17.5 116 | '@babel/helper-validator-option': 7.16.7 117 | browserslist: 4.19.3 118 | semver: 6.3.0 119 | dev: true 120 | 121 | /@babel/helper-environment-visitor/7.16.7: 122 | resolution: {integrity: sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==} 123 | engines: {node: '>=6.9.0'} 124 | dependencies: 125 | '@babel/types': 7.17.0 126 | dev: true 127 | 128 | /@babel/helper-function-name/7.16.7: 129 | resolution: {integrity: sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==} 130 | engines: {node: '>=6.9.0'} 131 | dependencies: 132 | '@babel/helper-get-function-arity': 7.16.7 133 | '@babel/template': 7.16.7 134 | '@babel/types': 7.17.0 135 | dev: true 136 | 137 | /@babel/helper-get-function-arity/7.16.7: 138 | resolution: {integrity: sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==} 139 | engines: {node: '>=6.9.0'} 140 | dependencies: 141 | '@babel/types': 7.17.0 142 | dev: true 143 | 144 | /@babel/helper-hoist-variables/7.16.7: 145 | resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} 146 | engines: {node: '>=6.9.0'} 147 | dependencies: 148 | '@babel/types': 7.17.0 149 | dev: true 150 | 151 | /@babel/helper-module-imports/7.16.7: 152 | resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} 153 | engines: {node: '>=6.9.0'} 154 | dependencies: 155 | '@babel/types': 7.17.0 156 | dev: true 157 | 158 | /@babel/helper-module-transforms/7.16.7: 159 | resolution: {integrity: sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==} 160 | engines: {node: '>=6.9.0'} 161 | dependencies: 162 | '@babel/helper-environment-visitor': 7.16.7 163 | '@babel/helper-module-imports': 7.16.7 164 | '@babel/helper-simple-access': 7.16.7 165 | '@babel/helper-split-export-declaration': 7.16.7 166 | '@babel/helper-validator-identifier': 7.16.7 167 | '@babel/template': 7.16.7 168 | '@babel/traverse': 7.17.3 169 | '@babel/types': 7.17.0 170 | transitivePeerDependencies: 171 | - supports-color 172 | dev: true 173 | 174 | /@babel/helper-simple-access/7.16.7: 175 | resolution: {integrity: sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==} 176 | engines: {node: '>=6.9.0'} 177 | dependencies: 178 | '@babel/types': 7.17.0 179 | dev: true 180 | 181 | /@babel/helper-split-export-declaration/7.16.7: 182 | resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==} 183 | engines: {node: '>=6.9.0'} 184 | dependencies: 185 | '@babel/types': 7.17.0 186 | dev: true 187 | 188 | /@babel/helper-validator-identifier/7.16.7: 189 | resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==} 190 | engines: {node: '>=6.9.0'} 191 | dev: true 192 | 193 | /@babel/helper-validator-option/7.16.7: 194 | resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==} 195 | engines: {node: '>=6.9.0'} 196 | dev: true 197 | 198 | /@babel/helpers/7.17.2: 199 | resolution: {integrity: sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==} 200 | engines: {node: '>=6.9.0'} 201 | dependencies: 202 | '@babel/template': 7.16.7 203 | '@babel/traverse': 7.17.3 204 | '@babel/types': 7.17.0 205 | transitivePeerDependencies: 206 | - supports-color 207 | dev: true 208 | 209 | /@babel/highlight/7.16.10: 210 | resolution: {integrity: sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==} 211 | engines: {node: '>=6.9.0'} 212 | dependencies: 213 | '@babel/helper-validator-identifier': 7.16.7 214 | chalk: 2.4.2 215 | js-tokens: 4.0.0 216 | dev: true 217 | 218 | /@babel/parser/7.17.3: 219 | resolution: {integrity: sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==} 220 | engines: {node: '>=6.0.0'} 221 | hasBin: true 222 | dependencies: 223 | '@babel/types': 7.17.0 224 | dev: true 225 | 226 | /@babel/template/7.16.7: 227 | resolution: {integrity: sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==} 228 | engines: {node: '>=6.9.0'} 229 | dependencies: 230 | '@babel/code-frame': 7.16.7 231 | '@babel/parser': 7.17.3 232 | '@babel/types': 7.17.0 233 | dev: true 234 | 235 | /@babel/traverse/7.17.3: 236 | resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==} 237 | engines: {node: '>=6.9.0'} 238 | dependencies: 239 | '@babel/code-frame': 7.16.7 240 | '@babel/generator': 7.17.3 241 | '@babel/helper-environment-visitor': 7.16.7 242 | '@babel/helper-function-name': 7.16.7 243 | '@babel/helper-hoist-variables': 7.16.7 244 | '@babel/helper-split-export-declaration': 7.16.7 245 | '@babel/parser': 7.17.3 246 | '@babel/types': 7.17.0 247 | debug: 4.3.3 248 | globals: 11.12.0 249 | transitivePeerDependencies: 250 | - supports-color 251 | dev: true 252 | 253 | /@babel/types/7.17.0: 254 | resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} 255 | engines: {node: '>=6.9.0'} 256 | dependencies: 257 | '@babel/helper-validator-identifier': 7.16.7 258 | to-fast-properties: 2.0.0 259 | dev: true 260 | 261 | /@esbuild/android-arm/0.16.17: 262 | resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} 263 | engines: {node: '>=12'} 264 | cpu: [arm] 265 | os: [android] 266 | requiresBuild: true 267 | dev: true 268 | optional: true 269 | 270 | /@esbuild/android-arm64/0.16.17: 271 | resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} 272 | engines: {node: '>=12'} 273 | cpu: [arm64] 274 | os: [android] 275 | requiresBuild: true 276 | dev: true 277 | optional: true 278 | 279 | /@esbuild/android-x64/0.16.17: 280 | resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} 281 | engines: {node: '>=12'} 282 | cpu: [x64] 283 | os: [android] 284 | requiresBuild: true 285 | dev: true 286 | optional: true 287 | 288 | /@esbuild/darwin-arm64/0.16.17: 289 | resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} 290 | engines: {node: '>=12'} 291 | cpu: [arm64] 292 | os: [darwin] 293 | requiresBuild: true 294 | dev: true 295 | optional: true 296 | 297 | /@esbuild/darwin-x64/0.16.17: 298 | resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} 299 | engines: {node: '>=12'} 300 | cpu: [x64] 301 | os: [darwin] 302 | requiresBuild: true 303 | dev: true 304 | optional: true 305 | 306 | /@esbuild/freebsd-arm64/0.16.17: 307 | resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} 308 | engines: {node: '>=12'} 309 | cpu: [arm64] 310 | os: [freebsd] 311 | requiresBuild: true 312 | dev: true 313 | optional: true 314 | 315 | /@esbuild/freebsd-x64/0.16.17: 316 | resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} 317 | engines: {node: '>=12'} 318 | cpu: [x64] 319 | os: [freebsd] 320 | requiresBuild: true 321 | dev: true 322 | optional: true 323 | 324 | /@esbuild/linux-arm/0.16.17: 325 | resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} 326 | engines: {node: '>=12'} 327 | cpu: [arm] 328 | os: [linux] 329 | requiresBuild: true 330 | dev: true 331 | optional: true 332 | 333 | /@esbuild/linux-arm64/0.16.17: 334 | resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} 335 | engines: {node: '>=12'} 336 | cpu: [arm64] 337 | os: [linux] 338 | requiresBuild: true 339 | dev: true 340 | optional: true 341 | 342 | /@esbuild/linux-ia32/0.16.17: 343 | resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} 344 | engines: {node: '>=12'} 345 | cpu: [ia32] 346 | os: [linux] 347 | requiresBuild: true 348 | dev: true 349 | optional: true 350 | 351 | /@esbuild/linux-loong64/0.16.17: 352 | resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} 353 | engines: {node: '>=12'} 354 | cpu: [loong64] 355 | os: [linux] 356 | requiresBuild: true 357 | dev: true 358 | optional: true 359 | 360 | /@esbuild/linux-mips64el/0.16.17: 361 | resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} 362 | engines: {node: '>=12'} 363 | cpu: [mips64el] 364 | os: [linux] 365 | requiresBuild: true 366 | dev: true 367 | optional: true 368 | 369 | /@esbuild/linux-ppc64/0.16.17: 370 | resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} 371 | engines: {node: '>=12'} 372 | cpu: [ppc64] 373 | os: [linux] 374 | requiresBuild: true 375 | dev: true 376 | optional: true 377 | 378 | /@esbuild/linux-riscv64/0.16.17: 379 | resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} 380 | engines: {node: '>=12'} 381 | cpu: [riscv64] 382 | os: [linux] 383 | requiresBuild: true 384 | dev: true 385 | optional: true 386 | 387 | /@esbuild/linux-s390x/0.16.17: 388 | resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} 389 | engines: {node: '>=12'} 390 | cpu: [s390x] 391 | os: [linux] 392 | requiresBuild: true 393 | dev: true 394 | optional: true 395 | 396 | /@esbuild/linux-x64/0.16.17: 397 | resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} 398 | engines: {node: '>=12'} 399 | cpu: [x64] 400 | os: [linux] 401 | requiresBuild: true 402 | dev: true 403 | optional: true 404 | 405 | /@esbuild/netbsd-x64/0.16.17: 406 | resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} 407 | engines: {node: '>=12'} 408 | cpu: [x64] 409 | os: [netbsd] 410 | requiresBuild: true 411 | dev: true 412 | optional: true 413 | 414 | /@esbuild/openbsd-x64/0.16.17: 415 | resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} 416 | engines: {node: '>=12'} 417 | cpu: [x64] 418 | os: [openbsd] 419 | requiresBuild: true 420 | dev: true 421 | optional: true 422 | 423 | /@esbuild/sunos-x64/0.16.17: 424 | resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} 425 | engines: {node: '>=12'} 426 | cpu: [x64] 427 | os: [sunos] 428 | requiresBuild: true 429 | dev: true 430 | optional: true 431 | 432 | /@esbuild/win32-arm64/0.16.17: 433 | resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} 434 | engines: {node: '>=12'} 435 | cpu: [arm64] 436 | os: [win32] 437 | requiresBuild: true 438 | dev: true 439 | optional: true 440 | 441 | /@esbuild/win32-ia32/0.16.17: 442 | resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} 443 | engines: {node: '>=12'} 444 | cpu: [ia32] 445 | os: [win32] 446 | requiresBuild: true 447 | dev: true 448 | optional: true 449 | 450 | /@esbuild/win32-x64/0.16.17: 451 | resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} 452 | engines: {node: '>=12'} 453 | cpu: [x64] 454 | os: [win32] 455 | requiresBuild: true 456 | dev: true 457 | optional: true 458 | 459 | /@eslint/eslintrc/1.1.0: 460 | resolution: {integrity: sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==} 461 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 462 | dependencies: 463 | ajv: 6.12.6 464 | debug: 4.3.3 465 | espree: 9.3.1 466 | globals: 13.12.1 467 | ignore: 4.0.6 468 | import-fresh: 3.3.0 469 | js-yaml: 4.1.0 470 | minimatch: 3.1.2 471 | strip-json-comments: 3.1.1 472 | transitivePeerDependencies: 473 | - supports-color 474 | dev: true 475 | 476 | /@humanwhocodes/config-array/0.9.3: 477 | resolution: {integrity: sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==} 478 | engines: {node: '>=10.10.0'} 479 | dependencies: 480 | '@humanwhocodes/object-schema': 1.2.1 481 | debug: 4.3.3 482 | minimatch: 3.1.2 483 | transitivePeerDependencies: 484 | - supports-color 485 | dev: true 486 | 487 | /@humanwhocodes/object-schema/1.2.1: 488 | resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} 489 | dev: true 490 | 491 | /@jridgewell/resolve-uri/3.0.5: 492 | resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==} 493 | engines: {node: '>=6.0.0'} 494 | dev: true 495 | 496 | /@jridgewell/sourcemap-codec/1.4.11: 497 | resolution: {integrity: sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==} 498 | dev: true 499 | 500 | /@jridgewell/trace-mapping/0.3.4: 501 | resolution: {integrity: sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==} 502 | dependencies: 503 | '@jridgewell/resolve-uri': 3.0.5 504 | '@jridgewell/sourcemap-codec': 1.4.11 505 | dev: true 506 | 507 | /@nodelib/fs.scandir/2.1.5: 508 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 509 | engines: {node: '>= 8'} 510 | dependencies: 511 | '@nodelib/fs.stat': 2.0.5 512 | run-parallel: 1.2.0 513 | dev: true 514 | 515 | /@nodelib/fs.stat/2.0.5: 516 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 517 | engines: {node: '>= 8'} 518 | dev: true 519 | 520 | /@nodelib/fs.walk/1.2.8: 521 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 522 | engines: {node: '>= 8'} 523 | dependencies: 524 | '@nodelib/fs.scandir': 2.1.5 525 | fastq: 1.13.0 526 | dev: true 527 | 528 | /@nodesecure/eslint-config/1.3.1_typescript@4.5.5: 529 | resolution: {integrity: sha512-/JrNJj4mPSBuC4i++UjsCkTqAIBLqoAqrtRC9bgmHt8lN9wB/aOGiRIkiZR+LX/d+x7ZfasZ32odzJZSIo+vCw==} 530 | dependencies: 531 | '@babel/core': 7.17.5 532 | '@babel/eslint-parser': 7.17.0_zed727toqyqv34g6wjqlfftrje 533 | '@typescript-eslint/eslint-plugin': 5.12.0_yrt47g5utmuvsqpihtshtjlyw4 534 | '@typescript-eslint/parser': 5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy 535 | eslint: 8.9.0 536 | transitivePeerDependencies: 537 | - supports-color 538 | - typescript 539 | dev: true 540 | 541 | /@types/chai-subset/1.3.3: 542 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} 543 | dependencies: 544 | '@types/chai': 4.3.4 545 | dev: true 546 | 547 | /@types/chai/4.3.4: 548 | resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} 549 | dev: true 550 | 551 | /@types/json-schema/7.0.9: 552 | resolution: {integrity: sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==} 553 | dev: true 554 | 555 | /@types/json5/0.0.29: 556 | resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} 557 | dev: true 558 | 559 | /@types/lodash.isequal/4.5.6: 560 | resolution: {integrity: sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==} 561 | dependencies: 562 | '@types/lodash': 4.14.186 563 | dev: true 564 | 565 | /@types/lodash.uniqwith/4.5.7: 566 | resolution: {integrity: sha512-qg55yHtu/t9S7J1dEcipoD7Oqr5lwVJxwIicRCJ8yabBNv6T9DpIxhWuVIAUdewxYeEl0mNKSnZHQXTw7Owmug==} 567 | dependencies: 568 | '@types/lodash': 4.14.186 569 | dev: true 570 | 571 | /@types/lodash/4.14.186: 572 | resolution: {integrity: sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==} 573 | dev: true 574 | 575 | /@types/node/16.11.25: 576 | resolution: {integrity: sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ==} 577 | dev: true 578 | 579 | /@typescript-eslint/eslint-plugin/5.12.0_yrt47g5utmuvsqpihtshtjlyw4: 580 | resolution: {integrity: sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ==} 581 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 582 | peerDependencies: 583 | '@typescript-eslint/parser': ^5.0.0 584 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 585 | typescript: '*' 586 | peerDependenciesMeta: 587 | typescript: 588 | optional: true 589 | dependencies: 590 | '@typescript-eslint/parser': 5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy 591 | '@typescript-eslint/scope-manager': 5.12.0 592 | '@typescript-eslint/type-utils': 5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy 593 | '@typescript-eslint/utils': 5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy 594 | debug: 4.3.3 595 | eslint: 8.9.0 596 | functional-red-black-tree: 1.0.1 597 | ignore: 5.2.0 598 | regexpp: 3.2.0 599 | semver: 7.3.5 600 | tsutils: 3.21.0_typescript@4.5.5 601 | typescript: 4.5.5 602 | transitivePeerDependencies: 603 | - supports-color 604 | dev: true 605 | 606 | /@typescript-eslint/parser/5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy: 607 | resolution: {integrity: sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==} 608 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 609 | peerDependencies: 610 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 611 | typescript: '*' 612 | peerDependenciesMeta: 613 | typescript: 614 | optional: true 615 | dependencies: 616 | '@typescript-eslint/scope-manager': 5.12.0 617 | '@typescript-eslint/types': 5.12.0 618 | '@typescript-eslint/typescript-estree': 5.12.0_typescript@4.5.5 619 | debug: 4.3.3 620 | eslint: 8.9.0 621 | typescript: 4.5.5 622 | transitivePeerDependencies: 623 | - supports-color 624 | dev: true 625 | 626 | /@typescript-eslint/scope-manager/5.12.0: 627 | resolution: {integrity: sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==} 628 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 629 | dependencies: 630 | '@typescript-eslint/types': 5.12.0 631 | '@typescript-eslint/visitor-keys': 5.12.0 632 | dev: true 633 | 634 | /@typescript-eslint/type-utils/5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy: 635 | resolution: {integrity: sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q==} 636 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 637 | peerDependencies: 638 | eslint: '*' 639 | typescript: '*' 640 | peerDependenciesMeta: 641 | typescript: 642 | optional: true 643 | dependencies: 644 | '@typescript-eslint/utils': 5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy 645 | debug: 4.3.3 646 | eslint: 8.9.0 647 | tsutils: 3.21.0_typescript@4.5.5 648 | typescript: 4.5.5 649 | transitivePeerDependencies: 650 | - supports-color 651 | dev: true 652 | 653 | /@typescript-eslint/types/5.12.0: 654 | resolution: {integrity: sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==} 655 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 656 | dev: true 657 | 658 | /@typescript-eslint/typescript-estree/5.12.0_typescript@4.5.5: 659 | resolution: {integrity: sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==} 660 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 661 | peerDependencies: 662 | typescript: '*' 663 | peerDependenciesMeta: 664 | typescript: 665 | optional: true 666 | dependencies: 667 | '@typescript-eslint/types': 5.12.0 668 | '@typescript-eslint/visitor-keys': 5.12.0 669 | debug: 4.3.3 670 | globby: 11.1.0 671 | is-glob: 4.0.3 672 | semver: 7.3.5 673 | tsutils: 3.21.0_typescript@4.5.5 674 | typescript: 4.5.5 675 | transitivePeerDependencies: 676 | - supports-color 677 | dev: true 678 | 679 | /@typescript-eslint/utils/5.12.0_fhzmdq77bspfhxkfuzq4fbrdsy: 680 | resolution: {integrity: sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw==} 681 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 682 | peerDependencies: 683 | eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 684 | dependencies: 685 | '@types/json-schema': 7.0.9 686 | '@typescript-eslint/scope-manager': 5.12.0 687 | '@typescript-eslint/types': 5.12.0 688 | '@typescript-eslint/typescript-estree': 5.12.0_typescript@4.5.5 689 | eslint: 8.9.0 690 | eslint-scope: 5.1.1 691 | eslint-utils: 3.0.0_eslint@8.9.0 692 | transitivePeerDependencies: 693 | - supports-color 694 | - typescript 695 | dev: true 696 | 697 | /@typescript-eslint/visitor-keys/5.12.0: 698 | resolution: {integrity: sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==} 699 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 700 | dependencies: 701 | '@typescript-eslint/types': 5.12.0 702 | eslint-visitor-keys: 3.3.0 703 | dev: true 704 | 705 | /@ungap/promise-all-settled/1.1.2: 706 | resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} 707 | dev: true 708 | 709 | /@vitest/expect/0.28.3: 710 | resolution: {integrity: sha512-dnxllhfln88DOvpAK1fuI7/xHwRgTgR4wdxHldPaoTaBu6Rh9zK5b//v/cjTkhOfNP/AJ8evbNO8H7c3biwd1g==} 711 | dependencies: 712 | '@vitest/spy': 0.28.3 713 | '@vitest/utils': 0.28.3 714 | chai: 4.3.7 715 | dev: true 716 | 717 | /@vitest/runner/0.28.3: 718 | resolution: {integrity: sha512-P0qYbATaemy1midOLkw7qf8jraJszCoEvjQOSlseiXZyEDaZTZ50J+lolz2hWiWv6RwDu1iNseL9XLsG0Jm2KQ==} 719 | dependencies: 720 | '@vitest/utils': 0.28.3 721 | p-limit: 4.0.0 722 | pathe: 1.1.0 723 | dev: true 724 | 725 | /@vitest/spy/0.28.3: 726 | resolution: {integrity: sha512-jULA6suS6CCr9VZfr7/9x97pZ0hC55prnUNHNrg5/q16ARBY38RsjsfhuUXt6QOwvIN3BhSS0QqPzyh5Di8g6w==} 727 | dependencies: 728 | tinyspy: 1.0.2 729 | dev: true 730 | 731 | /@vitest/utils/0.28.3: 732 | resolution: {integrity: sha512-YHiQEHQqXyIbhDqETOJUKx9/psybF7SFFVCNfOvap0FvyUqbzTSDCa3S5lL4C0CLXkwVZttz9xknDoyHMguFRQ==} 733 | dependencies: 734 | cli-truncate: 3.1.0 735 | diff: 5.1.0 736 | loupe: 2.3.6 737 | picocolors: 1.0.0 738 | pretty-format: 27.5.1 739 | dev: true 740 | 741 | /acorn-jsx/5.3.2_acorn@8.7.0: 742 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 743 | peerDependencies: 744 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 745 | dependencies: 746 | acorn: 8.7.0 747 | dev: true 748 | 749 | /acorn-walk/8.2.0: 750 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 751 | engines: {node: '>=0.4.0'} 752 | dev: true 753 | 754 | /acorn/8.7.0: 755 | resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==} 756 | engines: {node: '>=0.4.0'} 757 | hasBin: true 758 | dev: true 759 | 760 | /acorn/8.8.2: 761 | resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} 762 | engines: {node: '>=0.4.0'} 763 | hasBin: true 764 | dev: true 765 | 766 | /ajv/6.12.6: 767 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 768 | dependencies: 769 | fast-deep-equal: 3.1.3 770 | fast-json-stable-stringify: 2.1.0 771 | json-schema-traverse: 0.4.1 772 | uri-js: 4.4.1 773 | dev: true 774 | 775 | /ansi-colors/4.1.1: 776 | resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} 777 | engines: {node: '>=6'} 778 | dev: true 779 | 780 | /ansi-regex/5.0.1: 781 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 782 | engines: {node: '>=8'} 783 | dev: true 784 | 785 | /ansi-regex/6.0.1: 786 | resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} 787 | engines: {node: '>=12'} 788 | dev: true 789 | 790 | /ansi-styles/3.2.1: 791 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 792 | engines: {node: '>=4'} 793 | dependencies: 794 | color-convert: 1.9.3 795 | dev: true 796 | 797 | /ansi-styles/4.3.0: 798 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 799 | engines: {node: '>=8'} 800 | dependencies: 801 | color-convert: 2.0.1 802 | dev: true 803 | 804 | /ansi-styles/5.2.0: 805 | resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} 806 | engines: {node: '>=10'} 807 | dev: true 808 | 809 | /ansi-styles/6.2.1: 810 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 811 | engines: {node: '>=12'} 812 | dev: true 813 | 814 | /anymatch/3.1.2: 815 | resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} 816 | engines: {node: '>= 8'} 817 | dependencies: 818 | normalize-path: 3.0.0 819 | picomatch: 2.3.1 820 | dev: true 821 | 822 | /argparse/2.0.1: 823 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 824 | dev: true 825 | 826 | /array-includes/3.1.4: 827 | resolution: {integrity: sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==} 828 | engines: {node: '>= 0.4'} 829 | dependencies: 830 | call-bind: 1.0.2 831 | define-properties: 1.1.3 832 | es-abstract: 1.19.1 833 | get-intrinsic: 1.1.1 834 | is-string: 1.0.7 835 | dev: true 836 | 837 | /array-union/2.1.0: 838 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 839 | engines: {node: '>=8'} 840 | dev: true 841 | 842 | /array.prototype.flat/1.2.5: 843 | resolution: {integrity: sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==} 844 | engines: {node: '>= 0.4'} 845 | dependencies: 846 | call-bind: 1.0.2 847 | define-properties: 1.1.3 848 | es-abstract: 1.19.1 849 | dev: true 850 | 851 | /assertion-error/1.1.0: 852 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 853 | dev: true 854 | 855 | /balanced-match/1.0.2: 856 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 857 | dev: true 858 | 859 | /binary-extensions/2.2.0: 860 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 861 | engines: {node: '>=8'} 862 | dev: true 863 | 864 | /brace-expansion/1.1.11: 865 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 866 | dependencies: 867 | balanced-match: 1.0.2 868 | concat-map: 0.0.1 869 | dev: true 870 | 871 | /braces/3.0.2: 872 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 873 | engines: {node: '>=8'} 874 | dependencies: 875 | fill-range: 7.0.1 876 | dev: true 877 | 878 | /browser-stdout/1.3.1: 879 | resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} 880 | dev: true 881 | 882 | /browserslist/4.19.3: 883 | resolution: {integrity: sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==} 884 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 885 | hasBin: true 886 | dependencies: 887 | caniuse-lite: 1.0.30001312 888 | electron-to-chromium: 1.4.71 889 | escalade: 3.1.1 890 | node-releases: 2.0.2 891 | picocolors: 1.0.0 892 | dev: true 893 | 894 | /buffer-from/1.1.2: 895 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 896 | dev: true 897 | 898 | /cac/6.7.14: 899 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 900 | engines: {node: '>=8'} 901 | dev: true 902 | 903 | /call-bind/1.0.2: 904 | resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} 905 | dependencies: 906 | function-bind: 1.1.1 907 | get-intrinsic: 1.1.1 908 | dev: true 909 | 910 | /callsites/3.1.0: 911 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 912 | engines: {node: '>=6'} 913 | dev: true 914 | 915 | /camelcase/6.3.0: 916 | resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} 917 | engines: {node: '>=10'} 918 | dev: true 919 | 920 | /caniuse-lite/1.0.30001312: 921 | resolution: {integrity: sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==} 922 | dev: true 923 | 924 | /chai/4.3.7: 925 | resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} 926 | engines: {node: '>=4'} 927 | dependencies: 928 | assertion-error: 1.1.0 929 | check-error: 1.0.2 930 | deep-eql: 4.1.3 931 | get-func-name: 2.0.0 932 | loupe: 2.3.6 933 | pathval: 1.1.1 934 | type-detect: 4.0.8 935 | dev: true 936 | 937 | /chalk/2.4.2: 938 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 939 | engines: {node: '>=4'} 940 | dependencies: 941 | ansi-styles: 3.2.1 942 | escape-string-regexp: 1.0.5 943 | supports-color: 5.5.0 944 | dev: true 945 | 946 | /chalk/4.1.2: 947 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 948 | engines: {node: '>=10'} 949 | dependencies: 950 | ansi-styles: 4.3.0 951 | supports-color: 7.2.0 952 | dev: true 953 | 954 | /check-error/1.0.2: 955 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} 956 | dev: true 957 | 958 | /chokidar/3.5.3: 959 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 960 | engines: {node: '>= 8.10.0'} 961 | dependencies: 962 | anymatch: 3.1.2 963 | braces: 3.0.2 964 | glob-parent: 5.1.2 965 | is-binary-path: 2.1.0 966 | is-glob: 4.0.3 967 | normalize-path: 3.0.0 968 | readdirp: 3.6.0 969 | optionalDependencies: 970 | fsevents: 2.3.2 971 | dev: true 972 | 973 | /cli-truncate/3.1.0: 974 | resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} 975 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 976 | dependencies: 977 | slice-ansi: 5.0.0 978 | string-width: 5.1.2 979 | dev: true 980 | 981 | /cliui/7.0.4: 982 | resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 983 | dependencies: 984 | string-width: 4.2.3 985 | strip-ansi: 6.0.1 986 | wrap-ansi: 7.0.0 987 | dev: true 988 | 989 | /color-convert/1.9.3: 990 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 991 | dependencies: 992 | color-name: 1.1.3 993 | dev: true 994 | 995 | /color-convert/2.0.1: 996 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 997 | engines: {node: '>=7.0.0'} 998 | dependencies: 999 | color-name: 1.1.4 1000 | dev: true 1001 | 1002 | /color-name/1.1.3: 1003 | resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=} 1004 | dev: true 1005 | 1006 | /color-name/1.1.4: 1007 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 1008 | dev: true 1009 | 1010 | /concat-map/0.0.1: 1011 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 1012 | dev: true 1013 | 1014 | /convert-source-map/1.8.0: 1015 | resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} 1016 | dependencies: 1017 | safe-buffer: 5.1.2 1018 | dev: true 1019 | 1020 | /cross-spawn/7.0.3: 1021 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 1022 | engines: {node: '>= 8'} 1023 | dependencies: 1024 | path-key: 3.1.1 1025 | shebang-command: 2.0.0 1026 | which: 2.0.2 1027 | dev: true 1028 | 1029 | /debug/2.6.9: 1030 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 1031 | peerDependencies: 1032 | supports-color: '*' 1033 | peerDependenciesMeta: 1034 | supports-color: 1035 | optional: true 1036 | dependencies: 1037 | ms: 2.0.0 1038 | dev: true 1039 | 1040 | /debug/3.2.7: 1041 | resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} 1042 | peerDependencies: 1043 | supports-color: '*' 1044 | peerDependenciesMeta: 1045 | supports-color: 1046 | optional: true 1047 | dependencies: 1048 | ms: 2.1.3 1049 | dev: true 1050 | 1051 | /debug/4.3.3: 1052 | resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} 1053 | engines: {node: '>=6.0'} 1054 | peerDependencies: 1055 | supports-color: '*' 1056 | peerDependenciesMeta: 1057 | supports-color: 1058 | optional: true 1059 | dependencies: 1060 | ms: 2.1.2 1061 | dev: true 1062 | 1063 | /debug/4.3.3_supports-color@8.1.1: 1064 | resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} 1065 | engines: {node: '>=6.0'} 1066 | peerDependencies: 1067 | supports-color: '*' 1068 | peerDependenciesMeta: 1069 | supports-color: 1070 | optional: true 1071 | dependencies: 1072 | ms: 2.1.2 1073 | supports-color: 8.1.1 1074 | dev: true 1075 | 1076 | /debug/4.3.4: 1077 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 1078 | engines: {node: '>=6.0'} 1079 | peerDependencies: 1080 | supports-color: '*' 1081 | peerDependenciesMeta: 1082 | supports-color: 1083 | optional: true 1084 | dependencies: 1085 | ms: 2.1.2 1086 | dev: true 1087 | 1088 | /decamelize/4.0.0: 1089 | resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} 1090 | engines: {node: '>=10'} 1091 | dev: true 1092 | 1093 | /deep-eql/4.1.3: 1094 | resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} 1095 | engines: {node: '>=6'} 1096 | dependencies: 1097 | type-detect: 4.0.8 1098 | dev: true 1099 | 1100 | /deep-is/0.1.4: 1101 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 1102 | dev: true 1103 | 1104 | /define-properties/1.1.3: 1105 | resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} 1106 | engines: {node: '>= 0.4'} 1107 | dependencies: 1108 | object-keys: 1.1.1 1109 | dev: true 1110 | 1111 | /diff/5.0.0: 1112 | resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} 1113 | engines: {node: '>=0.3.1'} 1114 | dev: true 1115 | 1116 | /diff/5.1.0: 1117 | resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} 1118 | engines: {node: '>=0.3.1'} 1119 | dev: true 1120 | 1121 | /dir-glob/3.0.1: 1122 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 1123 | engines: {node: '>=8'} 1124 | dependencies: 1125 | path-type: 4.0.0 1126 | dev: true 1127 | 1128 | /doctrine/2.1.0: 1129 | resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} 1130 | engines: {node: '>=0.10.0'} 1131 | dependencies: 1132 | esutils: 2.0.3 1133 | dev: true 1134 | 1135 | /doctrine/3.0.0: 1136 | resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 1137 | engines: {node: '>=6.0.0'} 1138 | dependencies: 1139 | esutils: 2.0.3 1140 | dev: true 1141 | 1142 | /eastasianwidth/0.2.0: 1143 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 1144 | dev: true 1145 | 1146 | /electron-to-chromium/1.4.71: 1147 | resolution: {integrity: sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw==} 1148 | dev: true 1149 | 1150 | /emoji-regex/8.0.0: 1151 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1152 | dev: true 1153 | 1154 | /emoji-regex/9.2.2: 1155 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 1156 | dev: true 1157 | 1158 | /es-abstract/1.19.1: 1159 | resolution: {integrity: sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==} 1160 | engines: {node: '>= 0.4'} 1161 | dependencies: 1162 | call-bind: 1.0.2 1163 | es-to-primitive: 1.2.1 1164 | function-bind: 1.1.1 1165 | get-intrinsic: 1.1.1 1166 | get-symbol-description: 1.0.0 1167 | has: 1.0.3 1168 | has-symbols: 1.0.2 1169 | internal-slot: 1.0.3 1170 | is-callable: 1.2.4 1171 | is-negative-zero: 2.0.2 1172 | is-regex: 1.1.4 1173 | is-shared-array-buffer: 1.0.1 1174 | is-string: 1.0.7 1175 | is-weakref: 1.0.2 1176 | object-inspect: 1.12.0 1177 | object-keys: 1.1.1 1178 | object.assign: 4.1.2 1179 | string.prototype.trimend: 1.0.4 1180 | string.prototype.trimstart: 1.0.4 1181 | unbox-primitive: 1.0.1 1182 | dev: true 1183 | 1184 | /es-to-primitive/1.2.1: 1185 | resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} 1186 | engines: {node: '>= 0.4'} 1187 | dependencies: 1188 | is-callable: 1.2.4 1189 | is-date-object: 1.0.5 1190 | is-symbol: 1.0.4 1191 | dev: true 1192 | 1193 | /esbuild/0.16.17: 1194 | resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} 1195 | engines: {node: '>=12'} 1196 | hasBin: true 1197 | requiresBuild: true 1198 | optionalDependencies: 1199 | '@esbuild/android-arm': 0.16.17 1200 | '@esbuild/android-arm64': 0.16.17 1201 | '@esbuild/android-x64': 0.16.17 1202 | '@esbuild/darwin-arm64': 0.16.17 1203 | '@esbuild/darwin-x64': 0.16.17 1204 | '@esbuild/freebsd-arm64': 0.16.17 1205 | '@esbuild/freebsd-x64': 0.16.17 1206 | '@esbuild/linux-arm': 0.16.17 1207 | '@esbuild/linux-arm64': 0.16.17 1208 | '@esbuild/linux-ia32': 0.16.17 1209 | '@esbuild/linux-loong64': 0.16.17 1210 | '@esbuild/linux-mips64el': 0.16.17 1211 | '@esbuild/linux-ppc64': 0.16.17 1212 | '@esbuild/linux-riscv64': 0.16.17 1213 | '@esbuild/linux-s390x': 0.16.17 1214 | '@esbuild/linux-x64': 0.16.17 1215 | '@esbuild/netbsd-x64': 0.16.17 1216 | '@esbuild/openbsd-x64': 0.16.17 1217 | '@esbuild/sunos-x64': 0.16.17 1218 | '@esbuild/win32-arm64': 0.16.17 1219 | '@esbuild/win32-ia32': 0.16.17 1220 | '@esbuild/win32-x64': 0.16.17 1221 | dev: true 1222 | 1223 | /escalade/3.1.1: 1224 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 1225 | engines: {node: '>=6'} 1226 | dev: true 1227 | 1228 | /escape-string-regexp/1.0.5: 1229 | resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} 1230 | engines: {node: '>=0.8.0'} 1231 | dev: true 1232 | 1233 | /escape-string-regexp/4.0.0: 1234 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 1235 | engines: {node: '>=10'} 1236 | dev: true 1237 | 1238 | /eslint-config-prettier/8.3.0_eslint@8.9.0: 1239 | resolution: {integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==} 1240 | hasBin: true 1241 | peerDependencies: 1242 | eslint: '>=7.0.0' 1243 | dependencies: 1244 | eslint: 8.9.0 1245 | dev: true 1246 | 1247 | /eslint-import-resolver-node/0.3.6: 1248 | resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} 1249 | dependencies: 1250 | debug: 3.2.7 1251 | resolve: 1.22.0 1252 | transitivePeerDependencies: 1253 | - supports-color 1254 | dev: true 1255 | 1256 | /eslint-import-resolver-typescript/2.5.0_cmtdok55f7srt3k3ux6kqq5jcq: 1257 | resolution: {integrity: sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==} 1258 | engines: {node: '>=4'} 1259 | peerDependencies: 1260 | eslint: '*' 1261 | eslint-plugin-import: '*' 1262 | dependencies: 1263 | debug: 4.3.3 1264 | eslint: 8.9.0 1265 | eslint-plugin-import: 2.25.4_mcbq4n2nanbsepnnliqrsfzxaa 1266 | glob: 7.2.0 1267 | is-glob: 4.0.3 1268 | resolve: 1.22.0 1269 | tsconfig-paths: 3.12.0 1270 | transitivePeerDependencies: 1271 | - supports-color 1272 | dev: true 1273 | 1274 | /eslint-module-utils/2.7.3_3jhql574si4htjp345d7sl4gwu: 1275 | resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==} 1276 | engines: {node: '>=4'} 1277 | peerDependencies: 1278 | '@typescript-eslint/parser': '*' 1279 | eslint-import-resolver-node: '*' 1280 | eslint-import-resolver-typescript: '*' 1281 | eslint-import-resolver-webpack: '*' 1282 | peerDependenciesMeta: 1283 | '@typescript-eslint/parser': 1284 | optional: true 1285 | eslint-import-resolver-node: 1286 | optional: true 1287 | eslint-import-resolver-typescript: 1288 | optional: true 1289 | eslint-import-resolver-webpack: 1290 | optional: true 1291 | dependencies: 1292 | debug: 3.2.7 1293 | eslint-import-resolver-node: 0.3.6 1294 | eslint-import-resolver-typescript: 2.5.0_cmtdok55f7srt3k3ux6kqq5jcq 1295 | find-up: 2.1.0 1296 | transitivePeerDependencies: 1297 | - supports-color 1298 | dev: true 1299 | 1300 | /eslint-plugin-import/2.25.4_mcbq4n2nanbsepnnliqrsfzxaa: 1301 | resolution: {integrity: sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==} 1302 | engines: {node: '>=4'} 1303 | peerDependencies: 1304 | '@typescript-eslint/parser': '*' 1305 | eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 1306 | peerDependenciesMeta: 1307 | '@typescript-eslint/parser': 1308 | optional: true 1309 | dependencies: 1310 | array-includes: 3.1.4 1311 | array.prototype.flat: 1.2.5 1312 | debug: 2.6.9 1313 | doctrine: 2.1.0 1314 | eslint: 8.9.0 1315 | eslint-import-resolver-node: 0.3.6 1316 | eslint-module-utils: 2.7.3_3jhql574si4htjp345d7sl4gwu 1317 | has: 1.0.3 1318 | is-core-module: 2.8.1 1319 | is-glob: 4.0.3 1320 | minimatch: 3.1.2 1321 | object.values: 1.1.5 1322 | resolve: 1.22.0 1323 | tsconfig-paths: 3.12.0 1324 | transitivePeerDependencies: 1325 | - eslint-import-resolver-typescript 1326 | - eslint-import-resolver-webpack 1327 | - supports-color 1328 | dev: true 1329 | 1330 | /eslint-plugin-prettier/4.0.0_a62cezdlw5ot3n4rmiou7w6jsi: 1331 | resolution: {integrity: sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==} 1332 | engines: {node: '>=6.0.0'} 1333 | peerDependencies: 1334 | eslint: '>=7.28.0' 1335 | eslint-config-prettier: '*' 1336 | prettier: '>=2.0.0' 1337 | peerDependenciesMeta: 1338 | eslint-config-prettier: 1339 | optional: true 1340 | dependencies: 1341 | eslint: 8.9.0 1342 | eslint-config-prettier: 8.3.0_eslint@8.9.0 1343 | prettier: 2.5.1 1344 | prettier-linter-helpers: 1.0.0 1345 | dev: true 1346 | 1347 | /eslint-scope/5.1.1: 1348 | resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 1349 | engines: {node: '>=8.0.0'} 1350 | dependencies: 1351 | esrecurse: 4.3.0 1352 | estraverse: 4.3.0 1353 | dev: true 1354 | 1355 | /eslint-scope/7.1.1: 1356 | resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} 1357 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1358 | dependencies: 1359 | esrecurse: 4.3.0 1360 | estraverse: 5.3.0 1361 | dev: true 1362 | 1363 | /eslint-utils/3.0.0_eslint@8.9.0: 1364 | resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} 1365 | engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} 1366 | peerDependencies: 1367 | eslint: '>=5' 1368 | dependencies: 1369 | eslint: 8.9.0 1370 | eslint-visitor-keys: 2.1.0 1371 | dev: true 1372 | 1373 | /eslint-visitor-keys/2.1.0: 1374 | resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} 1375 | engines: {node: '>=10'} 1376 | dev: true 1377 | 1378 | /eslint-visitor-keys/3.3.0: 1379 | resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} 1380 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1381 | dev: true 1382 | 1383 | /eslint/8.9.0: 1384 | resolution: {integrity: sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==} 1385 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1386 | hasBin: true 1387 | dependencies: 1388 | '@eslint/eslintrc': 1.1.0 1389 | '@humanwhocodes/config-array': 0.9.3 1390 | ajv: 6.12.6 1391 | chalk: 4.1.2 1392 | cross-spawn: 7.0.3 1393 | debug: 4.3.3 1394 | doctrine: 3.0.0 1395 | escape-string-regexp: 4.0.0 1396 | eslint-scope: 7.1.1 1397 | eslint-utils: 3.0.0_eslint@8.9.0 1398 | eslint-visitor-keys: 3.3.0 1399 | espree: 9.3.1 1400 | esquery: 1.4.0 1401 | esutils: 2.0.3 1402 | fast-deep-equal: 3.1.3 1403 | file-entry-cache: 6.0.1 1404 | functional-red-black-tree: 1.0.1 1405 | glob-parent: 6.0.2 1406 | globals: 13.12.1 1407 | ignore: 5.2.0 1408 | import-fresh: 3.3.0 1409 | imurmurhash: 0.1.4 1410 | is-glob: 4.0.3 1411 | js-yaml: 4.1.0 1412 | json-stable-stringify-without-jsonify: 1.0.1 1413 | levn: 0.4.1 1414 | lodash.merge: 4.6.2 1415 | minimatch: 3.1.2 1416 | natural-compare: 1.4.0 1417 | optionator: 0.9.1 1418 | regexpp: 3.2.0 1419 | strip-ansi: 6.0.1 1420 | strip-json-comments: 3.1.1 1421 | text-table: 0.2.0 1422 | v8-compile-cache: 2.3.0 1423 | transitivePeerDependencies: 1424 | - supports-color 1425 | dev: true 1426 | 1427 | /espree/9.3.1: 1428 | resolution: {integrity: sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==} 1429 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1430 | dependencies: 1431 | acorn: 8.7.0 1432 | acorn-jsx: 5.3.2_acorn@8.7.0 1433 | eslint-visitor-keys: 3.3.0 1434 | dev: true 1435 | 1436 | /esquery/1.4.0: 1437 | resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} 1438 | engines: {node: '>=0.10'} 1439 | dependencies: 1440 | estraverse: 5.3.0 1441 | dev: true 1442 | 1443 | /esrecurse/4.3.0: 1444 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 1445 | engines: {node: '>=4.0'} 1446 | dependencies: 1447 | estraverse: 5.3.0 1448 | dev: true 1449 | 1450 | /estraverse/4.3.0: 1451 | resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} 1452 | engines: {node: '>=4.0'} 1453 | dev: true 1454 | 1455 | /estraverse/5.3.0: 1456 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 1457 | engines: {node: '>=4.0'} 1458 | dev: true 1459 | 1460 | /esutils/2.0.3: 1461 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 1462 | engines: {node: '>=0.10.0'} 1463 | dev: true 1464 | 1465 | /fast-deep-equal/3.1.3: 1466 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 1467 | dev: true 1468 | 1469 | /fast-diff/1.2.0: 1470 | resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} 1471 | dev: true 1472 | 1473 | /fast-glob/3.2.11: 1474 | resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} 1475 | engines: {node: '>=8.6.0'} 1476 | dependencies: 1477 | '@nodelib/fs.stat': 2.0.5 1478 | '@nodelib/fs.walk': 1.2.8 1479 | glob-parent: 5.1.2 1480 | merge2: 1.4.1 1481 | micromatch: 4.0.4 1482 | dev: true 1483 | 1484 | /fast-json-stable-stringify/2.1.0: 1485 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 1486 | dev: true 1487 | 1488 | /fast-levenshtein/2.0.6: 1489 | resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} 1490 | dev: true 1491 | 1492 | /fastq/1.13.0: 1493 | resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} 1494 | dependencies: 1495 | reusify: 1.0.4 1496 | dev: true 1497 | 1498 | /file-entry-cache/6.0.1: 1499 | resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 1500 | engines: {node: ^10.12.0 || >=12.0.0} 1501 | dependencies: 1502 | flat-cache: 3.0.4 1503 | dev: true 1504 | 1505 | /fill-range/7.0.1: 1506 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 1507 | engines: {node: '>=8'} 1508 | dependencies: 1509 | to-regex-range: 5.0.1 1510 | dev: true 1511 | 1512 | /find-up/2.1.0: 1513 | resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=} 1514 | engines: {node: '>=4'} 1515 | dependencies: 1516 | locate-path: 2.0.0 1517 | dev: true 1518 | 1519 | /find-up/5.0.0: 1520 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 1521 | engines: {node: '>=10'} 1522 | dependencies: 1523 | locate-path: 6.0.0 1524 | path-exists: 4.0.0 1525 | dev: true 1526 | 1527 | /flat-cache/3.0.4: 1528 | resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} 1529 | engines: {node: ^10.12.0 || >=12.0.0} 1530 | dependencies: 1531 | flatted: 3.2.5 1532 | rimraf: 3.0.2 1533 | dev: true 1534 | 1535 | /flat/5.0.2: 1536 | resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} 1537 | hasBin: true 1538 | dev: true 1539 | 1540 | /flatted/3.2.5: 1541 | resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} 1542 | dev: true 1543 | 1544 | /fs.realpath/1.0.0: 1545 | resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} 1546 | dev: true 1547 | 1548 | /fsevents/2.3.2: 1549 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 1550 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1551 | os: [darwin] 1552 | requiresBuild: true 1553 | dev: true 1554 | optional: true 1555 | 1556 | /function-bind/1.1.1: 1557 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 1558 | dev: true 1559 | 1560 | /functional-red-black-tree/1.0.1: 1561 | resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} 1562 | dev: true 1563 | 1564 | /gensync/1.0.0-beta.2: 1565 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1566 | engines: {node: '>=6.9.0'} 1567 | dev: true 1568 | 1569 | /get-caller-file/2.0.5: 1570 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 1571 | engines: {node: 6.* || 8.* || >= 10.*} 1572 | dev: true 1573 | 1574 | /get-func-name/2.0.0: 1575 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} 1576 | dev: true 1577 | 1578 | /get-intrinsic/1.1.1: 1579 | resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} 1580 | dependencies: 1581 | function-bind: 1.1.1 1582 | has: 1.0.3 1583 | has-symbols: 1.0.2 1584 | dev: true 1585 | 1586 | /get-symbol-description/1.0.0: 1587 | resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} 1588 | engines: {node: '>= 0.4'} 1589 | dependencies: 1590 | call-bind: 1.0.2 1591 | get-intrinsic: 1.1.1 1592 | dev: true 1593 | 1594 | /glob-parent/5.1.2: 1595 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1596 | engines: {node: '>= 6'} 1597 | dependencies: 1598 | is-glob: 4.0.3 1599 | dev: true 1600 | 1601 | /glob-parent/6.0.2: 1602 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 1603 | engines: {node: '>=10.13.0'} 1604 | dependencies: 1605 | is-glob: 4.0.3 1606 | dev: true 1607 | 1608 | /glob/7.2.0: 1609 | resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} 1610 | dependencies: 1611 | fs.realpath: 1.0.0 1612 | inflight: 1.0.6 1613 | inherits: 2.0.4 1614 | minimatch: 3.0.4 1615 | once: 1.4.0 1616 | path-is-absolute: 1.0.1 1617 | dev: true 1618 | 1619 | /globals/11.12.0: 1620 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1621 | engines: {node: '>=4'} 1622 | dev: true 1623 | 1624 | /globals/13.12.1: 1625 | resolution: {integrity: sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==} 1626 | engines: {node: '>=8'} 1627 | dependencies: 1628 | type-fest: 0.20.2 1629 | dev: true 1630 | 1631 | /globby/11.1.0: 1632 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 1633 | engines: {node: '>=10'} 1634 | dependencies: 1635 | array-union: 2.1.0 1636 | dir-glob: 3.0.1 1637 | fast-glob: 3.2.11 1638 | ignore: 5.2.0 1639 | merge2: 1.4.1 1640 | slash: 3.0.0 1641 | dev: true 1642 | 1643 | /growl/1.10.5: 1644 | resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} 1645 | engines: {node: '>=4.x'} 1646 | dev: true 1647 | 1648 | /has-bigints/1.0.1: 1649 | resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} 1650 | dev: true 1651 | 1652 | /has-flag/3.0.0: 1653 | resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=} 1654 | engines: {node: '>=4'} 1655 | dev: true 1656 | 1657 | /has-flag/4.0.0: 1658 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1659 | engines: {node: '>=8'} 1660 | dev: true 1661 | 1662 | /has-symbols/1.0.2: 1663 | resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} 1664 | engines: {node: '>= 0.4'} 1665 | dev: true 1666 | 1667 | /has-tostringtag/1.0.0: 1668 | resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} 1669 | engines: {node: '>= 0.4'} 1670 | dependencies: 1671 | has-symbols: 1.0.2 1672 | dev: true 1673 | 1674 | /has/1.0.3: 1675 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 1676 | engines: {node: '>= 0.4.0'} 1677 | dependencies: 1678 | function-bind: 1.1.1 1679 | dev: true 1680 | 1681 | /he/1.2.0: 1682 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 1683 | hasBin: true 1684 | dev: true 1685 | 1686 | /ignore/4.0.6: 1687 | resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} 1688 | engines: {node: '>= 4'} 1689 | dev: true 1690 | 1691 | /ignore/5.2.0: 1692 | resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} 1693 | engines: {node: '>= 4'} 1694 | dev: true 1695 | 1696 | /import-fresh/3.3.0: 1697 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 1698 | engines: {node: '>=6'} 1699 | dependencies: 1700 | parent-module: 1.0.1 1701 | resolve-from: 4.0.0 1702 | dev: true 1703 | 1704 | /imurmurhash/0.1.4: 1705 | resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} 1706 | engines: {node: '>=0.8.19'} 1707 | dev: true 1708 | 1709 | /inflight/1.0.6: 1710 | resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} 1711 | dependencies: 1712 | once: 1.4.0 1713 | wrappy: 1.0.2 1714 | dev: true 1715 | 1716 | /inherits/2.0.4: 1717 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 1718 | dev: true 1719 | 1720 | /internal-slot/1.0.3: 1721 | resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} 1722 | engines: {node: '>= 0.4'} 1723 | dependencies: 1724 | get-intrinsic: 1.1.1 1725 | has: 1.0.3 1726 | side-channel: 1.0.4 1727 | dev: true 1728 | 1729 | /is-bigint/1.0.4: 1730 | resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} 1731 | dependencies: 1732 | has-bigints: 1.0.1 1733 | dev: true 1734 | 1735 | /is-binary-path/2.1.0: 1736 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 1737 | engines: {node: '>=8'} 1738 | dependencies: 1739 | binary-extensions: 2.2.0 1740 | dev: true 1741 | 1742 | /is-boolean-object/1.1.2: 1743 | resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} 1744 | engines: {node: '>= 0.4'} 1745 | dependencies: 1746 | call-bind: 1.0.2 1747 | has-tostringtag: 1.0.0 1748 | dev: true 1749 | 1750 | /is-callable/1.2.4: 1751 | resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} 1752 | engines: {node: '>= 0.4'} 1753 | dev: true 1754 | 1755 | /is-core-module/2.11.0: 1756 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} 1757 | dependencies: 1758 | has: 1.0.3 1759 | dev: true 1760 | 1761 | /is-core-module/2.8.1: 1762 | resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==} 1763 | dependencies: 1764 | has: 1.0.3 1765 | dev: true 1766 | 1767 | /is-date-object/1.0.5: 1768 | resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} 1769 | engines: {node: '>= 0.4'} 1770 | dependencies: 1771 | has-tostringtag: 1.0.0 1772 | dev: true 1773 | 1774 | /is-extglob/2.1.1: 1775 | resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} 1776 | engines: {node: '>=0.10.0'} 1777 | dev: true 1778 | 1779 | /is-fullwidth-code-point/3.0.0: 1780 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1781 | engines: {node: '>=8'} 1782 | dev: true 1783 | 1784 | /is-fullwidth-code-point/4.0.0: 1785 | resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} 1786 | engines: {node: '>=12'} 1787 | dev: true 1788 | 1789 | /is-glob/4.0.3: 1790 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1791 | engines: {node: '>=0.10.0'} 1792 | dependencies: 1793 | is-extglob: 2.1.1 1794 | dev: true 1795 | 1796 | /is-negative-zero/2.0.2: 1797 | resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} 1798 | engines: {node: '>= 0.4'} 1799 | dev: true 1800 | 1801 | /is-number-object/1.0.6: 1802 | resolution: {integrity: sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==} 1803 | engines: {node: '>= 0.4'} 1804 | dependencies: 1805 | has-tostringtag: 1.0.0 1806 | dev: true 1807 | 1808 | /is-number/7.0.0: 1809 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1810 | engines: {node: '>=0.12.0'} 1811 | dev: true 1812 | 1813 | /is-plain-obj/2.1.0: 1814 | resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} 1815 | engines: {node: '>=8'} 1816 | dev: true 1817 | 1818 | /is-regex/1.1.4: 1819 | resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} 1820 | engines: {node: '>= 0.4'} 1821 | dependencies: 1822 | call-bind: 1.0.2 1823 | has-tostringtag: 1.0.0 1824 | dev: true 1825 | 1826 | /is-shared-array-buffer/1.0.1: 1827 | resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==} 1828 | dev: true 1829 | 1830 | /is-string/1.0.7: 1831 | resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} 1832 | engines: {node: '>= 0.4'} 1833 | dependencies: 1834 | has-tostringtag: 1.0.0 1835 | dev: true 1836 | 1837 | /is-symbol/1.0.4: 1838 | resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} 1839 | engines: {node: '>= 0.4'} 1840 | dependencies: 1841 | has-symbols: 1.0.2 1842 | dev: true 1843 | 1844 | /is-unicode-supported/0.1.0: 1845 | resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} 1846 | engines: {node: '>=10'} 1847 | dev: true 1848 | 1849 | /is-weakref/1.0.2: 1850 | resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} 1851 | dependencies: 1852 | call-bind: 1.0.2 1853 | dev: true 1854 | 1855 | /isexe/2.0.0: 1856 | resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} 1857 | dev: true 1858 | 1859 | /js-tokens/4.0.0: 1860 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1861 | dev: true 1862 | 1863 | /js-yaml/4.1.0: 1864 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1865 | hasBin: true 1866 | dependencies: 1867 | argparse: 2.0.1 1868 | dev: true 1869 | 1870 | /jsesc/2.5.2: 1871 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 1872 | engines: {node: '>=4'} 1873 | hasBin: true 1874 | dev: true 1875 | 1876 | /json-schema-traverse/0.4.1: 1877 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1878 | dev: true 1879 | 1880 | /json-stable-stringify-without-jsonify/1.0.1: 1881 | resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} 1882 | dev: true 1883 | 1884 | /json5/1.0.1: 1885 | resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} 1886 | hasBin: true 1887 | dependencies: 1888 | minimist: 1.2.5 1889 | dev: true 1890 | 1891 | /json5/2.2.0: 1892 | resolution: {integrity: sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==} 1893 | engines: {node: '>=6'} 1894 | hasBin: true 1895 | dependencies: 1896 | minimist: 1.2.5 1897 | dev: true 1898 | 1899 | /jsonc-parser/3.2.0: 1900 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 1901 | dev: true 1902 | 1903 | /levn/0.4.1: 1904 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1905 | engines: {node: '>= 0.8.0'} 1906 | dependencies: 1907 | prelude-ls: 1.2.1 1908 | type-check: 0.4.0 1909 | dev: true 1910 | 1911 | /local-pkg/0.4.3: 1912 | resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} 1913 | engines: {node: '>=14'} 1914 | dev: true 1915 | 1916 | /locate-path/2.0.0: 1917 | resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=} 1918 | engines: {node: '>=4'} 1919 | dependencies: 1920 | p-locate: 2.0.0 1921 | path-exists: 3.0.0 1922 | dev: true 1923 | 1924 | /locate-path/6.0.0: 1925 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1926 | engines: {node: '>=10'} 1927 | dependencies: 1928 | p-locate: 5.0.0 1929 | dev: true 1930 | 1931 | /lodash.isequal/4.5.0: 1932 | resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} 1933 | dev: false 1934 | 1935 | /lodash.merge/4.6.2: 1936 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1937 | dev: true 1938 | 1939 | /lodash.uniqwith/4.5.0: 1940 | resolution: {integrity: sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==} 1941 | dev: false 1942 | 1943 | /log-symbols/4.1.0: 1944 | resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} 1945 | engines: {node: '>=10'} 1946 | dependencies: 1947 | chalk: 4.1.2 1948 | is-unicode-supported: 0.1.0 1949 | dev: true 1950 | 1951 | /loupe/2.3.6: 1952 | resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} 1953 | dependencies: 1954 | get-func-name: 2.0.0 1955 | dev: true 1956 | 1957 | /lru-cache/6.0.0: 1958 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 1959 | engines: {node: '>=10'} 1960 | dependencies: 1961 | yallist: 4.0.0 1962 | dev: true 1963 | 1964 | /merge2/1.4.1: 1965 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1966 | engines: {node: '>= 8'} 1967 | dev: true 1968 | 1969 | /micromatch/4.0.4: 1970 | resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} 1971 | engines: {node: '>=8.6'} 1972 | dependencies: 1973 | braces: 3.0.2 1974 | picomatch: 2.3.1 1975 | dev: true 1976 | 1977 | /minimatch/3.0.4: 1978 | resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} 1979 | dependencies: 1980 | brace-expansion: 1.1.11 1981 | dev: true 1982 | 1983 | /minimatch/3.1.2: 1984 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1985 | dependencies: 1986 | brace-expansion: 1.1.11 1987 | dev: true 1988 | 1989 | /minimist/1.2.5: 1990 | resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} 1991 | dev: true 1992 | 1993 | /mlly/1.1.0: 1994 | resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} 1995 | dependencies: 1996 | acorn: 8.8.2 1997 | pathe: 1.1.0 1998 | pkg-types: 1.0.1 1999 | ufo: 1.0.1 2000 | dev: true 2001 | 2002 | /mocha/9.2.0: 2003 | resolution: {integrity: sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==} 2004 | engines: {node: '>= 12.0.0'} 2005 | hasBin: true 2006 | dependencies: 2007 | '@ungap/promise-all-settled': 1.1.2 2008 | ansi-colors: 4.1.1 2009 | browser-stdout: 1.3.1 2010 | chokidar: 3.5.3 2011 | debug: 4.3.3_supports-color@8.1.1 2012 | diff: 5.0.0 2013 | escape-string-regexp: 4.0.0 2014 | find-up: 5.0.0 2015 | glob: 7.2.0 2016 | growl: 1.10.5 2017 | he: 1.2.0 2018 | js-yaml: 4.1.0 2019 | log-symbols: 4.1.0 2020 | minimatch: 3.0.4 2021 | ms: 2.1.3 2022 | nanoid: 3.2.0 2023 | serialize-javascript: 6.0.0 2024 | strip-json-comments: 3.1.1 2025 | supports-color: 8.1.1 2026 | which: 2.0.2 2027 | workerpool: 6.2.0 2028 | yargs: 16.2.0 2029 | yargs-parser: 20.2.4 2030 | yargs-unparser: 2.0.0 2031 | dev: true 2032 | 2033 | /ms/2.0.0: 2034 | resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} 2035 | dev: true 2036 | 2037 | /ms/2.1.2: 2038 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 2039 | dev: true 2040 | 2041 | /ms/2.1.3: 2042 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 2043 | dev: true 2044 | 2045 | /nanoid/3.2.0: 2046 | resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} 2047 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2048 | hasBin: true 2049 | dev: true 2050 | 2051 | /nanoid/3.3.4: 2052 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 2053 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2054 | hasBin: true 2055 | dev: true 2056 | 2057 | /natural-compare/1.4.0: 2058 | resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} 2059 | dev: true 2060 | 2061 | /node-releases/2.0.2: 2062 | resolution: {integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==} 2063 | dev: true 2064 | 2065 | /normalize-path/3.0.0: 2066 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 2067 | engines: {node: '>=0.10.0'} 2068 | dev: true 2069 | 2070 | /object-inspect/1.12.0: 2071 | resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} 2072 | dev: true 2073 | 2074 | /object-keys/1.1.1: 2075 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 2076 | engines: {node: '>= 0.4'} 2077 | dev: true 2078 | 2079 | /object.assign/4.1.2: 2080 | resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} 2081 | engines: {node: '>= 0.4'} 2082 | dependencies: 2083 | call-bind: 1.0.2 2084 | define-properties: 1.1.3 2085 | has-symbols: 1.0.2 2086 | object-keys: 1.1.1 2087 | dev: true 2088 | 2089 | /object.values/1.1.5: 2090 | resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} 2091 | engines: {node: '>= 0.4'} 2092 | dependencies: 2093 | call-bind: 1.0.2 2094 | define-properties: 1.1.3 2095 | es-abstract: 1.19.1 2096 | dev: true 2097 | 2098 | /once/1.4.0: 2099 | resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} 2100 | dependencies: 2101 | wrappy: 1.0.2 2102 | dev: true 2103 | 2104 | /optionator/0.9.1: 2105 | resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} 2106 | engines: {node: '>= 0.8.0'} 2107 | dependencies: 2108 | deep-is: 0.1.4 2109 | fast-levenshtein: 2.0.6 2110 | levn: 0.4.1 2111 | prelude-ls: 1.2.1 2112 | type-check: 0.4.0 2113 | word-wrap: 1.2.3 2114 | dev: true 2115 | 2116 | /p-limit/1.3.0: 2117 | resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} 2118 | engines: {node: '>=4'} 2119 | dependencies: 2120 | p-try: 1.0.0 2121 | dev: true 2122 | 2123 | /p-limit/3.1.0: 2124 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 2125 | engines: {node: '>=10'} 2126 | dependencies: 2127 | yocto-queue: 0.1.0 2128 | dev: true 2129 | 2130 | /p-limit/4.0.0: 2131 | resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} 2132 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 2133 | dependencies: 2134 | yocto-queue: 1.0.0 2135 | dev: true 2136 | 2137 | /p-locate/2.0.0: 2138 | resolution: {integrity: sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=} 2139 | engines: {node: '>=4'} 2140 | dependencies: 2141 | p-limit: 1.3.0 2142 | dev: true 2143 | 2144 | /p-locate/5.0.0: 2145 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 2146 | engines: {node: '>=10'} 2147 | dependencies: 2148 | p-limit: 3.1.0 2149 | dev: true 2150 | 2151 | /p-try/1.0.0: 2152 | resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=} 2153 | engines: {node: '>=4'} 2154 | dev: true 2155 | 2156 | /parent-module/1.0.1: 2157 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 2158 | engines: {node: '>=6'} 2159 | dependencies: 2160 | callsites: 3.1.0 2161 | dev: true 2162 | 2163 | /path-exists/3.0.0: 2164 | resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} 2165 | engines: {node: '>=4'} 2166 | dev: true 2167 | 2168 | /path-exists/4.0.0: 2169 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 2170 | engines: {node: '>=8'} 2171 | dev: true 2172 | 2173 | /path-is-absolute/1.0.1: 2174 | resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} 2175 | engines: {node: '>=0.10.0'} 2176 | dev: true 2177 | 2178 | /path-key/3.1.1: 2179 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 2180 | engines: {node: '>=8'} 2181 | dev: true 2182 | 2183 | /path-parse/1.0.7: 2184 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 2185 | dev: true 2186 | 2187 | /path-type/4.0.0: 2188 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 2189 | engines: {node: '>=8'} 2190 | dev: true 2191 | 2192 | /pathe/1.1.0: 2193 | resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} 2194 | dev: true 2195 | 2196 | /pathval/1.1.1: 2197 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 2198 | dev: true 2199 | 2200 | /picocolors/1.0.0: 2201 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 2202 | dev: true 2203 | 2204 | /picomatch/2.3.1: 2205 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 2206 | engines: {node: '>=8.6'} 2207 | dev: true 2208 | 2209 | /pkg-types/1.0.1: 2210 | resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==} 2211 | dependencies: 2212 | jsonc-parser: 3.2.0 2213 | mlly: 1.1.0 2214 | pathe: 1.1.0 2215 | dev: true 2216 | 2217 | /postcss/8.4.21: 2218 | resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} 2219 | engines: {node: ^10 || ^12 || >=14} 2220 | dependencies: 2221 | nanoid: 3.3.4 2222 | picocolors: 1.0.0 2223 | source-map-js: 1.0.2 2224 | dev: true 2225 | 2226 | /prelude-ls/1.2.1: 2227 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 2228 | engines: {node: '>= 0.8.0'} 2229 | dev: true 2230 | 2231 | /prettier-linter-helpers/1.0.0: 2232 | resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} 2233 | engines: {node: '>=6.0.0'} 2234 | dependencies: 2235 | fast-diff: 1.2.0 2236 | dev: true 2237 | 2238 | /prettier/2.5.1: 2239 | resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==} 2240 | engines: {node: '>=10.13.0'} 2241 | hasBin: true 2242 | dev: true 2243 | 2244 | /pretty-format/27.5.1: 2245 | resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} 2246 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 2247 | dependencies: 2248 | ansi-regex: 5.0.1 2249 | ansi-styles: 5.2.0 2250 | react-is: 17.0.2 2251 | dev: true 2252 | 2253 | /punycode/2.1.1: 2254 | resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} 2255 | engines: {node: '>=6'} 2256 | dev: true 2257 | 2258 | /queue-microtask/1.2.3: 2259 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2260 | dev: true 2261 | 2262 | /randombytes/2.1.0: 2263 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 2264 | dependencies: 2265 | safe-buffer: 5.2.1 2266 | dev: true 2267 | 2268 | /react-is/17.0.2: 2269 | resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} 2270 | dev: true 2271 | 2272 | /readdirp/3.6.0: 2273 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 2274 | engines: {node: '>=8.10.0'} 2275 | dependencies: 2276 | picomatch: 2.3.1 2277 | dev: true 2278 | 2279 | /regexpp/3.2.0: 2280 | resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} 2281 | engines: {node: '>=8'} 2282 | dev: true 2283 | 2284 | /require-directory/2.1.1: 2285 | resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} 2286 | engines: {node: '>=0.10.0'} 2287 | dev: true 2288 | 2289 | /resolve-from/4.0.0: 2290 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 2291 | engines: {node: '>=4'} 2292 | dev: true 2293 | 2294 | /resolve/1.22.0: 2295 | resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} 2296 | hasBin: true 2297 | dependencies: 2298 | is-core-module: 2.8.1 2299 | path-parse: 1.0.7 2300 | supports-preserve-symlinks-flag: 1.0.0 2301 | dev: true 2302 | 2303 | /resolve/1.22.1: 2304 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 2305 | hasBin: true 2306 | dependencies: 2307 | is-core-module: 2.11.0 2308 | path-parse: 1.0.7 2309 | supports-preserve-symlinks-flag: 1.0.0 2310 | dev: true 2311 | 2312 | /reusify/1.0.4: 2313 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 2314 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2315 | dev: true 2316 | 2317 | /rimraf/3.0.2: 2318 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 2319 | hasBin: true 2320 | dependencies: 2321 | glob: 7.2.0 2322 | dev: true 2323 | 2324 | /rollup/3.12.0: 2325 | resolution: {integrity: sha512-4MZ8kA2HNYahIjz63rzrMMRvDqQDeS9LoriJvMuV0V6zIGysP36e9t4yObUfwdT9h/szXoHQideICftcdZklWg==} 2326 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 2327 | hasBin: true 2328 | optionalDependencies: 2329 | fsevents: 2.3.2 2330 | dev: true 2331 | 2332 | /run-parallel/1.2.0: 2333 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 2334 | dependencies: 2335 | queue-microtask: 1.2.3 2336 | dev: true 2337 | 2338 | /safe-buffer/5.1.2: 2339 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 2340 | dev: true 2341 | 2342 | /safe-buffer/5.2.1: 2343 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 2344 | dev: true 2345 | 2346 | /semver/6.3.0: 2347 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 2348 | hasBin: true 2349 | dev: true 2350 | 2351 | /semver/7.3.5: 2352 | resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} 2353 | engines: {node: '>=10'} 2354 | hasBin: true 2355 | dependencies: 2356 | lru-cache: 6.0.0 2357 | dev: true 2358 | 2359 | /serialize-javascript/6.0.0: 2360 | resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} 2361 | dependencies: 2362 | randombytes: 2.1.0 2363 | dev: true 2364 | 2365 | /shebang-command/2.0.0: 2366 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 2367 | engines: {node: '>=8'} 2368 | dependencies: 2369 | shebang-regex: 3.0.0 2370 | dev: true 2371 | 2372 | /shebang-regex/3.0.0: 2373 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2374 | engines: {node: '>=8'} 2375 | dev: true 2376 | 2377 | /side-channel/1.0.4: 2378 | resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 2379 | dependencies: 2380 | call-bind: 1.0.2 2381 | get-intrinsic: 1.1.1 2382 | object-inspect: 1.12.0 2383 | dev: true 2384 | 2385 | /siginfo/2.0.0: 2386 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 2387 | dev: true 2388 | 2389 | /slash/3.0.0: 2390 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 2391 | engines: {node: '>=8'} 2392 | dev: true 2393 | 2394 | /slice-ansi/5.0.0: 2395 | resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} 2396 | engines: {node: '>=12'} 2397 | dependencies: 2398 | ansi-styles: 6.2.1 2399 | is-fullwidth-code-point: 4.0.0 2400 | dev: true 2401 | 2402 | /source-map-js/1.0.2: 2403 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 2404 | engines: {node: '>=0.10.0'} 2405 | dev: true 2406 | 2407 | /source-map-support/0.5.21: 2408 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 2409 | dependencies: 2410 | buffer-from: 1.1.2 2411 | source-map: 0.6.1 2412 | dev: true 2413 | 2414 | /source-map/0.5.7: 2415 | resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} 2416 | engines: {node: '>=0.10.0'} 2417 | dev: true 2418 | 2419 | /source-map/0.6.1: 2420 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 2421 | engines: {node: '>=0.10.0'} 2422 | dev: true 2423 | 2424 | /stackback/0.0.2: 2425 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 2426 | dev: true 2427 | 2428 | /std-env/3.3.1: 2429 | resolution: {integrity: sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==} 2430 | dev: true 2431 | 2432 | /string-width/4.2.3: 2433 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 2434 | engines: {node: '>=8'} 2435 | dependencies: 2436 | emoji-regex: 8.0.0 2437 | is-fullwidth-code-point: 3.0.0 2438 | strip-ansi: 6.0.1 2439 | dev: true 2440 | 2441 | /string-width/5.1.2: 2442 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 2443 | engines: {node: '>=12'} 2444 | dependencies: 2445 | eastasianwidth: 0.2.0 2446 | emoji-regex: 9.2.2 2447 | strip-ansi: 7.0.1 2448 | dev: true 2449 | 2450 | /string.prototype.trimend/1.0.4: 2451 | resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} 2452 | dependencies: 2453 | call-bind: 1.0.2 2454 | define-properties: 1.1.3 2455 | dev: true 2456 | 2457 | /string.prototype.trimstart/1.0.4: 2458 | resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} 2459 | dependencies: 2460 | call-bind: 1.0.2 2461 | define-properties: 1.1.3 2462 | dev: true 2463 | 2464 | /strip-ansi/6.0.1: 2465 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 2466 | engines: {node: '>=8'} 2467 | dependencies: 2468 | ansi-regex: 5.0.1 2469 | dev: true 2470 | 2471 | /strip-ansi/7.0.1: 2472 | resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} 2473 | engines: {node: '>=12'} 2474 | dependencies: 2475 | ansi-regex: 6.0.1 2476 | dev: true 2477 | 2478 | /strip-bom/3.0.0: 2479 | resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} 2480 | engines: {node: '>=4'} 2481 | dev: true 2482 | 2483 | /strip-json-comments/3.1.1: 2484 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2485 | engines: {node: '>=8'} 2486 | dev: true 2487 | 2488 | /strip-literal/1.0.0: 2489 | resolution: {integrity: sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==} 2490 | dependencies: 2491 | acorn: 8.8.2 2492 | dev: true 2493 | 2494 | /supports-color/5.5.0: 2495 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 2496 | engines: {node: '>=4'} 2497 | dependencies: 2498 | has-flag: 3.0.0 2499 | dev: true 2500 | 2501 | /supports-color/7.2.0: 2502 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 2503 | engines: {node: '>=8'} 2504 | dependencies: 2505 | has-flag: 4.0.0 2506 | dev: true 2507 | 2508 | /supports-color/8.1.1: 2509 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 2510 | engines: {node: '>=10'} 2511 | dependencies: 2512 | has-flag: 4.0.0 2513 | dev: true 2514 | 2515 | /supports-preserve-symlinks-flag/1.0.0: 2516 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 2517 | engines: {node: '>= 0.4'} 2518 | dev: true 2519 | 2520 | /text-table/0.2.0: 2521 | resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} 2522 | dev: true 2523 | 2524 | /tinybench/2.3.1: 2525 | resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} 2526 | dev: true 2527 | 2528 | /tinypool/0.3.1: 2529 | resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} 2530 | engines: {node: '>=14.0.0'} 2531 | dev: true 2532 | 2533 | /tinyspy/1.0.2: 2534 | resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} 2535 | engines: {node: '>=14.0.0'} 2536 | dev: true 2537 | 2538 | /to-fast-properties/2.0.0: 2539 | resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} 2540 | engines: {node: '>=4'} 2541 | dev: true 2542 | 2543 | /to-regex-range/5.0.1: 2544 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2545 | engines: {node: '>=8.0'} 2546 | dependencies: 2547 | is-number: 7.0.0 2548 | dev: true 2549 | 2550 | /tsconfig-paths/3.12.0: 2551 | resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} 2552 | dependencies: 2553 | '@types/json5': 0.0.29 2554 | json5: 1.0.1 2555 | minimist: 1.2.5 2556 | strip-bom: 3.0.0 2557 | dev: true 2558 | 2559 | /tslib/1.14.1: 2560 | resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} 2561 | dev: true 2562 | 2563 | /tsutils/3.21.0_typescript@4.5.5: 2564 | resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} 2565 | engines: {node: '>= 6'} 2566 | peerDependencies: 2567 | typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' 2568 | dependencies: 2569 | tslib: 1.14.1 2570 | typescript: 4.5.5 2571 | dev: true 2572 | 2573 | /type-check/0.4.0: 2574 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 2575 | engines: {node: '>= 0.8.0'} 2576 | dependencies: 2577 | prelude-ls: 1.2.1 2578 | dev: true 2579 | 2580 | /type-detect/4.0.8: 2581 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 2582 | engines: {node: '>=4'} 2583 | dev: true 2584 | 2585 | /type-fest/0.20.2: 2586 | resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 2587 | engines: {node: '>=10'} 2588 | dev: true 2589 | 2590 | /typescript/4.5.5: 2591 | resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==} 2592 | engines: {node: '>=4.2.0'} 2593 | hasBin: true 2594 | dev: true 2595 | 2596 | /ufo/1.0.1: 2597 | resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==} 2598 | dev: true 2599 | 2600 | /unbox-primitive/1.0.1: 2601 | resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} 2602 | dependencies: 2603 | function-bind: 1.1.1 2604 | has-bigints: 1.0.1 2605 | has-symbols: 1.0.2 2606 | which-boxed-primitive: 1.0.2 2607 | dev: true 2608 | 2609 | /uri-js/4.4.1: 2610 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 2611 | dependencies: 2612 | punycode: 2.1.1 2613 | dev: true 2614 | 2615 | /v8-compile-cache/2.3.0: 2616 | resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} 2617 | dev: true 2618 | 2619 | /vite-node/0.28.3_@types+node@16.11.25: 2620 | resolution: {integrity: sha512-uJJAOkgVwdfCX8PUQhqLyDOpkBS5+j+FdbsXoPVPDlvVjRkb/W/mLYQPSL6J+t8R0UV8tJSe8c9VyxVQNsDSyg==} 2621 | engines: {node: '>=v14.16.0'} 2622 | hasBin: true 2623 | dependencies: 2624 | cac: 6.7.14 2625 | debug: 4.3.4 2626 | mlly: 1.1.0 2627 | pathe: 1.1.0 2628 | picocolors: 1.0.0 2629 | source-map: 0.6.1 2630 | source-map-support: 0.5.21 2631 | vite: 4.0.4_@types+node@16.11.25 2632 | transitivePeerDependencies: 2633 | - '@types/node' 2634 | - less 2635 | - sass 2636 | - stylus 2637 | - sugarss 2638 | - supports-color 2639 | - terser 2640 | dev: true 2641 | 2642 | /vite/4.0.4_@types+node@16.11.25: 2643 | resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} 2644 | engines: {node: ^14.18.0 || >=16.0.0} 2645 | hasBin: true 2646 | peerDependencies: 2647 | '@types/node': '>= 14' 2648 | less: '*' 2649 | sass: '*' 2650 | stylus: '*' 2651 | sugarss: '*' 2652 | terser: ^5.4.0 2653 | peerDependenciesMeta: 2654 | '@types/node': 2655 | optional: true 2656 | less: 2657 | optional: true 2658 | sass: 2659 | optional: true 2660 | stylus: 2661 | optional: true 2662 | sugarss: 2663 | optional: true 2664 | terser: 2665 | optional: true 2666 | dependencies: 2667 | '@types/node': 16.11.25 2668 | esbuild: 0.16.17 2669 | postcss: 8.4.21 2670 | resolve: 1.22.1 2671 | rollup: 3.12.0 2672 | optionalDependencies: 2673 | fsevents: 2.3.2 2674 | dev: true 2675 | 2676 | /vitest/0.28.3: 2677 | resolution: {integrity: sha512-N41VPNf3VGJlWQizGvl1P5MGyv3ZZA2Zvh+2V8L6tYBAAuqqDK4zExunT1Cdb6dGfZ4gr+IMrnG8d4Z6j9ctPw==} 2678 | engines: {node: '>=v14.16.0'} 2679 | hasBin: true 2680 | peerDependencies: 2681 | '@edge-runtime/vm': '*' 2682 | '@vitest/browser': '*' 2683 | '@vitest/ui': '*' 2684 | happy-dom: '*' 2685 | jsdom: '*' 2686 | peerDependenciesMeta: 2687 | '@edge-runtime/vm': 2688 | optional: true 2689 | '@vitest/browser': 2690 | optional: true 2691 | '@vitest/ui': 2692 | optional: true 2693 | happy-dom: 2694 | optional: true 2695 | jsdom: 2696 | optional: true 2697 | dependencies: 2698 | '@types/chai': 4.3.4 2699 | '@types/chai-subset': 1.3.3 2700 | '@types/node': 16.11.25 2701 | '@vitest/expect': 0.28.3 2702 | '@vitest/runner': 0.28.3 2703 | '@vitest/spy': 0.28.3 2704 | '@vitest/utils': 0.28.3 2705 | acorn: 8.8.2 2706 | acorn-walk: 8.2.0 2707 | cac: 6.7.14 2708 | chai: 4.3.7 2709 | debug: 4.3.4 2710 | local-pkg: 0.4.3 2711 | pathe: 1.1.0 2712 | picocolors: 1.0.0 2713 | source-map: 0.6.1 2714 | std-env: 3.3.1 2715 | strip-literal: 1.0.0 2716 | tinybench: 2.3.1 2717 | tinypool: 0.3.1 2718 | tinyspy: 1.0.2 2719 | vite: 4.0.4_@types+node@16.11.25 2720 | vite-node: 0.28.3_@types+node@16.11.25 2721 | why-is-node-running: 2.2.2 2722 | transitivePeerDependencies: 2723 | - less 2724 | - sass 2725 | - stylus 2726 | - sugarss 2727 | - supports-color 2728 | - terser 2729 | dev: true 2730 | 2731 | /which-boxed-primitive/1.0.2: 2732 | resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 2733 | dependencies: 2734 | is-bigint: 1.0.4 2735 | is-boolean-object: 1.1.2 2736 | is-number-object: 1.0.6 2737 | is-string: 1.0.7 2738 | is-symbol: 1.0.4 2739 | dev: true 2740 | 2741 | /which/2.0.2: 2742 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 2743 | engines: {node: '>= 8'} 2744 | hasBin: true 2745 | dependencies: 2746 | isexe: 2.0.0 2747 | dev: true 2748 | 2749 | /why-is-node-running/2.2.2: 2750 | resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} 2751 | engines: {node: '>=8'} 2752 | hasBin: true 2753 | dependencies: 2754 | siginfo: 2.0.0 2755 | stackback: 0.0.2 2756 | dev: true 2757 | 2758 | /word-wrap/1.2.3: 2759 | resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} 2760 | engines: {node: '>=0.10.0'} 2761 | dev: true 2762 | 2763 | /workerpool/6.2.0: 2764 | resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} 2765 | dev: true 2766 | 2767 | /wrap-ansi/7.0.0: 2768 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 2769 | engines: {node: '>=10'} 2770 | dependencies: 2771 | ansi-styles: 4.3.0 2772 | string-width: 4.2.3 2773 | strip-ansi: 6.0.1 2774 | dev: true 2775 | 2776 | /wrappy/1.0.2: 2777 | resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} 2778 | dev: true 2779 | 2780 | /y18n/5.0.8: 2781 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 2782 | engines: {node: '>=10'} 2783 | dev: true 2784 | 2785 | /yallist/4.0.0: 2786 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 2787 | dev: true 2788 | 2789 | /yargs-parser/20.2.4: 2790 | resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} 2791 | engines: {node: '>=10'} 2792 | dev: true 2793 | 2794 | /yargs-unparser/2.0.0: 2795 | resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} 2796 | engines: {node: '>=10'} 2797 | dependencies: 2798 | camelcase: 6.3.0 2799 | decamelize: 4.0.0 2800 | flat: 5.0.2 2801 | is-plain-obj: 2.1.0 2802 | dev: true 2803 | 2804 | /yargs/16.2.0: 2805 | resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 2806 | engines: {node: '>=10'} 2807 | dependencies: 2808 | cliui: 7.0.4 2809 | escalade: 3.1.1 2810 | get-caller-file: 2.0.5 2811 | require-directory: 2.1.1 2812 | string-width: 4.2.3 2813 | y18n: 5.0.8 2814 | yargs-parser: 20.2.4 2815 | dev: true 2816 | 2817 | /yocto-queue/0.1.0: 2818 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 2819 | engines: {node: '>=10'} 2820 | dev: true 2821 | 2822 | /yocto-queue/1.0.0: 2823 | resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} 2824 | engines: {node: '>=12.20'} 2825 | dev: true 2826 | -------------------------------------------------------------------------------- /src/digraph.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-lines */ 2 | /* eslint-disable max-depth */ 3 | /* eslint-disable no-inline-comments */ 4 | /* eslint-disable line-comment-position */ 5 | /* eslint-disable max-nested-callbacks */ 6 | 7 | import { describe, expect, it } from "vitest"; 8 | 9 | import { DiGraph } from "./digraph.js"; 10 | import { VertexDefinition, VertexId } from "./vertex.js"; 11 | 12 | type Vertex = VertexDefinition; 13 | 14 | function* createRawVertices(...ids: VertexId[]): Generator { 15 | for (const id of ids) { 16 | yield { 17 | id, 18 | adjacentTo: [], 19 | body: {} 20 | }; 21 | } 22 | } 23 | 24 | describe("Directed Graph Implementation", () => { 25 | describe("When managing vertices in the graph", () => { 26 | describe("When adding vertices", () => { 27 | it("should add the given vertex to the graph", () => { 28 | const digraph = new DiGraph(); 29 | const [vertexA] = [...createRawVertices("a")]; 30 | 31 | digraph.addVertex(vertexA); 32 | 33 | expect(digraph.hasVertex(vertexA.id)).to.equal(true); 34 | }); 35 | 36 | it("should not add vertices already in the graph", () => { 37 | const digraph = new DiGraph(); 38 | 39 | function expectGraphStructure() { 40 | expect(Object.keys(digraph.toDict()).length).to.equal(3); 41 | expect(digraph.toDict()).to.deep.equal({ 42 | a: { id: "a", adjacentTo: [], body: {} }, 43 | b: { id: "b", adjacentTo: [], body: {} }, 44 | c: { id: "c", adjacentTo: [], body: {} } 45 | }); 46 | } 47 | 48 | const [vertexA, vertexB, vertexBis, vertexC] = [ 49 | ...createRawVertices("a", "b", "b", "c") 50 | ]; 51 | 52 | digraph.addVertices(vertexA, vertexB, vertexBis, vertexC); 53 | 54 | expectGraphStructure(); 55 | 56 | const duplicatedVertexB: Vertex = { 57 | id: "b", 58 | adjacentTo: [], 59 | body: { someComponent: "x" } 60 | }; 61 | 62 | digraph.addVertices(duplicatedVertexB); 63 | 64 | expectGraphStructure(); 65 | 66 | digraph.addVertex(duplicatedVertexB); 67 | 68 | expectGraphStructure(); 69 | }); 70 | }); 71 | 72 | describe("When modifying vertices bodies", () => { 73 | describe("When updating vertices", () => { 74 | it("should only update one vertex with no dependencies", () => { 75 | const digraph = new DiGraph(); 76 | const vertexA: Vertex = { id: "a", adjacentTo: [], body: {} }; 77 | const vertexE: Vertex = { 78 | id: "e", 79 | adjacentTo: [vertexA.id], 80 | body: {} 81 | }; 82 | const vertexB: Vertex = { id: "b", adjacentTo: [], body: {} }; 83 | 84 | digraph.addVertices(vertexA, vertexB, vertexE); 85 | digraph.updateVertexBody(vertexB.id, { 86 | brandNewProp: "newValue" 87 | }); 88 | 89 | expect(vertexB.body).to.deep.equal({ 90 | brandNewProp: "newValue" 91 | }); 92 | expect(vertexA.body).to.deep.equal({}); 93 | expect(vertexE.body).to.deep.equal({}); 94 | 95 | digraph.updateVertexBody(vertexB.id, { 96 | otherProp: [] 97 | }); 98 | expect(vertexB.body).to.deep.equal({ 99 | otherProp: [] 100 | }); 101 | }); 102 | }); 103 | 104 | describe("When merging vertices", () => { 105 | describe("When the initial body is empty", () => { 106 | it("should merge add the new values to the vertex", () => { 107 | const digraph = new DiGraph(); 108 | const vertexA: Vertex = { id: "a", adjacentTo: [], body: {} }; 109 | 110 | digraph.addVertex(vertexA); 111 | digraph.mergeVertexBody(vertexA.id, (body) => { 112 | body.brandNewProp = "newValue"; 113 | }); 114 | expect(vertexA.body).to.deep.equal({ 115 | brandNewProp: "newValue" 116 | }); 117 | }); 118 | }); 119 | 120 | describe("When the new body contains new properties", () => { 121 | it("should merge new values with old values", () => { 122 | const digraph = new DiGraph(); 123 | const vertexA: Vertex = { 124 | id: "a", 125 | adjacentTo: [], 126 | body: { 127 | prop1: { 128 | a: 2 129 | } 130 | } 131 | }; 132 | 133 | digraph.addVertex(vertexA); 134 | digraph.mergeVertexBody(vertexA.id, (body) => { 135 | body.brandNewProp = "newValue"; 136 | }); 137 | expect(vertexA.body).to.deep.equal({ 138 | prop1: { 139 | a: 2 140 | }, 141 | brandNewProp: "newValue" 142 | }); 143 | }); 144 | }); 145 | 146 | describe("When then new body contains same properties with new values", () => { 147 | it("should merge old properties with new values", () => { 148 | const digraph = new DiGraph(); 149 | const vertexA: Vertex = { 150 | id: "a", 151 | adjacentTo: [], 152 | body: { 153 | list: ["a", "b"] 154 | } 155 | }; 156 | 157 | digraph.addVertex(vertexA); 158 | digraph.mergeVertexBody(vertexA.id, (vertex) => { 159 | vertex.list = [...(vertex.list as any[]), "c"]; 160 | }); 161 | expect(vertexA.body).to.deep.equal({ 162 | list: ["a", "b", "c"] 163 | }); 164 | }); 165 | }); 166 | }); 167 | }); 168 | 169 | describe("When deleting vertices", () => { 170 | describe("When no vertices depends on the deleted one", () => { 171 | it("should only delete the isolated vertex", () => { 172 | const digraph = new DiGraph(); 173 | const [vertexA, vertexB, vertexC, vertexD] = [ 174 | ...createRawVertices("a", "b", "c", "d") 175 | ]; 176 | 177 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 178 | 179 | expect(digraph.toDict()).to.deep.equal({ 180 | a: vertexA, 181 | b: vertexB, 182 | c: vertexC, 183 | d: vertexD 184 | }); 185 | 186 | digraph.deleteVertex(vertexC.id); 187 | 188 | expect(digraph.toDict()).to.deep.equal({ 189 | a: vertexA, 190 | b: vertexB, 191 | d: vertexD 192 | }); 193 | }); 194 | }); 195 | 196 | describe("When one or many vertices directly depends on the deleted one", () => { 197 | it("should delete the vertex and update the adjacency list of vertices directly depending on it", () => { 198 | const digraph = new DiGraph(); 199 | const [vertexA, vertexB, vertexC, vertexD] = [ 200 | ...createRawVertices("a", "b", "c", "d") 201 | ]; 202 | 203 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 204 | digraph.addEdge({ from: vertexA.id, to: vertexD.id }); 205 | digraph.addEdge({ from: vertexB.id, to: vertexD.id }); 206 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 207 | digraph.addEdge({ from: vertexC.id, to: vertexA.id }); 208 | 209 | expect(digraph.toDict()).to.deep.equal({ 210 | a: { ...vertexA, adjacentTo: [vertexD.id] }, 211 | b: { ...vertexB, adjacentTo: [vertexD.id, vertexC.id] }, 212 | c: { ...vertexC, adjacentTo: [vertexA.id] }, 213 | d: { ...vertexD, adjacentTo: [] } 214 | }); 215 | 216 | digraph.deleteVertex(vertexD.id); 217 | 218 | expect(digraph.toDict()).to.deep.equal({ 219 | a: { ...vertexA, adjacentTo: [] }, 220 | b: { ...vertexB, adjacentTo: [vertexC.id] }, 221 | c: { ...vertexC, adjacentTo: [vertexA.id] } 222 | }); 223 | }); 224 | }); 225 | }); 226 | }); 227 | 228 | describe("When managing edges in the graph", () => { 229 | describe("When adding edges to the graph", () => { 230 | it("should add edges between vertices", () => { 231 | const digraph = new DiGraph(); 232 | const [vertexA, vertexB, vertexC] = [ 233 | ...createRawVertices("a", "b", "c") 234 | ]; 235 | 236 | digraph.addVertices(vertexA, vertexB, vertexC); 237 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 238 | 239 | expect(vertexB.adjacentTo).deep.equal([vertexA.id]); 240 | 241 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 242 | digraph.addVertices(vertexA, vertexB, vertexC); 243 | 244 | expect(vertexB.adjacentTo).deep.equal([vertexA.id, vertexC.id]); 245 | }); 246 | 247 | it("should only add edges for vertices already added in the graph", () => { 248 | const digraph = new DiGraph(); 249 | const [vertexA, vertexB] = [...createRawVertices("a", "b")]; 250 | 251 | digraph.addVertices(vertexA); 252 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 253 | 254 | expect(vertexA.adjacentTo).deep.equal([]); 255 | expect(digraph.toDict()).to.deep.equal({ 256 | a: { id: "a", adjacentTo: [], body: {} } 257 | }); 258 | }); 259 | 260 | it("should not add duplicate edges", () => { 261 | const digraph = new DiGraph(); 262 | const [vertexA, vertexB, vertexC] = [ 263 | ...createRawVertices("a", "b", "c") 264 | ]; 265 | 266 | digraph.addVertices(vertexA, vertexB, vertexC); 267 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 268 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 269 | 270 | expect(vertexB.adjacentTo).deep.equal([vertexA.id]); 271 | 272 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 273 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 274 | 275 | expect(vertexB.adjacentTo).deep.equal([vertexA.id, vertexC.id]); 276 | }); 277 | 278 | it("should not allow adding an edge from a vertex to the same vertex", () => { 279 | const digraph = new DiGraph(); 280 | const vertexA: Vertex = { id: "a", adjacentTo: [], body: {} }; 281 | 282 | digraph.addVertices(vertexA); 283 | digraph.addEdge({ from: vertexA.id, to: vertexA.id }); 284 | 285 | expect(vertexA.adjacentTo).to.deep.equal([]); 286 | }); 287 | }); 288 | }); 289 | 290 | describe("When traversing the graph", () => { 291 | describe("When searching for all dependencies DEPENDING ON a given vertex", () => { 292 | it("should find direct adjacent vertices", () => { 293 | const digraph = new DiGraph(); 294 | const [vertexA, vertexB, vertexC] = [ 295 | ...createRawVertices("a", "b", "c") 296 | ]; 297 | 298 | digraph.addVertices(vertexA, vertexB, vertexC); 299 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 300 | 301 | expect(digraph.getParents(vertexB.id)).to.deep.equal([vertexA]); 302 | 303 | digraph.addEdge({ from: vertexC.id, to: vertexB.id }); 304 | 305 | expect(digraph.getParents(vertexB.id)).to.deep.equal([ 306 | vertexA, 307 | vertexC 308 | ]); 309 | }); 310 | 311 | it("should find and deeply collect all vertices", () => { 312 | const digraph = new DiGraph(); 313 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF, vertexG] = 314 | [...createRawVertices("a", "b", "c", "d", "e", "f", "g")]; 315 | 316 | digraph.addVertices( 317 | vertexF, 318 | vertexC, 319 | vertexD, 320 | vertexA, 321 | vertexB, 322 | vertexE, 323 | vertexG 324 | ); 325 | digraph.addEdge({ from: vertexF.id, to: vertexA.id }); 326 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 327 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 328 | digraph.addEdge({ from: vertexC.id, to: vertexB.id }); 329 | digraph.addEdge({ from: vertexE.id, to: vertexD.id }); 330 | digraph.addEdge({ from: vertexG.id, to: vertexD.id }); 331 | digraph.addEdge({ from: vertexG.id, to: vertexA.id }); 332 | 333 | expect([...digraph.getDeepParents(vertexA.id)]).deep.equal([ 334 | "f", 335 | "d", 336 | "e", 337 | "g", 338 | "b", 339 | "c" 340 | ]); 341 | }); 342 | 343 | describe("When cycles are in the graph", () => { 344 | it("should deeply explore all vertices anyway", () => { 345 | const digraph = new DiGraph(); 346 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF] = [ 347 | ...createRawVertices("a", "b", "c", "d", "e", "f") 348 | ]; 349 | 350 | digraph.addVertices( 351 | vertexF, 352 | vertexC, 353 | vertexD, 354 | vertexA, 355 | vertexB, 356 | vertexE 357 | ); 358 | digraph.addEdge({ from: vertexF.id, to: vertexA.id }); 359 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 360 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 361 | digraph.addEdge({ from: vertexC.id, to: vertexB.id }); 362 | digraph.addEdge({ from: vertexE.id, to: vertexD.id }); 363 | 364 | // cycle 365 | digraph.addEdge({ from: vertexC.id, to: vertexF.id }); 366 | digraph.addEdge({ from: vertexF.id, to: vertexC.id }); 367 | 368 | expect([...digraph.getDeepParents(vertexA.id)]).deep.equal([ 369 | "f", 370 | "c", 371 | "d", 372 | "e", 373 | "b" 374 | ]); 375 | }); 376 | }); 377 | }); 378 | 379 | describe("When searching for all dependencies OF a given vertex", () => { 380 | it("should find direct adjacent vertices", () => { 381 | const digraph = new DiGraph(); 382 | const [vertexA, vertexB, vertexC, vertexD] = [ 383 | ...createRawVertices("a", "b", "c", "d") 384 | ]; 385 | 386 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 387 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 388 | 389 | expect(digraph.getChildren(vertexB.id)).deep.equal([vertexA]); 390 | 391 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 392 | digraph.addEdge({ from: vertexD.id, to: vertexC.id }); 393 | 394 | expect(digraph.getChildren(vertexD.id)).deep.equal([vertexA, vertexC]); 395 | }); 396 | 397 | it("should deeply find and collect all dependencies", () => { 398 | const digraph = new DiGraph(); 399 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF] = [ 400 | ...createRawVertices("a", "b", "c", "d", "e", "f", "g") 401 | ]; 402 | 403 | digraph.addVertices( 404 | vertexA, 405 | vertexB, 406 | vertexC, 407 | vertexD, 408 | vertexE, 409 | vertexF 410 | ); 411 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 412 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 413 | digraph.addEdge({ from: vertexA.id, to: vertexD.id }); 414 | digraph.addEdge({ from: vertexD.id, to: vertexE.id }); 415 | digraph.addEdge({ from: vertexE.id, to: vertexF.id }); 416 | 417 | expect([...digraph.getDeepChildren(vertexA.id)]).deep.equal([ 418 | "b", 419 | "c", 420 | "d", 421 | "e", 422 | "f" 423 | ]); 424 | }); 425 | 426 | describe("When there are cycles in the graph", () => { 427 | it("scenario n°1: should explore all vertices anyway", () => { 428 | const digraph = new DiGraph(); 429 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF] = [ 430 | ...createRawVertices("a", "b", "c", "d", "e", "f") 431 | ]; 432 | 433 | digraph.addVertices( 434 | vertexA, 435 | vertexB, 436 | vertexC, 437 | vertexD, 438 | vertexE, 439 | vertexF 440 | ); 441 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 442 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 443 | digraph.addEdge({ from: vertexA.id, to: vertexD.id }); 444 | digraph.addEdge({ from: vertexD.id, to: vertexE.id }); 445 | 446 | // cycle deep in the graph 447 | digraph.addEdge({ from: vertexE.id, to: vertexF.id }); 448 | digraph.addEdge({ from: vertexF.id, to: vertexE.id }); 449 | 450 | expect([...digraph.getDeepChildren(vertexA.id)]).deep.equal([ 451 | "b", 452 | "c", 453 | "d", 454 | "e", 455 | "f" 456 | ]); 457 | }); 458 | 459 | it("scenario n°2: should explore all vertices anyway", () => { 460 | const digraph = new DiGraph(); 461 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF] = [ 462 | ...createRawVertices("a", "b", "c", "d", "e", "f") 463 | ]; 464 | 465 | digraph.addVertices( 466 | vertexA, 467 | vertexB, 468 | vertexC, 469 | vertexD, 470 | vertexE, 471 | vertexF 472 | ); 473 | digraph.addEdge({ from: vertexA.id, to: vertexF.id }); 474 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 475 | digraph.addEdge({ from: vertexA.id, to: vertexD.id }); 476 | digraph.addEdge({ from: vertexD.id, to: vertexE.id }); 477 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 478 | 479 | // cycle deep in the graph 480 | digraph.addEdge({ from: vertexC.id, to: vertexF.id }); 481 | digraph.addEdge({ from: vertexF.id, to: vertexC.id }); 482 | 483 | expect([...digraph.getDeepChildren(vertexA.id)]).deep.equal([ 484 | "f", 485 | "c", 486 | "b", 487 | "d", 488 | "e" 489 | ]); 490 | }); 491 | }); 492 | }); 493 | }); 494 | 495 | describe("When search for circular dependencies in the graph", () => { 496 | describe("When no vertices have edges directly pointing to each other", () => { 497 | it("should not detect a cycle", () => { 498 | const digraph = new DiGraph(); 499 | const [vertexA, vertexB, vertexC] = [ 500 | ...createRawVertices("a", "b", "c") 501 | ]; 502 | 503 | digraph.addVertices(vertexA, vertexB, vertexC); 504 | 505 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 506 | expect(digraph.hasCycles()).to.equal(false); 507 | 508 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 509 | expect(digraph.hasCycles()).to.equal(false); 510 | }); 511 | }); 512 | 513 | describe("When there is only one cycle in the graph", () => { 514 | describe("When the cycle is starting from the root vertex", () => { 515 | describe("When using infinite depth limit for detection", () => { 516 | it("should detect a cycle of depth 1 between vertices with edges pointing directly to each other", () => { 517 | const digraph = new DiGraph(); 518 | const [vertexA, vertexB] = [...createRawVertices("a", "b")]; 519 | 520 | digraph.addVertices(vertexB, vertexA); 521 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 522 | 523 | expect(digraph.hasCycles()).to.equal(false); 524 | 525 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 526 | 527 | expect(digraph.hasCycles()).to.equal(true); 528 | expect(digraph.findCycles()).to.deep.equal([["b", "a"]]); 529 | }); 530 | 531 | it("should detect a cycle of depth 2 with indirect edges pointing to each other", () => { 532 | const digraph = new DiGraph(); 533 | const [vertexA, vertexB, vertexC, vertexD, vertexE] = [ 534 | ...createRawVertices("a", "b", "c", "d", "e") 535 | ]; 536 | 537 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD, vertexE); 538 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 539 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 540 | digraph.addEdge({ from: vertexC.id, to: vertexD.id }); 541 | expect(digraph.hasCycles()).to.equal(false); 542 | 543 | // D ----> A => cycle between A and D traversing B, C 544 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 545 | expect(digraph.hasCycles()).to.equal(true); 546 | expect(digraph.findCycles()).to.deep.equal([["a", "b", "c", "d"]]); 547 | }); 548 | 549 | it("should detect cyclic paths of any given depth", () => { 550 | const digraph = new DiGraph(); 551 | const [vertexA, vertexB, vertexC, vertexD] = [ 552 | ...createRawVertices("a", "b", "c", "d") 553 | ]; 554 | 555 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 556 | digraph.addEdge({ from: vertexC.id, to: vertexD.id }); 557 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 558 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 559 | // D ----> A => cycle between A and D traversing B, C 560 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 561 | 562 | expect(digraph.findCycles()).to.deep.equal([["a", "b", "c", "d"]]); 563 | }); 564 | 565 | it("should keep only one occurrence of a same cyclic path", () => { 566 | const digraph = new DiGraph(); 567 | 568 | const [fileA, fileB, fileC] = [ 569 | ...createRawVertices("A.js", "B.js", "C.js") 570 | ]; 571 | 572 | digraph.addVertices(fileA, fileB, fileC); 573 | digraph.addEdge({ from: fileA.id, to: fileB.id }); 574 | digraph.addEdge({ from: fileB.id, to: fileC.id }); 575 | digraph.addEdge({ from: fileC.id, to: fileA.id }); 576 | 577 | expect(digraph.findCycles().length).to.equal(1); 578 | expect(digraph.findCycles()).to.deep.equal([ 579 | ["A.js", "B.js", "C.js"] 580 | ]); 581 | }); 582 | 583 | it("should only return nodes involved in the cycle when dealing with direct circular dependency", () => { 584 | const digraph = new DiGraph(); 585 | const [vertexA, vertexB, vertexC] = [ 586 | ...createRawVertices("a", "b", "c") 587 | ]; 588 | 589 | digraph.addVertices(vertexC, vertexA, vertexB); 590 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 591 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 592 | expect(digraph.hasCycles()).to.equal(false); 593 | 594 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 595 | 596 | const cycles = digraph.findCycles(); 597 | expect(cycles).to.deep.equal([["a", "b"]]); 598 | }); 599 | 600 | describe("When dealing with an indirect circular dependency", () => { 601 | it("scenario n°1: should only keep nodes involved in the cycle", () => { 602 | const digraph = new DiGraph(); 603 | const [vertexA, vertexB, vertexC, vertexD, vertexE] = [ 604 | ...createRawVertices("a", "b", "c", "d", "e") 605 | ]; 606 | 607 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD, vertexE); 608 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 609 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 610 | digraph.addEdge({ from: vertexB.id, to: vertexD.id }); 611 | expect(digraph.hasCycles()).to.equal(false); 612 | 613 | digraph.addEdge({ from: vertexC.id, to: vertexA.id }); 614 | digraph.addEdge({ from: vertexC.id, to: vertexE.id }); 615 | 616 | const cycles = digraph.findCycles(); 617 | expect(digraph.hasCycles()).to.equal(true); 618 | expect(cycles).to.deep.equal([["a", "b", "c"]]); 619 | }); 620 | 621 | it("scenario n°2: should only keep nodes involved in the cycle", () => { 622 | const digraph = new DiGraph(); 623 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexZ] = [ 624 | ...createRawVertices("a", "b", "c", "d", "e", "z") 625 | ]; 626 | 627 | digraph.addVertices( 628 | vertexA, 629 | vertexB, 630 | vertexC, 631 | vertexD, 632 | vertexE, 633 | vertexZ 634 | ); 635 | 636 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 637 | digraph.addEdge({ from: vertexA.id, to: vertexC.id }); 638 | digraph.addEdge({ from: vertexB.id, to: vertexD.id }); 639 | digraph.addEdge({ from: vertexB.id, to: vertexE.id }); 640 | digraph.addEdge({ from: vertexD.id, to: vertexZ.id }); 641 | digraph.addEdge({ from: vertexE.id, to: vertexA.id }); 642 | 643 | expect(digraph.findCycles()).to.deep.equal([["a", "b", "e"]]); 644 | }); 645 | }); 646 | }); 647 | 648 | describe("When providing a max depth limit for detection", () => { 649 | it("should not detect any cycle as the specified depth is zero", () => { 650 | const digraph = new DiGraph(); 651 | const [vertexA, vertexB] = [...createRawVertices("a", "b")]; 652 | 653 | digraph.addVertices(vertexA, vertexB); 654 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 655 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 656 | expect(digraph.hasCycles({ maxDepth: 0 })).to.equal(false); 657 | }); 658 | 659 | it("should detect the cycle once the specified depth is greather than or equal to the depth of the cycle", () => { 660 | const digraph = new DiGraph(); 661 | const [vertexA, vertexB, vertexC, vertexD] = [ 662 | ...createRawVertices("a", "b", "c", "d", "e") 663 | ]; 664 | 665 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 666 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 667 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 668 | digraph.addEdge({ from: vertexC.id, to: vertexD.id }); 669 | expect(digraph.hasCycles()).to.equal(false); 670 | 671 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 672 | expect(digraph.hasCycles({ maxDepth: 0 })).to.equal(false); 673 | expect(digraph.hasCycles({ maxDepth: 1 })).to.equal(false); 674 | expect(digraph.hasCycles({ maxDepth: 2 })).to.equal(false); 675 | expect(digraph.hasCycles({ maxDepth: 3 })).to.equal(false); 676 | expect(digraph.hasCycles({ maxDepth: 4 })).to.equal(true); 677 | expect(digraph.hasCycles({ maxDepth: 20 })).to.equal(true); 678 | }); 679 | }); 680 | }); 681 | }); 682 | 683 | describe("When there are many circular dependencies in the graph", () => { 684 | describe("When any cycle is starting other than from the root vertex", () => { 685 | describe("When only one direct cycle should be detected", () => { 686 | it("scenario n°1: should only keep vertices involved", () => { 687 | const digraph = new DiGraph(); 688 | const [vertexA, vertexB, vertexC, vertexD, vertexE] = [ 689 | ...createRawVertices("a", "b", "c", "d", "e") 690 | ]; 691 | 692 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD, vertexE); 693 | 694 | // root node as it was added first in the graph 695 | digraph.addEdge({ from: vertexA.id, to: vertexE.id }); 696 | 697 | // other vertices that should not be included in the cycle 698 | digraph.addEdge({ from: vertexC.id, to: vertexA.id }); 699 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 700 | 701 | // cycle here (C <-> D) 702 | digraph.addEdge({ from: vertexC.id, to: vertexD.id }); 703 | digraph.addEdge({ from: vertexD.id, to: vertexC.id }); 704 | 705 | const cycles = digraph.findCycles(); 706 | expect(digraph.hasCycles()).to.equal(true); 707 | expect(cycles).to.deep.equal([["c", "d"]]); 708 | }); 709 | 710 | it("scenario n°2: should only keep vertices involved", () => { 711 | const digraph = new DiGraph(); 712 | const [vertexA, vertexB, vertexC, vertexD, vertexE, vertexF] = [ 713 | ...createRawVertices("a", "b", "c", "d", "e", "f") 714 | ]; 715 | 716 | digraph.addVertices( 717 | vertexF, 718 | vertexC, 719 | vertexD, 720 | vertexA, 721 | vertexB, 722 | vertexE 723 | ); 724 | digraph.addEdge({ from: vertexF.id, to: vertexA.id }); 725 | digraph.addEdge({ from: vertexB.id, to: vertexA.id }); 726 | digraph.addEdge({ from: vertexD.id, to: vertexA.id }); 727 | digraph.addEdge({ from: vertexC.id, to: vertexB.id }); 728 | digraph.addEdge({ from: vertexE.id, to: vertexD.id }); 729 | 730 | // cycle C <-> F 731 | digraph.addEdge({ from: vertexC.id, to: vertexF.id }); 732 | digraph.addEdge({ from: vertexF.id, to: vertexC.id }); 733 | 734 | const cycles = digraph.findCycles(); 735 | expect(digraph.hasCycles()).to.equal(true); 736 | expect(cycles).to.deep.equal([["f", "c"]]); 737 | }); 738 | 739 | it("scenario n°3: should only keep vertices involved", () => { 740 | const digraph = new DiGraph(); 741 | const [vertexA, vertexB, vertexP, vertexD, vertexX] = [ 742 | ...createRawVertices("a", "b", "p", "d", "x") 743 | ]; 744 | 745 | digraph.addVertices(vertexA, vertexB, vertexP, vertexD, vertexX); 746 | 747 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 748 | digraph.addEdge({ from: vertexA.id, to: vertexP.id }); 749 | digraph.addEdge({ from: vertexB.id, to: vertexD.id }); 750 | digraph.addEdge({ from: vertexP.id, to: vertexD.id }); 751 | digraph.addEdge({ from: vertexD.id, to: vertexX.id }); 752 | expect(digraph.hasCycles()).to.equal(false); 753 | 754 | digraph.addEdge({ from: vertexX.id, to: vertexA.id }); 755 | expect(digraph.findCycles()).to.deep.equal([ 756 | ["a", "b", "d", "x"], 757 | ["a", "p", "d", "x"] 758 | ]); 759 | }); 760 | }); 761 | 762 | it("should keep two connected cycles separated", () => { 763 | const digraph = new DiGraph(); 764 | const [vertexA, vertexB, vertexC, vertexD] = [ 765 | ...createRawVertices("a", "b", "c", "d") 766 | ]; 767 | 768 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD); 769 | 770 | // first cycle (A -> B -> C -> A) 771 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 772 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 773 | digraph.addEdge({ from: vertexC.id, to: vertexA.id }); 774 | 775 | // second cycle (C <-> D) 776 | digraph.addEdge({ from: vertexC.id, to: vertexD.id }); 777 | digraph.addEdge({ from: vertexD.id, to: vertexC.id }); 778 | 779 | // third and global cycle formed (A -> B -> C -> D) 780 | // but we want to keep both cycles separated to facilitate the cycle 781 | // resolution 782 | const cycles = digraph.findCycles(); 783 | expect(digraph.hasCycles()).to.equal(true); 784 | expect(cycles).to.deep.equal([ 785 | ["a", "b", "c"], 786 | ["c", "d"] 787 | ]); 788 | }); 789 | 790 | it("should detect both independent cycles", () => { 791 | const digraph = new DiGraph(); 792 | const [vertexA, vertexB, vertexC, vertexD, vertexE] = [ 793 | ...createRawVertices("a", "b", "c", "d", "e") 794 | ]; 795 | 796 | digraph.addVertices(vertexA, vertexB, vertexC, vertexD, vertexE); 797 | 798 | digraph.addEdge({ from: vertexA.id, to: vertexB.id }); 799 | digraph.addEdge({ from: vertexA.id, to: vertexD.id }); 800 | 801 | // first cycle (B <-> C) 802 | digraph.addEdge({ from: vertexC.id, to: vertexB.id }); 803 | digraph.addEdge({ from: vertexB.id, to: vertexC.id }); 804 | 805 | // second cycle (D <-> E) 806 | digraph.addEdge({ from: vertexE.id, to: vertexD.id }); 807 | digraph.addEdge({ from: vertexD.id, to: vertexE.id }); 808 | 809 | const cycles = digraph.findCycles(); 810 | expect(digraph.hasCycles()).to.equal(true); 811 | expect(cycles).to.deep.equal([ 812 | ["b", "c"], 813 | ["d", "e"] 814 | ]); 815 | }); 816 | }); 817 | }); 818 | }); 819 | 820 | describe("When constructing DiGraph instances from a raw record", () => { 821 | it("should construct a DiGraph instance with vertices linked by edges", () => { 822 | const rawGraph = { 823 | a: { 824 | id: "a", 825 | adjacentTo: [], 826 | body: { 827 | someProperty: "someValue" 828 | } 829 | }, 830 | b: { 831 | id: "b", 832 | adjacentTo: ["a"], 833 | body: { 834 | dependencies: [] 835 | } 836 | }, 837 | c: { 838 | id: "c", 839 | adjacentTo: ["b"], 840 | body: {} 841 | } 842 | }; 843 | 844 | const digraph = DiGraph.fromRaw(rawGraph); 845 | 846 | expect(digraph.toDict()).to.deep.equal({ 847 | a: { id: "a", adjacentTo: [], body: { someProperty: "someValue" } }, 848 | b: { id: "b", adjacentTo: ["a"], body: { dependencies: [] } }, 849 | c: { id: "c", adjacentTo: ["b"], body: {} } 850 | }); 851 | }); 852 | }); 853 | }); 854 | -------------------------------------------------------------------------------- /src/digraph.ts: -------------------------------------------------------------------------------- 1 | import isEqual from "lodash.isequal"; 2 | import uniqWith from "lodash.uniqwith"; 3 | 4 | import { VertexBody, VertexDefinition, VertexId } from "./vertex.js"; 5 | 6 | export type Traversal = "bfs" | "dfs"; 7 | 8 | export class DiGraph> { 9 | #vertices: Map; 10 | 11 | constructor() { 12 | this.#vertices = new Map(); 13 | } 14 | 15 | static fromRaw( 16 | raw: Record> 17 | ): DiGraph> { 18 | const digraph = new DiGraph(); 19 | 20 | for (const vertex of Object.values(raw)) { 21 | digraph.addVertex({ 22 | id: vertex.id, 23 | adjacentTo: vertex.adjacentTo, 24 | body: vertex.body 25 | }); 26 | } 27 | 28 | return digraph; 29 | } 30 | 31 | public get isAcyclic(): boolean { 32 | return !this.hasCycles(); 33 | } 34 | 35 | public toDict(): Record { 36 | return Object.fromEntries(this.#vertices.entries()); 37 | } 38 | 39 | public hasVertex(vertexId: VertexId): boolean { 40 | return this.#vertices.has(vertexId); 41 | } 42 | 43 | public addVertex(vertex: Vertex): void { 44 | const graphVerticesIds = [...this.#vertices.keys()]; 45 | 46 | if (!graphVerticesIds.includes(vertex.id)) { 47 | this.#vertices.set(vertex.id, vertex); 48 | } 49 | } 50 | 51 | public addVertices(...vertices: Vertex[]): void { 52 | const graphVerticesIds = [...this.#vertices.keys()]; 53 | for (const uniqueVertex of this.keepUniqueVertices(vertices)) { 54 | if (!graphVerticesIds.includes(uniqueVertex.id)) { 55 | this.addVertex(uniqueVertex); 56 | } 57 | } 58 | } 59 | 60 | public deleteVertex(vertexId: VertexId): void { 61 | this.#vertices.delete(vertexId); 62 | 63 | for (const vertexDependingOnDeletedVertex of this.getParents(vertexId)) { 64 | this.deleteEdge({ 65 | from: vertexDependingOnDeletedVertex.id, 66 | to: vertexId 67 | }); 68 | } 69 | } 70 | 71 | public addEdge({ from, to }: { from: VertexId; to: VertexId }): void { 72 | if (from === to) { 73 | return; 74 | } 75 | 76 | const [fromVertex, toVertex] = [ 77 | this.#vertices.get(from), 78 | this.#vertices.get(to) 79 | ]; 80 | 81 | if (fromVertex && toVertex) { 82 | const hasNotSameAdjacentVertex = !fromVertex.adjacentTo.find( 83 | (adjacentVertex) => adjacentVertex === toVertex.id 84 | ); 85 | if (hasNotSameAdjacentVertex) { 86 | fromVertex.adjacentTo = fromVertex.adjacentTo.concat(toVertex.id); 87 | } 88 | } 89 | } 90 | 91 | public deleteEdge({ from, to }: { from: VertexId; to: VertexId }): void { 92 | const fromVertex = this.#vertices.get(from); 93 | 94 | if (fromVertex) { 95 | fromVertex.adjacentTo = fromVertex.adjacentTo.filter( 96 | (adjacentVertexId) => adjacentVertexId !== to 97 | ); 98 | } 99 | } 100 | 101 | /** 102 | * This function updates the vertex's body with the provided value without 103 | * doing any merging with the previous value. If you want to preserve/update 104 | * values, check `mergeVertexBody` instead. 105 | * @example 106 | * updateVertexBody("Node1", { 107 | * // body only contains this property "newProperty" now. 108 | * newProperty: [] 109 | * }); 110 | * 111 | */ 112 | public updateVertexBody(vertexId: VertexId, body: Vertex["body"]): void { 113 | const rootVertexToMutate = this.#vertices.get(vertexId); 114 | 115 | if (rootVertexToMutate) { 116 | rootVertexToMutate.body = body; 117 | } 118 | } 119 | 120 | /** 121 | * This function lets you choose the way of merging the vertex's body 122 | * by providing a callback function with the corresponding vertex instance. 123 | * @example 124 | * mergeVertexBody("Node1", (nodeBody) => { 125 | * // either by directly mutating the value 126 | * nodeBody.someProperty.list[0] = {}; 127 | * // either by providing a new reference 128 | * nodeBody.someProperty.list = newCollection.map(operation); 129 | * }); 130 | */ 131 | public mergeVertexBody( 132 | vertexId: VertexId, 133 | mergeCallback: (vertex: Vertex["body"]) => void 134 | ): void { 135 | const rootVertexToMutate = this.#vertices.get(vertexId); 136 | 137 | if (rootVertexToMutate) { 138 | mergeCallback(rootVertexToMutate.body); 139 | } 140 | } 141 | 142 | /** 143 | * Base API to traverse walk through a DiGraph instance either in a DFS or BFS 144 | * manner. Providing `rootVertexId` will force the traversal to start from it. 145 | * If no `rootVertexId` is provided, the traversal will start from the first vertex 146 | * found in the graph, which will most likely be the first entry that was added 147 | * in it. 148 | */ 149 | public *traverse(options?: { 150 | rootVertexId?: VertexId; 151 | traversal?: Traversal; 152 | }): Generator { 153 | const { rootVertexId, traversal } = { 154 | traversal: options?.traversal ?? "bfs", 155 | rootVertexId: options?.rootVertexId 156 | }; 157 | 158 | if (rootVertexId) { 159 | if (traversal === "bfs") { 160 | return yield* this.breadthFirstTraversalFrom(rootVertexId); 161 | } 162 | 163 | return yield* this.depthFirstTraversalFrom(rootVertexId); 164 | } 165 | 166 | return yield* this.traverseAll(traversal); 167 | } 168 | 169 | public traverseEager(options?: { 170 | rootVertexId?: VertexId; 171 | traversal?: Traversal; 172 | }): Vertex[] { 173 | return Array.from(this.traverse(options)); 174 | } 175 | 176 | /** 177 | * Allows top-to-bottom traversals by finding only the first relationship level 178 | * of children dependencies of the provided vertex. 179 | * @example 180 | * // given A --> B, A depends on B hence B is a children dependency of A 181 | * assert.deepEqual(graph.getChildren("A"), [VertexB]) // ok 182 | */ 183 | public getChildren(rootVertexId: VertexId): Vertex[] { 184 | return [...this.#vertices.values()].filter((vertex) => 185 | this.#vertices.get(rootVertexId)?.adjacentTo.includes(vertex.id) 186 | ); 187 | } 188 | 189 | /** 190 | * Same as `getChildren()`, but doesn't stop at the first level hence deeply 191 | * collects all children dependencies in a Depth-First Search manner. 192 | * Allows top-to-bottom traversals i.e: which nodes are dependencies of 193 | * the provided rootVertexId. 194 | */ 195 | public *getDeepChildren( 196 | rootVertexId: VertexId, 197 | depthLimit?: number 198 | ): Generator { 199 | const rootVertex = this.#vertices.get(rootVertexId); 200 | if (!rootVertex) { 201 | return; 202 | } 203 | 204 | const visitedVertices: VertexId[] = []; 205 | 206 | for (const adjacentVertexId of rootVertex.adjacentTo) { 207 | const adjacentVertex = this.#vertices.get(adjacentVertexId); 208 | 209 | if (!adjacentVertex) { 210 | continue; 211 | } 212 | 213 | yield* this.findDeepDependencies( 214 | "top-to-bottom", 215 | rootVertex, 216 | adjacentVertex, 217 | depthLimit, 218 | visitedVertices 219 | ); 220 | } 221 | } 222 | 223 | /** 224 | * Allows bottom-to-top traversals by finding only the first relationship level 225 | * of parent dependencies of the provided vertex. 226 | * @example 227 | * // given A --> B, A depends on B hence A is a parent dependency of B 228 | * assert.deepEqual(graph.getParents("B"), [VertexA]) // ok 229 | */ 230 | public getParents(rootVertexId: VertexId): Vertex[] { 231 | return [...this.#vertices.values()].filter((vertex) => 232 | vertex.adjacentTo.includes(rootVertexId) 233 | ); 234 | } 235 | 236 | /** 237 | * Same as `getParents()`, but doesn't stop at the first level hence deeply 238 | * collects all parent dependencies in a Depth-First Search manner. 239 | * Allows bottom-to-top traversals i.e: which nodes are depending on 240 | * the provided rootVertexId. 241 | */ 242 | public *getDeepParents( 243 | rootVertexId: VertexId, 244 | depthLimit?: number 245 | ): Generator { 246 | const rootVertex = this.#vertices.get(rootVertexId); 247 | if (!rootVertex) { 248 | return; 249 | } 250 | 251 | const visitedVertices: VertexId[] = []; 252 | 253 | for (const adjacentVertex of this.getParents(rootVertex.id)) { 254 | yield* this.findDeepDependencies( 255 | "bottom-to-top", 256 | rootVertex, 257 | adjacentVertex, 258 | depthLimit, 259 | visitedVertices 260 | ); 261 | } 262 | } 263 | 264 | /** 265 | * Returns `true` if atleast one circular dependency exists in the graph, 266 | * otherwise, returns `false`. 267 | * If you want to know precisely what are the circular dependencies and 268 | * know what vertices are involved, use `findCycles()` instead. 269 | */ 270 | public hasCycles( 271 | { maxDepth } = { maxDepth: Number.POSITIVE_INFINITY } 272 | ): boolean { 273 | let hasCycles = false; 274 | 275 | if (maxDepth === 0) { 276 | return hasCycles; 277 | } 278 | 279 | for (const [ 280 | rootVertex, 281 | rootAdjacentVertex 282 | ] of this.collectRootAdjacencyLists()) { 283 | // early exit as we stop on the first cycle found 284 | if (hasCycles) { 285 | break; 286 | } 287 | const adjacencyList = new Set(); 288 | for (const deepAdjacentVertexId of this.findDeepDependencies( 289 | "top-to-bottom", 290 | rootVertex, 291 | rootAdjacentVertex, 292 | maxDepth 293 | )) { 294 | adjacencyList.add(deepAdjacentVertexId); 295 | 296 | if ( 297 | deepAdjacentVertexId === rootVertex.id || 298 | adjacencyList.has(rootVertex.id) 299 | ) { 300 | hasCycles = true; 301 | break; 302 | } 303 | } 304 | } 305 | 306 | return hasCycles; 307 | } 308 | 309 | public findCycles( 310 | { maxDepth } = { maxDepth: Number.POSITIVE_INFINITY } 311 | ): VertexId[][] { 312 | const cyclicPathsWithMaybeDuplicates: VertexId[][] = []; 313 | 314 | if (maxDepth === 0) { 315 | return []; 316 | } 317 | 318 | for (const [ 319 | rootVertex, 320 | rootAdjacentVertex 321 | ] of this.collectRootAdjacencyLists()) { 322 | const adjacencyList = new Set(); 323 | 324 | for (const deepAdjacentVertexId of this.findDeepDependencies( 325 | "top-to-bottom", 326 | rootVertex, 327 | rootAdjacentVertex, 328 | maxDepth 329 | )) { 330 | adjacencyList.add(deepAdjacentVertexId); 331 | 332 | if ( 333 | deepAdjacentVertexId === rootVertex.id || 334 | adjacencyList.has(rootVertex.id) 335 | ) { 336 | const adjacencyListAsArray = [...adjacencyList]; 337 | /** 338 | * We found a cycle, the first thing to do is to only keep the segment 339 | * from X to X with "X" being the root vertex of the current DFS. 340 | * It allows us to build sub cycles at any point in the path. 341 | */ 342 | const verticesInBetweenCycle = adjacencyListAsArray.slice( 343 | 0, 344 | adjacencyListAsArray.indexOf(rootVertex.id) + 1 345 | ); 346 | cyclicPathsWithMaybeDuplicates.push( 347 | this.backtrackVerticesInvolvedInCycle([ 348 | rootVertex.id, 349 | ...verticesInBetweenCycle 350 | ]) 351 | ); 352 | } 353 | } 354 | } 355 | 356 | return this.keepUniqueVerticesPaths([...cyclicPathsWithMaybeDuplicates]); 357 | } 358 | 359 | private *limitCycleDetectionDepth( 360 | dependenciesWalker: Generator, 361 | maxDepth: number 362 | ) { 363 | /** 364 | * At this point, we already traversed 2 levels of depth dependencies by: 365 | * - accessing the root's node adjacency list (depth === 1) 366 | * - then we continue by accessing the adjacent's node adjacency list (depth === 2) 367 | * Consequently we start recursing using the limit only at depth 2 already 368 | */ 369 | const TRAVERSAL_STEPS_ALREADY_DONE = 2; 370 | for ( 371 | let depth = 0; 372 | depth <= maxDepth - TRAVERSAL_STEPS_ALREADY_DONE; 373 | depth++ 374 | ) { 375 | const { done, value } = dependenciesWalker.next(); 376 | if (done) { 377 | return; 378 | } 379 | yield value; 380 | } 381 | } 382 | 383 | private *collectRootAdjacencyLists(): Generator<[Vertex, Vertex]> { 384 | for (const rootVertex of this.#vertices.values()) { 385 | for (const rootAdjacentVertexId of rootVertex.adjacentTo) { 386 | const rootAdjacentVertex = this.#vertices.get(rootAdjacentVertexId); 387 | if (!rootAdjacentVertex) { 388 | continue; 389 | } 390 | 391 | yield [rootVertex, rootAdjacentVertex]; 392 | } 393 | } 394 | } 395 | 396 | /** 397 | * This method is used to deeply find either all lower dependencies of a given 398 | * vertex or all its upper dependencies. 399 | */ 400 | // eslint-disable-next-line max-params 401 | private *findDeepDependencies( 402 | dependencyTraversal: "bottom-to-top" | "top-to-bottom", 403 | rootVertex: Vertex, 404 | traversedVertex: Vertex, 405 | depthLimit: number = Number.POSITIVE_INFINITY, 406 | verticesAlreadyVisited: VertexId[] = [] 407 | ): Generator { 408 | if (verticesAlreadyVisited.includes(traversedVertex.id)) { 409 | return; 410 | } 411 | 412 | yield traversedVertex.id; 413 | verticesAlreadyVisited.push(traversedVertex.id); 414 | 415 | // Cycle reached, we must exit before entering in the infinite loop 416 | if (rootVertex.id === traversedVertex.id) { 417 | return; 418 | } 419 | 420 | const nextDependencies = 421 | dependencyTraversal === "top-to-bottom" 422 | ? traversedVertex.adjacentTo 423 | : this.getParents(traversedVertex.id).map(({ id }) => id); 424 | 425 | for (const adjacentVertexId of nextDependencies) { 426 | const adjacentVertex = this.#vertices.get(adjacentVertexId); 427 | if (adjacentVertex) { 428 | yield* this.limitCycleDetectionDepth( 429 | this.findDeepDependencies( 430 | dependencyTraversal, 431 | rootVertex, 432 | adjacentVertex, 433 | depthLimit, 434 | verticesAlreadyVisited 435 | ), 436 | depthLimit 437 | ); 438 | } 439 | } 440 | } 441 | 442 | private keepUniqueVerticesPaths(paths: VertexId[][]): VertexId[][] { 443 | return uniqWith(paths, (pathA, pathB) => { 444 | // Narrow down the comparison to avoid unnecessary operations 445 | if (pathA.length !== pathB.length) { 446 | return false; 447 | } 448 | 449 | /** 450 | * In order for paths to be compared by values, arrays must be sorted e.g: 451 | * [a, b] !== [b, a] when strictly comparing values. 452 | */ 453 | return isEqual(pathA.slice().sort(), pathB.slice().sort()); 454 | }); 455 | } 456 | 457 | /** 458 | * Once the cycle found, many vertices actually not involved in the cycle 459 | * might have been visited. To only keep vertices that are effectively involved 460 | * in the cyclic path, we must check that for any vertex there is an existing 461 | * path from its ancestor leading to the root node. 462 | */ 463 | private backtrackVerticesInvolvedInCycle( 464 | verticesInCyclicPath: VertexId[] 465 | ): VertexId[] { 466 | for (let i = verticesInCyclicPath.length; i > 1; i--) { 467 | const currentNode = verticesInCyclicPath[i - 1]; 468 | // The node just before the current one who is eventually its parent 469 | const nodeBeforeInPath = this.#vertices.get(verticesInCyclicPath[i - 2]); 470 | const isCurrentNodeParent = 471 | nodeBeforeInPath?.adjacentTo.includes(currentNode); 472 | /** 473 | * there is no path existing from the node just before to the current node, 474 | * meaning that the cycle path can't be coming from that path. 475 | */ 476 | if (!isCurrentNodeParent) { 477 | // We must remove incrementally vertices that aren't involved in the cycle 478 | verticesInCyclicPath.splice(i - 2, 1); 479 | } 480 | } 481 | 482 | return [...new Set(verticesInCyclicPath)]; 483 | } 484 | 485 | private *keepUniqueVertices(vertices: Vertex[]): Generator { 486 | const uniqueVerticesIds = new Set(); 487 | 488 | for (const vertex of vertices) { 489 | if (!uniqueVerticesIds.has(vertex.id)) { 490 | uniqueVerticesIds.add(vertex.id); 491 | 492 | yield vertex; 493 | } 494 | } 495 | } 496 | 497 | private *depthFirstTraversalFrom( 498 | rootVertexId: VertexId, 499 | traversedVertices = new Set() 500 | ): Generator { 501 | if (traversedVertices.has(rootVertexId)) { 502 | return; 503 | } 504 | 505 | const rootVertex = this.#vertices.get(rootVertexId); 506 | 507 | if (!rootVertex) { 508 | return; 509 | } 510 | 511 | yield rootVertex; 512 | traversedVertices.add(rootVertexId); 513 | 514 | for (const vertexId of rootVertex.adjacentTo) { 515 | yield* this.depthFirstTraversalFrom(vertexId, traversedVertices); 516 | } 517 | } 518 | 519 | private *breadthFirstTraversalFrom( 520 | rootVertexId: VertexId, 521 | visitedVerticesIds = new Set() 522 | ): Generator { 523 | const vertex = this.#vertices.get(rootVertexId); 524 | 525 | if (!vertex) return; 526 | 527 | if (!visitedVerticesIds.has(rootVertexId)) { 528 | visitedVerticesIds.add(rootVertexId); 529 | yield vertex; 530 | } 531 | 532 | const nextVerticesToVisit: Vertex[] = []; 533 | for (const vertexId of vertex.adjacentTo) { 534 | const adjacentVertex = this.#vertices.get(vertexId); 535 | 536 | if (!adjacentVertex || visitedVerticesIds.has(adjacentVertex.id)) 537 | continue; 538 | 539 | visitedVerticesIds.add(adjacentVertex.id); 540 | nextVerticesToVisit.push(adjacentVertex); 541 | yield adjacentVertex; 542 | } 543 | 544 | for (const nextVertex of nextVerticesToVisit) { 545 | yield* this.breadthFirstTraversalFrom(nextVertex.id, visitedVerticesIds); 546 | } 547 | } 548 | 549 | private *traverseAll(traversal: Traversal) { 550 | const visitedVertices = new Set(); 551 | 552 | for (const vertexId of this.#vertices.keys()) { 553 | if (traversal === "dfs") { 554 | yield* this.depthFirstTraversalFrom(vertexId, visitedVertices); 555 | } else { 556 | yield* this.breadthFirstTraversalFrom(vertexId, visitedVertices); 557 | } 558 | } 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { DiGraph, Traversal } from "./digraph.js"; 2 | export type { VertexDefinition, VertexId, VertexBody } from "./vertex.js"; 3 | -------------------------------------------------------------------------------- /src/traversal.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | 3 | import { DiGraph, Traversal } from "./digraph.js"; 4 | import type { VertexBody, VertexDefinition, VertexId } from "./vertex"; 5 | 6 | type Vertex = VertexDefinition; 7 | 8 | function* createRawVertices(...ids: VertexId[]): Generator { 9 | for (const id of ids) { 10 | yield { 11 | id, 12 | adjacentTo: [], 13 | body: {} 14 | }; 15 | } 16 | } 17 | 18 | type VertexBuilder = { 19 | raw: (adjacentTo?: VertexId[]) => Vertex; 20 | adjacentTo: (...adjacentTo: VertexId[]) => VertexBuilder; 21 | }; 22 | 23 | function makeVertex(id: VertexId): VertexBuilder { 24 | return { 25 | raw: (adjacentTo = []) => { 26 | return { id, adjacentTo, body: {} }; 27 | }, 28 | adjacentTo(...adjacentTo: VertexId[]) { 29 | return { 30 | raw: () => { 31 | return { 32 | id, 33 | adjacentTo, 34 | body: {} 35 | }; 36 | }, 37 | adjacentTo: this.adjacentTo 38 | }; 39 | } 40 | }; 41 | } 42 | 43 | describe("Graph traversal", () => { 44 | test("Should expose a graph traversal method without root vertex", () => { 45 | const graph = new DiGraph(); 46 | graph.addVertices(...createRawVertices("a", "b", "c", "d", "e", "f", "g")); 47 | const vertices = [...graph.traverse()]; 48 | 49 | expect(vertices.map(({ id }) => id)).toEqual([ 50 | "a", 51 | "b", 52 | "c", 53 | "d", 54 | "e", 55 | "f", 56 | "g" 57 | ]); 58 | }); 59 | 60 | describe("When providing a root vertex", () => { 61 | test("Should return an empty array or element when the provided vertex does not exists", () => { 62 | const graph = new DiGraph(); 63 | const vertexB = makeVertex("b").raw(); 64 | const vertexC = makeVertex("c").raw(); 65 | 66 | const vertices: VertexDefinition[] = [vertexB, vertexC]; 67 | graph.addVertices(...vertices); 68 | 69 | const it = graph.traverse({ rootVertexId: "a" }); 70 | const { value, done } = it.next(); 71 | expect({ value, done }).toEqual({ value: undefined, done: true }); 72 | expect([...it]).toEqual([]); 73 | }); 74 | 75 | test("Should expose a graph DFS traversal method starting from an existing root vertex", () => { 76 | const graph = new DiGraph(); 77 | const vertexA = makeVertex("a").adjacentTo("d", "c").raw(); 78 | const vertexB = makeVertex("b").raw(); 79 | const vertexC = makeVertex("c").raw(); 80 | const vertexD = makeVertex("d").adjacentTo("b").raw(); 81 | graph.addVertices(vertexB, vertexA, vertexC, vertexD); 82 | 83 | const it = graph.traverse({ rootVertexId: "a", traversal: "dfs" }); 84 | 85 | expect(it.next().value?.id).toEqual(vertexA.id); 86 | expect(it.next().value?.id).toEqual(vertexD.id); 87 | expect(it.next().value?.id).toEqual(vertexB.id); 88 | expect(it.next().value?.id).toEqual(vertexC.id); 89 | 90 | expect(it.next().done).toBeTruthy(); 91 | }); 92 | 93 | test("Should expose a graph BFS traversal method starting from an existing root vertex with multiple children", () => { 94 | const graph = new DiGraph(); 95 | const vertexA = makeVertex("a").adjacentTo("d", "c").raw(); 96 | const vertexB = makeVertex("b").raw(); 97 | const vertexC = makeVertex("c").adjacentTo("e").raw(); 98 | const vertexD = makeVertex("d").adjacentTo("b").raw(); 99 | const vertexE = makeVertex("e").raw(); 100 | graph.addVertices(vertexB, vertexA, vertexC, vertexD, vertexE); 101 | 102 | const it = graph.traverse({ rootVertexId: "a", traversal: "bfs" }); 103 | 104 | expect(it.next().value?.id).toEqual(vertexA.id); 105 | expect(it.next().value?.id).toEqual(vertexD.id); 106 | expect(it.next().value?.id).toEqual(vertexC.id); 107 | expect(it.next().value?.id).toEqual(vertexB.id); 108 | expect(it.next().value?.id).toEqual(vertexE.id); 109 | 110 | expect(it.next().done).toBeTruthy(); 111 | }); 112 | 113 | test("Should expose a graph BFS traversal method starting from an existing root vertex with one child", () => { 114 | const graph = new DiGraph(); 115 | const vertexA = makeVertex("a").adjacentTo("b").raw(); 116 | const vertexB = makeVertex("b").adjacentTo("d", "c").raw(); 117 | const vertexC = makeVertex("c").raw(); 118 | const vertexD = makeVertex("d").adjacentTo("e").raw(); 119 | const vertexE = makeVertex("e").raw(); 120 | 121 | graph.addVertices(vertexB, vertexA, vertexC, vertexD, vertexE); 122 | 123 | const it = graph.traverse({ rootVertexId: "a", traversal: "bfs" }); 124 | expect(it.next().value?.id).toEqual("a"); 125 | expect(it.next().value?.id).toEqual("b"); 126 | expect(it.next().value?.id).toEqual("d"); 127 | expect(it.next().value?.id).toEqual("c"); 128 | expect(it.next().value?.id).toEqual("e"); 129 | }); 130 | }); 131 | 132 | describe("When not providing a root vertex", () => { 133 | test("Should return an empty array or there are no vertices", () => { 134 | const graph = new DiGraph(); 135 | 136 | const it = graph.traverse(); 137 | const { value, done } = it.next(); 138 | expect({ value, done }).toEqual({ value: undefined, done: true }); 139 | expect([...it]).toEqual([]); 140 | }); 141 | 142 | test("DFS traversal", () => { 143 | const graph = new DiGraph(); 144 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 145 | const vertexB = makeVertex("b").adjacentTo("d").raw(); 146 | const vertexC = makeVertex("c").raw(); 147 | const vertexD = makeVertex("d").raw(); 148 | 149 | graph.addVertices(vertexA, vertexB, vertexC, vertexD); 150 | 151 | const it = graph.traverse({ traversal: "dfs" }); 152 | 153 | expect(it.next().value?.id).toEqual(vertexA.id); 154 | expect(it.next().value?.id).toEqual(vertexB.id); 155 | expect(it.next().value?.id).toEqual(vertexD.id); 156 | expect(it.next().value?.id).toEqual(vertexC.id); 157 | 158 | expect(it.next().done).toBeTruthy(); 159 | }); 160 | 161 | test("BFS traversal", () => { 162 | const graph = new DiGraph(); 163 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 164 | const vertexB = makeVertex("b").adjacentTo("d").raw(); 165 | const vertexC = makeVertex("c").raw(); 166 | const vertexD = makeVertex("d").raw(); 167 | 168 | graph.addVertices(vertexA, vertexB, vertexC, vertexD); 169 | 170 | const it = graph.traverse({ traversal: "bfs" }); 171 | 172 | expect(it.next().value?.id).toEqual(vertexA.id); 173 | expect(it.next().value?.id).toEqual(vertexB.id); 174 | expect(it.next().value?.id).toEqual(vertexC.id); 175 | expect(it.next().value?.id).toEqual(vertexD.id); 176 | 177 | expect(it.next().done).toBeTruthy(); 178 | }); 179 | }); 180 | 181 | describe("When multiple vertices are connected to the same vertex", () => { 182 | test("DFS: Should traverse the same vertex only once", async () => { 183 | const graph = new DiGraph(); 184 | const vertexA = makeVertex("a").adjacentTo("b").raw(); 185 | const vertexB = makeVertex("b").adjacentTo("c").raw(); 186 | const vertexC = makeVertex("c").raw(); 187 | 188 | graph.addVertices(vertexB, vertexA, vertexC); 189 | 190 | const it = graph.traverse({ traversal: "dfs" }); 191 | 192 | expect(it.next().value?.id).toEqual(vertexB.id); 193 | expect(it.next().value?.id).toEqual(vertexC.id); 194 | expect(it.next().value?.id).toEqual(vertexA.id); 195 | 196 | expect(it.next().done).toBeTruthy(); 197 | }); 198 | 199 | test("DFS with root: Should traverse the same vertex only once", async () => { 200 | const graph = new DiGraph(); 201 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 202 | const vertexB = makeVertex("b").adjacentTo("c").raw(); 203 | const vertexC = makeVertex("c").raw(); 204 | 205 | graph.addVertices(vertexB, vertexA, vertexC); 206 | 207 | const it = graph.traverse({ rootVertexId: "a", traversal: "dfs" }); 208 | 209 | expect(it.next().value?.id).toEqual(vertexA.id); 210 | expect(it.next().value?.id).toEqual(vertexB.id); 211 | expect(it.next().value?.id).toEqual(vertexC.id); 212 | 213 | expect(it.next().done).toBeTruthy(); 214 | }); 215 | 216 | test("BFS: Should traverse the same vertex only once", async () => { 217 | const graph = new DiGraph(); 218 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 219 | const vertexB = makeVertex("b").adjacentTo("c").raw(); 220 | const vertexC = makeVertex("c").raw(); 221 | 222 | graph.addVertices(vertexB, vertexA, vertexC); 223 | 224 | const it = graph.traverse({ traversal: "bfs" }); 225 | 226 | expect(it.next().value?.id).toEqual(vertexB.id); 227 | expect(it.next().value?.id).toEqual(vertexC.id); 228 | expect(it.next().value?.id).toEqual(vertexA.id); 229 | 230 | expect(it.next().done).toBeTruthy(); 231 | }); 232 | 233 | test("BFS with root: Should traverse the same vertex only once", async () => { 234 | const graph = new DiGraph(); 235 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 236 | const vertexB = makeVertex("b").adjacentTo("c").raw(); 237 | const vertexC = makeVertex("c").adjacentTo("d").raw(); 238 | const vertexD = makeVertex("d").raw(); 239 | 240 | graph.addVertices(vertexB, vertexA, vertexC, vertexD); 241 | 242 | const it = graph.traverse({ rootVertexId: "a", traversal: "bfs" }); 243 | 244 | expect(it.next().value?.id).toEqual(vertexA.id); 245 | expect(it.next().value?.id).toEqual(vertexB.id); 246 | expect(it.next().value?.id).toEqual(vertexC.id); 247 | expect(it.next().value?.id).toEqual(vertexD.id); 248 | 249 | expect(it.next().done).toBeTruthy(); 250 | }); 251 | }); 252 | 253 | describe("When the graph is cyclic", () => { 254 | const traversals = ["dfs", "bfs"]; 255 | 256 | test.each(traversals)( 257 | "%s: Should traverse all vertices in the graph", 258 | (traversal) => { 259 | const graph = new DiGraph(); 260 | const vertexA = makeVertex("a").adjacentTo("b", "c").raw(); 261 | const vertexB = makeVertex("b").adjacentTo("a").raw(); 262 | const vertexC = makeVertex("c").adjacentTo("d").raw(); 263 | const vertexD = makeVertex("d").raw(); 264 | 265 | graph.addVertices(vertexB, vertexA, vertexC, vertexD); 266 | 267 | const it = graph.traverse({ 268 | rootVertexId: "b", 269 | traversal: traversal as Traversal 270 | }); 271 | 272 | expect(it.next().value?.id).toEqual(vertexB.id); 273 | expect(it.next().value?.id).toEqual(vertexA.id); 274 | expect(it.next().value?.id).toEqual(vertexC.id); 275 | expect(it.next().value?.id).toEqual(vertexD.id); 276 | 277 | expect(it.next().done).toBeTruthy(); 278 | } 279 | ); 280 | }); 281 | }); 282 | -------------------------------------------------------------------------------- /src/vertex.ts: -------------------------------------------------------------------------------- 1 | export type VertexId = string; 2 | 3 | export type VertexBody = Record; 4 | 5 | export type VertexDefinition = { 6 | id: VertexId; 7 | adjacentTo: VertexId[]; 8 | body: Body; 9 | }; 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "declaration": true, 6 | "sourceMap": true 7 | }, 8 | "exclude": ["src/**/*.spec.ts", "examples"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es2020"], 5 | "module": "es2020", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "allowSyntheticDefaultImports": true 13 | }, 14 | "include": ["src"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | --------------------------------------------------------------------------------