├── .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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------