From 4b8378f934a08a79670f6156d4d5ba8c977a4c7e Mon Sep 17 00:00:00 2001 From: Carlos Herrero <26092748+hbcarlos@users.noreply.github.com> Date: Sat, 28 Nov 2020 16:36:23 +0100 Subject: [PATCH] Package Graph (#102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Package Graph * Vis-network and new query argument in PackagesHandler GET route * Added graph to jupyter_conda * Changes * Cancel task * delete jupyter_conda bundles * Check test * highlight and font size * eslint * Apply suggestions from code review * Apply some more suggestions from code review * Fixing usage when mamba is missing * Fix eslint error Co-authored-by: Frédéric Collonval --- mamba_gator/envmanager.py | 29 ++ mamba_gator/handlers.py | 12 +- mamba_gator/rest_api.yml | 5 + packages/common/package.json | 3 + .../common/src/components/CondaPkgList.tsx | 23 +- .../common/src/components/CondaPkgPanel.tsx | 12 +- packages/common/src/components/PkgGraph.tsx | 174 +++++++++++ packages/common/src/services.ts | 66 ++++- packages/common/src/tokens.ts | 18 ++ yarn.lock | 277 +++++++++++++++++- 10 files changed, 596 insertions(+), 23 deletions(-) create mode 100644 packages/common/src/components/PkgGraph.tsx diff --git a/mamba_gator/envmanager.py b/mamba_gator/envmanager.py index f3bddb80..296eda59 100644 --- a/mamba_gator/envmanager.py +++ b/mamba_gator/envmanager.py @@ -524,6 +524,35 @@ async def env_packages(self, env: str) -> Dict[str, List[str]]: return {"packages": [normalize_pkg_info(package) for package in data]} + async def pkg_depends(self, pkg: str) -> Dict[str, List[str]]: + """List environment packages dependencies. + + Args: + pkg (str): Package name + + Returns: + {"package": List[dependencies]} + """ + if self.manager != "mamba" : + self.log.warning("Package manager '{}' does not support dependency query.".format(self.manager)) + return { pkg: None } + + resp = {} + ans = await self._execute(self.manager, "repoquery", "depends", "--json", pkg) + _, output = ans + query = self._clean_conda_json(output) + + if "error" not in query: + for dep in query['result']['pkgs'] : + if type(dep) is dict : + deps = dep.get('depends', None) + if deps : + resp[dep['name']] = deps + else : + resp[dep['name']] = [] + + return resp + async def list_available(self) -> Dict[str, List[Dict[str, str]]]: """List all available packages diff --git a/mamba_gator/handlers.py b/mamba_gator/handlers.py index 53c0d3ba..bd096bc9 100644 --- a/mamba_gator/handlers.py +++ b/mamba_gator/handlers.py @@ -349,13 +349,19 @@ async def get(self): """`GET /packages` Search for packages. Query arguments: + dependencies: 0 (default) or 1 query (str): optional string query """ + dependencies = self.get_query_argument("dependencies", 0) query = self.get_query_argument("query", "") - + idx = None - if query: # Specific search - idx = self._stack.put(self.env_manager.package_search, query) + if query: + if dependencies : + idx = self._stack.put(self.env_manager.pkg_depends, query) + + else: # Specific search + idx = self._stack.put(self.env_manager.package_search, query) else: # List all available cache_file = os.path.join(tempfile.gettempdir(), AVAILABLE_CACHE + ".json") diff --git a/mamba_gator/rest_api.yml b/mamba_gator/rest_api.yml index c3f9bc77..3fed0a15 100644 --- a/mamba_gator/rest_api.yml +++ b/mamba_gator/rest_api.yml @@ -201,6 +201,11 @@ paths: produces: - "application/json" parameters: + - name: "dependencies" + in: "query" + description: "Whether to return the package dependencies or doing a generic search" + type: "number" + default: "0" - name: "query" in: "query" description: "Query string to pass to conda search" diff --git a/packages/common/package.json b/packages/common/package.json index 89ffa065..03abf6cb 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -50,7 +50,9 @@ "@lumino/coreutils": "^1.3.0", "@lumino/signaling": "^1.2.0", "@lumino/widgets": "^1.6.0", + "d3": "^5.5.0", "jupyterlab_toastify": "^4.1.2", + "react-d3-graph": "^2.5.0", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.6", "semver": "^6.0.0||^7.0.0", @@ -62,6 +64,7 @@ "@jupyterlab/testutils": "^2.2.2", "@types/jest": "^25", "@types/react": "~16.9.0", + "@types/react-d3-graph": "^2.3.4", "@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-window": "^1.8.2", "@types/semver": "^7.3.1", diff --git a/packages/common/src/components/CondaPkgList.tsx b/packages/common/src/components/CondaPkgList.tsx index 337ea061..662d1064 100644 --- a/packages/common/src/components/CondaPkgList.tsx +++ b/packages/common/src/components/CondaPkgList.tsx @@ -42,6 +42,10 @@ export interface IPkgListProps { * Package item version selection handler */ onPkgChange: (pkg: Conda.IPackage, version: string) => void; + /** + * Package item graph dependencies handler + */ + onPkgGraph: (pkg: Conda.IPackage) => void; } /** React component for the package list */ @@ -157,6 +161,21 @@ export class CondaPkgList extends React.Component { return {pkg.name}; }; + protected versionRender = (pkg: Conda.IPackage): JSX.Element => ( + { + evt.stopPropagation(); + this.props.onPkgGraph(pkg); + }} + rel="noopener noreferrer" + title="Show dependency graph" + > + {pkg.version_installed} + + ); + protected rowClassName = (index: number, pkg: Conda.IPackage): string => { if (index >= 0) { const isSelected = this.isSelected(pkg); @@ -194,9 +213,7 @@ export class CondaPkgList extends React.Component { )}
- - {pkg.version_installed} - + {this.versionRender(pkg)}
{this.changeRender(pkg)} diff --git a/packages/common/src/components/CondaPkgPanel.tsx b/packages/common/src/components/CondaPkgPanel.tsx index 400aef90..a21aafa1 100644 --- a/packages/common/src/components/CondaPkgPanel.tsx +++ b/packages/common/src/components/CondaPkgPanel.tsx @@ -1,4 +1,4 @@ -import { showDialog } from '@jupyterlab/apputils'; +import { showDialog, Dialog } from '@jupyterlab/apputils'; import { INotification } from 'jupyterlab_toastify'; import * as React from 'react'; import semver from 'semver'; @@ -10,6 +10,7 @@ import { PACKAGE_TOOLBAR_HEIGHT, PkgFilters } from './CondaPkgToolBar'; +import { PkgGraphWidget } from './PkgGraph'; // Minimal panel width to show package description const PANEL_SMALL_WIDTH = 500; @@ -239,6 +240,14 @@ export class CondaPkgPanel extends React.Component< }); } + handleDependenciesGraph = (pkg: Conda.IPackage): void => { + showDialog({ + title: pkg.name, + body: new PkgGraphWidget(this._model, pkg.name), + buttons: [Dialog.okButton()] + }); + }; + handleSearch(event: any): void { if (this.state.isApplyingChanges) { return; @@ -505,6 +514,7 @@ export class CondaPkgPanel extends React.Component< packages={searchPkgs} onPkgClick={this.handleClick} onPkgChange={this.handleVersionSelection} + onPkgGraph={this.handleDependenciesGraph} />
); diff --git a/packages/common/src/components/PkgGraph.tsx b/packages/common/src/components/PkgGraph.tsx new file mode 100644 index 00000000..af15cff8 --- /dev/null +++ b/packages/common/src/components/PkgGraph.tsx @@ -0,0 +1,174 @@ +import { ReactWidget } from '@jupyterlab/apputils'; + +import { + Graph, + GraphData, + GraphNode, + GraphLink, + GraphConfiguration +} from 'react-d3-graph'; + +import * as React from 'react'; + +import { Conda } from '../tokens'; + +/** + * Package graph property + */ +export interface IPkgGraphProps { + /** + * Package manager for the selected environment + */ + pkgManager: Conda.IPackageManager; + /** + * Package name + */ + package: string; + /** + * Graph configuration + */ + config: GraphConfiguration | Record; +} + +/** + * Package graph state + */ +export interface IPkgGraphState { + /** + * Graph data + */ + data: GraphData | null; + /** + * Error message + */ + error: React.ReactNode; +} + +export class PkgGraph extends React.Component { + public static defaultProps: Partial = { + config: { + directed: true, + collapsible: true, + highlightDegree: 1, + highlightOpacity: 0.1, + nodeHighlightBehavior: true, + linkHighlightBehavior: true, + node: { + color: 'var(--jp-brand-color1)', + highlightColor: 'var(--jp-brand-color2)', + highlightStrokeColor: 'var(--jp-brand-color2)', + highlightFontSize: 'var(--jp-ui-font-size0)', + fontSize: '--jp-ui-font-size0', + fontColor: 'var(--jp-ui-font-color1)' + }, + link: { + highlightColor: 'var(--jp-brand-color2)' + } + } + }; + + constructor(props: IPkgGraphProps) { + super(props); + this.state = { + data: null, + error: null + }; + } + + componentDidMount(): void { + this._updatePackages(); + } + + componentDidUpdate(prevProps: IPkgGraphProps): void { + if (this.props.package !== prevProps.package) { + this._updatePackages(); + } + } + + private async _updatePackages(): Promise { + try { + const available = await this.props.pkgManager.getDependencies( + this.props.package, + true + ); + const data: GraphData = { nodes: [], links: [] }; + let error: React.ReactNode = null; + if (available[this.props.package] === null) { + // Manager does not support dependency query + error = ( + + Please install{' '} + + mamba + {' '} + manager to resolve dependencies. + + ); + } else { + Object.keys(available).forEach(key => { + if (key === this.props.package) { + data.nodes.push({ id: key, color: 'orange' }); + } else { + data.nodes.push({ id: key }); + } + + available[key].forEach(dep => { + const dependencie = dep.split(' ')[0]; + if (!data.nodes.find(value => value.id === dependencie)) { + data.nodes.push({ id: dependencie }); + } + data.links.push({ source: key, target: dependencie }); + }); + }); + if (data.nodes.length === 0) { + error = This is a pip package; + } + } + this.setState({ data, error }); + } catch (error) { + if (error.message !== 'cancelled') { + console.error('Error when looking for dependencies.', error); + } + } + } + + render(): JSX.Element { + return ( +
+ {this.state.data === null ? ( + Loading dependencies + ) : ( +
+ {this.state.error || ( + + )} +
+ )} +
+ ); + } +} + +export class PkgGraphWidget extends ReactWidget { + constructor(pkgManager: Conda.IPackageManager, pkg: string) { + super(); + this._package = pkg; + this._pkgManager = pkgManager; + } + + render(): JSX.Element { + return ; + } + + private _pkgManager: Conda.IPackageManager; + private _package: string; +} diff --git a/packages/common/src/services.ts b/packages/common/src/services.ts index c68d4a3b..94420ac6 100644 --- a/packages/common/src/services.ts +++ b/packages/common/src/services.ts @@ -54,6 +54,20 @@ namespace RESTAPI { } } +/** + * Cancellable actions + */ +export interface ICancellableAction { + /** + * Type of cancellable action + */ + type: string; + /** + * Cancellable function + */ + cancel: () => void; +} + /** * Conda Environment Manager */ @@ -435,7 +449,7 @@ export class CondaPackage implements Conda.IPackageManager { return Promise.resolve([]); } - this._cancelTasks(); + this._cancelTasks('default'); try { const request: RequestInit = { @@ -447,7 +461,7 @@ export class CondaPackage implements Conda.IPackageManager { URLExt.join('conda', 'environments', theEnvironment), request ); - const idx = this._cancellableStack.push(cancel) - 1; + const idx = this._cancellableStack.push({ type: 'default', cancel }) - 1; const response = await promise; this._cancellableStack.splice(idx, 1); const data = (await response.json()) as { @@ -630,7 +644,7 @@ export class CondaPackage implements Conda.IPackageManager { return Promise.resolve([]); } - this._cancelTasks(); + this._cancelTasks('default'); try { const request: RequestInit = { @@ -641,7 +655,7 @@ export class CondaPackage implements Conda.IPackageManager { URLExt.objectToQueryString({ status: 'has_update' }), request ); - const idx = this._cancellableStack.push(cancel) - 1; + const idx = this._cancellableStack.push({ type: 'default', cancel }) - 1; const response = await promise; this._cancellableStack.splice(idx, 1); const data = (await response.json()) as { @@ -724,6 +738,36 @@ export class CondaPackage implements Conda.IPackageManager { } } + async getDependencies( + pkg: string, + cancellable = true + ): Promise { + this._cancelTasks('getDependencies'); + + const request: RequestInit = { + method: 'GET' + }; + + const { promise, cancel } = Private.requestServer( + URLExt.join('conda', 'packages') + + URLExt.objectToQueryString({ dependencies: 1, query: pkg }), + request + ); + + let idx: number; + if (cancellable) { + idx = + this._cancellableStack.push({ type: 'getDependencies', cancel }) - 1; + } + + const response = await promise; + if (idx) { + this._cancellableStack.splice(idx, 1); + } + const data = (await response.json()) as Conda.IPackageDeps; + return Promise.resolve(data); + } + async refreshAvailablePackages(cancellable = true): Promise { await this._getAvailablePackages(true, cancellable); } @@ -747,7 +791,7 @@ export class CondaPackage implements Conda.IPackageManager { force = false, cancellable = true ): Promise> { - this._cancelTasks(); + this._cancelTasks('default'); if (CondaPackage._availablePackages === null || force) { const request: RequestInit = { @@ -760,7 +804,7 @@ export class CondaPackage implements Conda.IPackageManager { ); let idx: number; if (cancellable) { - idx = this._cancellableStack.push(cancel) - 1; + idx = this._cancellableStack.push({ type: 'default', cancel }) - 1; } const response = await promise; if (idx) { @@ -779,14 +823,16 @@ export class CondaPackage implements Conda.IPackageManager { /** * Cancel optional tasks */ - private _cancelTasks(): void { + private _cancelTasks(type: string): void { if (this._cancellableStack.length > 0) { const tasks = this._cancellableStack.splice( 0, this._cancellableStack.length ); - tasks.forEach(task => { - task(); + tasks.forEach(action => { + if (action.type === type) { + action.cancel(); + } }); } } @@ -795,7 +841,7 @@ export class CondaPackage implements Conda.IPackageManager { CondaPackage, Conda.IPackageChange > = new Signal(this); - private _cancellableStack: Array<() => void> = []; + private _cancellableStack: Array = []; private static _availablePackages: Array = null; private static _hasDescription = false; } diff --git a/packages/common/src/tokens.ts b/packages/common/src/tokens.ts index 5b7716a3..febf18ed 100644 --- a/packages/common/src/tokens.ts +++ b/packages/common/src/tokens.ts @@ -192,6 +192,18 @@ export namespace Conda { * @param environment Environment name */ remove(packages: Array, environment?: string): Promise; + /** + * Get packages dependencies list. + * + * @param package Package name + * @param cancellable Can this asynchronous action be cancelled? + * + * @returns The package list + */ + getDependencies( + pkg: string, + cancellable: boolean + ): Promise; /** * Signal emitted when some package actions are executed. */ @@ -233,6 +245,12 @@ export namespace Conda { version_selected?: string; updatable?: boolean; } + /** + * Packages dependencies + */ + export interface IPackageDeps { + [package_name: string]: string[]; + } export interface IPackageChange { /** diff --git a/yarn.lock b/yarn.lock index 9f1daf28..e2774aa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2706,6 +2706,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/react-d3-graph@^2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/react-d3-graph/-/react-d3-graph-2.3.4.tgz#c1e1d73749a0e00727c1103dabfc6618d92b7b75" + integrity sha512-b8PYdASWJAT+fE126e2ib+HT39mXv9UFYAE5zmlVzh551xAvIxYsx7cWkfOV9PSqaYJJbfAM/WtUob9CTN9saw== + dependencies: + "@types/react" "*" + "@types/react-virtualized-auto-sizer@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.0.tgz#fc32f30a8dab527b5816f3a757e1e1d040c8f272" @@ -4094,16 +4101,16 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@2, commander@^2.18.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@2.17.x: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.18.0, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" @@ -4484,6 +4491,254 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" + integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== + +d3-axis@1: + version "1.0.12" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9" + integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ== + +d3-brush@1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b" + integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA== + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3-chord@1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f" + integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA== + dependencies: + d3-array "1" + d3-path "1" + +d3-collection@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" + integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== + +d3-color@1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== + +d3-contour@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3" + integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg== + dependencies: + d3-array "^1.1.1" + +d3-dispatch@1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" + integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== + +d3-drag@1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70" + integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w== + dependencies: + d3-dispatch "1" + d3-selection "1" + +d3-dsv@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c" + integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g== + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +d3-ease@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2" + integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ== + +d3-fetch@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7" + integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA== + dependencies: + d3-dsv "1" + +d3-force@1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b" + integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg== + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-quadtree "1" + d3-timer "1" + +d3-format@1: + version "1.4.5" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" + integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== + +d3-geo@1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f" + integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg== + dependencies: + d3-array "1" + +d3-hierarchy@1: + version "1.1.9" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" + integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== + +d3-interpolate@1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" + integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== + dependencies: + d3-color "1" + +d3-path@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-polygon@1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e" + integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ== + +d3-quadtree@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135" + integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA== + +d3-random@1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291" + integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ== + +d3-scale-chromatic@1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98" + integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg== + dependencies: + d3-color "1" + d3-interpolate "1" + +d3-scale@2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" + integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-selection@1, d3-selection@^1.1.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c" + integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg== + +d3-shape@1: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-time-format@2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850" + integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ== + dependencies: + d3-time "1" + +d3-time@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" + integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== + +d3-timer@1: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" + integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== + +d3-transition@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398" + integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA== + dependencies: + d3-color "1" + d3-dispatch "1" + d3-ease "1" + d3-interpolate "1" + d3-selection "^1.1.0" + d3-timer "1" + +d3-voronoi@1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" + integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg== + +d3-zoom@1: + version "1.8.3" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a" + integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ== + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3@^5.5.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877" + integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw== + dependencies: + d3-array "1" + d3-axis "1" + d3-brush "1" + d3-chord "1" + d3-collection "1" + d3-color "1" + d3-contour "1" + d3-dispatch "1" + d3-drag "1" + d3-dsv "1" + d3-ease "1" + d3-fetch "1" + d3-force "1" + d3-format "1" + d3-geo "1" + d3-hierarchy "1" + d3-interpolate "1" + d3-path "1" + d3-polygon "1" + d3-quadtree "1" + d3-random "1" + d3-scale "2" + d3-scale-chromatic "1" + d3-selection "1" + d3-shape "1" + d3-time "1" + d3-time-format "2" + d3-timer "1" + d3-transition "1" + d3-voronoi "1" + d3-zoom "1" + dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" @@ -6343,7 +6598,7 @@ husky@^3.0.9: run-node "^1.0.0" slash "^3.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -9576,6 +9831,11 @@ raw-loader@~4.0.0: loader-utils "^2.0.0" schema-utils "^3.0.0" +react-d3-graph@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/react-d3-graph/-/react-d3-graph-2.5.0.tgz#b5ce024472c660dcf1146336f424fab8df551431" + integrity sha512-/DSDbBXlmHLMb1P6Ac/d3Nf/2IE6Eh7PBuyOpW3uZMvJl+crViJ0NhAl6vVkGKSpPvRWuV7Pn4749Wry5i6HoA== + react-dom@~16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962" @@ -10142,6 +10402,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= + rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0: version "6.6.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"