diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 6bb07489ec..d7ed058fa3 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -58,6 +58,7 @@ jobs: - script: make test-extensions displayName: Run In-tree Extension tests - bash: | + set -e rm -rf extensions/telemetry make integration-win git checkout extensions/telemetry @@ -102,6 +103,7 @@ jobs: - script: make test-extensions displayName: Run In-tree Extension tests - bash: | + set -e rm -rf extensions/telemetry make integration-mac git checkout extensions/telemetry @@ -159,6 +161,7 @@ jobs: sudo chown -R $USER $HOME/.kube $HOME/.minikube displayName: Install integration test dependencies - bash: | + set -e rm -rf extensions/telemetry xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux git checkout extensions/telemetry diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 0000000000..a77a36c653 --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,17 @@ +# See https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates +# for config options + +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 4 + reviewers: + - "lensapp/lens-maintainers" + labels: + - "dependencies" + versioning-strategy: + lockfile-only: false + increase: true diff --git a/.eslintrc.js b/.eslintrc.js index 3fd52c2465..57ee07348f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,6 +46,8 @@ module.exports = { "avoidEscape": true, "allowTemplateLiterals": true, }], + "linebreak-style": ["error", "unix"], + "eol-last": ["error", "always"], "semi": ["error", "always"], "object-shorthand": "error", "prefer-template": "error", @@ -101,6 +103,8 @@ module.exports = { }], "semi": "off", "@typescript-eslint/semi": ["error"], + "linebreak-style": ["error", "unix"], + "eol-last": ["error", "always"], "object-shorthand": "error", "prefer-template": "error", "template-curly-spacing": "error", @@ -162,6 +166,8 @@ module.exports = { }], "semi": "off", "@typescript-eslint/semi": ["error"], + "linebreak-style": ["error", "unix"], + "eol-last": ["error", "always"], "object-shorthand": "error", "prefer-template": "error", "template-curly-spacing": "error", diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000000..02ae36727e --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,30 @@ +exclude-labels: + - 'skip-changelog' +categories: + - title: '🚀 Features' + labels: + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'bug' + - title: '🧰 Maintenance' + labels: + - 'chore' + - 'area/ci' + - 'area/tests' + - 'dependencies' + +template: | + ## Changes since $PREVIOUS_TAG + + $CHANGES + + ### Download + + - Lens v$RESOLVED_VERSION - Linux + - [AppImage](https://github.com/lensapp/lens/releases/download/v$RESOLVED_VERSION/Lens-$RESOLVED_VERSION.x86_64.AppImage) + - [DEB](https://github.com/lensapp/lens/releases/download/v$RESOLVED_VERSION/Lens-$RESOLVED_VERSION.amd64.deb) + - [RPM](https://github.com/lensapp/lens/releases/download/v$RESOLVED_VERSION/Lens-$RESOLVED_VERSION.x86_64.rpm) + - [Snapcraft](https://snapcraft.io/kontena-lens) + - [Lens v$RESOLVED_VERSION - MacOS](https://github.com/lensapp/lens/releases/download/v$RESOLVED_VERSION/Lens-$RESOLVED_VERSION.dmg) + - [Lens v$RESOLVED_VERSION - Windows](https://github.com/lensapp/lens/releases/download/v$RESOLVED_VERSION/Lens-Setup-$RESOLVED_VERSION.exe) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000000..ec49fec6e5 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,16 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/Makefile b/Makefile index 362ef3b830..000682e039 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ endif binaries/client: yarn download-bins -node_modules: +node_modules: yarn.lock yarn install --frozen-lockfile yarn check --verify-tree --integrity diff --git a/__mocks__/imageMock.ts b/__mocks__/imageMock.ts index a099545376..f053ebf797 100644 --- a/__mocks__/imageMock.ts +++ b/__mocks__/imageMock.ts @@ -1 +1 @@ -module.exports = {}; \ No newline at end of file +module.exports = {}; diff --git a/__mocks__/styleMock.ts b/__mocks__/styleMock.ts index a099545376..f053ebf797 100644 --- a/__mocks__/styleMock.ts +++ b/__mocks__/styleMock.ts @@ -1 +1 @@ -module.exports = {}; \ No newline at end of file +module.exports = {}; diff --git a/build/icons/512x512.png b/build/icons/512x512.png index 2c953f6efd..e08b9f5b15 100644 Binary files a/build/icons/512x512.png and b/build/icons/512x512.png differ diff --git a/build/icons/512x512@2x.png b/build/icons/512x512@2x.png new file mode 100644 index 0000000000..da0aa8ae80 Binary files /dev/null and b/build/icons/512x512@2x.png differ diff --git a/docs/extensions/capabilities/color-reference.md b/docs/extensions/capabilities/color-reference.md index 660e0fe067..6a38ba861c 100644 --- a/docs/extensions/capabilities/color-reference.md +++ b/docs/extensions/capabilities/color-reference.md @@ -43,6 +43,7 @@ You can use theme-based CSS Variables to style an extension according to the act ## Button Colors - `--buttonPrimaryBackground`: button background color for primary actions. - `--buttonDefaultBackground`: default button background color. +- `--buttonLightBackground`: light button background color. - `--buttonAccentBackground`: accent button background color. - `--buttonDisabledBackground`: disabled button background color. diff --git a/docs/extensions/capabilities/common-capabilities.md b/docs/extensions/capabilities/common-capabilities.md index 6fdb87db82..1410dfc148 100644 --- a/docs/extensions/capabilities/common-capabilities.md +++ b/docs/extensions/capabilities/common-capabilities.md @@ -40,7 +40,7 @@ This extension can register custom app menus that will be displayed on OS native Example: -``` typescript +```typescript import { LensMainExtension, windowManager } from "@k8slens/extensions" export default class ExampleMainExtension extends LensMainExtension { @@ -92,7 +92,7 @@ export default class ExampleMainExtension extends LensRendererExtension { This extension can register custom global pages (views) to Lens's main window. The global page is a full-screen page that hides all other content from a window. -``` typescript +```typescript import React from "react" import { Component, LensRendererExtension } from "@k8slens/extensions" import { ExamplePage } from "./src/example-page" @@ -123,7 +123,7 @@ export default class ExampleRendererExtension extends LensRendererExtension { This extension can register custom app preferences. It is responsible for storing a state for custom preferences. -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions" import { myCustomPreferencesStore } from "./src/my-custom-preferences-store" @@ -147,7 +147,7 @@ export default class ExampleRendererExtension extends LensRendererExtension { This extension can register custom cluster pages. These pages are visible in a cluster menu when a cluster is opened. -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions"; import { ExampleIcon, ExamplePage } from "./src/page" @@ -180,7 +180,7 @@ export default class ExampleExtension extends LensRendererExtension { This extension can register installable features for a cluster. These features are visible in the "Cluster Settings" page. -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions" import { MyCustomFeature } from "./src/my-custom-feature" @@ -209,18 +209,20 @@ export default class ExampleExtension extends LensRendererExtension { This extension can register custom icons and text to a status bar area. -``` typescript +```typescript import React from "react"; import { Component, LensRendererExtension, Navigation } from "@k8slens/extensions"; export default class ExampleExtension extends LensRendererExtension { statusBarItems = [ { - item: ( -
this.navigate("/example-page")} > - -
- ) + components: { + Item: ( +
this.navigate("/example-page")} > + +
+ ) + } } ] } @@ -231,7 +233,7 @@ export default class ExampleExtension extends LensRendererExtension { This extension can register custom menu items (actions) for specified Kubernetes kinds/apiVersions. -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions"; import { CustomMenuItem, CustomMenuItemProps } from "./src/custom-menu-item" @@ -254,7 +256,7 @@ export default class ExampleExtension extends LensRendererExtension { This extension can register custom details (content) for specified Kubernetes kinds/apiVersions. -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions"; import { CustomKindDetails, CustomKindDetailsProps } from "./src/custom-kind-details" diff --git a/docs/extensions/guides/generator.md b/docs/extensions/guides/generator.md index 46524e97d5..3433f98e6f 100644 --- a/docs/extensions/guides/generator.md +++ b/docs/extensions/guides/generator.md @@ -42,7 +42,7 @@ Next, you'll try changing the way the new menu item appears in the UI. You'll ch Open `my-first-lens-ext/renderer.tsx` and change the value of `title` from `"Hello World"` to `"Hello Lens"`: -```tsx +```typescript clusterPageMenus = [ { target: { pageId: "hello" }, diff --git a/docs/extensions/guides/renderer-extension.md b/docs/extensions/guides/renderer-extension.md index c3ff953b2a..ffe7ca12e9 100644 --- a/docs/extensions/guides/renderer-extension.md +++ b/docs/extensions/guides/renderer-extension.md @@ -22,7 +22,7 @@ All UI elements are based on React components. To create a renderer extension, extend the `LensRendererExtension` class: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; export default class ExampleExtensionMain extends LensRendererExtension { @@ -44,7 +44,7 @@ Two methods enable you to run custom code: `onActivate()` and `onDeactivate()`. 1. Navigate to **File** > **Extensions** in the top menu bar. (On Mac, it is **Lens** > **Extensions**.) 2. Click **Disable** on the extension you want to disable. -The example above logs messages when the extension is enabled and disabled. +The example above logs messages when the extension is enabled and disabled. ### `clusterPages` @@ -52,7 +52,7 @@ Cluster pages appear in the cluster dashboard. Use cluster pages to display info Add a cluster page definition to a `LensRendererExtension` subclass with the following example: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import { ExampleIcon, ExamplePage } from "./page" import React from "react" @@ -77,7 +77,7 @@ export default class ExampleExtension extends LensRendererExtension { `ExamplePage` in the example above can be defined in `page.tsx`: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import React from "react" @@ -102,7 +102,7 @@ The above example shows how to create a cluster page, but not how to make that p By expanding on the above example, you can add a cluster page menu item to the `ExampleExtension` definition: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import { ExampleIcon, ExamplePage } from "./page" import React from "react" @@ -133,14 +133,14 @@ export default class ExampleExtension extends LensRendererExtension { * `target` links to the relevant cluster page using `pageId`. * `pageId` takes the value of the relevant cluster page's `id` property. -* `title` sets the name of the cluster page menu item that will appear in the left side menu. +* `title` sets the name of the cluster page menu item that will appear in the left side menu. * `components` is used to set an icon that appears to the left of the `title` text in the left side menu. The above example creates a menu item that reads **Hello World**. When users click **Hello World**, the cluster dashboard will show the contents of `Example Page`. This example requires the definition of another React-based component, `ExampleIcon`, which has been added to `page.tsx`, as follows: -``` typescript +```typescript import { LensRendererExtension, Component } from "@k8slens/extensions"; import React from "react" @@ -167,7 +167,7 @@ Lens includes various built-in components available for extension developers to `clusterPageMenus` can also be used to define sub menu items, so that you can create groups of cluster pages. The following example groups two sub menu items under one parent menu item: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import { ExampleIcon, ExamplePage } from "./page" import React from "react" @@ -216,21 +216,29 @@ export default class ExampleExtension extends LensRendererExtension { } ``` -The above defines two cluster pages and three cluster page menu objects. The three cluster page menu objects include one parent menu item and two sub menu items. Parent items require an `id` value, whereas sub items require a `parentId` value. The value of the sub item `parentId` will match the value of the corresponding parent item `id`. Parent items don't require a `target` value. Assign values to the remaining properties as explained above. +The above defines two cluster pages and three cluster page menu objects. +The cluster page definitions are straightforward. +The three cluster page menu objects include one parent menu item and two sub menu items. +The first cluster page menu object defines the parent of a foldout submenu. +Setting the `id` field in a cluster page menu definition implies that it is defining a foldout submenu. +Also note that the `target` field is not specified (it is ignored if the `id` field is specified). +This cluster page menu object specifies the `title` and `components` fields, which are used in displaying the menu item in the cluster dashboard sidebar. +Initially the submenu is hidden. +Activating this menu item toggles on and off the appearance of the submenu below it. +The remaining two cluster page menu objects define the contents of the submenu. +A cluster page menu object is defined to be a submenu item by setting the `parentId` field to the id of the parent of a foldout submenu, `"example"` in this case. This is what the example will look like, including how the menu item will appear in the secondary left nav: -![clusterPageMenus](images/clusterpagemenus.png) - ### `globalPages` -Global pages are independent of the cluster dashboard and can fill the entire Lens UI. Their primary use is to display information and provide functionality across clusters, including customized data and functionality unique to your extension. +Global pages are independent of the cluster dashboard and can fill the entire Lens UI. Their primary use is to display information and provide functionality across clusters, including customized data and functionality unique to your extension. Typically, you would use a [global page menu](#globalpagemenus) located in the left nav to trigger a global page. You can also trigger a global page with a [custom app menu selection](../main-extension#appmenus) from a Main Extension or a [custom status bar item](#statusbaritems). Unlike cluster pages, users can trigger global pages even when there is no active cluster. The following example defines a `LensRendererExtension` subclass with a single global page definition: -``` typescript +```typescript import { LensRendererExtension } from '@k8slens/extensions'; import { HelpPage } from './page'; import React from 'react'; @@ -255,7 +263,7 @@ export default class HelpExtension extends LensRendererExtension { `HelpPage` in the example above can be defined in `page.tsx`: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import React from "react" @@ -284,7 +292,7 @@ This example code shows how to create a global page, but not how to make that pa By expanding on the above example, you can add a global page menu item to the `HelpExtension` definition: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import { HelpIcon, HelpPage } from "./page" import React from "react" @@ -322,7 +330,7 @@ The above example creates a "Help" icon menu item. When users click the icon, th This example requires the definition of another React-based component, `HelpIcon`. Update `page.tsx` from the example above with the `HelpIcon` definition, as follows: -``` typescript +```typescript import { LensRendererExtension, Component } from "@k8slens/extensions"; import React from "react" @@ -359,7 +367,7 @@ They can be installed and uninstalled by the Lens user from the cluster **Settin The following example shows how to add a cluster feature as part of a `LensRendererExtension`: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions" import { ExampleFeature } from "./src/example-feature" import React from "react" @@ -388,7 +396,7 @@ The properties of the `clusterFeatures` array objects are defined as follows: * `title` and `components.Description` provide content that appears on the cluster settings page, in the **Features** section. * `feature` specifies an instance which extends the abstract class `ClusterFeature.Feature`, and specifically implements the following methods: -``` typescript +```typescript abstract install(cluster: Cluster): Promise; abstract upgrade(cluster: Cluster): Promise; abstract uninstall(cluster: Cluster): Promise; @@ -397,13 +405,19 @@ The properties of the `clusterFeatures` array objects are defined as follows: The four methods listed above are defined as follows: -* The `install()` method installs Kubernetes resources using the `applyResources()` method, or by directly accessing the [Kubernetes API](../api/README.md). This method is typically called when a user indicates that they want to install the feature (i.e., by clicking **Install** for the feature in the cluster settings page). +* The `install()` method installs Kubernetes resources using the `applyResources()` method, or by directly accessing the [Kubernetes API](../api/README.md). +This method is typically called when a user indicates that they want to install the feature (i.e., by clicking **Install** for the feature in the cluster settings page). -* The `upgrade()` method upgrades the Kubernetes resources already installed, if they are relevant to the feature. This method is typically called when a user indicates that they want to upgrade the feature (i.e., by clicking **Upgrade** for the feature in the cluster settings page). +* The `upgrade()` method upgrades the Kubernetes resources already installed, if they are relevant to the feature. +This method is typically called when a user indicates that they want to upgrade the feature (i.e., by clicking **Upgrade** for the feature in the cluster settings page). -* The `uninstall()` method uninstalls Kubernetes resources using the [Kubernetes API](../api/README.md). This method is typically called when a user indicates that they want to uninstall the feature (i.e., by clicking **Uninstall** for the feature in the cluster settings page). +* The `uninstall()` method uninstalls Kubernetes resources using the [Kubernetes API](../api/README.md). +This method is typically called when a user indicates that they want to uninstall the feature (i.e., by clicking **Uninstall** for the feature in the cluster settings page). -* The `updateStatus()` method provides the current status information in the `status` field of the `ClusterFeature.Feature` parent class. Lens periodically calls this method to determine details about the feature's current status. Consider using the following properties with `updateStatus()`: +* The `updateStatus()` method provides the current status information in the `status` field of the `ClusterFeature.Feature` parent class. +Lens periodically calls this method to determine details about the feature's current status. +The implementation of this method should uninstall Kubernetes resources using the Kubernetes api (`K8sApi`) +Consider using the following properties with `updateStatus()`: * `status.currentVersion` and `status.latestVersion` may be displayed by Lens in the feature's description. @@ -413,7 +427,7 @@ The four methods listed above are defined as follows: The following shows a very simple implementation of a `ClusterFeature`: -``` typescript +```typescript import { ClusterFeature, Store, K8sApi } from "@k8slens/extensions"; import * as path from "path"; @@ -487,7 +501,7 @@ The Lens **Preferences** page is a built-in global page. You can use Lens extens The following example demonstrates adding a custom preference: -``` typescript +```typescript import { LensRendererExtension } from "@k8slens/extensions"; import { ExamplePreferenceHint, ExamplePreferenceInput } from "./src/example-preference"; import { observable } from "mobx"; @@ -513,7 +527,7 @@ export default class ExampleRendererExtension extends LensRendererExtension { * `title` sets the heading text displayed on the Preferences page. * `components` specifies two `React.Component` objects that define the interface for the preference. - * `Input` specifies an interactive input element for the preference. + * `Input` specifies an interactive input element for the preference. * `Hint` provides descriptive information for the preference, shown below the `Input` element. !!! note @@ -524,7 +538,7 @@ export default class ExampleRendererExtension extends LensRendererExtension { In this example `ExamplePreferenceInput`, `ExamplePreferenceHint`, and `ExamplePreferenceProps` are defined in `./src/example-preference.tsx` as follows: -``` typescript +```typescript import { Component } from "@k8slens/extensions"; import { observer } from "mobx-react"; import React from "react"; @@ -580,7 +594,7 @@ The status bar is the blue strip along the bottom of the Lens UI. `statusBarItem The following example adds a `statusBarItems` definition and a `globalPages` definition to a `LensRendererExtension` subclass. It configures the status bar item to navigate to the global page upon activation (normally a mouse click): -``` typescript +```typescript import { LensRendererExtension } from '@k8slens/extensions'; import { HelpIcon, HelpPage } from "./page" import React from 'react'; @@ -597,15 +611,17 @@ export default class HelpExtension extends LensRendererExtension { statusBarItems = [ { - item: ( -
this.navigate("help")} - > - - My Status Bar Item -
- ), + components: { + Item: ( +
this.navigate("help")} + > + + My Status Bar Item +
+ ) + }, }, ]; } @@ -613,7 +629,7 @@ export default class HelpExtension extends LensRendererExtension { The properties of the `statusBarItems` array objects are defined as follows: -* `item` specifies the `React.Component` that will be shown on the status bar. By default, items are added starting from the right side of the status bar. Due to limited space in the status bar, `item` will typically specify only an icon or a short string of text. The example above reuses the `HelpIcon` from the [`globalPageMenus` guide](#globalpagemenus). +* `Item` specifies the `React.Component` that will be shown on the status bar. By default, items are added starting from the right side of the status bar. Due to limited space in the status bar, `Item` will typically specify only an icon or a short string of text. The example above reuses the `HelpIcon` from the [`globalPageMenus` guide](#globalpagemenus). * `onClick` determines what the `statusBarItem` does when it is clicked. In the example, `onClick` is set to a function that calls the `LensRendererExtension` `navigate()` method. `navigate` takes the `id` of the associated global page as a parameter. Thus, clicking the status bar item activates the associated global pages. ### `kubeObjectMenuItems` @@ -629,7 +645,7 @@ They also appear on the title bar of the details page for specific resources: The following example shows how to add a `kubeObjectMenuItems` for namespace resources with an associated action: -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions"; import { NamespaceMenuItem } from "./src/namespace-menu-item" @@ -650,7 +666,7 @@ export default class ExampleExtension extends LensRendererExtension { `kubeObjectMenuItems` is an array of objects matching the `KubeObjectMenuRegistration` interface. The example above adds a menu item for namespaces in the cluster dashboard. The properties of the `kubeObjectMenuItems` array objects are defined as follows: -* `kind` specifies the Kubernetes resource type the menu item will apply to. +* `kind` specifies the Kubernetes resource type the menu item will apply to. * `apiVersion` specifies the Kubernetes API version number to use with the resource type. * `components` defines the menu item's appearance and behavior. * `MenuItem` provides a function that returns a `React.Component` given a set of menu item properties. In this example a `NamespaceMenuItem` object is returned. @@ -702,7 +718,7 @@ These custom details appear on the details page for a specific resource, such as The following example shows how to use `kubeObjectDetailItems` to add a tabulated list of pods to the Namespace resource details page: -``` typescript +```typescript import React from "react" import { LensRendererExtension } from "@k8slens/extensions"; import { NamespaceDetailsItem } from "./src/namespace-details-item" @@ -757,9 +773,20 @@ export class NamespaceDetailsItem extends React.Component>`, it can access the current namespace object (type `K8sApi.Namespace`) through `this.props.object`. You can query this object for many details about the current namespace. In the example above, `componentDidMount()` gets the namespace's name using the `K8sApi.Namespace` `getName()` method. Use the namespace's name to limit the list of pods only to those in the relevant namespace. To get this list of pods, this example uses the Kubernetes pods API `K8sApi.podsApi.list()` method. The `K8sApi.podsApi` is automatically configured for the active cluster. +Since `NamespaceDetailsItem` extends `React.Component>`, it can access the current namespace object (type `K8sApi.Namespace`) through `this.props.object`. +You can query this object for many details about the current namespace. +In the example above, `componentDidMount()` gets the namespace's name using the `K8sApi.Namespace` `getName()` method. +Use the namespace's name to limit the list of pods only to those in the relevant namespace. +To get this list of pods, this example uses the Kubernetes pods API `K8sApi.podsApi.list()` method. +The `K8sApi.podsApi` is automatically configured for the active cluster. -Note that `K8sApi.podsApi.list()` is an asynchronous method. Getting the pods list should occur prior to rendering the `NamespaceDetailsItem`. It is a common technique in React development to await async calls in `componentDidMount()`. However, `componentDidMount()` is called right after the first call to `render()`. In order to effect a subsequent `render()` call, React must be made aware of a state change. Like in the [`appPreferences` guide](#apppreferences), [`mobx`](https://mobx.js.org/README.html) and [`mobx-react`](https://github.com/mobxjs/mobx-react#mobx-react) are used to ensure `NamespaceDetailsItem` renders when the pods list updates. This is done simply by marking the `pods` field as an `observable` and the `NamespaceDetailsItem` class itself as an `observer`. +Note that `K8sApi.podsApi.list()` is an asynchronous method. +Getting the pods list should occur prior to rendering the `NamespaceDetailsItem`. +It is a common technique in React development to await async calls in `componentDidMount()`. +However, `componentDidMount()` is called right after the first call to `render()`. +In order to effect a subsequent `render()` call, React must be made aware of a state change. +Like in the [`appPreferences` guide](#apppreferences), [`mobx`](https://mobx.js.org/README.html) and [`mobx-react`](https://github.com/mobxjs/mobx-react#mobx-react) are used to ensure `NamespaceDetailsItem` renders when the pods list updates. +This is done simply by marking the `pods` field as an `observable` and the `NamespaceDetailsItem` class itself as an `observer`. Finally, the `NamespaceDetailsItem` renders using the `render()` method. Details are placed in drawers, and using `Component.DrawerTitle` provides a separator from details above this one. @@ -820,4 +847,4 @@ Obtain the name, age, and status for each pod using the `K8sApi.Pod` methods. Co For each pod the name, age, and status are obtained using the `K8sApi.Pod` methods. The table is constructed using the `Component.Table` and related elements. -See [`Component` documentation](https://docs.k8slens.dev/master/extensions/api/modules/_renderer_api_components_/) for further details. \ No newline at end of file +See [`Component` documentation](https://docs.k8slens.dev/master/extensions/api/modules/_renderer_api_components_/) for further details. diff --git a/docs/extensions/testing-and-publishing/testing.md b/docs/extensions/testing-and-publishing/testing.md index ab10c5eab3..7b9d9723ba 100644 --- a/docs/extensions/testing-and-publishing/testing.md +++ b/docs/extensions/testing-and-publishing/testing.md @@ -10,7 +10,7 @@ For example, I have a component `GlobalPageMenuIcon` and want to test if `props. My component `GlobalPageMenuIcon` -```tsx +```typescript import React from "react" import { Component: { Icon } } from "@k8slens/extensions"; @@ -61,7 +61,7 @@ In the Renderer process, `console.log()` is printed in the Console in Developer ### Main Process Logs -Viewing the logs from the Main process is a little trickier, since they cannot be printed using Developer Tools. +Viewing the logs from the Main process is a little trickier, since they cannot be printed using Developer Tools. #### macOS diff --git a/extensions/example-extension/package-lock.json b/extensions/example-extension/package-lock.json index 954ba2e41f..16febd433c 100644 --- a/extensions/example-extension/package-lock.json +++ b/extensions/example-extension/package-lock.json @@ -2796,7 +2796,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3226,6 +3227,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4367,6 +4369,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4381,6 +4384,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4390,6 +4394,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4399,6 +4404,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4407,7 +4413,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5398,7 +5405,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6275,7 +6283,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/kube-object-event-status/src/resolver.tsx b/extensions/kube-object-event-status/src/resolver.tsx index 69691c2e79..5e9151288f 100644 --- a/extensions/kube-object-event-status/src/resolver.tsx +++ b/extensions/kube-object-event-status/src/resolver.tsx @@ -56,4 +56,4 @@ export function resolveStatusForCronJobs(cronJob: K8sApi.CronJob): K8sApi.KubeOb text: `${event.message}`, timestamp: event.metadata.creationTimestamp }; -} \ No newline at end of file +} diff --git a/extensions/license-menu-item/package-lock.json b/extensions/license-menu-item/package-lock.json index 071d2f62a6..5d6de53633 100644 --- a/extensions/license-menu-item/package-lock.json +++ b/extensions/license-menu-item/package-lock.json @@ -2868,7 +2868,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3298,6 +3299,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4460,6 +4462,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4474,6 +4477,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4483,6 +4487,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4492,6 +4497,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4500,7 +4506,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5516,7 +5523,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6406,7 +6414,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/metrics-cluster-feature/package-lock.json b/extensions/metrics-cluster-feature/package-lock.json index 334135b4eb..ea68169ca9 100644 --- a/extensions/metrics-cluster-feature/package-lock.json +++ b/extensions/metrics-cluster-feature/package-lock.json @@ -2816,7 +2816,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3246,6 +3247,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4394,6 +4396,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4408,6 +4411,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -5434,7 +5438,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6311,7 +6316,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/node-menu/package-lock.json b/extensions/node-menu/package-lock.json index 5e1eec009a..2595e825f7 100644 --- a/extensions/node-menu/package-lock.json +++ b/extensions/node-menu/package-lock.json @@ -2796,7 +2796,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3226,6 +3227,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4382,6 +4384,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4396,6 +4399,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4405,6 +4409,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4414,6 +4419,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4422,7 +4428,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5438,7 +5445,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6315,7 +6323,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/pod-menu/package-lock.json b/extensions/pod-menu/package-lock.json index ea98213fb3..4409d2a89d 100644 --- a/extensions/pod-menu/package-lock.json +++ b/extensions/pod-menu/package-lock.json @@ -626,7 +626,644 @@ }, "@k8slens/extensions": { "version": "file:../../src/extensions/npm/extensions", - "dev": true + "dev": true, + "requires": { + "@material-ui/core": "*", + "@types/node": "*", + "@types/react-select": "*", + "conf": "^7.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "@material-ui/core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.2.tgz", + "integrity": "sha512-/D1+AQQeYX/WhT/FUk78UCRj8ch/RCglsQLYujYTIqPSJlwZHKcvHidNeVhODXeApojeXjkl0tWdk5C9ofwOkQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.2", + "@material-ui/system": "^4.11.2", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styles": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.2.tgz", + "integrity": "sha512-xbItf8zkfD3FuGoD9f2vlcyPf9jTEtj9YTJoNNV+NMWaSAHXgrW6geqRoo/IwBuMjqpwqsZhct13e2nUyU9Ljw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.2.tgz", + "integrity": "sha512-BELFJEel5E+5DMiZb6XXT3peWRn6UixRvBtKwSxqntmD0+zwbbfCij6jtGwwdJhN1qX/aXrKu10zX31GBaeR7A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.2", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "dev": true + }, + "@material-ui/utils": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz", + "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, + "@types/node": { + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-select": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.1.2.tgz", + "integrity": "sha512-ygvR/2FL87R2OLObEWFootYzkvm67LRA+URYEAcBuvKk7IXmdsnIwSGm60cVXGaqkJQHozb2Cy1t94tCYb6rJA==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "@types/react-transition-group": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "dev": true + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true + }, + "conf": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-7.1.2.tgz", + "integrity": "sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==", + "dev": true, + "requires": { + "ajv": "^6.12.2", + "atomically": "^1.3.1", + "debounce-fn": "^4.0.0", + "dot-prop": "^5.2.0", + "env-paths": "^2.2.0", + "json-schema-typed": "^7.0.3", + "make-dir": "^3.1.0", + "onetime": "^5.1.0", + "pkg-up": "^3.1.0", + "semver": "^7.3.2" + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "csstype": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", + "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==", + "dev": true + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dev": true, + "requires": { + "mimic-fn": "^3.0.0" + } + }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, + "indefinite-observable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz", + "integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==", + "dev": true, + "requires": { + "symbol-observable": "1.2.0" + } + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "dev": true + }, + "jss": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.0.tgz", + "integrity": "sha512-B6151NvG+thUg3murLNHRPLxTLwQ13ep4SH5brj4d8qKtogOx/jupnpfkPGSHPqvcwKJaCLctpj2lEk+5yGwMw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "indefinite-observable": "^2.0.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "jss-plugin-camel-case": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.0.tgz", + "integrity": "sha512-GSjPL0adGAkuoqeYiXTgO7PlIrmjv5v8lA6TTBdfxbNYpxADOdGKJgIEkffhlyuIZHlPuuiFYTwUreLUmSn7rg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.5.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.0.tgz", + "integrity": "sha512-rsbTtZGCMrbcb9beiDd+TwL991NGmsAgVYH0hATrYJtue9e+LH/Gn4yFD1ENwE+3JzF3A+rPnM2JuD9L/SIIWw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-global": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.0.tgz", + "integrity": "sha512-FZd9+JE/3D7HMefEG54fEC0XiQ9rhGtDHAT/ols24y8sKQ1D5KIw6OyXEmIdKFmACgxZV2ARQ5pAUypxkk2IFQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-nested": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.0.tgz", + "integrity": "sha512-ejPlCLNlEGgx8jmMiDk/zarsCZk+DV0YqXfddpgzbO9Toamo0HweCFuwJ3ZO40UFOfqKwfpKMVH/3HUXgxkTMg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.0.tgz", + "integrity": "sha512-kTLRvrOetFKz5vM88FAhLNeJIxfjhCepnvq65G7xsAQ/Wgy7HwO1BS/2wE5mx8iLaAWC6Rj5h16mhMk9sKdZxg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.0.tgz", + "integrity": "sha512-jXINGr8BSsB13JVuK274oEtk0LoooYSJqTBCGeBu2cG/VJ3+4FPs1gwLgsq24xTgKshtZ+WEQMVL34OprLidRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.0.tgz", + "integrity": "sha512-rux3gmfwDdOKCLDx0IQjTwTm03IfBa+Rm/hs747cOw5Q7O3RaTUIMPKjtVfc31Xr/XI9Abz2XEupk1/oMQ7zRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.5.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } }, "@sinonjs/commons": { "version": "1.8.1", @@ -2796,7 +3433,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3226,6 +3864,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4382,6 +5021,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4396,6 +5036,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4405,6 +5046,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4414,6 +5056,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4422,7 +5065,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5438,7 +6082,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6315,7 +6960,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/pod-menu/src/logs-menu.tsx b/extensions/pod-menu/src/logs-menu.tsx index 706efcf128..1063207d0c 100644 --- a/extensions/pod-menu/src/logs-menu.tsx +++ b/extensions/pod-menu/src/logs-menu.tsx @@ -9,13 +9,9 @@ export class PodLogsMenu extends React.Component { Navigation.hideDetails(); const pod = this.props.object; - Component.createPodLogsTab({ - pod, - containers: pod.getContainers(), - initContainers: pod.getInitContainers(), + Component.logTabStore.createPodTab({ + selectedPod: pod, selectedContainer: container, - showTimestamps: false, - previous: false, }); } diff --git a/extensions/survey/main.ts b/extensions/survey/main.ts new file mode 100644 index 0000000000..6fc78f3554 --- /dev/null +++ b/extensions/survey/main.ts @@ -0,0 +1,9 @@ +import { LensMainExtension } from "@k8slens/extensions"; +import { surveyPreferencesStore } from "./src/survey-preferences-store"; + +export default class SurveyMainExtension extends LensMainExtension { + + async onActivate() { + await surveyPreferencesStore.loadExtension(this); + } +} diff --git a/extensions/survey/package-lock.json b/extensions/survey/package-lock.json new file mode 100644 index 0000000000..03edf5a3c2 --- /dev/null +++ b/extensions/survey/package-lock.json @@ -0,0 +1,7928 @@ +{ + "name": "lens-survey", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@k8slens/extensions": { + "version": "file:../../src/extensions/npm/extensions", + "dev": true, + "requires": { + "@material-ui/core": "*", + "@types/node": "*", + "@types/react-select": "*", + "conf": "^7.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "@material-ui/core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.2.tgz", + "integrity": "sha512-/D1+AQQeYX/WhT/FUk78UCRj8ch/RCglsQLYujYTIqPSJlwZHKcvHidNeVhODXeApojeXjkl0tWdk5C9ofwOkQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.2", + "@material-ui/system": "^4.11.2", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styles": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.2.tgz", + "integrity": "sha512-xbItf8zkfD3FuGoD9f2vlcyPf9jTEtj9YTJoNNV+NMWaSAHXgrW6geqRoo/IwBuMjqpwqsZhct13e2nUyU9Ljw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.2.tgz", + "integrity": "sha512-BELFJEel5E+5DMiZb6XXT3peWRn6UixRvBtKwSxqntmD0+zwbbfCij6jtGwwdJhN1qX/aXrKu10zX31GBaeR7A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.2", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "dev": true + }, + "@material-ui/utils": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz", + "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, + "@types/node": { + "version": "14.14.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", + "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + } + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-select": { + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.28.tgz", + "integrity": "sha512-Gfg3a/EPLyUQkfezcCQkmLW1Vz6+ziclJhn8dpBUEYJF3IUoxS81ToAi3ky2xtnAyk2wJFMXLvE73KiUd56yTA==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "@types/react-transition-group": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "dev": true + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true + }, + "conf": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-7.1.2.tgz", + "integrity": "sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==", + "dev": true, + "requires": { + "ajv": "^6.12.2", + "atomically": "^1.3.1", + "debounce-fn": "^4.0.0", + "dot-prop": "^5.2.0", + "env-paths": "^2.2.0", + "json-schema-typed": "^7.0.3", + "make-dir": "^3.1.0", + "onetime": "^5.1.0", + "pkg-up": "^3.1.0", + "semver": "^7.3.2" + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "csstype": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", + "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==", + "dev": true + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dev": true, + "requires": { + "mimic-fn": "^3.0.0" + } + }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, + "indefinite-observable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz", + "integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==", + "dev": true, + "requires": { + "symbol-observable": "1.2.0" + } + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "dev": true + }, + "jss": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.0.tgz", + "integrity": "sha512-B6151NvG+thUg3murLNHRPLxTLwQ13ep4SH5brj4d8qKtogOx/jupnpfkPGSHPqvcwKJaCLctpj2lEk+5yGwMw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "indefinite-observable": "^2.0.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + } + } + }, + "jss-plugin-camel-case": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.0.tgz", + "integrity": "sha512-GSjPL0adGAkuoqeYiXTgO7PlIrmjv5v8lA6TTBdfxbNYpxADOdGKJgIEkffhlyuIZHlPuuiFYTwUreLUmSn7rg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.5.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.0.tgz", + "integrity": "sha512-rsbTtZGCMrbcb9beiDd+TwL991NGmsAgVYH0hATrYJtue9e+LH/Gn4yFD1ENwE+3JzF3A+rPnM2JuD9L/SIIWw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-global": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.0.tgz", + "integrity": "sha512-FZd9+JE/3D7HMefEG54fEC0XiQ9rhGtDHAT/ols24y8sKQ1D5KIw6OyXEmIdKFmACgxZV2ARQ5pAUypxkk2IFQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-nested": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.0.tgz", + "integrity": "sha512-ejPlCLNlEGgx8jmMiDk/zarsCZk+DV0YqXfddpgzbO9Toamo0HweCFuwJ3ZO40UFOfqKwfpKMVH/3HUXgxkTMg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.0.tgz", + "integrity": "sha512-kTLRvrOetFKz5vM88FAhLNeJIxfjhCepnvq65G7xsAQ/Wgy7HwO1BS/2wE5mx8iLaAWC6Rj5h16mhMk9sKdZxg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.0.tgz", + "integrity": "sha512-jXINGr8BSsB13JVuK274oEtk0LoooYSJqTBCGeBu2cG/VJ3+4FPs1gwLgsq24xTgKshtZ+WEQMVL34OprLidRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.0.tgz", + "integrity": "sha512-rux3gmfwDdOKCLDx0IQjTwTm03IfBa+Rm/hs747cOw5Q7O3RaTUIMPKjtVfc31Xr/XI9Abz2XEupk1/oMQ7zRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.5.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@sindresorhus/is": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", + "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.14.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", + "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", + "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/yargs": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", + "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chokidar": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", + "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "got": { + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.1.tgz", + "integrity": "sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "dependencies": { + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "dev": true, + "requires": { + "mime-db": "1.45.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "refiner-js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/refiner-js/-/refiner-js-1.0.1.tgz", + "integrity": "sha512-k8AWRzOOa+FfV1stuJZfCA7qxdI7fqqpu9171X3EN29KoDPir8IedttHQWANDBvrW4lmCUgg5T5hWUVXwUtiAw==", + "dev": true + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "ts-loader": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.14.tgz", + "integrity": "sha512-Jt/hHlUnApOZjnSjTmZ+AbD5BGlQFx3f1D0nYuNKwz0JJnuDGHJas6az+FlWKwwRTu+26GXpv249A8UAnYUpqA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true + }, + "v8-to-istanbul": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", + "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", + "dev": true + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/extensions/survey/package.json b/extensions/survey/package.json new file mode 100644 index 0000000000..fb8dfcce11 --- /dev/null +++ b/extensions/survey/package.json @@ -0,0 +1,28 @@ +{ + "name": "lens-survey", + "version": "0.1.0", + "description": "Lens survey", + "main": "dist/main.js", + "renderer": "dist/renderer.js", + "lens": { + "metadata": {}, + "styles": [] + }, + "scripts": { + "build": "webpack -p", + "dev": "webpack --watch", + "test": "jest --passWithNoTests --env=jsdom src $@" + }, + "dependencies": {}, + "devDependencies": { + "@k8slens/extensions": "file:../../src/extensions/npm/extensions", + "got": "^11.8.1", + "jest": "^26.6.3", + "node-machine-id": "^1.1.12", + "react": "^16.13.1", + "refiner-js": "^1.0.1", + "ts-loader": "^8.0.4", + "typescript": "^4.0.3", + "webpack": "^4.44.2" + } +} diff --git a/extensions/survey/renderer.tsx b/extensions/survey/renderer.tsx new file mode 100644 index 0000000000..e8b2e4758c --- /dev/null +++ b/extensions/survey/renderer.tsx @@ -0,0 +1,21 @@ +import { LensRendererExtension } from "@k8slens/extensions"; +import { survey } from "./src/survey"; +import { SurveyPreferenceHint, SurveyPreferenceInput } from "./src/survey-preference"; +import { surveyPreferencesStore } from "./src/survey-preferences-store"; +import React from "react"; + +export default class SurveyRendererExtension extends LensRendererExtension { + appPreferences = [ + { + title: "In-App Surveys", + components: { + Hint: () => , + Input: () => + } + } + ]; + async onActivate() { + await surveyPreferencesStore.loadExtension(this); + survey.start(); + } +} diff --git a/extensions/survey/src/refiner-js.d.ts b/extensions/survey/src/refiner-js.d.ts new file mode 100644 index 0000000000..5d63c98e2d --- /dev/null +++ b/extensions/survey/src/refiner-js.d.ts @@ -0,0 +1,3 @@ +declare module "refiner-js" { + export default function Refiner(key: string, value: string|object|number|Boolean|Array): void; +} diff --git a/extensions/survey/src/survey-preference.tsx b/extensions/survey/src/survey-preference.tsx new file mode 100644 index 0000000000..edd9d3cc03 --- /dev/null +++ b/extensions/survey/src/survey-preference.tsx @@ -0,0 +1,27 @@ +import { Component } from "@k8slens/extensions"; +import React from "react"; +import { observer } from "mobx-react"; +import { SurveyPreferencesStore } from "./survey-preferences-store"; + +@observer +export class SurveyPreferenceInput extends React.Component<{survey: SurveyPreferencesStore}, {}> { + render() { + const { survey } = this.props; + + return ( + survey.enabled = v } + /> + ); + } +} + +export class SurveyPreferenceHint extends React.Component { + render() { + return ( + This will allow you to participate in surveys to improve the Lens experience. + ); + } +} diff --git a/extensions/survey/src/survey-preferences-store.ts b/extensions/survey/src/survey-preferences-store.ts new file mode 100644 index 0000000000..f15e582fe3 --- /dev/null +++ b/extensions/survey/src/survey-preferences-store.ts @@ -0,0 +1,36 @@ +import { Store } from "@k8slens/extensions"; +import { observable, toJS, when } from "mobx"; + +export type SurveyPreferencesModel = { + enabled: boolean; +}; + +export class SurveyPreferencesStore extends Store.ExtensionStore { + + @observable enabled = true; + + whenEnabled = when(() => this.enabled); + + private constructor() { + super({ + configName: "preferences-store", + defaults: { + enabled: true + } + }); + } + + protected fromStore({ enabled }: SurveyPreferencesModel): void { + this.enabled = enabled; + } + + toJSON(): SurveyPreferencesModel { + return toJS({ + enabled: this.enabled + }, { + recurseEverything: true + }); + } +} + +export const surveyPreferencesStore = SurveyPreferencesStore.getInstance(); diff --git a/extensions/survey/src/survey.ts b/extensions/survey/src/survey.ts new file mode 100644 index 0000000000..8bd6d2579b --- /dev/null +++ b/extensions/survey/src/survey.ts @@ -0,0 +1,46 @@ +import { Util } from "@k8slens/extensions"; +import { machineId } from "node-machine-id"; +import Refiner from "refiner-js"; +import got from "got"; +import { surveyPreferencesStore } from "./survey-preferences-store"; + +type SurveyIdResponse = { + surveyId: string; +}; +export class Survey extends Util.Singleton { + static readonly PROJECT_ID = "af468d00-4f8f-11eb-b01d-23b6562fef43"; + protected anonymousId: string; + + private constructor() { + super(); + } + + async start() { + await surveyPreferencesStore.whenEnabled; + + const surveyId = await this.fetchSurveyId(); + + if (surveyId) { + Refiner("setProject", Survey.PROJECT_ID); + Refiner("identifyUser", { + id: surveyId, + }); + + } + } + + async fetchSurveyId() { + try { + const surveyApi = process.env.SURVEY_API_URL || "https://survey.k8slens.dev"; + const anonymousId = await machineId(); + const { body } = await got(`${surveyApi}/api/survey-id?anonymousId=${anonymousId}`, { responseType: "json"}); + + return (body as SurveyIdResponse).surveyId; + } catch(error) { + return null; + } + + } +} + +export const survey = Survey.getInstance(); diff --git a/extensions/survey/tsconfig.json b/extensions/survey/tsconfig.json new file mode 100644 index 0000000000..05c3fa6671 --- /dev/null +++ b/extensions/survey/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "outDir": "dist", + "baseUrl": ".", + "module": "CommonJS", + "target": "ES2017", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "moduleResolution": "Node", + "sourceMap": false, + "declaration": false, + "strict": false, + "noImplicitAny": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "jsx": "react", + "paths": { + "*": [ + "node_modules/*", + "../../types/*" + ] + } + }, + "include": [ + "renderer.ts", + "src/**/*" + ] +} diff --git a/extensions/survey/webpack.config.js b/extensions/survey/webpack.config.js new file mode 100644 index 0000000000..c7862dc2c9 --- /dev/null +++ b/extensions/survey/webpack.config.js @@ -0,0 +1,67 @@ +const path = require("path"); + +module.exports = [ + { + entry: "./main.ts", + context: __dirname, + target: "electron-main", + mode: "production", + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + externals: [ + { + "@k8slens/extensions": "var global.LensExtensions", + "react": "var global.React", + "mobx": "var global.Mobx" + } + ], + resolve: { + extensions: [ ".tsx", ".ts", ".js" ], + }, + output: { + libraryTarget: "commonjs2", + globalObject: "this", + filename: "main.js", + path: path.resolve(__dirname, "dist"), + }, + }, + { + entry: "./renderer.tsx", + context: __dirname, + target: "electron-renderer", + mode: "production", + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + externals: [ + { + "@k8slens/extensions": "var global.LensExtensions", + "react": "var global.React", + "mobx": "var global.Mobx", + "mobx-react": "var global.MobxReact" + } + ], + resolve: { + extensions: [ ".tsx", ".ts", ".js" ], + }, + output: { + libraryTarget: "commonjs2", + globalObject: "this", + filename: "renderer.js", + path: path.resolve(__dirname, "dist"), + }, + }, +]; diff --git a/extensions/telemetry/package-lock.json b/extensions/telemetry/package-lock.json index 89e30dc2f4..9829eebb6b 100644 --- a/extensions/telemetry/package-lock.json +++ b/extensions/telemetry/package-lock.json @@ -2901,7 +2901,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3337,6 +3338,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4533,6 +4535,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4547,6 +4550,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4556,6 +4560,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4564,13 +4569,15 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "dev": true, + "optional": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4579,7 +4586,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5595,7 +5603,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", diff --git a/integration/__tests__/app.tests.ts b/integration/__tests__/app.tests.ts index c986e4804e..af029a23e9 100644 --- a/integration/__tests__/app.tests.ts +++ b/integration/__tests__/app.tests.ts @@ -1,76 +1,17 @@ -/* - Cluster tests are run if there is a pre-existing minikube cluster. Before running cluster tests the TEST_NAMESPACE - namespace is removed, if it exists, from the minikube cluster. Resources are created as part of the cluster tests in the - TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube - cluster and vice versa. -*/ import { Application } from "spectron"; import * as utils from "../helpers/utils"; -import { spawnSync, exec } from "child_process"; -import * as util from "util"; +import { listHelmRepositories } from "../helpers/utils"; +import { fail } from "assert"; -export const promiseExec = util.promisify(exec); jest.setTimeout(60000); // FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) describe("Lens integration tests", () => { - const TEST_NAMESPACE = "integration-tests"; - const BACKSPACE = "\uE003"; let app: Application; - const appStart = async () => { - app = utils.setup(); - await app.start(); - // Wait for splash screen to be closed - while (await app.client.getWindowCount() > 1); - await app.client.windowByIndex(0); - await app.client.waitUntilWindowLoaded(); - }; - const clickWhatsNew = async (app: Application) => { - await app.client.waitUntilTextExists("h1", "What's new?"); - await app.client.click("button.primary"); - await app.client.waitUntilTextExists("h1", "Welcome"); - }; - const minikubeReady = (): boolean => { - // determine if minikube is running - { - const { status } = spawnSync("minikube status", { shell: true }); - - if (status !== 0) { - console.warn("minikube not running"); - - return false; - } - } - - // Remove TEST_NAMESPACE if it already exists - { - const { status } = spawnSync(`minikube kubectl -- get namespace ${TEST_NAMESPACE}`, { shell: true }); - - if (status === 0) { - console.warn(`Removing existing ${TEST_NAMESPACE} namespace`); - - const { status, stdout, stderr } = spawnSync( - `minikube kubectl -- delete namespace ${TEST_NAMESPACE}`, - { shell: true }, - ); - - if (status !== 0) { - console.warn(`Error removing ${TEST_NAMESPACE} namespace: ${stderr.toString()}`); - - return false; - } - - console.log(stdout.toString()); - } - } - - return true; - }; - const ready = minikubeReady(); describe("app start", () => { - beforeAll(appStart, 20000); + beforeAll(async () => app = await utils.appStart(), 20000); afterAll(async () => { if (app?.isRunning()) { @@ -79,7 +20,7 @@ describe("Lens integration tests", () => { }); it('shows "whats new"', async () => { - await clickWhatsNew(app); + await utils.clickWhatsNew(app); }); it('shows "add cluster"', async () => { @@ -96,8 +37,11 @@ describe("Lens integration tests", () => { }); it("ensures helm repos", async () => { - const { stdout: reposJson } = await promiseExec("helm repo list -o json"); - const repos = JSON.parse(reposJson); + const repos = await listHelmRepositories(); + + if (!repos[0]) { + fail("Lens failed to add Bitnami repository"); + } await app.client.waitUntilTextExists("div.repos #message-bitnami", repos[0].name); // wait for the helm-cli to fetch the repo(s) await app.client.click("#HelmRepoSelect"); // click the repo select to activate the drop-down @@ -110,476 +54,4 @@ describe("Lens integration tests", () => { await app.client.keys("Meta"); }); }); - - utils.describeIf(ready)("workspaces", () => { - beforeAll(appStart, 20000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it("creates new workspace", async () => { - await clickWhatsNew(app); - await app.client.click("#current-workspace .Icon"); - await app.client.click('a[href="/workspaces"]'); - await app.client.click(".Workspaces button.Button"); - await app.client.keys("test-workspace"); - await app.client.click(".Workspaces .Input.description input"); - await app.client.keys("test description"); - await app.client.click(".Workspaces .workspace.editing .Icon"); - await app.client.waitUntilTextExists(".workspace .name a", "test-workspace"); - }); - - it("adds cluster in default workspace", async () => { - await addMinikubeCluster(app); - await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); - await app.client.waitForExist(`iframe[name="minikube"]`); - await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); - }); - - it("adds cluster in test-workspace", async () => { - await app.client.click("#current-workspace .Icon"); - await app.client.waitForVisible('.WorkspaceMenu li[title="test description"]'); - await app.client.click('.WorkspaceMenu li[title="test description"]'); - await addMinikubeCluster(app); - await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); - await app.client.waitForExist(`iframe[name="minikube"]`); - }); - - it("checks if default workspace has active cluster", async () => { - await app.client.click("#current-workspace .Icon"); - await app.client.waitForVisible(".WorkspaceMenu > li:first-of-type"); - await app.client.click(".WorkspaceMenu > li:first-of-type"); - await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); - }); - }); - - const addMinikubeCluster = async (app: Application) => { - await app.client.click("div.add-cluster"); - await app.client.waitUntilTextExists("div", "Select kubeconfig file"); - await app.client.click("div.Select__control"); // show the context drop-down list - await app.client.waitUntilTextExists("div", "minikube"); - - if (!await app.client.$("button.primary").isEnabled()) { - await app.client.click("div.minikube"); // select minikube context - } // else the only context, which must be 'minikube', is automatically selected - await app.client.click("div.Select__control"); // hide the context drop-down list (it might be obscuring the Add cluster(s) button) - await app.client.click("button.primary"); // add minikube cluster - }; - const waitForMinikubeDashboard = async (app: Application) => { - await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); - await app.client.waitForExist(`iframe[name="minikube"]`); - await app.client.frame("minikube"); - await app.client.waitUntilTextExists("span.link-text", "Cluster"); - }; - - utils.describeIf(ready)("cluster tests", () => { - let clusterAdded = false; - const addCluster = async () => { - await clickWhatsNew(app); - await addMinikubeCluster(app); - await waitForMinikubeDashboard(app); - await app.client.click('a[href="/nodes"]'); - await app.client.waitUntilTextExists("div.TableCell", "Ready"); - }; - - describe("cluster add", () => { - beforeAll(appStart, 20000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it("allows to add a cluster", async () => { - await addCluster(); - clusterAdded = true; - }); - }); - - const appStartAddCluster = async () => { - if (clusterAdded) { - await appStart(); - await addCluster(); - } - }; - - describe("cluster pages", () => { - - beforeAll(appStartAddCluster, 40000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - const tests: { - drawer?: string - drawerId?: string - pages: { - name: string, - href: string, - expectedSelector: string, - expectedText: string - }[] - }[] = [{ - drawer: "", - drawerId: "", - pages: [{ - name: "Cluster", - href: "cluster", - expectedSelector: "div.ClusterOverview div.label", - expectedText: "Master" - }] - }, - { - drawer: "", - drawerId: "", - pages: [{ - name: "Nodes", - href: "nodes", - expectedSelector: "h5.title", - expectedText: "Nodes" - }] - }, - { - drawer: "Workloads", - drawerId: "workloads", - pages: [{ - name: "Overview", - href: "workloads", - expectedSelector: "h5.box", - expectedText: "Overview" - }, - { - name: "Pods", - href: "pods", - expectedSelector: "h5.title", - expectedText: "Pods" - }, - { - name: "Deployments", - href: "deployments", - expectedSelector: "h5.title", - expectedText: "Deployments" - }, - { - name: "DaemonSets", - href: "daemonsets", - expectedSelector: "h5.title", - expectedText: "Daemon Sets" - }, - { - name: "StatefulSets", - href: "statefulsets", - expectedSelector: "h5.title", - expectedText: "Stateful Sets" - }, - { - name: "ReplicaSets", - href: "replicasets", - expectedSelector: "h5.title", - expectedText: "Replica Sets" - }, - { - name: "Jobs", - href: "jobs", - expectedSelector: "h5.title", - expectedText: "Jobs" - }, - { - name: "CronJobs", - href: "cronjobs", - expectedSelector: "h5.title", - expectedText: "Cron Jobs" - }] - }, - { - drawer: "Configuration", - drawerId: "config", - pages: [{ - name: "ConfigMaps", - href: "configmaps", - expectedSelector: "h5.title", - expectedText: "Config Maps" - }, - { - name: "Secrets", - href: "secrets", - expectedSelector: "h5.title", - expectedText: "Secrets" - }, - { - name: "Resource Quotas", - href: "resourcequotas", - expectedSelector: "h5.title", - expectedText: "Resource Quotas" - }, - { - name: "Limit Ranges", - href: "limitranges", - expectedSelector: "h5.title", - expectedText: "Limit Ranges" - }, - { - name: "HPA", - href: "hpa", - expectedSelector: "h5.title", - expectedText: "Horizontal Pod Autoscalers" - }, - { - name: "Pod Disruption Budgets", - href: "poddisruptionbudgets", - expectedSelector: "h5.title", - expectedText: "Pod Disruption Budgets" - }] - }, - { - drawer: "Network", - drawerId: "networks", - pages: [{ - name: "Services", - href: "services", - expectedSelector: "h5.title", - expectedText: "Services" - }, - { - name: "Endpoints", - href: "endpoints", - expectedSelector: "h5.title", - expectedText: "Endpoints" - }, - { - name: "Ingresses", - href: "ingresses", - expectedSelector: "h5.title", - expectedText: "Ingresses" - }, - { - name: "Network Policies", - href: "network-policies", - expectedSelector: "h5.title", - expectedText: "Network Policies" - }] - }, - { - drawer: "Storage", - drawerId: "storage", - pages: [{ - name: "Persistent Volume Claims", - href: "persistent-volume-claims", - expectedSelector: "h5.title", - expectedText: "Persistent Volume Claims" - }, - { - name: "Persistent Volumes", - href: "persistent-volumes", - expectedSelector: "h5.title", - expectedText: "Persistent Volumes" - }, - { - name: "Storage Classes", - href: "storage-classes", - expectedSelector: "h5.title", - expectedText: "Storage Classes" - }] - }, - { - drawer: "", - drawerId: "", - pages: [{ - name: "Namespaces", - href: "namespaces", - expectedSelector: "h5.title", - expectedText: "Namespaces" - }] - }, - { - drawer: "", - drawerId: "", - pages: [{ - name: "Events", - href: "events", - expectedSelector: "h5.title", - expectedText: "Events" - }] - }, - { - drawer: "Apps", - drawerId: "apps", - pages: [{ - name: "Charts", - href: "apps/charts", - expectedSelector: "div.HelmCharts input", - expectedText: "" - }, - { - name: "Releases", - href: "apps/releases", - expectedSelector: "h5.title", - expectedText: "Releases" - }] - }, - { - drawer: "Access Control", - drawerId: "users", - pages: [{ - name: "Service Accounts", - href: "service-accounts", - expectedSelector: "h5.title", - expectedText: "Service Accounts" - }, - { - name: "Role Bindings", - href: "role-bindings", - expectedSelector: "h5.title", - expectedText: "Role Bindings" - }, - { - name: "Roles", - href: "roles", - expectedSelector: "h5.title", - expectedText: "Roles" - }, - { - name: "Pod Security Policies", - href: "pod-security-policies", - expectedSelector: "h5.title", - expectedText: "Pod Security Policies" - }] - }, - { - drawer: "Custom Resources", - drawerId: "custom-resources", - pages: [{ - name: "Definitions", - href: "crd/definitions", - expectedSelector: "h5.title", - expectedText: "Custom Resources" - }] - }]; - - tests.forEach(({ drawer = "", drawerId = "", pages }) => { - if (drawer !== "") { - it(`shows ${drawer} drawer`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`); - await app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name); - }); - } - pages.forEach(({ name, href, expectedSelector, expectedText }) => { - it(`shows ${drawer}->${name} page`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(`a[href^="/${href}"]`); - await app.client.waitUntilTextExists(expectedSelector, expectedText); - }); - }); - - if (drawer !== "") { - // hide the drawer - it(`hides ${drawer} drawer`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`); - await expect(app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow(); - }); - } - }); - }); - - describe("viewing pod logs", () => { - beforeEach(appStartAddCluster, 40000); - - afterEach(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it(`shows a logs for a pod`, async () => { - expect(clusterAdded).toBe(true); - // Go to Pods page - await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text"); - await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); - await app.client.click('a[href^="/pods"]'); - await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); - // Open logs tab in dock - await app.client.click(".list .TableRow:first-child"); - await app.client.waitForVisible(".Drawer"); - await app.client.click(".drawer-title .Menu li:nth-child(2)"); - // Check if controls are available - await app.client.waitForVisible(".Logs .VirtualList"); - await app.client.waitForVisible(".LogResourceSelector"); - await app.client.waitForVisible(".LogResourceSelector .SearchInput"); - await app.client.waitForVisible(".LogResourceSelector .SearchInput input"); - // Search for semicolon - await app.client.keys(":"); - await app.client.waitForVisible(".Logs .list span.active"); - // Click through controls - await app.client.click(".LogControls .show-timestamps"); - await app.client.click(".LogControls .show-previous"); - }); - }); - - describe("cluster operations", () => { - beforeEach(appStartAddCluster, 40000); - - afterEach(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it("shows default namespace", async () => { - expect(clusterAdded).toBe(true); - await app.client.click('a[href="/namespaces"]'); - await app.client.waitUntilTextExists("div.TableCell", "default"); - await app.client.waitUntilTextExists("div.TableCell", "kube-system"); - }); - - it(`creates ${TEST_NAMESPACE} namespace`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click('a[href="/namespaces"]'); - await app.client.waitUntilTextExists("div.TableCell", "default"); - await app.client.waitUntilTextExists("div.TableCell", "kube-system"); - await app.client.click("button.add-button"); - await app.client.waitUntilTextExists("div.AddNamespaceDialog", "Create Namespace"); - await app.client.keys(`${TEST_NAMESPACE}\n`); - await app.client.waitForExist(`.name=${TEST_NAMESPACE}`); - }); - - it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text"); - await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); - await app.client.click('a[href^="/pods"]'); - await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); - await app.client.click(".Icon.new-dock-tab"); - await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource"); - await app.client.click("li.MenuItem.create-resource-tab"); - await app.client.waitForVisible(".CreateResource div.ace_content"); - // Write pod manifest to editor - await app.client.keys("apiVersion: v1\n"); - await app.client.keys("kind: Pod\n"); - await app.client.keys("metadata:\n"); - await app.client.keys(" name: nginx-create-pod-test\n"); - await app.client.keys(`namespace: ${TEST_NAMESPACE}\n`); - await app.client.keys(`${BACKSPACE}spec:\n`); - await app.client.keys(" containers:\n"); - await app.client.keys("- name: nginx-create-pod-test\n"); - await app.client.keys(" image: nginx:alpine\n"); - // Create deployment - await app.client.waitForEnabled("button.Button=Create & Close"); - await app.client.click("button.Button=Create & Close"); - // Wait until first bits of pod appears on dashboard - await app.client.waitForExist(".name=nginx-create-pod-test"); - // Open pod details - await app.client.click(".name=nginx-create-pod-test"); - await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test"); - }); - }); - }); }); diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts new file mode 100644 index 0000000000..e73774f86a --- /dev/null +++ b/integration/__tests__/cluster-pages.tests.ts @@ -0,0 +1,450 @@ +/* + Cluster tests are run if there is a pre-existing minikube cluster. Before running cluster tests the TEST_NAMESPACE + namespace is removed, if it exists, from the minikube cluster. Resources are created as part of the cluster tests in the + TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube + cluster and vice versa. +*/ +import { Application } from "spectron"; +import * as utils from "../helpers/utils"; +import { addMinikubeCluster, minikubeReady, waitForMinikubeDashboard } from "../helpers/minikube"; +import { exec } from "child_process"; +import * as util from "util"; + +export const promiseExec = util.promisify(exec); + +jest.setTimeout(60000); + +// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) +describe("Lens cluster pages", () => { + const TEST_NAMESPACE = "integration-tests"; + const BACKSPACE = "\uE003"; + let app: Application; + const ready = minikubeReady(TEST_NAMESPACE); + + utils.describeIf(ready)("test common pages", () => { + let clusterAdded = false; + const addCluster = async () => { + await utils.clickWhatsNew(app); + await addMinikubeCluster(app); + await waitForMinikubeDashboard(app); + await app.client.click('a[href="/nodes"]'); + await app.client.waitUntilTextExists("div.TableCell", "Ready"); + }; + + describe("cluster add", () => { + beforeAll(async () => app = await utils.appStart(), 20000); + + afterAll(async () => { + if (app && app.isRunning()) { + return utils.tearDown(app); + } + }); + + it("allows to add a cluster", async () => { + await addCluster(); + clusterAdded = true; + }); + }); + + const appStartAddCluster = async () => { + if (clusterAdded) { + app = await utils.appStart(); + await addCluster(); + } + }; + + describe("cluster pages", () => { + + beforeAll(appStartAddCluster, 40000); + + afterAll(async () => { + if (app && app.isRunning()) { + return utils.tearDown(app); + } + }); + + const tests: { + drawer?: string + drawerId?: string + pages: { + name: string, + href: string, + expectedSelector: string, + expectedText: string + }[] + }[] = [{ + drawer: "", + drawerId: "", + pages: [{ + name: "Cluster", + href: "cluster", + expectedSelector: "div.ClusterOverview div.label", + expectedText: "Master" + }] + }, + { + drawer: "", + drawerId: "", + pages: [{ + name: "Nodes", + href: "nodes", + expectedSelector: "h5.title", + expectedText: "Nodes" + }] + }, + { + drawer: "Workloads", + drawerId: "workloads", + pages: [{ + name: "Overview", + href: "workloads", + expectedSelector: "h5.box", + expectedText: "Overview" + }, + { + name: "Pods", + href: "pods", + expectedSelector: "h5.title", + expectedText: "Pods" + }, + { + name: "Deployments", + href: "deployments", + expectedSelector: "h5.title", + expectedText: "Deployments" + }, + { + name: "DaemonSets", + href: "daemonsets", + expectedSelector: "h5.title", + expectedText: "Daemon Sets" + }, + { + name: "StatefulSets", + href: "statefulsets", + expectedSelector: "h5.title", + expectedText: "Stateful Sets" + }, + { + name: "ReplicaSets", + href: "replicasets", + expectedSelector: "h5.title", + expectedText: "Replica Sets" + }, + { + name: "Jobs", + href: "jobs", + expectedSelector: "h5.title", + expectedText: "Jobs" + }, + { + name: "CronJobs", + href: "cronjobs", + expectedSelector: "h5.title", + expectedText: "Cron Jobs" + }] + }, + { + drawer: "Configuration", + drawerId: "config", + pages: [{ + name: "ConfigMaps", + href: "configmaps", + expectedSelector: "h5.title", + expectedText: "Config Maps" + }, + { + name: "Secrets", + href: "secrets", + expectedSelector: "h5.title", + expectedText: "Secrets" + }, + { + name: "Resource Quotas", + href: "resourcequotas", + expectedSelector: "h5.title", + expectedText: "Resource Quotas" + }, + { + name: "Limit Ranges", + href: "limitranges", + expectedSelector: "h5.title", + expectedText: "Limit Ranges" + }, + { + name: "HPA", + href: "hpa", + expectedSelector: "h5.title", + expectedText: "Horizontal Pod Autoscalers" + }, + { + name: "Pod Disruption Budgets", + href: "poddisruptionbudgets", + expectedSelector: "h5.title", + expectedText: "Pod Disruption Budgets" + }] + }, + { + drawer: "Network", + drawerId: "networks", + pages: [{ + name: "Services", + href: "services", + expectedSelector: "h5.title", + expectedText: "Services" + }, + { + name: "Endpoints", + href: "endpoints", + expectedSelector: "h5.title", + expectedText: "Endpoints" + }, + { + name: "Ingresses", + href: "ingresses", + expectedSelector: "h5.title", + expectedText: "Ingresses" + }, + { + name: "Network Policies", + href: "network-policies", + expectedSelector: "h5.title", + expectedText: "Network Policies" + }] + }, + { + drawer: "Storage", + drawerId: "storage", + pages: [{ + name: "Persistent Volume Claims", + href: "persistent-volume-claims", + expectedSelector: "h5.title", + expectedText: "Persistent Volume Claims" + }, + { + name: "Persistent Volumes", + href: "persistent-volumes", + expectedSelector: "h5.title", + expectedText: "Persistent Volumes" + }, + { + name: "Storage Classes", + href: "storage-classes", + expectedSelector: "h5.title", + expectedText: "Storage Classes" + }] + }, + { + drawer: "", + drawerId: "", + pages: [{ + name: "Namespaces", + href: "namespaces", + expectedSelector: "h5.title", + expectedText: "Namespaces" + }] + }, + { + drawer: "", + drawerId: "", + pages: [{ + name: "Events", + href: "events", + expectedSelector: "h5.title", + expectedText: "Events" + }] + }, + { + drawer: "Apps", + drawerId: "apps", + pages: [{ + name: "Charts", + href: "apps/charts", + expectedSelector: "div.HelmCharts input", + expectedText: "" + }, + { + name: "Releases", + href: "apps/releases", + expectedSelector: "h5.title", + expectedText: "Releases" + }] + }, + { + drawer: "Access Control", + drawerId: "users", + pages: [{ + name: "Service Accounts", + href: "service-accounts", + expectedSelector: "h5.title", + expectedText: "Service Accounts" + }, + { + name: "Role Bindings", + href: "role-bindings", + expectedSelector: "h5.title", + expectedText: "Role Bindings" + }, + { + name: "Roles", + href: "roles", + expectedSelector: "h5.title", + expectedText: "Roles" + }, + { + name: "Pod Security Policies", + href: "pod-security-policies", + expectedSelector: "h5.title", + expectedText: "Pod Security Policies" + }] + }, + { + drawer: "Custom Resources", + drawerId: "custom-resources", + pages: [{ + name: "Definitions", + href: "crd/definitions", + expectedSelector: "h5.title", + expectedText: "Custom Resources" + }] + }]; + + tests.forEach(({ drawer = "", drawerId = "", pages }) => { + if (drawer !== "") { + it(`shows ${drawer} drawer`, async () => { + expect(clusterAdded).toBe(true); + await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`); + await app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name); + }); + } + pages.forEach(({ name, href, expectedSelector, expectedText }) => { + it(`shows ${drawer}->${name} page`, async () => { + expect(clusterAdded).toBe(true); + await app.client.click(`a[href^="/${href}"]`); + await app.client.waitUntilTextExists(expectedSelector, expectedText); + }); + }); + + if (drawer !== "") { + // hide the drawer + it(`hides ${drawer} drawer`, async () => { + expect(clusterAdded).toBe(true); + await app.client.click(`.sidebar-nav [data-test-id="${drawerId}"] span.link-text`); + await expect(app.client.waitUntilTextExists(`a[href^="/${pages[0].href}"]`, pages[0].name, 100)).rejects.toThrow(); + }); + } + }); + }); + + describe("viewing pod logs", () => { + beforeEach(appStartAddCluster, 40000); + + afterEach(async () => { + if (app && app.isRunning()) { + return utils.tearDown(app); + } + }); + + it(`shows a logs for a pod`, async () => { + expect(clusterAdded).toBe(true); + // Go to Pods page + await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text"); + await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); + await app.client.click('a[href^="/pods"]'); + await app.client.click(".NamespaceSelect"); + await app.client.keys("kube-system"); + await app.client.keys("Enter");// "\uE007" + await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); + let podMenuItemEnabled = false; + + // Wait until extensions are enabled on renderer + while (!podMenuItemEnabled) { + const logs = await app.client.getRenderProcessLogs(); + + podMenuItemEnabled = !!logs.find(entry => entry.message.includes("[EXTENSION]: enabled lens-pod-menu@")); + + if (!podMenuItemEnabled) { + await new Promise(r => setTimeout(r, 1000)); + } + } + await new Promise(r => setTimeout(r, 500)); // Give some extra time to prepare extensions + // Open logs tab in dock + await app.client.click(".list .TableRow:first-child"); + await app.client.waitForVisible(".Drawer"); + await app.client.click(".drawer-title .Menu li:nth-child(2)"); + // Check if controls are available + await app.client.waitForVisible(".LogList .VirtualList"); + await app.client.waitForVisible(".LogResourceSelector"); + //await app.client.waitForVisible(".LogSearch .SearchInput"); + await app.client.waitForVisible(".LogSearch .SearchInput input"); + // Search for semicolon + await app.client.keys(":"); + await app.client.waitForVisible(".LogList .list span.active"); + // Click through controls + await app.client.click(".LogControls .show-timestamps"); + await app.client.click(".LogControls .show-previous"); + }); + }); + + describe("cluster operations", () => { + beforeEach(appStartAddCluster, 40000); + + afterEach(async () => { + if (app && app.isRunning()) { + return utils.tearDown(app); + } + }); + + it("shows default namespace", async () => { + expect(clusterAdded).toBe(true); + await app.client.click('a[href="/namespaces"]'); + await app.client.waitUntilTextExists("div.TableCell", "default"); + await app.client.waitUntilTextExists("div.TableCell", "kube-system"); + }); + + it(`creates ${TEST_NAMESPACE} namespace`, async () => { + expect(clusterAdded).toBe(true); + await app.client.click('a[href="/namespaces"]'); + await app.client.waitUntilTextExists("div.TableCell", "default"); + await app.client.waitUntilTextExists("div.TableCell", "kube-system"); + await app.client.click("button.add-button"); + await app.client.waitUntilTextExists("div.AddNamespaceDialog", "Create Namespace"); + await app.client.keys(`${TEST_NAMESPACE}\n`); + await app.client.waitForExist(`.name=${TEST_NAMESPACE}`); + }); + + it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => { + expect(clusterAdded).toBe(true); + await app.client.click(".sidebar-nav [data-test-id='workloads'] span.link-text"); + await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); + await app.client.click('a[href^="/pods"]'); + + await app.client.click(".NamespaceSelect"); + await app.client.keys(TEST_NAMESPACE); + await app.client.keys("Enter");// "\uE007" + await app.client.click(".Icon.new-dock-tab"); + await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource"); + await app.client.click("li.MenuItem.create-resource-tab"); + await app.client.waitForVisible(".CreateResource div.ace_content"); + // Write pod manifest to editor + await app.client.keys("apiVersion: v1\n"); + await app.client.keys("kind: Pod\n"); + await app.client.keys("metadata:\n"); + await app.client.keys(" name: nginx-create-pod-test\n"); + await app.client.keys(`namespace: ${TEST_NAMESPACE}\n`); + await app.client.keys(`${BACKSPACE}spec:\n`); + await app.client.keys(" containers:\n"); + await app.client.keys("- name: nginx-create-pod-test\n"); + await app.client.keys(" image: nginx:alpine\n"); + // Create deployment + await app.client.waitForEnabled("button.Button=Create & Close"); + await app.client.click("button.Button=Create & Close"); + // Wait until first bits of pod appears on dashboard + await app.client.waitForExist(".name=nginx-create-pod-test"); + // Open pod details + await app.client.click(".name=nginx-create-pod-test"); + await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test"); + }); + }); + }); +}); diff --git a/integration/__tests__/command-palette.tests.ts b/integration/__tests__/command-palette.tests.ts new file mode 100644 index 0000000000..789806c445 --- /dev/null +++ b/integration/__tests__/command-palette.tests.ts @@ -0,0 +1,25 @@ +import { Application } from "spectron"; +import * as utils from "../helpers/utils"; + +jest.setTimeout(60000); + +describe("Lens command palette", () => { + let app: Application; + + describe("menu", () => { + beforeAll(async () => app = await utils.appStart(), 20000); + + afterAll(async () => { + if (app?.isRunning()) { + await utils.tearDown(app); + } + }); + + it("opens command dialog from menu", async () => { + await utils.clickWhatsNew(app); + await app.electron.ipcRenderer.send("test-menu-item-click", "View", "Command Palette..."); + await app.client.waitUntilTextExists(".Select__option", "Preferences: Open"); + await app.client.keys("Escape"); + }); + }); +}); diff --git a/integration/__tests__/workspace.tests.ts b/integration/__tests__/workspace.tests.ts new file mode 100644 index 0000000000..4164151b0f --- /dev/null +++ b/integration/__tests__/workspace.tests.ts @@ -0,0 +1,75 @@ +import { Application } from "spectron"; +import * as utils from "../helpers/utils"; +import { addMinikubeCluster, minikubeReady } from "../helpers/minikube"; +import { exec } from "child_process"; +import * as util from "util"; + +export const promiseExec = util.promisify(exec); + +jest.setTimeout(60000); + +describe("Lens integration tests", () => { + let app: Application; + const ready = minikubeReady("workspace-int-tests"); + + utils.describeIf(ready)("workspaces", () => { + beforeAll(async () => { + app = await utils.appStart(); + await utils.clickWhatsNew(app); + }, 20000); + + afterAll(async () => { + if (app && app.isRunning()) { + return utils.tearDown(app); + } + }); + + const switchToWorkspace = async (name: string) => { + await app.client.click("[data-test-id=current-workspace]"); + await app.client.keys(name); + await app.client.keys("Enter"); + await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + }; + + const createWorkspace = async (name: string) => { + await app.client.click("[data-test-id=current-workspace]"); + await app.client.keys("add workspace"); + await app.client.keys("Enter"); + await app.client.keys(name); + await app.client.keys("Enter"); + await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + }; + + it("creates new workspace", async () => { + const name = "test-workspace"; + + await createWorkspace(name); + await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + }); + + it("edits current workspaces", async () => { + await createWorkspace("to-be-edited"); + await app.client.click("[data-test-id=current-workspace]"); + await app.client.keys("edit current workspace"); + await app.client.keys("Enter"); + await app.client.keys("edited-workspace"); + await app.client.keys("Enter"); + await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", "edited-workspace"); + }); + + it("adds cluster in default workspace", async () => { + await switchToWorkspace("default"); + await addMinikubeCluster(app); + await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); + await app.client.waitForExist(`iframe[name="minikube"]`); + await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); + }); + + it("adds cluster in test-workspace", async () => { + await switchToWorkspace("test-workspace"); + await addMinikubeCluster(app); + await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); + await app.client.waitForExist(`iframe[name="minikube"]`); + }); + }); +}); diff --git a/integration/helpers/minikube.ts b/integration/helpers/minikube.ts new file mode 100644 index 0000000000..67ef0145d5 --- /dev/null +++ b/integration/helpers/minikube.ts @@ -0,0 +1,59 @@ +import { spawnSync } from "child_process"; +import { Application } from "spectron"; + +export function minikubeReady(testNamespace: string): boolean { + // determine if minikube is running + { + const { status } = spawnSync("minikube status", { shell: true }); + + if (status !== 0) { + console.warn("minikube not running"); + + return false; + } + } + + // Remove TEST_NAMESPACE if it already exists + { + const { status } = spawnSync(`minikube kubectl -- get namespace ${testNamespace}`, { shell: true }); + + if (status === 0) { + console.warn(`Removing existing ${testNamespace} namespace`); + + const { status, stdout, stderr } = spawnSync( + `minikube kubectl -- delete namespace ${testNamespace}`, + { shell: true }, + ); + + if (status !== 0) { + console.warn(`Error removing ${testNamespace} namespace: ${stderr.toString()}`); + + return false; + } + + console.log(stdout.toString()); + } + } + + return true; +} + +export async function addMinikubeCluster(app: Application) { + await app.client.click("div.add-cluster"); + await app.client.waitUntilTextExists("div", "Select kubeconfig file"); + await app.client.click("div.Select__control"); // show the context drop-down list + await app.client.waitUntilTextExists("div", "minikube"); + + if (!await app.client.$("button.primary").isEnabled()) { + await app.client.click("div.minikube"); // select minikube context + } // else the only context, which must be 'minikube', is automatically selected + await app.client.click("div.Select__control"); // hide the context drop-down list (it might be obscuring the Add cluster(s) button) + await app.client.click("button.primary"); // add minikube cluster +} + +export async function waitForMinikubeDashboard(app: Application) { + await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); + await app.client.waitForExist(`iframe[name="minikube"]`); + await app.client.frame("minikube"); + await app.client.waitUntilTextExists("span.link-text", "Cluster"); +} diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index f445a9ae48..f96c9124e2 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -1,4 +1,6 @@ import { Application } from "spectron"; +import * as util from "util"; +import { exec } from "child_process"; const AppPaths: Partial> = { "win32": "./dist/win-unpacked/Lens.exe", @@ -26,6 +28,28 @@ export function setup(): Application { }); } +export const keys = { + backspace: "\uE003" +}; + +export async function appStart() { + const app = setup(); + + await app.start(); + // Wait for splash screen to be closed + while (await app.client.getWindowCount() > 1); + await app.client.windowByIndex(0); + await app.client.waitUntilWindowLoaded(); + + return app; +} + +export async function clickWhatsNew(app: Application) { + await app.client.waitUntilTextExists("h1", "What's new?"); + await app.client.click("button.primary"); + await app.client.waitUntilTextExists("h1", "Welcome"); +} + type AsyncPidGetter = () => Promise; export async function tearDown(app: Application) { @@ -39,3 +63,26 @@ export async function tearDown(app: Application) { console.error(e); } } + +export const promiseExec = util.promisify(exec); + +type HelmRepository = { + name: string; + url: string; +}; + +export async function listHelmRepositories(retries = 0): Promise{ + if (retries < 5) { + try { + const { stdout: reposJson } = await promiseExec("helm repo list -o json"); + + return JSON.parse(reposJson); + } catch { + await new Promise(r => setTimeout(r, 2000)); // if no repositories, wait for Lens adding bitnami repository + + return await listHelmRepositories((retries + 1)); + } + } + + return []; +} diff --git a/package.json b/package.json index 0c818db7bd..9aa55e7584 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kontena-lens", "productName": "Lens", "description": "Lens - The Kubernetes IDE", - "version": "4.1.0-alpha.0", + "version": "4.1.0-beta.2", "main": "static/build/main.js", "copyright": "© 2020, Mirantis, Inc.", "license": "MIT", @@ -16,7 +16,7 @@ "dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"", "dev:main": "yarn run compile:main --watch", "dev:renderer": "yarn run webpack-dev-server --config webpack.renderer.ts", - "dev:extension-types": "yarn run compile:extension-types --watch", + "dev:extension-types": "yarn run compile:extension-types --watch --progress", "compile": "env NODE_ENV=production concurrently yarn:compile:*", "compile:main": "yarn run webpack --config webpack.main.ts", "compile:renderer": "yarn run webpack --config webpack.renderer.ts", @@ -26,7 +26,7 @@ "build:mac": "yarn run compile && electron-builder --mac --dir -c.productName=Lens", "build:win": "yarn run compile && electron-builder --win --dir -c.productName=Lens", "test": "jest --env=jsdom src $@", - "integration": "jest --coverage integration $@", + "integration": "jest --runInBand integration", "dist": "yarn run compile && electron-builder --publish onTag", "dist:win": "yarn run compile && electron-builder --publish onTag --x64 --ia32", "dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null", @@ -42,7 +42,7 @@ "typedocs-extensions-api": "yarn run typedoc --ignoreCompilerErrors --readme docs/extensions/typedoc-readme.md.tpl --name @k8slens/extensions --out docs/extensions/api --mode library --excludePrivate --hideBreadcrumbs --includes src/ src/extensions/extension-api.ts" }, "config": { - "bundledKubectlVersion": "1.17.15", + "bundledKubectlVersion": "1.18.15", "bundledHelmVersion": "3.4.2" }, "engines": { @@ -103,7 +103,10 @@ ], "linux": { "category": "Network", + "artifactName": "${productName}-${version}.${arch}.${ext}", "target": [ + "deb", + "rpm", "snap", "AppImage" ], @@ -154,7 +157,12 @@ ] }, "nsis": { - "include": "build/installer.nsh" + "include": "build/installer.nsh", + "oneClick": false, + "allowToChangeInstallationDirectory": true + }, + "snap": { + "confinement": "classic" }, "publish": [ { @@ -162,10 +170,7 @@ "repo": "lens", "owner": "lensapp" } - ], - "snap": { - "confinement": "classic" - } + ] }, "lens": { "extensions": [ @@ -174,7 +179,8 @@ "node-menu", "metrics-cluster-feature", "license-menu-item", - "kube-object-event-status" + "kube-object-event-status", + "survey" ] }, "dependencies": { @@ -183,6 +189,7 @@ "@kubernetes/client-node": "^0.12.0", "array-move": "^3.0.0", "await-lock": "^2.1.0", + "byline": "^5.0.0", "chalk": "^4.1.0", "chokidar": "^3.4.3", "command-exists": "1.2.9", @@ -200,7 +207,7 @@ "jsonpath": "^1.0.2", "lodash": "^4.17.15", "mac-ca": "^1.0.4", - "marked": "^1.1.0", + "marked": "^1.2.7", "md5-file": "^5.0.0", "mobx": "^5.15.7", "mobx-observable-history": "^1.0.3", @@ -215,12 +222,13 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-router": "^5.2.0", + "readable-web-to-node-stream": "^3.0.1", "request": "^2.88.2", "request-promise-native": "^1.0.8", "selfsigned": "^1.10.8", "semver": "^7.3.2", "serializr": "^2.0.3", - "shell-env": "^3.0.0", + "shell-env": "^3.0.1", "spdy": "^4.0.2", "tar": "^6.0.5", "tcp-port-used": "^1.0.1", @@ -237,6 +245,7 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", "@testing-library/jest-dom": "^5.11.5", "@testing-library/react": "^11.1.0", + "@types/byline": "^4.2.32", "@types/chart.js": "^2.9.21", "@types/circular-dependency-plugin": "^5.0.1", "@types/color": "^3.0.1", @@ -286,7 +295,7 @@ "@types/webpack-dev-server": "^3.11.1", "@types/webpack-env": "^1.15.2", "@types/webpack-node-externals": "^1.7.1", - "@typescript-eslint/eslint-plugin": "^4.12.0", + "@typescript-eslint/eslint-plugin": "^4.14.2", "@typescript-eslint/parser": "^4.0.0", "ace-builds": "^1.4.11", "ansi_up": "^4.0.4", @@ -314,7 +323,7 @@ "jest-canvas-mock": "^2.3.0", "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^1.0.10", - "make-plural": "^6.2.1", + "make-plural": "^6.2.2", "mini-css-extract-plugin": "^0.9.0", "moment": "^2.26.0", "node-loader": "^0.6.0", @@ -329,6 +338,7 @@ "react-refresh": "^0.9.0", "react-router-dom": "^5.2.0", "react-select": "^3.1.0", + "react-select-event": "^5.1.0", "react-window": "^1.8.5", "sass-loader": "^8.0.2", "sharp": "^0.26.1", diff --git a/src/common/__tests__/search-store.test.ts b/src/common/__tests__/search-store.test.ts index 7939ef1d8c..d361c858fd 100644 --- a/src/common/__tests__/search-store.test.ts +++ b/src/common/__tests__/search-store.test.ts @@ -77,4 +77,4 @@ describe("search store tests", () => { searchStore.onSearch(logs, "Starting"); expect(searchStore.totalFinds).toBe(2); }); -}); \ No newline at end of file +}); diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts index 08ca359ce5..b74941a790 100644 --- a/src/common/__tests__/user-store.test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -101,4 +101,4 @@ describe("user store tests", () => { expect(us.lastSeenAppVersion).toBe("0.0.0"); }); }); -}); \ No newline at end of file +}); diff --git a/src/common/custom-errors.ts b/src/common/custom-errors.ts index 9bcf3a998a..177ef7578f 100644 --- a/src/common/custom-errors.ts +++ b/src/common/custom-errors.ts @@ -10,4 +10,4 @@ export class ExecValidationNotFoundError extends Error { this.name = this.constructor.name; Error.captureStackTrace(this, this.constructor); } -} \ No newline at end of file +} diff --git a/src/common/ipc/__tests__/type-enforced-ipc.test.ts b/src/common/ipc/__tests__/type-enforced-ipc.test.ts new file mode 100644 index 0000000000..4e20249392 --- /dev/null +++ b/src/common/ipc/__tests__/type-enforced-ipc.test.ts @@ -0,0 +1,126 @@ +import { EventEmitter } from "events"; +import { onCorrect, onceCorrect } from "../type-enforced-ipc"; + +describe("type enforced ipc tests", () => { + describe("onCorrect tests", () => { + it("should call the handler if the args are valid", () => { + let called = false; + const source = new EventEmitter(); + const listener = () => called = true; + const verifier = (args: unknown[]): args is [] => true; + const channel = "foobar"; + + onCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + expect(called).toBe(true); + }); + + it("should not call the handler if the args are not valid", () => { + let called = false; + const source = new EventEmitter(); + const listener = () => called = true; + const verifier = (args: unknown[]): args is [] => false; + const channel = "foobar"; + + onCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + expect(called).toBe(false); + }); + + it("should call the handler twice if the args are valid on two emits", () => { + let called = 0; + const source = new EventEmitter(); + const listener = () => called += 1; + const verifier = (args: unknown[]): args is [] => true; + const channel = "foobar"; + + onCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + source.emit(channel); + expect(called).toBe(2); + }); + + it("should call the handler twice if the args are [valid, invalid, valid]", () => { + let called = 0; + const source = new EventEmitter(); + const listener = () => called += 1; + const results = [true, false, true]; + const verifier = (args: unknown[]): args is [] => results.pop(); + const channel = "foobar"; + + onCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + source.emit(channel); + source.emit(channel); + expect(called).toBe(2); + }); + }); + + describe("onceCorrect tests", () => { + it("should call the handler if the args are valid", () => { + let called = false; + const source = new EventEmitter(); + const listener = () => called = true; + const verifier = (args: unknown[]): args is [] => true; + const channel = "foobar"; + + onceCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + expect(called).toBe(true); + }); + + it("should not call the handler if the args are not valid", () => { + let called = false; + const source = new EventEmitter(); + const listener = () => called = true; + const verifier = (args: unknown[]): args is [] => false; + const channel = "foobar"; + + onceCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + expect(called).toBe(false); + }); + + it("should call the handler only once even if args are valid multiple times", () => { + let called = 0; + const source = new EventEmitter(); + const listener = () => called += 1; + const verifier = (args: unknown[]): args is [] => true; + const channel = "foobar"; + + onceCorrect({ source, listener, verifier, channel }); + + source.emit(channel); + source.emit(channel); + expect(called).toBe(1); + }); + + it("should call the handler on only the first valid set of args", () => { + let called = ""; + let verifierCalled = 0; + const source = new EventEmitter(); + const listener = (info: any, arg: string) => called = arg; + const verifier = (args: unknown[]): args is [string] => (++verifierCalled) % 3 === 0; + const channel = "foobar"; + + onceCorrect({ source, listener, verifier, channel }); + + source.emit(channel, {}, "a"); + source.emit(channel, {}, "b"); + source.emit(channel, {}, "c"); + source.emit(channel, {}, "d"); + source.emit(channel, {}, "e"); + source.emit(channel, {}, "f"); + source.emit(channel, {}, "g"); + source.emit(channel, {}, "h"); + source.emit(channel, {}, "i"); + expect(called).toBe("c"); + }); + }); +}); diff --git a/src/common/ipc/index.ts b/src/common/ipc/index.ts new file mode 100644 index 0000000000..a34890472e --- /dev/null +++ b/src/common/ipc/index.ts @@ -0,0 +1,3 @@ +export * from "./ipc"; +export * from "./update-available"; +export * from "./type-enforced-ipc"; diff --git a/src/common/ipc.ts b/src/common/ipc/ipc.ts similarity index 53% rename from src/common/ipc.ts rename to src/common/ipc/ipc.ts index 369221815b..48b0b89153 100644 --- a/src/common/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -3,10 +3,13 @@ // https://www.electronjs.org/docs/api/ipc-renderer import { ipcMain, ipcRenderer, webContents, remote } from "electron"; -import logger from "../main/logger"; -import { ClusterFrameInfo, clusterFrameMap } from "./cluster-frames"; +import { toJS } from "mobx"; +import logger from "../../main/logger"; +import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames"; -export function handleRequest(channel: string, listener: (...args: any[]) => any) { +const subFramesChannel = "ipc:get-sub-frames"; + +export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { ipcMain.handle(channel, listener); } @@ -14,38 +17,39 @@ export async function requestMain(channel: string, ...args: any[]) { return ipcRenderer.invoke(channel, ...args); } -async function getSubFrames(): Promise { - const subFrames: ClusterFrameInfo[] = []; - - clusterFrameMap.forEach(frameInfo => { - subFrames.push(frameInfo); - }); - - return subFrames; +function getSubFrames(): ClusterFrameInfo[] { + return toJS(Array.from(clusterFrameMap.values()), { recurseEverything: true }); } -export function broadcastMessage(channel: string, ...args: any[]) { +export async function broadcastMessage(channel: string, ...args: any[]) { const views = (webContents || remote?.webContents)?.getAllWebContents(); if (!views) return; - views.forEach(webContent => { - const type = webContent.getType(); - - logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${webContent.id}`, { args }); - webContent.send(channel, ...args); - getSubFrames().then((frames) => { - frames.map((frameInfo) => { - webContent.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args); - }); - }).catch((e) => e); - }); - if (ipcRenderer) { ipcRenderer.send(channel, ...args); } else { ipcMain.emit(channel, ...args); } + + for (const view of views) { + const type = view.getType(); + + logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${view.id}`, { args }); + view.send(channel, ...args); + + try { + const subFrames: ClusterFrameInfo[] = ipcRenderer + ? await requestMain(subFramesChannel) + : getSubFrames(); + + for (const frameInfo of subFrames) { + view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args); + } + } catch (error) { + logger.error("[IPC]: failed to send IPC message", { error }); + } + } } export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) { @@ -73,3 +77,9 @@ export function unsubscribeAllFromBroadcast(channel: string) { ipcMain.removeAllListeners(channel); } } + +export function bindBroadcastHandlers() { + handleRequest(subFramesChannel, () => { + return getSubFrames(); + }); +} diff --git a/src/common/ipc/type-enforced-ipc.ts b/src/common/ipc/type-enforced-ipc.ts new file mode 100644 index 0000000000..be54992008 --- /dev/null +++ b/src/common/ipc/type-enforced-ipc.ts @@ -0,0 +1,71 @@ +import { EventEmitter } from "events"; +import logger from "../../main/logger"; + +export type HandlerEvent = Parameters[1]>[0]; +export type ListVerifier = (args: unknown[]) => args is T; +export type Rest = T extends [any, ...infer R] ? R : []; + +/** + * Adds a listener to `source` that waits for the first IPC message with the correct + * argument data is sent. + * @param channel The channel to be listened on + * @param listener The function for the channel to be called if the args of the correct type + * @param verifier The function to be called to verify that the args are the correct type + */ +export function onceCorrect< + EM extends EventEmitter, + L extends (event: HandlerEvent, ...args: any[]) => any +>({ + source, + channel, + listener, + verifier, +}: { + source: EM, + channel: string | symbol, + listener: L, + verifier: ListVerifier>>, +}): void { + function handler(event: HandlerEvent, ...args: unknown[]): void { + if (verifier(args)) { + source.removeListener(channel, handler); // remove immediately + + (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject + .catch((error: any) => logger.error("[IPC]: channel once handler threw error", { channel, error })); + } else { + logger.error("[IPC]: channel was emitted with invalid data", { channel, args }); + } + } + + source.on(channel, handler); +} + +/** + * Adds a listener to `source` that checks to verify the arguments before calling the handler. + * @param channel The channel to be listened on + * @param listener The function for the channel to be called if the args of the correct type + * @param verifier The function to be called to verify that the args are the correct type + */ +export function onCorrect< + EM extends EventEmitter, + L extends (event: HandlerEvent, ...args: any[]) => any +>({ + source, + channel, + listener, + verifier, +}: { + source: EM, + channel: string | symbol, + listener: L, + verifier: ListVerifier>>, +}): void { + source.on(channel, (event, ...args: unknown[]) => { + if (verifier(args)) { + (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject + .catch(error => logger.error("[IPC]: channel on handler threw error", { channel, error })); + } else { + logger.error("[IPC]: channel was emitted with invalid data", { channel, args }); + } + }); +} diff --git a/src/common/ipc/update-available/index.ts b/src/common/ipc/update-available/index.ts new file mode 100644 index 0000000000..1e3fcf1268 --- /dev/null +++ b/src/common/ipc/update-available/index.ts @@ -0,0 +1,48 @@ +import { UpdateInfo } from "electron-updater"; + +export const UpdateAvailableChannel = "update-available"; +export const AutoUpdateLogPrefix = "[UPDATE-CHECKER]"; + +/** + * [, ] + */ +export type UpdateAvailableFromMain = [string, UpdateInfo]; + +export function areArgsUpdateAvailableFromMain(args: unknown[]): args is UpdateAvailableFromMain { + if (args.length !== 2) { + return false; + } + + if (typeof args[0] !== "string") { + return false; + } + + if (typeof args[1] !== "object" || args[1] === null) { + // TODO: improve this checking + return false; + } + + return true; +} + +export type BackchannelArg = { + doUpdate: false; +} | { + doUpdate: true; + now: boolean; +}; + +export type UpdateAvailableToBackchannel = [BackchannelArg]; + +export function areArgsUpdateAvailableToBackchannel(args: unknown[]): args is UpdateAvailableToBackchannel { + if (args.length !== 1) { + return false; + } + + if (typeof args[0] !== "object" || args[0] === null) { + // TODO: improve this checking + return false; + } + + return true; +} diff --git a/src/common/kube-helpers.ts b/src/common/kube-helpers.ts index bb0e6b86d2..02a9faef92 100644 --- a/src/common/kube-helpers.ts +++ b/src/common/kube-helpers.ts @@ -175,4 +175,4 @@ export function validateKubeConfig (config: KubeConfig) { throw new ExecValidationNotFoundError(execCommand, isAbsolute); } } -} \ No newline at end of file +} diff --git a/src/common/prometheus-providers.ts b/src/common/prometheus-providers.ts index a5c515b338..5496163c38 100644 --- a/src/common/prometheus-providers.ts +++ b/src/common/prometheus-providers.ts @@ -10,4 +10,4 @@ import { PrometheusProviderRegistry } from "../main/prometheus/provider-registry PrometheusProviderRegistry.registerProvider(provider.id, provider); }); -export const prometheusProviders = PrometheusProviderRegistry.getProviders(); \ No newline at end of file +export const prometheusProviders = PrometheusProviderRegistry.getProviders(); diff --git a/src/common/rbac.ts b/src/common/rbac.ts index fbcf7c98d8..de242b114a 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -7,37 +7,38 @@ export type KubeResource = "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets"; export interface KubeApiResource { - resource: KubeResource; // valid resource name + kind: string; // resource type (e.g. "Namespace") + apiName: KubeResource; // valid api resource name (e.g. "namespaces") group?: string; // api-group } // TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7) export const apiResources: KubeApiResource[] = [ - { resource: "configmaps" }, - { resource: "cronjobs", group: "batch" }, - { resource: "customresourcedefinitions", group: "apiextensions.k8s.io" }, - { resource: "daemonsets", group: "apps" }, - { resource: "deployments", group: "apps" }, - { resource: "endpoints" }, - { resource: "events" }, - { resource: "horizontalpodautoscalers" }, - { resource: "ingresses", group: "networking.k8s.io" }, - { resource: "jobs", group: "batch" }, - { resource: "limitranges" }, - { resource: "namespaces" }, - { resource: "networkpolicies", group: "networking.k8s.io" }, - { resource: "nodes" }, - { resource: "persistentvolumes" }, - { resource: "persistentvolumeclaims" }, - { resource: "pods" }, - { resource: "poddisruptionbudgets" }, - { resource: "podsecuritypolicies" }, - { resource: "resourcequotas" }, - { resource: "replicasets", group: "apps" }, - { resource: "secrets" }, - { resource: "services" }, - { resource: "statefulsets", group: "apps" }, - { resource: "storageclasses", group: "storage.k8s.io" }, + { kind: "ConfigMap", apiName: "configmaps" }, + { kind: "CronJob", apiName: "cronjobs", group: "batch" }, + { kind: "CustomResourceDefinition", apiName: "customresourcedefinitions", group: "apiextensions.k8s.io" }, + { kind: "DaemonSet", apiName: "daemonsets", group: "apps" }, + { kind: "Deployment", apiName: "deployments", group: "apps" }, + { kind: "Endpoint", apiName: "endpoints" }, + { kind: "Event", apiName: "events" }, + { kind: "HorizontalPodAutoscaler", apiName: "horizontalpodautoscalers" }, + { kind: "Ingress", apiName: "ingresses", group: "networking.k8s.io" }, + { kind: "Job", apiName: "jobs", group: "batch" }, + { kind: "Namespace", apiName: "namespaces" }, + { kind: "LimitRange", apiName: "limitranges" }, + { kind: "NetworkPolicy", apiName: "networkpolicies", group: "networking.k8s.io" }, + { kind: "Node", apiName: "nodes" }, + { kind: "PersistentVolume", apiName: "persistentvolumes" }, + { kind: "PersistentVolumeClaim", apiName: "persistentvolumeclaims" }, + { kind: "Pod", apiName: "pods" }, + { kind: "PodDisruptionBudget", apiName: "poddisruptionbudgets" }, + { kind: "PodSecurityPolicy", apiName: "podsecuritypolicies" }, + { kind: "ResourceQuota", apiName: "resourcequotas" }, + { kind: "ReplicaSet", apiName: "replicasets", group: "apps" }, + { kind: "Secret", apiName: "secrets" }, + { kind: "Service", apiName: "services" }, + { kind: "StatefulSet", apiName: "statefulsets", group: "apps" }, + { kind: "StorageClass", apiName: "storageclasses", group: "storage.k8s.io" }, ]; export function isAllowedResource(resources: KubeResource | KubeResource[]) { diff --git a/src/common/search-store.ts b/src/common/search-store.ts index a3aba9dcbe..86a6054af3 100644 --- a/src/common/search-store.ts +++ b/src/common/search-store.ts @@ -1,4 +1,5 @@ -import { action, computed, observable } from "mobx"; +import { action, computed, observable,reaction } from "mobx"; +import { dockStore } from "../renderer/components/dock/dock.store"; import { autobind } from "../renderer/utils"; export class SearchStore { @@ -6,6 +7,12 @@ export class SearchStore { @observable occurrences: number[] = []; // Array with line numbers, eg [0, 0, 10, 21, 21, 40...] @observable activeOverlayIndex = -1; // Index withing the occurences array. Showing where is activeOverlay currently located + constructor() { + reaction(() => dockStore.selectedTabId, () => { + searchStore.reset(); + }); + } + /** * Sets default activeOverlayIndex * @param text An array of any textual data (logs, for example) @@ -133,4 +140,4 @@ export class SearchStore { } } -export const searchStore = new SearchStore; \ No newline at end of file +export const searchStore = new SearchStore; diff --git a/src/common/user-store.ts b/src/common/user-store.ts index cf271a011d..b0294d9e5a 100644 --- a/src/common/user-store.ts +++ b/src/common/user-store.ts @@ -84,6 +84,15 @@ export class UserStore extends BaseStore { return semver.gt(getAppVersion(), this.lastSeenAppVersion); } + @action + setHiddenTableColumns(tableId: string, names: Set | string[]) { + this.preferences.hiddenTableColumns[tableId] = Array.from(names); + } + + getHiddenTableColumns(tableId: string): Set { + return new Set(this.preferences.hiddenTableColumns[tableId]); + } + @action resetKubeConfigPath() { this.kubeConfigPath = kubeConfigDefaultPath; diff --git a/src/common/utils/__tests__/splitArray.test.ts b/src/common/utils/__tests__/splitArray.test.ts index a401e07701..1e1589fee2 100644 --- a/src/common/utils/__tests__/splitArray.test.ts +++ b/src/common/utils/__tests__/splitArray.test.ts @@ -28,4 +28,4 @@ describe("split array on element tests", () => { test("ten elements, in end array", () => { expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8], [], true]); }); -}); \ No newline at end of file +}); diff --git a/src/common/utils/app-version.ts b/src/common/utils/app-version.ts index a45f5a55bb..d625f44cd7 100644 --- a/src/common/utils/app-version.ts +++ b/src/common/utils/app-version.ts @@ -1,3 +1,4 @@ +import requestPromise from "request-promise-native"; import packageInfo from "../../../package.json"; export function getAppVersion(): string { @@ -11,3 +12,13 @@ export function getBundledKubectlVersion(): string { export function getBundledExtensions(): string[] { return packageInfo.lens?.extensions || []; } + +export async function getAppVersionFromProxyServer(proxyPort: number): Promise { + const response = await requestPromise({ + method: "GET", + uri: `http://localhost:${proxyPort}/version`, + resolveWithFullResponse: true + }); + + return JSON.parse(response.body).version; +} diff --git a/src/common/utils/delay.ts b/src/common/utils/delay.ts new file mode 100644 index 0000000000..7d0686d29b --- /dev/null +++ b/src/common/utils/delay.ts @@ -0,0 +1,8 @@ +/** + * Return a promise that will be resolved after at least `timeout` ms have + * passed + * @param timeout The number of milliseconds before resolving + */ +export function delay(timeout = 1000): Promise { + return new Promise(resolve => setTimeout(resolve, timeout)); +} diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 582135d7f0..2b8147fad9 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -7,6 +7,7 @@ export * from "./autobind"; export * from "./base64"; export * from "./camelCase"; export * from "./cloneJson"; +export * from "./delay"; export * from "./debouncePromise"; export * from "./defineGlobal"; export * from "./getRandId"; @@ -17,3 +18,4 @@ export * from "./openExternal"; export * from "./downloadFile"; export * from "./escapeRegExp"; export * from "./tar"; +export * from "./delay"; diff --git a/src/common/utils/singleton.ts b/src/common/utils/singleton.ts index 61269d10b1..caa5471072 100644 --- a/src/common/utils/singleton.ts +++ b/src/common/utils/singleton.ts @@ -26,4 +26,4 @@ class Singleton { } export { Singleton }; -export default Singleton; \ No newline at end of file +export default Singleton; diff --git a/src/extensions/interfaces/index.ts b/src/extensions/interfaces/index.ts index c91d8cdd19..7b1c601537 100644 --- a/src/extensions/interfaces/index.ts +++ b/src/extensions/interfaces/index.ts @@ -1 +1 @@ -export * from "./registrations"; \ No newline at end of file +export * from "./registrations"; diff --git a/src/extensions/interfaces/registrations.ts b/src/extensions/interfaces/registrations.ts index 47c63062ea..ff51d9a824 100644 --- a/src/extensions/interfaces/registrations.ts +++ b/src/extensions/interfaces/registrations.ts @@ -5,4 +5,4 @@ export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../re export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry"; export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry"; export type { PageMenuRegistration, ClusterPageMenuRegistration, PageMenuComponents } from "../registries/page-menu-registry"; -export type { StatusBarRegistration } from "../registries/status-bar-registry"; \ No newline at end of file +export type { StatusBarRegistration } from "../registries/status-bar-registry"; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 25afaa76fe..8b9b132114 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -2,6 +2,7 @@ import type { AppPreferenceRegistration, ClusterFeatureRegistration, ClusterPage import type { Cluster } from "../main/cluster"; import { LensExtension } from "./lens-extension"; import { getExtensionPageUrl } from "./registries/page-registry"; +import { CommandRegistration } from "./registries/command-registry"; export class LensRendererExtension extends LensExtension { globalPages: PageRegistration[] = []; @@ -14,6 +15,7 @@ export class LensRendererExtension extends LensExtension { statusBarItems: StatusBarRegistration[] = []; kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; + commands: CommandRegistration[] = []; async navigate

(pageId?: string, params?: P) { const { navigate } = await import("../renderer/navigation"); diff --git a/src/extensions/registries/command-registry.ts b/src/extensions/registries/command-registry.ts new file mode 100644 index 0000000000..0b1fc0252c --- /dev/null +++ b/src/extensions/registries/command-registry.ts @@ -0,0 +1,37 @@ +// Extensions API -> Commands + +import type { Cluster } from "../../main/cluster"; +import type { Workspace } from "../../common/workspace-store"; +import { BaseRegistry } from "./base-registry"; +import { action } from "mobx"; +import { LensExtension } from "../lens-extension"; + +export type CommandContext = { + cluster?: Cluster; + workspace?: Workspace; +}; + +export interface CommandRegistration { + id: string; + title: string; + scope: "cluster" | "global"; + action: (context: CommandContext) => void; + isActive?: (context: CommandContext) => boolean; +} + +export class CommandRegistry extends BaseRegistry { + @action + add(items: CommandRegistration | CommandRegistration[], extension?: LensExtension) { + const itemArray = [items].flat(); + + const newIds = itemArray.map((item) => item.id); + const currentIds = this.getItems().map((item) => item.id); + + const filteredIds = newIds.filter((id) => !currentIds.includes(id)); + const filteredItems = itemArray.filter((item) => filteredIds.includes(item.id)); + + return super.add(filteredItems, extension); + } +} + +export const commandRegistry = new CommandRegistry(); diff --git a/src/extensions/registries/status-bar-registry.ts b/src/extensions/registries/status-bar-registry.ts index 88c4132d30..698c5f5f24 100644 --- a/src/extensions/registries/status-bar-registry.ts +++ b/src/extensions/registries/status-bar-registry.ts @@ -3,7 +3,18 @@ import React from "react"; import { BaseRegistry } from "./base-registry"; -export interface StatusBarRegistration { +interface StatusBarComponents { + Item?: React.ComponentType; +} + +interface StatusBarRegistrationV2 { + components: StatusBarComponents; +} + +export interface StatusBarRegistration extends StatusBarRegistrationV2 { + /** + * @deprecated use components.Item instead + */ item?: React.ReactNode; } diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index a9a519498b..55de99bf80 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -13,6 +13,9 @@ export * from "../../renderer/components/select"; export * from "../../renderer/components/slider"; export * from "../../renderer/components/input/input"; +// command-overlay +export { CommandOverlay } from "../../renderer/components/command-palette"; + // other components export * from "../../renderer/components/icon"; export * from "../../renderer/components/tooltip"; @@ -38,4 +41,4 @@ export * from "../../renderer/components/+events/kube-event-details"; // specific exports export * from "../../renderer/components/status-brick"; export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store"; -export { createPodLogsTab } from "../../renderer/components/dock/log.store"; +export { logTabStore } from "../../renderer/components/dock/log-tab.store"; diff --git a/src/extensions/renderer-api/kube-object-status.ts b/src/extensions/renderer-api/kube-object-status.ts index f609d736fe..616ead1bb2 100644 --- a/src/extensions/renderer-api/kube-object-status.ts +++ b/src/extensions/renderer-api/kube-object-status.ts @@ -8,4 +8,4 @@ export enum KubeObjectStatusLevel { INFO = 1, WARNING = 2, CRITICAL = 3 -} \ No newline at end of file +} diff --git a/src/extensions/renderer-api/theming.ts b/src/extensions/renderer-api/theming.ts index f819036803..b3da69bdbc 100644 --- a/src/extensions/renderer-api/theming.ts +++ b/src/extensions/renderer-api/theming.ts @@ -2,4 +2,4 @@ import { themeStore } from "../../renderer/theme.store"; export function getActiveTheme() { return themeStore.activeTheme; -} \ No newline at end of file +} diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts index b3f0442cc2..4b11a19879 100644 --- a/src/main/__test__/cluster.test.ts +++ b/src/main/__test__/cluster.test.ts @@ -126,6 +126,7 @@ describe("create clusters", () => { }; jest.spyOn(Cluster.prototype, "isClusterAdmin").mockReturnValue(Promise.resolve(true)); + jest.spyOn(Cluster.prototype, "canUseWatchApi").mockReturnValue(Promise.resolve(true)); jest.spyOn(Cluster.prototype, "canI") .mockImplementation((attr: V1ResourceAttributes): Promise => { expect(attr.namespace).toBe("default"); diff --git a/src/main/app-updater.ts b/src/main/app-updater.ts index dd9ed97e69..618f714b49 100644 --- a/src/main/app-updater.ts +++ b/src/main/app-updater.ts @@ -1,20 +1,78 @@ -import { autoUpdater } from "electron-updater"; +import { autoUpdater, UpdateInfo } from "electron-updater"; import logger from "./logger"; +import { isDevelopment, isTestEnv } from "../common/vars"; +import { delay } from "../common/utils"; +import { areArgsUpdateAvailableToBackchannel, AutoUpdateLogPrefix, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc"; +import { ipcMain } from "electron"; -export class AppUpdater { - static readonly defaultUpdateIntervalMs = 1000 * 60 * 60 * 24; // once a day - - static checkForUpdates() { - return autoUpdater.checkForUpdatesAndNotify(); - } - - constructor(protected updateInterval = AppUpdater.defaultUpdateIntervalMs) { - autoUpdater.logger = logger; - } - - public start() { - setInterval(AppUpdater.checkForUpdates, this.updateInterval); - - return AppUpdater.checkForUpdates(); +function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: UpdateAvailableToBackchannel) { + if (arg.doUpdate) { + if (arg.now) { + logger.info(`${AutoUpdateLogPrefix}: User chose to update now`); + autoUpdater.downloadUpdate() + .then(() => autoUpdater.quitAndInstall()) + .catch(error => logger.error(`${AutoUpdateLogPrefix}: Failed to download or install update`, { error })); + } else { + logger.info(`${AutoUpdateLogPrefix}: User chose to update on quit`); + autoUpdater.autoInstallOnAppQuit = true; + autoUpdater.downloadUpdate() + .catch(error => logger.error(`${AutoUpdateLogPrefix}: Failed to download update`, { error })); + } + } else { + logger.info(`${AutoUpdateLogPrefix}: User chose not to update`); + } +} + +/** + * starts the automatic update checking + * @param interval milliseconds between interval to check on, defaults to 24h + */ +export function startUpdateChecking(interval = 1000 * 60 * 60 * 24): void { + if (isDevelopment || isTestEnv) { + return; + } + + autoUpdater.logger = logger; + autoUpdater.autoDownload = false; + autoUpdater.autoInstallOnAppQuit = false; + + autoUpdater + .on("update-available", (args: UpdateInfo) => { + try { + const backchannel = `auto-update:${args.version}`; + + ipcMain.removeAllListeners(backchannel); // only one handler should be present + + // make sure that the handler is in place before broadcasting (prevent race-condition) + onceCorrect({ + source: ipcMain, + channel: backchannel, + listener: handleAutoUpdateBackChannel, + verifier: areArgsUpdateAvailableToBackchannel, + }); + logger.info(`${AutoUpdateLogPrefix}: broadcasting update available`, { backchannel, version: args.version }); + broadcastMessage(UpdateAvailableChannel, backchannel, args); + } catch (error) { + logger.error(`${AutoUpdateLogPrefix}: broadcasting failed`, { error }); + } + }); + + async function helper() { + while (true) { + await checkForUpdates(); + await delay(interval); + } + } + + helper(); +} + +export async function checkForUpdates(): Promise { + try { + logger.info(`📡 Checking for app updates`); + + await autoUpdater.checkForUpdates(); + } catch (error) { + logger.error(`${AutoUpdateLogPrefix}: failed with an error`, { error: String(error) }); } } diff --git a/src/main/cluster-detectors/cluster-id-detector.ts b/src/main/cluster-detectors/cluster-id-detector.ts index 2e0cc694ff..810955afae 100644 --- a/src/main/cluster-detectors/cluster-id-detector.ts +++ b/src/main/cluster-detectors/cluster-id-detector.ts @@ -23,4 +23,4 @@ export class ClusterIdDetector extends BaseClusterDetector { return response.metadata.uid; } -} \ No newline at end of file +} diff --git a/src/main/cluster-detectors/detector-registry.ts b/src/main/cluster-detectors/detector-registry.ts index 43c56153c9..b1d1b73447 100644 --- a/src/main/cluster-detectors/detector-registry.ts +++ b/src/main/cluster-detectors/detector-registry.ts @@ -48,4 +48,4 @@ detectorRegistry.add(ClusterIdDetector); detectorRegistry.add(LastSeenDetector); detectorRegistry.add(VersionDetector); detectorRegistry.add(DistributionDetector); -detectorRegistry.add(NodesCountDetector); \ No newline at end of file +detectorRegistry.add(NodesCountDetector); diff --git a/src/main/cluster-detectors/last-seen-detector.ts b/src/main/cluster-detectors/last-seen-detector.ts index e648d5f2f9..0a9bcf9f74 100644 --- a/src/main/cluster-detectors/last-seen-detector.ts +++ b/src/main/cluster-detectors/last-seen-detector.ts @@ -11,4 +11,4 @@ export class LastSeenDetector extends BaseClusterDetector { return { value: new Date().toJSON(), accuracy: 100 }; } -} \ No newline at end of file +} diff --git a/src/main/cluster-detectors/nodes-count-detector.ts b/src/main/cluster-detectors/nodes-count-detector.ts index 0ece5dd080..45584df5bd 100644 --- a/src/main/cluster-detectors/nodes-count-detector.ts +++ b/src/main/cluster-detectors/nodes-count-detector.ts @@ -16,4 +16,4 @@ export class NodesCountDetector extends BaseClusterDetector { return response.items.length; } -} \ No newline at end of file +} diff --git a/src/main/cluster-detectors/version-detector.ts b/src/main/cluster-detectors/version-detector.ts index 8080ef57a1..b19979db8a 100644 --- a/src/main/cluster-detectors/version-detector.ts +++ b/src/main/cluster-detectors/version-detector.ts @@ -16,4 +16,4 @@ export class VersionDetector extends BaseClusterDetector { return response.gitVersion; } -} \ No newline at end of file +} diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 1b468e3bb6..dfcda98203 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -1,7 +1,7 @@ import "../common/cluster-ipc"; import type http from "http"; import { ipcMain } from "electron"; -import { autorun } from "mobx"; +import { autorun, reaction } from "mobx"; import { clusterStore, getClusterIdFromHost } from "../common/cluster-store"; import { Cluster } from "./cluster"; import logger from "./logger"; @@ -12,14 +12,14 @@ export class ClusterManager extends Singleton { constructor(public readonly port: number) { super(); // auto-init clusters - autorun(() => { - clusterStore.enabledClustersList.forEach(cluster => { + reaction(() => clusterStore.enabledClustersList, (clusters) => { + clusters.forEach((cluster) => { if (!cluster.initialized && !cluster.initializing) { logger.info(`[CLUSTER-MANAGER]: init cluster`, cluster.getMeta()); cluster.init(port); } }); - }); + }, { fireImmediately: true }); // auto-stop removed clusters autorun(() => { diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 003579e266..15c3786cf7 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -49,6 +49,7 @@ export interface ClusterState { isAdmin: boolean; allowedNamespaces: string[] allowedResources: string[] + isGlobalWatchEnabled: boolean; } /** @@ -92,7 +93,6 @@ export class Cluster implements ClusterModel, ClusterState { */ @observable initializing = false; - /** * Is cluster object initialized * @@ -178,6 +178,12 @@ export class Cluster implements ClusterModel, ClusterState { * @observable */ @observable isAdmin = false; + /** + * Global watch-api accessibility , e.g. "/api/v1/services?watch=1" + * + * @observable + */ + @observable isGlobalWatchEnabled = false; /** * Preferences * @@ -191,7 +197,7 @@ export class Cluster implements ClusterModel, ClusterState { */ @observable metadata: ClusterMetadata = {}; /** - * List of allowed namespaces + * List of allowed namespaces verified via K8S::SelfSubjectAccessReview api * * @observable */ @@ -204,7 +210,7 @@ export class Cluster implements ClusterModel, ClusterState { */ @observable allowedResources: string[] = []; /** - * List of accessible namespaces + * List of accessible namespaces provided by user in the Cluster Settings * * @observable */ @@ -225,7 +231,7 @@ export class Cluster implements ClusterModel, ClusterState { * @computed */ @computed get name() { - return this.preferences.clusterName || this.contextName; + return this.preferences.clusterName || this.contextName; } /** @@ -280,7 +286,8 @@ export class Cluster implements ClusterModel, ClusterState { * @param port port where internal auth proxy is listening * @internal */ - @action async init(port: number) { + @action + async init(port: number) { try { this.initializing = true; this.contextHandler = new ContextHandler(this); @@ -335,7 +342,8 @@ export class Cluster implements ClusterModel, ClusterState { * @param force force activation * @internal */ - @action async activate(force = false) { + @action + async activate(force = false) { if (this.activated && !force) { return this.pushState(); } @@ -352,9 +360,7 @@ export class Cluster implements ClusterModel, ClusterState { await this.refreshConnectionStatus(); if (this.accessible) { - await this.refreshAllowedResources(); - this.isAdmin = await this.isClusterAdmin(); - this.ready = true; + await this.refreshAccessibility(); this.ensureKubectl(); } this.activated = true; @@ -374,7 +380,8 @@ export class Cluster implements ClusterModel, ClusterState { /** * @internal */ - @action async reconnect() { + @action + async reconnect() { logger.info(`[CLUSTER]: reconnect`, this.getMeta()); this.contextHandler?.stopServer(); await this.contextHandler?.ensureServer(); @@ -401,19 +408,18 @@ export class Cluster implements ClusterModel, ClusterState { * @internal * @param opts refresh options */ - @action async refresh(opts: ClusterRefreshOptions = {}) { + @action + async refresh(opts: ClusterRefreshOptions = {}) { logger.info(`[CLUSTER]: refresh`, this.getMeta()); await this.whenInitialized; await this.refreshConnectionStatus(); if (this.accessible) { - this.isAdmin = await this.isClusterAdmin(); - await this.refreshAllowedResources(); + await this.refreshAccessibility(); if (opts.refreshMetadata) { this.refreshMetadata(); } - this.ready = true; } this.pushState(); } @@ -421,7 +427,8 @@ export class Cluster implements ClusterModel, ClusterState { /** * @internal */ - @action async refreshMetadata() { + @action + async refreshMetadata() { logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta()); const metadata = await detectorRegistry.detectForCluster(this); const existingMetadata = this.metadata; @@ -432,7 +439,20 @@ export class Cluster implements ClusterModel, ClusterState { /** * @internal */ - @action async refreshConnectionStatus() { + private async refreshAccessibility(): Promise { + this.isAdmin = await this.isClusterAdmin(); + this.isGlobalWatchEnabled = await this.canUseWatchApi({ resource: "*" }); + + await this.refreshAllowedResources(); + + this.ready = true; + } + + /** + * @internal + */ + @action + async refreshConnectionStatus() { const connectionStatus = await this.getConnectionStatus(); this.online = connectionStatus > ClusterStatus.Offline; @@ -442,7 +462,8 @@ export class Cluster implements ClusterModel, ClusterState { /** * @internal */ - @action async refreshAllowedResources() { + @action + async refreshAllowedResources() { this.allowedNamespaces = await this.getAllowedNamespaces(); this.allowedResources = await this.getAllowedResources(); } @@ -568,6 +589,17 @@ export class Cluster implements ClusterModel, ClusterState { }); } + /** + * @internal + */ + async canUseWatchApi(customizeResource: V1ResourceAttributes = {}): Promise { + return this.canI({ + verb: "watch", + resource: "*", + ...customizeResource, + }); + } + toJSON(): ClusterModel { const model: ClusterModel = { id: this.id, @@ -601,6 +633,7 @@ export class Cluster implements ClusterModel, ClusterState { isAdmin: this.isAdmin, allowedNamespaces: this.allowedNamespaces, allowedResources: this.allowedResources, + isGlobalWatchEnabled: this.isGlobalWatchEnabled, }; return toJS(state, { @@ -672,7 +705,7 @@ export class Cluster implements ClusterModel, ClusterState { for (const namespace of this.allowedNamespaces.slice(0, 10)) { if (!this.resourceAccessStatuses.get(apiResource)) { const result = await this.canI({ - resource: apiResource.resource, + resource: apiResource.apiName, group: apiResource.group, verb: "list", namespace @@ -687,9 +720,19 @@ export class Cluster implements ClusterModel, ClusterState { return apiResources .filter((resource) => this.resourceAccessStatuses.get(resource)) - .map(apiResource => apiResource.resource); + .map(apiResource => apiResource.apiName); } catch (error) { return []; } } + + isAllowedResource(kind: string): boolean { + const apiResource = apiResources.find(resource => resource.kind === kind || resource.apiName === kind); + + if (apiResource) { + return this.allowedResources.includes(apiResource.apiName); + } + + return true; // allowed by default for other resources + } } diff --git a/src/main/developer-tools.ts b/src/main/developer-tools.ts index 3a3d5009f8..d0d7e2ae01 100644 --- a/src/main/developer-tools.ts +++ b/src/main/developer-tools.ts @@ -1,9 +1,12 @@ +import logger from "./logger"; + /** * Installs Electron developer tools in the development build. * The dependency is not bundled to the production build. */ export const installDeveloperTools = async () => { if (process.env.NODE_ENV === "development") { + logger.info("🤓 Installing developer tools"); const { default: devToolsInstaller, REACT_DEVELOPER_TOOLS } = await import("electron-devtools-installer"); return devToolsInstaller([REACT_DEVELOPER_TOOLS]); diff --git a/src/main/index.ts b/src/main/index.ts index abe77eb2d3..ac5dcb4dc3 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -10,7 +10,6 @@ import path from "path"; import { LensProxy } from "./lens-proxy"; import { WindowManager } from "./window-manager"; import { ClusterManager } from "./cluster-manager"; -import { AppUpdater } from "./app-updater"; import { shellSync } from "./shell-sync"; import { getFreePort } from "./port"; import { mangleProxyEnv } from "./proxy-env"; @@ -27,6 +26,9 @@ import { InstalledExtension, extensionDiscovery } from "../extensions/extension- import type { LensExtensionId } from "../extensions/lens-extension"; import { installDeveloperTools } from "./developer-tools"; import { filesystemProvisionerStore } from "./extension-filesystem"; +import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; +import { bindBroadcastHandlers } from "../common/ipc"; +import { startUpdateChecking } from "./app-updater"; const workingDir = path.join(app.getPath("appData"), appName); let proxyPort: number; @@ -62,20 +64,20 @@ if (process.env.LENS_DISABLE_GPU) { app.on("ready", async () => { logger.info(`🚀 Starting Lens from "${workingDir}"`); + logger.info("🐚 Syncing shell environment"); await shellSync(); + bindBroadcastHandlers(); + powerMonitor.on("shutdown", () => { app.exit(); }); - const updater = new AppUpdater(); - - updater.start(); - registerFileProtocol("static", __static); await installDeveloperTools(); + logger.info("💾 Loading stores"); // preload await Promise.all([ userStore.load(), @@ -87,6 +89,7 @@ app.on("ready", async () => { // find free port try { + logger.info("🔑 Getting free port for LensProxy server"); proxyPort = await getFreePort(); } catch (error) { logger.error(error); @@ -99,6 +102,7 @@ app.on("ready", async () => { // run proxy try { + logger.info("🔌 Starting LensProxy"); // eslint-disable-next-line unused-imports/no-unused-vars-ts proxyServer = LensProxy.create(proxyPort, clusterManager); } catch (error) { @@ -117,9 +121,27 @@ app.on("ready", async () => { } }); + // test proxy connection + try { + logger.info("🔎 Testing LensProxy connection ..."); + const versionFromProxy = await getAppVersionFromProxyServer(proxyPort); + + if (getAppVersion() !== versionFromProxy) { + logger.error(`Proxy server responded with invalid response`); + } + logger.info("⚡ LensProxy connection OK"); + } catch (error) { + logger.error("Checking proxy server connection failed", error); + } + extensionLoader.init(); extensionDiscovery.init(); + + logger.info("🖥️ Starting WindowManager"); windowManager = WindowManager.getInstance(proxyPort); + windowManager.whenLoaded.then(() => startUpdateChecking()); + + logger.info("🧩 Initializing extensions"); // call after windowManager to see splash earlier try { diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index ebfd2a6a98..7e0d6ed5c7 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -23,10 +23,10 @@ const kubectlMap: Map = new Map([ ["1.14", "1.14.10"], ["1.15", "1.15.11"], ["1.16", "1.16.15"], - ["1.17", bundledVersion], - ["1.18", "1.18.14"], - ["1.19", "1.19.5"], - ["1.20", "1.20.0"] + ["1.17", "1.17.17"], + ["1.18", bundledVersion], + ["1.19", "1.19.7"], + ["1.20", "1.20.2"] ]); const packageMirrors: Map = new Map([ ["default", "https://storage.googleapis.com/kubernetes-release/release"], diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts index ad1e4bfda4..c9e2554ce5 100644 --- a/src/main/lens-proxy.ts +++ b/src/main/lens-proxy.ts @@ -30,7 +30,7 @@ export class LensProxy { listen(port = this.port): this { this.proxyServer = this.buildCustomProxy().listen(port); - logger.info(`LensProxy server has started at ${this.origin}`); + logger.info(`[LENS-PROXY]: Proxy server has started at ${this.origin}`); return this; } @@ -43,13 +43,13 @@ export class LensProxy { protected buildCustomProxy(): http.Server { const proxy = this.createProxy(); - const proxyCert = getProxyCertificate() + const proxyCert = getProxyCertificate(); const spdyProxy = spdy.createServer({ key: proxyCert.private, cert: proxyCert.cert, spdy: { plain: false, - protocols: ["http/1.1", "spdy/3.1"] + protocols: ["h2", "http/1.1", "spdy/3.1"] } }, (req: http.IncomingMessage, res: http.ServerResponse) => { this.handleRequest(proxy, req, res); @@ -198,7 +198,8 @@ export class LensProxy { if (proxyTarget) { // allow to fetch apis in "clusterId.localhost:port" from "localhost:port" - res.setHeader("Access-Control-Allow-Origin", this.origin); + // this should be safe because we have already validated cluster uuid + res.setHeader("Access-Control-Allow-Origin", "*"); return proxy.web(req, res, proxyTarget); } diff --git a/src/main/menu.ts b/src/main/menu.ts index 2cddbb1b01..57c6ccab5e 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -10,6 +10,7 @@ import { extensionsURL } from "../renderer/components/+extensions/extensions.rou import { menuRegistry } from "../extensions/registries/menu-registry"; import logger from "./logger"; import { exitApp } from "./exit-app"; +import { broadcastMessage } from "../common/ipc"; export type MenuTopId = "mac" | "file" | "edit" | "view" | "help"; @@ -173,6 +174,14 @@ export function buildMenu(windowManager: WindowManager) { const viewMenu: MenuItemConstructorOptions = { label: "View", submenu: [ + { + label: "Command Palette...", + accelerator: "Shift+CmdOrCtrl+P", + click() { + broadcastMessage("command-palette:open"); + } + }, + { type: "separator" }, { label: "Back", accelerator: "CmdOrCtrl+[", diff --git a/src/main/router.ts b/src/main/router.ts index 896893a592..bb49aacdab 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -5,7 +5,7 @@ import path from "path"; import { readFile } from "fs-extra"; import { Cluster } from "./cluster"; import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars"; -import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes"; +import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, versionRoute } from "./routes"; import logger from "./logger"; export interface RouterRequestOpts { @@ -143,11 +143,9 @@ export class Router { this.handleStaticFile(params.path, response, req); }); + this.router.add({ method: "get", path: "/version"}, versionRoute.getVersion.bind(versionRoute)); this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute)); - // Watch API - this.router.add({ method: "get", path: `${apiPrefix}/watch` }, watchRoute.routeWatch.bind(watchRoute)); - // Metrics API this.router.add({ method: "post", path: `${apiPrefix}/metrics` }, metricsRoute.routeMetrics.bind(metricsRoute)); diff --git a/src/main/routes/index.ts b/src/main/routes/index.ts index 5bc5b3f3dd..c194d8f8b2 100644 --- a/src/main/routes/index.ts +++ b/src/main/routes/index.ts @@ -1,6 +1,6 @@ export * from "./kubeconfig-route"; export * from "./metrics-route"; export * from "./port-forward-route"; -export * from "./watch-route"; export * from "./helm-route"; export * from "./resource-applier-route"; +export * from "./version-route"; diff --git a/src/main/routes/version-route.ts b/src/main/routes/version-route.ts new file mode 100644 index 0000000000..81ada9eca7 --- /dev/null +++ b/src/main/routes/version-route.ts @@ -0,0 +1,13 @@ +import { LensApiRequest } from "../router"; +import { LensApi } from "../lens-api"; +import { getAppVersion } from "../../common/utils"; + +class VersionRoute extends LensApi { + public async getVersion(request: LensApiRequest) { + const { response } = request; + + this.respondJson(response, { version: getAppVersion()}, 200); + } +} + +export const versionRoute = new VersionRoute(); diff --git a/src/main/routes/watch-route.ts b/src/main/routes/watch-route.ts deleted file mode 100644 index eb9f007eae..0000000000 --- a/src/main/routes/watch-route.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; -import { Watch, KubeConfig } from "@kubernetes/client-node"; -import { ServerResponse } from "http"; -import { Request } from "request"; -import logger from "../logger"; - -class ApiWatcher { - private apiUrl: string; - private response: ServerResponse; - private watchRequest: Request; - private watch: Watch; - private processor: NodeJS.Timeout; - private eventBuffer: any[] = []; - - constructor(apiUrl: string, kubeConfig: KubeConfig, response: ServerResponse) { - this.apiUrl = apiUrl; - this.watch = new Watch(kubeConfig); - this.response = response; - } - - public async start() { - if (this.processor) { - clearInterval(this.processor); - } - this.processor = setInterval(() => { - const events = this.eventBuffer.splice(0); - - events.map(event => this.sendEvent(event)); - this.response.flushHeaders(); - }, 50); - this.watchRequest = await this.watch.watch(this.apiUrl, {}, this.watchHandler.bind(this), this.doneHandler.bind(this)); - } - - public stop() { - if (!this.watchRequest) { return; } - - if (this.processor) { - clearInterval(this.processor); - } - logger.debug(`Stopping watcher for api: ${this.apiUrl}`); - - try { - this.watchRequest.abort(); - this.sendEvent({ - type: "STREAM_END", - url: this.apiUrl, - status: 410, - }); - logger.debug("watch aborted"); - } catch (error) { - logger.error(`Watch abort errored:${error}`); - } - } - - private watchHandler(phase: string, obj: any) { - this.eventBuffer.push({ - type: phase, - object: obj - }); - } - - private doneHandler(error: Error) { - if (error) logger.warn(`watch ended: ${error.toString()}`); - this.watchRequest.abort(); - } - - private sendEvent(evt: any) { - // convert to "text/event-stream" format - this.response.write(`data: ${JSON.stringify(evt)}\n\n`); - } -} - -class WatchRoute extends LensApi { - - public async routeWatch(request: LensApiRequest) { - const { response, cluster} = request; - const apis: string[] = request.query.getAll("api"); - const watchers: ApiWatcher[] = []; - - if (!apis.length) { - this.respondJson(response, { - message: "Empty request. Query params 'api' are not provided.", - example: "?api=/api/v1/pods&api=/api/v1/nodes", - }, 400); - - return; - } - - response.setHeader("Content-Type", "text/event-stream"); - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Connection", "keep-alive"); - logger.debug(`watch using kubeconfig:${JSON.stringify(cluster.getProxyKubeconfig(), null, 2)}`); - - apis.forEach(apiUrl => { - const watcher = new ApiWatcher(apiUrl, cluster.getProxyKubeconfig(), response); - - watcher.start(); - watchers.push(watcher); - }); - - request.raw.req.on("close", () => { - logger.debug("Watch request closed"); - watchers.map(watcher => watcher.stop()); - }); - - request.raw.req.on("end", () => { - logger.debug("Watch request ended"); - watchers.map(watcher => watcher.stop()); - }); - - } -} - -export const watchRoute = new WatchRoute(); diff --git a/src/main/tray.ts b/src/main/tray.ts index 47f641ad72..3d6d2dd624 100644 --- a/src/main/tray.ts +++ b/src/main/tray.ts @@ -1,9 +1,9 @@ import path from "path"; import packageInfo from "../../package.json"; -import { dialog, Menu, NativeImage, Tray } from "electron"; +import { Menu, NativeImage, Tray } from "electron"; import { autorun } from "mobx"; import { showAbout } from "./menu"; -import { AppUpdater } from "./app-updater"; +import { checkForUpdates } from "./app-updater"; import { WindowManager } from "./window-manager"; import { clusterStore } from "../common/cluster-store"; import { workspaceStore } from "../common/workspace-store"; @@ -62,16 +62,6 @@ function buildTray(icon: string | NativeImage, menu: Menu, windowManager: Window function createTrayMenu(windowManager: WindowManager): Menu { return Menu.buildFromTemplate([ - { - label: "About Lens", - async click() { - // note: argument[1] (browserWindow) not available when app is not focused / hidden - const browserWindow = await windowManager.ensureMainWindow(); - - showAbout(browserWindow); - }, - }, - { type: "separator" }, { label: "Open Lens", async click() { @@ -112,16 +102,17 @@ function createTrayMenu(windowManager: WindowManager): Menu { { label: "Check for updates", async click() { - const result = await AppUpdater.checkForUpdates(); + await checkForUpdates(); + await windowManager.ensureMainWindow(); + }, + }, + { + label: "About Lens", + async click() { + // note: argument[1] (browserWindow) not available when app is not focused / hidden + const browserWindow = await windowManager.ensureMainWindow(); - if (!result) { - const browserWindow = await windowManager.ensureMainWindow(); - - dialog.showMessageBoxSync(browserWindow, { - message: "No updates available", - type: "info", - }); - } + showAbout(browserWindow); }, }, { type: "separator" }, diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index fbf7219c7a..b52d907226 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -1,5 +1,5 @@ import type { ClusterId } from "../common/cluster-store"; -import { observable } from "mobx"; +import { observable, when } from "mobx"; import { app, BrowserWindow, dialog, shell, webContents } from "electron"; import windowStateKeeper from "electron-window-state"; import { appEventBus } from "../common/event-bus"; @@ -8,6 +8,7 @@ import { initMenu } from "./menu"; import { initTray } from "./tray"; import { Singleton } from "../common/utils"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; +import logger from "./logger"; export class WindowManager extends Singleton { protected mainWindow: BrowserWindow; @@ -15,6 +16,9 @@ export class WindowManager extends Singleton { protected windowState: windowStateKeeper.State; protected disposers: Record = {}; + @observable mainViewInitiallyLoaded = false; + whenLoaded = when(() => this.mainViewInitiallyLoaded); + @observable activeClusterId: ClusterId; constructor(protected proxyPort: number) { @@ -81,16 +85,26 @@ export class WindowManager extends Singleton { this.splashWindow = null; app.dock?.hide(); // hide icon in dock (mac-os) }); + + this.mainWindow.webContents.on("did-fail-load", (_event, code, desc) => { + logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, { code, desc }); + }); + + this.mainWindow.webContents.on("did-finish-load", () => { + logger.info("[WINDOW-MANAGER]: Main window loaded"); + }); } try { if (showSplash) await this.showSplash(); + logger.info(`[WINDOW-MANAGER]: Loading Main window from url: ${this.mainUrl} ...`); await this.mainWindow.loadURL(this.mainUrl); this.mainWindow.show(); this.splashWindow?.close(); setTimeout(() => { appEventBus.emit({ name: "app", action: "start" }); }, 1000); + this.mainViewInitiallyLoaded = true; } catch (err) { dialog.showErrorBox("ERROR!", err.toString()); } diff --git a/src/migrations/cluster-store/index.ts b/src/migrations/cluster-store/index.ts index c546fdaeda..4a71d4f7ad 100644 --- a/src/migrations/cluster-store/index.ts +++ b/src/migrations/cluster-store/index.ts @@ -18,4 +18,4 @@ export default { ...version270Beta1, ...version360Beta1, ...snap -}; \ No newline at end of file +}; diff --git a/src/renderer/api/__tests__/kube-api.test.ts b/src/renderer/api/__tests__/kube-api.test.ts index 41078e77a3..7481bd096a 100644 --- a/src/renderer/api/__tests__/kube-api.test.ts +++ b/src/renderer/api/__tests__/kube-api.test.ts @@ -79,4 +79,4 @@ describe("KubeApi", () => { expect(kubeApi.apiPrefix).toEqual("/apis"); expect(kubeApi.apiGroup).toEqual("extensions"); }); -}); \ No newline at end of file +}); diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index 629a0f29c2..a802851f65 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -2,16 +2,16 @@ import type { KubeObjectStore } from "../kube-object.store"; import { action, observable } from "mobx"; import { autobind } from "../utils"; -import { KubeApi } from "./kube-api"; +import { KubeApi, parseKubeApi } from "./kube-api"; @autobind() export class ApiManager { private apis = observable.map(); - private stores = observable.map(); + private stores = observable.map(); getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { if (typeof pathOrCallback === "string") { - return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase); + return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase); } return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true)); @@ -46,12 +46,12 @@ export class ApiManager { @action registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) { apis.forEach(api => { - this.stores.set(api, store); + this.stores.set(api.apiBase, store); }); } getStore(api: string | KubeApi): S { - return this.stores.get(this.resolveApi(api)) as S; + return this.stores.get(this.resolveApi(api)?.apiBase) as S; } } diff --git a/src/renderer/api/endpoints/helm-charts.api.ts b/src/renderer/api/endpoints/helm-charts.api.ts index a1fd497798..8beff01779 100644 --- a/src/renderer/api/endpoints/helm-charts.api.ts +++ b/src/renderer/api/endpoints/helm-charts.api.ts @@ -86,7 +86,7 @@ export class HelmChart { tillerVersion?: string; getId() { - return this.digest; + return `${this.apiVersion}/${this.name}@${this.getAppVersion()}`; } getName() { diff --git a/src/renderer/api/endpoints/helm-releases.api.ts b/src/renderer/api/endpoints/helm-releases.api.ts index 84e095721b..6831c508cb 100644 --- a/src/renderer/api/endpoints/helm-releases.api.ts +++ b/src/renderer/api/endpoints/helm-releases.api.ts @@ -57,6 +57,7 @@ export interface IReleaseRevision { updated: string; status: string; chart: string; + app_version: string; description: string; } diff --git a/src/renderer/api/endpoints/pods.api.ts b/src/renderer/api/endpoints/pods.api.ts index eca15251a7..3176cae4d2 100644 --- a/src/renderer/api/endpoints/pods.api.ts +++ b/src/renderer/api/endpoints/pods.api.ts @@ -104,6 +104,9 @@ export interface IPodContainer { configMapRef?: { name: string; }; + secretRef?: { + name: string; + } }[]; volumeMounts?: { name: string; diff --git a/src/renderer/api/json-api.ts b/src/renderer/api/json-api.ts index 49c2cb1a28..124248bb67 100644 --- a/src/renderer/api/json-api.ts +++ b/src/renderer/api/json-api.ts @@ -3,7 +3,6 @@ import { stringify } from "querystring"; import { EventEmitter } from "../../common/event-emitter"; import { cancelableFetch } from "../utils/cancelableFetch"; - export interface JsonApiData { } @@ -55,6 +54,34 @@ export class JsonApi { return this.request(path, params, { ...reqInit, method: "get" }); } + getResponse(path: string, params?: P, init: RequestInit = {}): Promise { + const reqPath = `${this.config.apiBase}${path}`; + //const subdomain = randomBytes(2).toString("hex"); + let reqUrl = reqPath; //`http://${subdomain}.${window.location.host}${reqPath}`; // hack around browser connection limits (chromium allows 6 per domain) + const reqInit: RequestInit = { ...init }; + const { query } = params || {} as P; + + if (!reqInit.method) { + reqInit.method = "get"; + } + + if (query) { + const queryString = stringify(query); + + reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; + } + + const infoLog: JsonApiLog = { + method: reqInit.method.toUpperCase(), + reqUrl: reqPath, + reqInit, + }; + + this.writeLog({ ...infoLog }); + + return fetch(reqUrl, reqInit); + } + post(path: string, params?: P, reqInit: RequestInit = {}) { return this.request(path, params, { ...reqInit, method: "post" }); } diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 8a3a2517c2..0be497a8f1 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -9,7 +9,9 @@ import { apiKube } from "./index"; import { createKubeApiURL, parseKubeApi } from "./kube-api-parse"; import { KubeJsonApi, KubeJsonApiData, KubeJsonApiDataList } from "./kube-json-api"; import { IKubeObjectConstructor, KubeObject } from "./kube-object"; -import { kubeWatchApi } from "./kube-watch-api"; +import byline from "byline"; +import { ReadableWebToNodeStream } from "readable-web-to-node-stream"; +import { IKubeWatchEvent } from "./kube-watch-api"; export interface IKubeApiOptions { /** @@ -91,15 +93,13 @@ export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { } } +type KubeApiWatchOptions = { + namespace: string; + callback?: (data: IKubeWatchEvent) => void; + abortController?: AbortController +}; + export class KubeApi { - static parseApi = parseKubeApi; - - static watchAll(...apis: KubeApi[]) { - const disposers = apis.map(api => api.watch()); - - return () => disposers.forEach(unwatch => unwatch()); - } - readonly kind: string; readonly apiBase: string; readonly apiPrefix: string; @@ -112,6 +112,7 @@ export class KubeApi { public objectConstructor: IKubeObjectConstructor; protected request: KubeJsonApi; protected resourceVersions = new Map(); + protected watchDisposer: () => void; constructor(protected options: IKubeApiOptions) { const { @@ -124,7 +125,7 @@ export class KubeApi { if (!options.apiBase) { options.apiBase = objectConstructor.apiBase; } - const { apiBase, apiPrefix, apiGroup, apiVersion, resource } = KubeApi.parseApi(options.apiBase); + const { apiBase, apiPrefix, apiGroup, apiVersion, resource } = parseKubeApi(options.apiBase); this.kind = kind; this.isNamespaced = isNamespaced; @@ -157,7 +158,7 @@ export class KubeApi { for (const apiUrl of apiBases) { // Split e.g. "/apis/extensions/v1beta1/ingresses" to parts - const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = KubeApi.parseApi(apiUrl); + const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl); // Request available resources try { @@ -365,8 +366,88 @@ export class KubeApi { }); } - watch(): () => void { - return kubeWatchApi.subscribe(this); + watch(opts: KubeApiWatchOptions = { namespace: "" }): () => void { + if (!opts.abortController) { + opts.abortController = new AbortController(); + } + const { abortController, namespace, callback } = opts; + + const watchUrl = this.getWatchUrl(namespace); + const responsePromise = this.request.getResponse(watchUrl, null, { + signal: abortController.signal + }); + + responsePromise.then((response) => { + if (!response.ok && !abortController.signal.aborted) { + if (response.status === 410) { // resourceVersion has gone + setTimeout(() => { + this.refreshResourceVersion().then(() => { + this.watch({...opts, abortController}); + }); + }, 1000); + + } else if (response.status >= 500) { // k8s is having hard time + setTimeout(() => { + this.watch({...opts, abortController}); + }, 5000); + } + + return; + } + const nodeStream = new ReadableWebToNodeStream(response.body); + const stream = byline(nodeStream); + + stream.on("data", (line) => { + try { + const event: IKubeWatchEvent = JSON.parse(line); + + this.modifyWatchEvent(event); + + if (callback) { + callback(event); + } + } catch (ignore) { + // ignore parse errors + } + }); + + stream.on("close", () => { + setTimeout(() => { + if (!abortController.signal.aborted) this.watch({...opts, namespace, callback}); + }, 1000); + }); + }, (error) => { + if (error instanceof DOMException) return; // AbortController rejects, we can ignore it + + console.error("watch rejected", error); + }).catch((error) => { + console.error("watch error", error); + }); + + const disposer = () => { + abortController.abort(); + }; + + return disposer; + } + + protected modifyWatchEvent(event: IKubeWatchEvent) { + + switch (event.type) { + case "ADDED": + case "DELETED": + + case "MODIFIED": { + ensureObjectSelfLink(this, event.object); + + const { namespace, resourceVersion } = event.object.metadata; + + this.setResourceVersion(namespace, resourceVersion); + this.setResourceVersion("", resourceVersion); + + break; + } + } } } diff --git a/src/renderer/api/kube-json-api.ts b/src/renderer/api/kube-json-api.ts index 3026a9a956..362ee5438e 100644 --- a/src/renderer/api/kube-json-api.ts +++ b/src/renderer/api/kube-json-api.ts @@ -21,7 +21,7 @@ export interface KubeJsonApiData extends JsonApiData { resourceVersion: string; continue?: string; finalizers?: string[]; - selfLink: string; + selfLink?: string; labels?: { [label: string]: string; }; diff --git a/src/renderer/api/kube-watch-api.ts b/src/renderer/api/kube-watch-api.ts index 78ca25256e..5523c5c5a9 100644 --- a/src/renderer/api/kube-watch-api.ts +++ b/src/renderer/api/kube-watch-api.ts @@ -1,183 +1,141 @@ -// Kubernetes watch-api consumer +// Kubernetes watch-api client +// API: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams -import { computed, observable, reaction } from "mobx"; -import { stringify } from "querystring"; -import { autobind, EventEmitter } from "../utils"; -import { KubeJsonApiData } from "./kube-json-api"; import type { KubeObjectStore } from "../kube-object.store"; -import { ensureObjectSelfLink, KubeApi } from "./kube-api"; -import { apiManager } from "./api-manager"; -import { apiPrefix, isDevelopment } from "../../common/vars"; -import { getHostedCluster } from "../../common/cluster-store"; +import type { ClusterContext } from "../components/context"; -export interface IKubeWatchEvent { +import plimit from "p-limit"; +import { comparer, IReactionDisposer, observable, reaction, when } from "mobx"; +import { autobind, noop } from "../utils"; +import { KubeApi } from "./kube-api"; +import { KubeJsonApiData } from "./kube-json-api"; +import { isDebugging, isProduction } from "../../common/vars"; + +export interface IKubeWatchEvent { type: "ADDED" | "MODIFIED" | "DELETED"; object?: T; } -export interface IKubeWatchRouteEvent { - type: "STREAM_END"; - url: string; - status: number; +export interface IKubeWatchSubscribeStoreOptions { + namespaces?: string[]; // default: all accessible namespaces + preload?: boolean; // preload store items, default: true + waitUntilLoaded?: boolean; // subscribe only after loading all stores, default: true + loadOnce?: boolean; // check store.isLoaded to skip loading if done already, default: false } -export interface IKubeWatchRouteQuery { - api: string | string[]; +export interface IKubeWatchLog { + message: string | string[] | Error; + meta?: object; + cssStyle?: string; } @autobind() export class KubeWatchApi { - protected evtSource: EventSource; - protected onData = new EventEmitter<[IKubeWatchEvent]>(); - protected subscribers = observable.map(); - protected reconnectTimeoutMs = 5000; - protected maxReconnectsOnError = 10; - protected reconnectAttempts = this.maxReconnectsOnError; + @observable context: ClusterContext = null; + @observable subscribers = observable.map(); + @observable isConnected = false; + + contextReady = when(() => Boolean(this.context)); constructor() { - reaction(() => this.activeApis, () => this.connect(), { - fireImmediately: true, - delay: 500, - }); + this.init(); } - @computed get activeApis() { - return Array.from(this.subscribers.keys()); + private async init() { + await this.contextReady; } - getSubscribersCount(api: KubeApi) { - return this.subscribers.get(api) || 0; + isAllowedApi(api: KubeApi): boolean { + return Boolean(this.context?.cluster.isAllowedResource(api.kind)); } - subscribe(...apis: KubeApi[]) { - apis.forEach(api => { - this.subscribers.set(api, this.getSubscribersCount(api) + 1); - }); + preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) { + const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages + const preloading: Promise[] = []; - return () => apis.forEach(api => { - const count = this.getSubscribersCount(api) - 1; + for (const store of stores) { + preloading.push(limitRequests(async () => { + if (store.isLoaded && opts.loadOnce) return; // skip - if (count <= 0) this.subscribers.delete(api); - else this.subscribers.set(api, count); - }); - } - - protected getQuery(): Partial { - const { isAdmin, allowedNamespaces } = getHostedCluster(); + return store.loadAll({ namespaces: opts.namespaces }); + })); + } return { - api: this.activeApis.map(api => { - if (isAdmin) return api.getWatchUrl(); - - return allowedNamespaces.map(namespace => api.getWatchUrl(namespace)); - }).flat() + loading: Promise.allSettled(preloading), + cancelLoading: () => limitRequests.clearQueue(), }; } - // todo: maybe switch to websocket to avoid often reconnects - @autobind() - protected connect() { - if (this.evtSource) this.disconnect(); // close previous connection + subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): () => void { + const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts; + const subscribingNamespaces = opts.namespaces ?? this.context?.allNamespaces ?? []; + const unsubscribeList: Function[] = []; + let isUnsubscribed = false; - if (!this.activeApis.length) { + const load = (namespaces = subscribingNamespaces) => this.preloadStores(stores, { namespaces, loadOnce }); + let preloading = preload && load(); + let cancelReloading: IReactionDisposer = noop; + + const subscribe = () => { + if (isUnsubscribed) return; + + stores.forEach((store) => { + unsubscribeList.push(store.subscribe()); + }); + }; + + if (preloading) { + if (waitUntilLoaded) { + preloading.loading.then(subscribe, error => { + this.log({ + message: new Error("Loading stores has failed"), + meta: { stores, error, options: opts }, + }); + }); + } else { + subscribe(); + } + + // reload stores only for context namespaces change + cancelReloading = reaction(() => this.context?.contextNamespaces, namespaces => { + preloading?.cancelLoading(); + unsubscribeList.forEach(unsubscribe => unsubscribe()); + unsubscribeList.length = 0; + preloading = load(namespaces); + preloading.loading.then(subscribe); + }, { + equals: comparer.shallow, + }); + } + + // unsubscribe + return () => { + if (isUnsubscribed) return; + isUnsubscribed = true; + cancelReloading(); + preloading?.cancelLoading(); + unsubscribeList.forEach(unsubscribe => unsubscribe()); + unsubscribeList.length = 0; + }; + } + + protected log({ message, cssStyle = "", meta = {} }: IKubeWatchLog) { + if (isProduction && !isDebugging) { return; } - const query = this.getQuery(); - const apiUrl = `${apiPrefix}/watch?${stringify(query)}`; - this.evtSource = new EventSource(apiUrl); - this.evtSource.onmessage = this.onMessage; - this.evtSource.onerror = this.onError; - this.writeLog("CONNECTING", query.api); - } - - reconnect() { - if (!this.evtSource || this.evtSource.readyState !== EventSource.OPEN) { - this.reconnectAttempts = this.maxReconnectsOnError; - this.connect(); - } - } - - protected disconnect() { - if (!this.evtSource) return; - this.evtSource.close(); - this.evtSource.onmessage = null; - this.evtSource = null; - } - - protected onMessage(evt: MessageEvent) { - if (!evt.data) return; - const data = JSON.parse(evt.data); - - if ((data as IKubeWatchEvent).object) { - this.onData.emit(data); - } else { - this.onRouteEvent(data); - } - } - - protected async onRouteEvent(event: IKubeWatchRouteEvent) { - if (event.type === "STREAM_END") { - this.disconnect(); - const { apiBase, namespace } = KubeApi.parseApi(event.url); - const api = apiManager.getApi(apiBase); - - if (api) { - try { - await api.refreshResourceVersion({ namespace }); - this.reconnect(); - } catch (error) { - console.error("failed to refresh resource version", error); - - if (this.subscribers.size > 0) { - setTimeout(() => { - this.onRouteEvent(event); - }, 1000); - } - } - } - } - } - - protected onError(evt: MessageEvent) { - const { reconnectAttempts: attemptsRemain, reconnectTimeoutMs } = this; - - if (evt.eventPhase === EventSource.CLOSED) { - if (attemptsRemain > 0) { - this.reconnectAttempts--; - setTimeout(() => this.connect(), reconnectTimeoutMs); - } - } - } - - protected writeLog(...data: any[]) { - if (isDevelopment) { - console.log("%cKUBE-WATCH-API:", `font-weight: bold`, ...data); - } - } - - addListener(store: KubeObjectStore, callback: (evt: IKubeWatchEvent) => void) { - const listener = (evt: IKubeWatchEvent) => { - const { namespace, resourceVersion } = evt.object.metadata; - const api = apiManager.getApiByKind(evt.object.kind, evt.object.apiVersion); - - api.setResourceVersion(namespace, resourceVersion); - api.setResourceVersion("", resourceVersion); - - ensureObjectSelfLink(api, evt.object); - - if (store == apiManager.getStore(api)) { - callback(evt); - } + const logInfo = [`%c[KUBE-WATCH-API]:`, `font-weight: bold; ${cssStyle}`, message].flat().map(String); + const logMeta = { + time: new Date().toLocaleString(), + ...meta, }; - this.onData.addListener(listener); - - return () => this.onData.removeListener(listener); - } - - reset() { - this.subscribers.clear(); + if (message instanceof Error) { + console.error(...logInfo, logMeta); + } else { + console.info(...logInfo, logMeta); + } } } diff --git a/src/renderer/api/workload-kube-object.ts b/src/renderer/api/workload-kube-object.ts index e0c6d3f121..185d3d502c 100644 --- a/src/renderer/api/workload-kube-object.ts +++ b/src/renderer/api/workload-kube-object.ts @@ -1,7 +1,7 @@ import get from "lodash/get"; import { KubeObject } from "./kube-object"; -interface IToleration { +export interface IToleration { key?: string; operator?: string; effect?: string; @@ -82,4 +82,4 @@ export class WorkloadKubeObject extends KubeObject { return Object.keys(affinity).length; } -} \ No newline at end of file +} diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.route.ts b/src/renderer/components/+apps-helm-charts/helm-charts.route.ts index 9b8aecc499..97a0923d97 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.route.ts +++ b/src/renderer/components/+apps-helm-charts/helm-charts.route.ts @@ -11,4 +11,4 @@ export interface IHelmChartsRouteParams { repo?: string; } -export const helmChartsURL = buildURL(helmChartsRoute.path); \ No newline at end of file +export const helmChartsURL = buildURL(helmChartsRoute.path); diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index 8bf5486a55..348ce00969 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -11,8 +11,11 @@ import { navigation } from "../../navigation"; import { ItemListLayout } from "../item-object-list/item-list-layout"; import { SearchInputUrl } from "../input"; -enum sortBy { +enum columnId { name = "name", + description = "description", + version = "version", + appVersion = "app-version", repo = "repo", } @@ -53,13 +56,15 @@ export class HelmCharts extends Component { return ( <> chart.getName(), - [sortBy.repo]: (chart: HelmChart) => chart.getRepository(), + [columnId.name]: (chart: HelmChart) => chart.getName(), + [columnId.repo]: (chart: HelmChart) => chart.getRepository(), }} searchFilters={[ (chart: HelmChart) => chart.getName(), @@ -74,13 +79,12 @@ export class HelmCharts extends Component { )} renderTableHeader={[ - { className: "icon" }, - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Description", className: "description" }, - { title: "Version", className: "version" }, - { title: "App Version", className: "app-version" }, - { title: "Repository", className: "repository", sortBy: sortBy.repo }, - + { className: "icon", showWithColumn: columnId.name }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Description", className: "description", id: columnId.description }, + { title: "Version", className: "version", id: columnId.version }, + { title: "App Version", className: "app-version", id: columnId.appVersion }, + { title: "Repository", className: "repository", sortBy: columnId.repo, id: columnId.repo }, ]} renderTableContents={(chart: HelmChart) => [

@@ -93,7 +97,8 @@ export class HelmCharts extends Component { chart.getDescription(), chart.getVersion(), chart.getAppVersion(), - { title: chart.getRepository(), className: chart.getRepository().toLowerCase() } + { title: chart.getRepository(), className: chart.getRepository().toLowerCase() }, + { className: "menu" } ]} detailsItem={this.selectedChart} onDetails={this.showDetails} diff --git a/src/renderer/components/+apps-helm-charts/index.ts b/src/renderer/components/+apps-helm-charts/index.ts index a9403c097c..c0649f3f38 100644 --- a/src/renderer/components/+apps-helm-charts/index.ts +++ b/src/renderer/components/+apps-helm-charts/index.ts @@ -1,2 +1,2 @@ export * from "./helm-charts"; -export * from "./helm-charts.route"; \ No newline at end of file +export * from "./helm-charts.route"; diff --git a/src/renderer/components/+apps-releases/index.ts b/src/renderer/components/+apps-releases/index.ts index 32a4871769..bd80c60404 100644 --- a/src/renderer/components/+apps-releases/index.ts +++ b/src/renderer/components/+apps-releases/index.ts @@ -1,2 +1,2 @@ export * from "./releases"; -export * from "./release.route"; \ No newline at end of file +export * from "./release.route"; diff --git a/src/renderer/components/+apps-releases/release-rollback-dialog.tsx b/src/renderer/components/+apps-releases/release-rollback-dialog.tsx index bca0b23c4f..4aef4ec356 100644 --- a/src/renderer/components/+apps-releases/release-rollback-dialog.tsx +++ b/src/renderer/components/+apps-releases/release-rollback-dialog.tsx @@ -77,7 +77,8 @@ export class ReleaseRollbackDialog extends React.Component { themeName="light" value={revision} options={revisions} - formatOptionLabel={({ value }: SelectOption) => `${value.revision} - ${value.chart}`} + formatOptionLabel={({ value }: SelectOption) => `${value.revision} - ${value.chart} + - ${value.app_version}, updated: ${new Date(value.updated).toLocaleString()}`} onChange={({ value }: SelectOption) => this.revision = value} /> diff --git a/src/renderer/components/+apps-releases/release.store.ts b/src/renderer/components/+apps-releases/release.store.ts index b6d5c2fb5f..9548e494f7 100644 --- a/src/renderer/components/+apps-releases/release.store.ts +++ b/src/renderer/components/+apps-releases/release.store.ts @@ -5,7 +5,7 @@ import { HelmRelease, helmReleasesApi, IReleaseCreatePayload, IReleaseUpdatePayl import { ItemStore } from "../../item.store"; import { Secret } from "../../api/endpoints"; import { secretsStore } from "../+config-secrets/secrets.store"; -import { getHostedCluster } from "../../../common/cluster-store"; +import { namespaceStore } from "../+namespaces/namespace.store"; @autobind() export class ReleaseStore extends ItemStore { @@ -58,38 +58,35 @@ export class ReleaseStore extends ItemStore { } @action - async loadAll() { + async loadAll(namespaces = namespaceStore.allowedNamespaces) { this.isLoading = true; - let items; try { - const { isAdmin, allowedNamespaces } = getHostedCluster(); + const items = await this.loadItems(namespaces); - items = await this.loadItems(!isAdmin ? allowedNamespaces : null); - } finally { - if (items) { - items = this.sortItems(items); - this.items.replace(items); - } + this.items.replace(this.sortItems(items)); this.isLoaded = true; + } catch (error) { + console.error(`Loading Helm Chart releases has failed: ${error}`); + } finally { this.isLoading = false; } } - async loadItems(namespaces?: string[]) { - if (!namespaces) { - return helmReleasesApi.list(); - } else { - return Promise - .all(namespaces.map(namespace => helmReleasesApi.list(namespace))) - .then(items => items.flat()); - } + async loadFromContextNamespaces(): Promise { + return this.loadAll(namespaceStore.contextNamespaces); + } + + async loadItems(namespaces: string[]) { + return Promise + .all(namespaces.map(namespace => helmReleasesApi.list(namespace))) + .then(items => items.flat()); } async create(payload: IReleaseCreatePayload) { const response = await helmReleasesApi.create(payload); - if (this.isLoaded) this.loadAll(); + if (this.isLoaded) this.loadFromContextNamespaces(); return response; } @@ -97,7 +94,7 @@ export class ReleaseStore extends ItemStore { async update(name: string, namespace: string, payload: IReleaseUpdatePayload) { const response = await helmReleasesApi.update(name, namespace, payload); - if (this.isLoaded) this.loadAll(); + if (this.isLoaded) this.loadFromContextNamespaces(); return response; } @@ -105,7 +102,7 @@ export class ReleaseStore extends ItemStore { async rollback(name: string, namespace: string, revision: number) { const response = await helmReleasesApi.rollback(name, namespace, revision); - if (this.isLoaded) this.loadAll(); + if (this.isLoaded) this.loadFromContextNamespaces(); return response; } diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx index 709c6f9bbd..71cf3d954f 100644 --- a/src/renderer/components/+apps-releases/releases.tsx +++ b/src/renderer/components/+apps-releases/releases.tsx @@ -14,11 +14,13 @@ import { ItemListLayout } from "../item-object-list/item-list-layout"; import { HelmReleaseMenu } from "./release-menu"; import { secretsStore } from "../+config-secrets/secrets.store"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", revision = "revision", chart = "chart", + version = "version", + appVersion = "app-version", status = "status", updated = "update" } @@ -81,16 +83,18 @@ export class HelmReleases extends Component { return ( <> release.getName(), - [sortBy.namespace]: (release: HelmRelease) => release.getNs(), - [sortBy.revision]: (release: HelmRelease) => release.getRevision(), - [sortBy.chart]: (release: HelmRelease) => release.getChart(), - [sortBy.status]: (release: HelmRelease) => release.getStatus(), - [sortBy.updated]: (release: HelmRelease) => release.getUpdated(false, false), + [columnId.name]: (release: HelmRelease) => release.getName(), + [columnId.namespace]: (release: HelmRelease) => release.getNs(), + [columnId.revision]: (release: HelmRelease) => release.getRevision(), + [columnId.chart]: (release: HelmRelease) => release.getChart(), + [columnId.status]: (release: HelmRelease) => release.getStatus(), + [columnId.updated]: (release: HelmRelease) => release.getUpdated(false, false), }} searchFilters={[ (release: HelmRelease) => release.getName(), @@ -101,14 +105,14 @@ export class HelmReleases extends Component { ]} renderHeaderTitle="Releases" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Chart", className: "chart", sortBy: sortBy.chart }, - { title: "Revision", className: "revision", sortBy: sortBy.revision }, - { title: "Version", className: "version" }, - { title: "App Version", className: "app-version" }, - { title: "Status", className: "status", sortBy: sortBy.status }, - { title: "Updated", className: "updated", sortBy: sortBy.updated }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Chart", className: "chart", sortBy: columnId.chart, id: columnId.chart }, + { title: "Revision", className: "revision", sortBy: columnId.revision, id: columnId.revision }, + { title: "Version", className: "version", id: columnId.version }, + { title: "App Version", className: "app-version", id: columnId.appVersion }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, + { title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated }, ]} renderTableContents={(release: HelmRelease) => { const version = release.getVersion(); diff --git a/src/renderer/components/+apps/apps.command.ts b/src/renderer/components/+apps/apps.command.ts new file mode 100644 index 0000000000..ff6c9d615d --- /dev/null +++ b/src/renderer/components/+apps/apps.command.ts @@ -0,0 +1,18 @@ +import { navigate } from "../../navigation"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { helmChartsURL } from "../+apps-helm-charts"; +import { releaseURL } from "../+apps-releases"; + +commandRegistry.add({ + id: "cluster.viewHelmCharts", + title: "Cluster: View Helm Charts", + scope: "cluster", + action: () => navigate(helmChartsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewHelmReleases", + title: "Cluster: View Helm Releases", + scope: "cluster", + action: () => navigate(releaseURL()) +}); diff --git a/src/renderer/components/+apps/index.ts b/src/renderer/components/+apps/index.ts index 70c0169777..30fbf65316 100644 --- a/src/renderer/components/+apps/index.ts +++ b/src/renderer/components/+apps/index.ts @@ -1,2 +1,3 @@ export * from "./apps"; -export * from "./apps.route"; \ No newline at end of file +export * from "./apps.route"; +export * from "./apps.command"; diff --git a/src/renderer/components/+cluster-settings/cluster-settings.command.ts b/src/renderer/components/+cluster-settings/cluster-settings.command.ts new file mode 100644 index 0000000000..a3b3c8792e --- /dev/null +++ b/src/renderer/components/+cluster-settings/cluster-settings.command.ts @@ -0,0 +1,16 @@ +import { navigate } from "../../navigation"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { clusterSettingsURL } from "./cluster-settings.route"; +import { clusterStore } from "../../../common/cluster-store"; + +commandRegistry.add({ + id: "cluster.viewCurrentClusterSettings", + title: "Cluster: View Settings", + scope: "global", + action: () => navigate(clusterSettingsURL({ + params: { + clusterId: clusterStore.active.id + } + })), + isActive: (context) => !!context.cluster +}); diff --git a/src/renderer/components/+cluster-settings/components/cluster-home-dir-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-home-dir-setting.tsx index 35c18cc5e5..10aabf3ff7 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-home-dir-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-home-dir-setting.tsx @@ -48,4 +48,4 @@ export class ClusterHomeDirSetting extends React.Component { ); } -} \ No newline at end of file +} diff --git a/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx index 631c6d54ef..9d953ef9ca 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-name-setting.tsx @@ -45,4 +45,4 @@ export class ClusterNameSetting extends React.Component { ); } -} \ No newline at end of file +} diff --git a/src/renderer/components/+cluster-settings/components/cluster-proxy-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-proxy-setting.tsx index 3887487816..eb122ac444 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-proxy-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-proxy-setting.tsx @@ -45,4 +45,4 @@ export class ClusterProxySetting extends React.Component { ); } -} \ No newline at end of file +} diff --git a/src/renderer/components/+cluster-settings/components/cluster-workspace-setting.tsx b/src/renderer/components/+cluster-settings/components/cluster-workspace-setting.tsx index ea4ee5a571..fa76dde806 100644 --- a/src/renderer/components/+cluster-settings/components/cluster-workspace-setting.tsx +++ b/src/renderer/components/+cluster-settings/components/cluster-workspace-setting.tsx @@ -1,7 +1,5 @@ import React from "react"; import { observer } from "mobx-react"; -import { Link } from "react-router-dom"; -import { workspacesURL } from "../../+workspaces"; import { workspaceStore } from "../../../../common/workspace-store"; import { Cluster } from "../../../../main/cluster"; import { Select } from "../../../components/select"; @@ -18,10 +16,7 @@ export class ClusterWorkspaceSetting extends React.Component { <>

- Define cluster{" "} - - workspace - . + Define cluster workspace.

{ ); } } - -@observer -export class NamespaceSelectFilter extends React.Component { - render() { - const { contextNs, hasContext, toggleContext } = namespaceStore; - let placeholder = <>All namespaces; - - if (contextNs.length == 1) placeholder = <>Namespace: {contextNs[0]}; - if (contextNs.length >= 2) placeholder = <>Namespaces: {contextNs.join(", ")}; - - return ( - false} - controlShouldRenderValue={false} - isMulti - onChange={([{ value }]: SelectOption[]) => toggleContext(value)} - formatOptionLabel={({ value: namespace }: SelectOption) => { - const isSelected = hasContext(namespace); - - return ( -
- - {namespace} - {isSelected && } -
- ); - }} - /> - ); - } -} diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index ad02dd137c..1f928fe2f3 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -1,11 +1,9 @@ -import { action, comparer, observable, reaction } from "mobx"; +import { action, comparer, computed, IReactionDisposer, IReactionOptions, observable, reaction } from "mobx"; import { autobind, createStorage } from "../../utils"; -import { KubeObjectStore } from "../../kube-object.store"; -import { Namespace, namespacesApi } from "../../api/endpoints"; +import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store"; +import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api"; import { createPageParam } from "../../navigation"; import { apiManager } from "../../api/api-manager"; -import { isAllowedResource } from "../../../common/rbac"; -import { getHostedCluster } from "../../../common/cluster-store"; const storage = createStorage("context_namespaces", []); @@ -14,107 +12,171 @@ export const namespaceUrlParam = createPageParam({ isSystem: true, multiValues: true, get defaultValue() { - return storage.get(); // initial namespaces coming from URL or local-storage (default) + return storage.get() ?? []; // initial namespaces coming from URL or local-storage (default) } }); +export function getDummyNamespace(name: string) { + return new Namespace({ + kind: Namespace.kind, + apiVersion: "v1", + metadata: { + name, + uid: "", + resourceVersion: "", + selfLink: `/api/v1/namespaces/${name}` + } + }); +} + @autobind() export class NamespaceStore extends KubeObjectStore { api = namespacesApi; - contextNs = observable.array(); + + @observable private contextNs = observable.set(); constructor() { super(); this.init(); } - private init() { - this.setContext(this.initNamespaces); + private async init() { + await this.contextReady; - return reaction(() => this.contextNs.toJS(), namespaces => { + this.setContext(this.initialNamespaces); + this.autoLoadAllowedNamespaces(); + this.autoUpdateUrlAndLocalStorage(); + } + + public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer { + return reaction(() => Array.from(this.contextNs), callback, { + equals: comparer.shallow, + ...opts, + }); + } + + private autoUpdateUrlAndLocalStorage(): IReactionDisposer { + return this.onContextChange(namespaces => { storage.set(namespaces); // save to local-storage namespaceUrlParam.set(namespaces, { replaceHistory: true }); // update url }, { fireImmediately: true, - equals: comparer.identity, }); } - get initNamespaces() { - return namespaceUrlParam.get(); + private autoLoadAllowedNamespaces(): IReactionDisposer { + return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), { + fireImmediately: true, + equals: comparer.shallow, + }); } - getContextParams() { - return { - namespaces: this.contextNs.toJS(), - }; - } + @computed + private get initialNamespaces(): string[] { + const namespaces = new Set(this.allowedNamespaces); + const prevSelected = storage.get().filter(namespace => namespaces.has(namespace)); - subscribe(apis = [this.api]) { - const { accessibleNamespaces } = getHostedCluster(); - - // if user has given static list of namespaces let's not start watches because watch adds stuff that's not wanted - if (accessibleNamespaces.length > 0) { - return Function; // no-op + // return previously saved namespaces from local-storage + if (prevSelected.length > 0) { + return prevSelected; } - return super.subscribe(apis); + // otherwise select "default" or first allowed namespace + if (namespaces.has("default")) { + return ["default"]; + } else if (namespaces.size) { + return [Array.from(namespaces)[0]]; + } + + return []; } - protected async loadItems(namespaces?: string[]) { - if (!isAllowedResource("namespaces")) { - if (namespaces) return namespaces.map(this.getDummyNamespace); + @computed get allowedNamespaces(): string[] { + return Array.from(new Set([ + ...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s + ...this.items.map(item => item.getName()), // loaded namespaces from k8s api + ].flat())); + } + @computed get contextNamespaces(): string[] { + const namespaces = Array.from(this.contextNs); + + if (!namespaces.length) { + return this.allowedNamespaces; // show all namespaces when nothing selected + } + + return namespaces; + } + + getSubscribeApis() { + // if user has given static list of namespaces let's not start watches because watch adds stuff that's not wanted + if (this.context?.cluster.accessibleNamespaces.length > 0) { return []; } - if (namespaces) { - return Promise.all(namespaces.map(name => this.api.get({ name }))); - } else { - return super.loadItems(); - } + return super.getSubscribeApis(); } - protected getDummyNamespace(name: string) { - return new Namespace({ - kind: "Namespace", - apiVersion: "v1", - metadata: { - name, - uid: "", - resourceVersion: "", - selfLink: `/api/v1/namespaces/${name}` - } - }); + protected async loadItems(params: KubeObjectStoreLoadingParams) { + const { allowedNamespaces } = this; + + let namespaces = await super.loadItems(params); + + namespaces = namespaces.filter(namespace => allowedNamespaces.includes(namespace.getName())); + + if (!namespaces.length && allowedNamespaces.length > 0) { + return allowedNamespaces.map(getDummyNamespace); + } + + return namespaces; } @action - setContext(namespaces: string[]) { + setContext(namespace: string | string[]) { + const namespaces = [namespace].flat(); + this.contextNs.replace(namespaces); } - hasContext(namespace: string | string[]) { - const context = Array.isArray(namespace) ? namespace : [namespace]; + @action + resetContext() { + this.contextNs.clear(); + } - return context.every(namespace => this.contextNs.includes(namespace)); + hasContext(namespaces: string | string[]) { + return [namespaces].flat().every(namespace => this.contextNs.has(namespace)); + } + + @computed get hasAllContexts(): boolean { + return this.contextNs.size === this.allowedNamespaces.length; } @action toggleContext(namespace: string) { - if (this.hasContext(namespace)) this.contextNs.remove(namespace); - else this.contextNs.push(namespace); + if (this.hasContext(namespace)) { + this.contextNs.delete(namespace); + } else { + this.contextNs.add(namespace); + } } @action - reset() { - super.reset(); - this.contextNs.clear(); + toggleAll(showAll?: boolean) { + if (typeof showAll === "boolean") { + if (showAll) { + this.setContext(this.allowedNamespaces); + } else { + this.contextNs.clear(); + } + } else { + this.toggleAll(!this.hasAllContexts); + } } @action async remove(item: Namespace) { await super.remove(item); - this.contextNs.remove(item.getName()); + this.contextNs.delete(item.getName()); } } diff --git a/src/renderer/components/+namespaces/namespaces.tsx b/src/renderer/components/+namespaces/namespaces.tsx index f097657493..3972f3d180 100644 --- a/src/renderer/components/+namespaces/namespaces.tsx +++ b/src/renderer/components/+namespaces/namespaces.tsx @@ -11,7 +11,7 @@ import { INamespacesRouteParams } from "./namespaces.route"; import { namespaceStore } from "./namespace.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", labels = "labels", age = "age", @@ -27,12 +27,14 @@ export class Namespaces extends React.Component { ns.getName(), - [sortBy.labels]: (ns: Namespace) => ns.getLabels(), - [sortBy.age]: (ns: Namespace) => ns.metadata.creationTimestamp, - [sortBy.status]: (ns: Namespace) => ns.getStatus(), + [columnId.name]: (ns: Namespace) => ns.getName(), + [columnId.labels]: (ns: Namespace) => ns.getLabels(), + [columnId.age]: (ns: Namespace) => ns.metadata.creationTimestamp, + [columnId.status]: (ns: Namespace) => ns.getStatus(), }} searchFilters={[ (item: Namespace) => item.getSearchFields(), @@ -40,11 +42,11 @@ export class Namespaces extends React.Component { ]} renderHeaderTitle="Namespaces" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Labels", className: "labels", sortBy: sortBy.labels }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Status", className: "status", sortBy: sortBy.status }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Labels", className: "labels", sortBy: columnId.labels, id: columnId.labels }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(item: Namespace) => [ item.getName(), diff --git a/src/renderer/components/+network-endpoints/endpoints.tsx b/src/renderer/components/+network-endpoints/endpoints.tsx index 3b859c46f3..ce87c14a4a 100644 --- a/src/renderer/components/+network-endpoints/endpoints.tsx +++ b/src/renderer/components/+network-endpoints/endpoints.tsx @@ -9,9 +9,10 @@ import { endpointStore } from "./endpoints.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + endpoints = "endpoints", age = "age", } @@ -23,22 +24,24 @@ export class Endpoints extends React.Component { render() { return ( endpoint.getName(), - [sortBy.namespace]: (endpoint: Endpoint) => endpoint.getNs(), - [sortBy.age]: (endpoint: Endpoint) => endpoint.metadata.creationTimestamp, + [columnId.name]: (endpoint: Endpoint) => endpoint.getName(), + [columnId.namespace]: (endpoint: Endpoint) => endpoint.getNs(), + [columnId.age]: (endpoint: Endpoint) => endpoint.metadata.creationTimestamp, }} searchFilters={[ (endpoint: Endpoint) => endpoint.getSearchFields() ]} renderHeaderTitle="Endpoints" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Endpoints", className: "endpoints" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Endpoints", className: "endpoints", id: columnId.endpoints }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(endpoint: Endpoint) => [ endpoint.getName(), diff --git a/src/renderer/components/+network-ingresses/ingresses.tsx b/src/renderer/components/+network-ingresses/ingresses.tsx index adb6c84528..945f2b8f0a 100644 --- a/src/renderer/components/+network-ingresses/ingresses.tsx +++ b/src/renderer/components/+network-ingresses/ingresses.tsx @@ -9,9 +9,11 @@ import { ingressStore } from "./ingress.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + loadBalancers ="load-balancers", + rules = "rules", age = "age", } @@ -23,11 +25,13 @@ export class Ingresses extends React.Component { render() { return ( ingress.getName(), - [sortBy.namespace]: (ingress: Ingress) => ingress.getNs(), - [sortBy.age]: (ingress: Ingress) => ingress.metadata.creationTimestamp, + [columnId.name]: (ingress: Ingress) => ingress.getName(), + [columnId.namespace]: (ingress: Ingress) => ingress.getNs(), + [columnId.age]: (ingress: Ingress) => ingress.metadata.creationTimestamp, }} searchFilters={[ (ingress: Ingress) => ingress.getSearchFields(), @@ -35,12 +39,12 @@ export class Ingresses extends React.Component { ]} renderHeaderTitle="Ingresses" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "LoadBalancers", className: "loadbalancers" }, - { title: "Rules", className: "rules" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers }, + { title: "Rules", className: "rules", id: columnId.rules }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(ingress: Ingress) => [ ingress.getName(), diff --git a/src/renderer/components/+network-policies/network-policies.tsx b/src/renderer/components/+network-policies/network-policies.tsx index d4dc0e2fa9..6899c14558 100644 --- a/src/renderer/components/+network-policies/network-policies.tsx +++ b/src/renderer/components/+network-policies/network-policies.tsx @@ -9,9 +9,10 @@ import { INetworkPoliciesRouteParams } from "./network-policies.route"; import { networkPolicyStore } from "./network-policy.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + types = "types", age = "age", } @@ -23,22 +24,24 @@ export class NetworkPolicies extends React.Component { render() { return ( item.getName(), - [sortBy.namespace]: (item: NetworkPolicy) => item.getNs(), - [sortBy.age]: (item: NetworkPolicy) => item.metadata.creationTimestamp, + [columnId.name]: (item: NetworkPolicy) => item.getName(), + [columnId.namespace]: (item: NetworkPolicy) => item.getNs(), + [columnId.age]: (item: NetworkPolicy) => item.metadata.creationTimestamp, }} searchFilters={[ (item: NetworkPolicy) => item.getSearchFields(), ]} renderHeaderTitle="Network Policies" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Policy Types", className: "type" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Policy Types", className: "type", id: columnId.types }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(item: NetworkPolicy) => [ item.getName(), diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 58cbe0a86e..80c9bd4dc7 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -1,17 +1,18 @@ import "./service-details.scss"; import React from "react"; -import { observer } from "mobx-react"; +import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; import { KubeEventDetails } from "../+events/kube-event-details"; import { KubeObjectDetailsProps } from "../kube-object"; -import { Service, endpointApi } from "../../api/endpoints"; +import { Service } from "../../api/endpoints"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { ServicePortComponent } from "./service-port-component"; import { endpointStore } from "../+network-endpoints/endpoints.store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; +import { kubeWatchApi } from "../../api/kube-watch-api"; interface Props extends KubeObjectDetailsProps { } @@ -19,10 +20,11 @@ interface Props extends KubeObjectDetailsProps { @observer export class ServiceDetails extends React.Component { componentDidMount() { - if (!endpointStore.isLoaded) { - endpointStore.loadAll(); - } - endpointApi.watch(); + disposeOnUnmount(this, [ + kubeWatchApi.subscribeStores([endpointStore], { + preload: true, + }), + ]); } render() { @@ -77,7 +79,7 @@ export class ServiceDetails extends React.Component { )} - + ); } diff --git a/src/renderer/components/+network-services/services.tsx b/src/renderer/components/+network-services/services.tsx index 3452c10a68..740e0bfdf1 100644 --- a/src/renderer/components/+network-services/services.tsx +++ b/src/renderer/components/+network-services/services.tsx @@ -10,12 +10,13 @@ import { Badge } from "../badge"; import { serviceStore } from "./services.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", selector = "selector", ports = "port", clusterIp = "cluster-ip", + externalIp = "external-ip", age = "age", type = "type", status = "status", @@ -29,16 +30,18 @@ export class Services extends React.Component { render() { return ( service.getName(), - [sortBy.namespace]: (service: Service) => service.getNs(), - [sortBy.selector]: (service: Service) => service.getSelector(), - [sortBy.ports]: (service: Service) => (service.spec.ports || []).map(({ port }) => port)[0], - [sortBy.clusterIp]: (service: Service) => service.getClusterIp(), - [sortBy.type]: (service: Service) => service.getType(), - [sortBy.age]: (service: Service) => service.metadata.creationTimestamp, - [sortBy.status]: (service: Service) => service.getStatus(), + [columnId.name]: (service: Service) => service.getName(), + [columnId.namespace]: (service: Service) => service.getNs(), + [columnId.selector]: (service: Service) => service.getSelector(), + [columnId.ports]: (service: Service) => (service.spec.ports || []).map(({ port }) => port)[0], + [columnId.clusterIp]: (service: Service) => service.getClusterIp(), + [columnId.type]: (service: Service) => service.getType(), + [columnId.age]: (service: Service) => service.metadata.creationTimestamp, + [columnId.status]: (service: Service) => service.getStatus(), }} searchFilters={[ (service: Service) => service.getSearchFields(), @@ -47,16 +50,16 @@ export class Services extends React.Component { ]} renderHeaderTitle="Services" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Type", className: "type", sortBy: sortBy.type }, - { title: "Cluster IP", className: "clusterIp", sortBy: sortBy.clusterIp, }, - { title: "Ports", className: "ports", sortBy: sortBy.ports }, - { title: "External IP", className: "externalIp" }, - { title: "Selector", className: "selector", sortBy: sortBy.selector }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Status", className: "status", sortBy: sortBy.status }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Type", className: "type", sortBy: columnId.type, id: columnId.type }, + { title: "Cluster IP", className: "clusterIp", sortBy: columnId.clusterIp, id: columnId.clusterIp }, + { title: "Ports", className: "ports", sortBy: columnId.ports, id: columnId.ports }, + { title: "External IP", className: "externalIp", id: columnId.externalIp }, + { title: "Selector", className: "selector", sortBy: columnId.selector, id: columnId.selector }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(service: Service) => [ service.getName(), diff --git a/src/renderer/components/+network/index.ts b/src/renderer/components/+network/index.ts index 9a58dc59a6..e37b11c3b7 100644 --- a/src/renderer/components/+network/index.ts +++ b/src/renderer/components/+network/index.ts @@ -1,2 +1,3 @@ export * from "./network.route"; export * from "./network"; +export * from "./network.command"; diff --git a/src/renderer/components/+network/network.command.ts b/src/renderer/components/+network/network.command.ts new file mode 100644 index 0000000000..6dfa907136 --- /dev/null +++ b/src/renderer/components/+network/network.command.ts @@ -0,0 +1,34 @@ +import { navigate } from "../../navigation"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { servicesURL } from "../+network-services"; +import { endpointURL } from "../+network-endpoints"; +import { ingressURL } from "../+network-ingresses"; +import { networkPoliciesURL } from "../+network-policies"; + +commandRegistry.add({ + id: "cluster.viewServices", + title: "Cluster: View Services", + scope: "cluster", + action: () => navigate(servicesURL()) +}); + +commandRegistry.add({ + id: "cluster.viewEndpoints", + title: "Cluster: View Endpoints", + scope: "cluster", + action: () => navigate(endpointURL()) +}); + +commandRegistry.add({ + id: "cluster.viewIngresses", + title: "Cluster: View Ingresses", + scope: "cluster", + action: () => navigate(ingressURL()) +}); + +commandRegistry.add({ + id: "cluster.viewNetworkPolicies", + title: "Cluster: View NetworkPolicies", + scope: "cluster", + action: () => navigate(networkPoliciesURL()) +}); diff --git a/src/renderer/components/+nodes/index.ts b/src/renderer/components/+nodes/index.ts index 515a3d85f6..8af90267d4 100644 --- a/src/renderer/components/+nodes/index.ts +++ b/src/renderer/components/+nodes/index.ts @@ -1,3 +1,4 @@ export * from "./nodes"; export * from "./nodes.route"; export * from "./node-details"; +export * from "./node.command"; diff --git a/src/renderer/components/+nodes/node-details.tsx b/src/renderer/components/+nodes/node-details.tsx index 824affafee..810837d59d 100644 --- a/src/renderer/components/+nodes/node-details.tsx +++ b/src/renderer/components/+nodes/node-details.tsx @@ -29,9 +29,7 @@ export class NodeDetails extends React.Component { }); async componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } componentWillUnmount() { diff --git a/src/renderer/components/+nodes/node.command.ts b/src/renderer/components/+nodes/node.command.ts new file mode 100644 index 0000000000..9257cea668 --- /dev/null +++ b/src/renderer/components/+nodes/node.command.ts @@ -0,0 +1,10 @@ +import { navigate } from "../../navigation"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { nodesURL } from "./nodes.route"; + +commandRegistry.add({ + id: "cluster.viewNodes", + title: "Cluster: View Nodes", + scope: "cluster", + action: () => navigate(nodesURL()) +}); diff --git a/src/renderer/components/+nodes/nodes.store.ts b/src/renderer/components/+nodes/nodes.store.ts index c0385b078b..b301015747 100644 --- a/src/renderer/components/+nodes/nodes.store.ts +++ b/src/renderer/components/+nodes/nodes.store.ts @@ -1,3 +1,4 @@ +import { sum } from "lodash"; import { action, computed, observable } from "mobx"; import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../../api/endpoints"; import { autobind } from "../../utils"; @@ -62,6 +63,10 @@ export class NodesStore extends KubeObjectStore { }); } + getWarningsCount(): number { + return sum(this.items.map((node: Node) => node.getWarningConditions().length)); + } + reset() { super.reset(); this.metrics = {}; diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index edfa5c4026..32da6a13db 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -17,7 +17,7 @@ import upperFirst from "lodash/upperFirst"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { Badge } from "../badge/badge"; -enum sortBy { +enum columnId { name = "name", cpu = "cpu", memory = "memory", @@ -51,6 +51,10 @@ export class Nodes extends React.Component { if (!metrics || !metrics[1]) return ; const usage = metrics[0]; const cores = metrics[1]; + const cpuUsagePercent = Math.ceil(usage * 100) / cores; + const cpuUsagePercentLabel: String = cpuUsagePercent % 1 === 0 + ? cpuUsagePercent.toString() + : cpuUsagePercent.toFixed(2); return ( { value={usage} tooltip={{ preferredPositions: TooltipPosition.BOTTOM, - children: `CPU: ${Math.ceil(usage * 100) / cores}\%, cores: ${cores}` + children: `CPU: ${cpuUsagePercentLabel}\%, cores: ${cores}` }} /> ); @@ -131,21 +135,23 @@ export class Nodes extends React.Component { return ( node.getName(), - [sortBy.cpu]: (node: Node) => nodesStore.getLastMetricValues(node, ["cpuUsage"]), - [sortBy.memory]: (node: Node) => nodesStore.getLastMetricValues(node, ["memoryUsage"]), - [sortBy.disk]: (node: Node) => nodesStore.getLastMetricValues(node, ["fsUsage"]), - [sortBy.conditions]: (node: Node) => node.getNodeConditionText(), - [sortBy.taints]: (node: Node) => node.getTaints().length, - [sortBy.roles]: (node: Node) => node.getRoleLabels(), - [sortBy.age]: (node: Node) => node.metadata.creationTimestamp, - [sortBy.version]: (node: Node) => node.getKubeletVersion(), + [columnId.name]: (node: Node) => node.getName(), + [columnId.cpu]: (node: Node) => nodesStore.getLastMetricValues(node, ["cpuUsage"]), + [columnId.memory]: (node: Node) => nodesStore.getLastMetricValues(node, ["memoryUsage"]), + [columnId.disk]: (node: Node) => nodesStore.getLastMetricValues(node, ["fsUsage"]), + [columnId.conditions]: (node: Node) => node.getNodeConditionText(), + [columnId.taints]: (node: Node) => node.getTaints().length, + [columnId.roles]: (node: Node) => node.getRoleLabels(), + [columnId.age]: (node: Node) => node.metadata.creationTimestamp, + [columnId.version]: (node: Node) => node.getKubeletVersion(), }} searchFilters={[ (node: Node) => node.getSearchFields(), @@ -155,16 +161,16 @@ export class Nodes extends React.Component { ]} renderHeaderTitle="Nodes" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "CPU", className: "cpu", sortBy: sortBy.cpu }, - { title: "Memory", className: "memory", sortBy: sortBy.memory }, - { title: "Disk", className: "disk", sortBy: sortBy.disk }, - { title: "Taints", className: "taints", sortBy: sortBy.taints }, - { title: "Roles", className: "roles", sortBy: sortBy.roles }, - { title: "Version", className: "version", sortBy: sortBy.version }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Conditions", className: "conditions", sortBy: sortBy.conditions }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "CPU", className: "cpu", sortBy: columnId.cpu, id: columnId.cpu }, + { title: "Memory", className: "memory", sortBy: columnId.memory, id: columnId.memory }, + { title: "Disk", className: "disk", sortBy: columnId.disk, id: columnId.disk }, + { title: "Taints", className: "taints", sortBy: columnId.taints, id: columnId.taints }, + { title: "Roles", className: "roles", sortBy: columnId.roles, id: columnId.roles }, + { title: "Version", className: "version", sortBy: columnId.version, id: columnId.version }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} renderTableContents={(node: Node) => { const tooltipId = `node-taints-${node.getId()}`; diff --git a/src/renderer/components/+pod-security-policies/index.ts b/src/renderer/components/+pod-security-policies/index.ts index d037873b5b..c9379d3381 100644 --- a/src/renderer/components/+pod-security-policies/index.ts +++ b/src/renderer/components/+pod-security-policies/index.ts @@ -1,3 +1,3 @@ export * from "./pod-security-policies.route"; export * from "./pod-security-policies"; -export * from "./pod-security-policy-details"; \ No newline at end of file +export * from "./pod-security-policy-details"; diff --git a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx index 30ec1d6304..a91e0114d6 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx @@ -7,7 +7,7 @@ import { podSecurityPoliciesStore } from "./pod-security-policies.store"; import { PodSecurityPolicy } from "../../api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", volumes = "volumes", privileged = "privileged", @@ -19,14 +19,16 @@ export class PodSecurityPolicies extends React.Component { render() { return ( item.getName(), - [sortBy.volumes]: (item: PodSecurityPolicy) => item.getVolumes(), - [sortBy.privileged]: (item: PodSecurityPolicy) => +item.isPrivileged(), - [sortBy.age]: (item: PodSecurityPolicy) => item.metadata.creationTimestamp, + [columnId.name]: (item: PodSecurityPolicy) => item.getName(), + [columnId.volumes]: (item: PodSecurityPolicy) => item.getVolumes(), + [columnId.privileged]: (item: PodSecurityPolicy) => +item.isPrivileged(), + [columnId.age]: (item: PodSecurityPolicy) => item.metadata.creationTimestamp, }} searchFilters={[ (item: PodSecurityPolicy) => item.getSearchFields(), @@ -35,11 +37,11 @@ export class PodSecurityPolicies extends React.Component { ]} renderHeaderTitle="Pod Security Policies" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Privileged", className: "privileged", sortBy: sortBy.privileged }, - { title: "Volumes", className: "volumes", sortBy: sortBy.volumes }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Privileged", className: "privileged", sortBy: columnId.privileged, id: columnId.privileged }, + { title: "Volumes", className: "volumes", sortBy: columnId.volumes, id: columnId.volumes }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(item: PodSecurityPolicy) => { return [ diff --git a/src/renderer/components/+preferences/preferences.route.ts b/src/renderer/components/+preferences/preferences.route.ts index 7c880bd5fc..2915b68a28 100644 --- a/src/renderer/components/+preferences/preferences.route.ts +++ b/src/renderer/components/+preferences/preferences.route.ts @@ -1,8 +1,17 @@ import type { RouteProps } from "react-router"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; import { buildURL } from "../../../common/utils/buildUrl"; +import { navigate } from "../../navigation"; export const preferencesRoute: RouteProps = { path: "/preferences" }; export const preferencesURL = buildURL(preferencesRoute.path); + +commandRegistry.add({ + id: "app.showPreferences", + title: "Preferences: Open", + scope: "global", + action: () => navigate(preferencesURL()) +}); diff --git a/src/renderer/components/+storage-classes/storage-classes.tsx b/src/renderer/components/+storage-classes/storage-classes.tsx index ec7e1c8e05..1a8ed346fd 100644 --- a/src/renderer/components/+storage-classes/storage-classes.tsx +++ b/src/renderer/components/+storage-classes/storage-classes.tsx @@ -9,10 +9,11 @@ import { IStorageClassesRouteParams } from "./storage-classes.route"; import { storageClassStore } from "./storage-class.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", age = "age", provisioner = "provision", + default = "default", reclaimPolicy = "reclaim", } @@ -24,13 +25,15 @@ export class StorageClasses extends React.Component { render() { return ( item.getName(), - [sortBy.age]: (item: StorageClass) => item.metadata.creationTimestamp, - [sortBy.provisioner]: (item: StorageClass) => item.provisioner, - [sortBy.reclaimPolicy]: (item: StorageClass) => item.reclaimPolicy, + [columnId.name]: (item: StorageClass) => item.getName(), + [columnId.age]: (item: StorageClass) => item.metadata.creationTimestamp, + [columnId.provisioner]: (item: StorageClass) => item.provisioner, + [columnId.reclaimPolicy]: (item: StorageClass) => item.reclaimPolicy, }} searchFilters={[ (item: StorageClass) => item.getSearchFields(), @@ -38,12 +41,12 @@ export class StorageClasses extends React.Component { ]} renderHeaderTitle="Storage Classes" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Provisioner", className: "provisioner", sortBy: sortBy.provisioner }, - { title: "Reclaim Policy", className: "reclaim-policy", sortBy: sortBy.reclaimPolicy }, - { title: "Default", className: "is-default" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Provisioner", className: "provisioner", sortBy: columnId.provisioner, id: columnId.provisioner }, + { title: "Reclaim Policy", className: "reclaim-policy", sortBy: columnId.reclaimPolicy, id: columnId.reclaimPolicy }, + { title: "Default", className: "is-default", id: columnId.default }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(storageClass: StorageClass) => [ storageClass.getName(), diff --git a/src/renderer/components/+storage-volume-claims/volume-claims.tsx b/src/renderer/components/+storage-volume-claims/volume-claims.tsx index bb9a4a05a7..e93529b8d2 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claims.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claims.tsx @@ -13,7 +13,7 @@ import { stopPropagation } from "../../utils"; import { storageClassApi } from "../../api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", pods = "pods", @@ -31,17 +31,19 @@ export class PersistentVolumeClaims extends React.Component { render() { return ( pvc.getName(), - [sortBy.namespace]: (pvc: PersistentVolumeClaim) => pvc.getNs(), - [sortBy.pods]: (pvc: PersistentVolumeClaim) => pvc.getPods(podsStore.items).map(pod => pod.getName()), - [sortBy.status]: (pvc: PersistentVolumeClaim) => pvc.getStatus(), - [sortBy.size]: (pvc: PersistentVolumeClaim) => unitsToBytes(pvc.getStorage()), - [sortBy.storageClass]: (pvc: PersistentVolumeClaim) => pvc.spec.storageClassName, - [sortBy.age]: (pvc: PersistentVolumeClaim) => pvc.metadata.creationTimestamp, + [columnId.name]: (pvc: PersistentVolumeClaim) => pvc.getName(), + [columnId.namespace]: (pvc: PersistentVolumeClaim) => pvc.getNs(), + [columnId.pods]: (pvc: PersistentVolumeClaim) => pvc.getPods(podsStore.items).map(pod => pod.getName()), + [columnId.status]: (pvc: PersistentVolumeClaim) => pvc.getStatus(), + [columnId.size]: (pvc: PersistentVolumeClaim) => unitsToBytes(pvc.getStorage()), + [columnId.storageClass]: (pvc: PersistentVolumeClaim) => pvc.spec.storageClassName, + [columnId.age]: (pvc: PersistentVolumeClaim) => pvc.metadata.creationTimestamp, }} searchFilters={[ (item: PersistentVolumeClaim) => item.getSearchFields(), @@ -49,14 +51,14 @@ export class PersistentVolumeClaims extends React.Component { ]} renderHeaderTitle="Persistent Volume Claims" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Storage class", className: "storageClass", sortBy: sortBy.storageClass }, - { title: "Size", className: "size", sortBy: sortBy.size }, - { title: "Pods", className: "pods", sortBy: sortBy.pods }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Status", className: "status", sortBy: sortBy.status }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Storage class", className: "storageClass", sortBy: columnId.storageClass, id: columnId.storageClass }, + { title: "Size", className: "size", sortBy: columnId.size, id: columnId.size }, + { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(pvc: PersistentVolumeClaim) => { const pods = pvc.getPods(podsStore.items); diff --git a/src/renderer/components/+storage-volumes/volumes.tsx b/src/renderer/components/+storage-volumes/volumes.tsx index 412093a7ad..6822e3d4f7 100644 --- a/src/renderer/components/+storage-volumes/volumes.tsx +++ b/src/renderer/components/+storage-volumes/volumes.tsx @@ -11,10 +11,11 @@ import { volumesStore } from "./volumes.store"; import { pvcApi, storageClassApi } from "../../api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", storageClass = "storage-class", capacity = "capacity", + claim = "claim", status = "status", age = "age", } @@ -27,14 +28,16 @@ export class PersistentVolumes extends React.Component { render() { return ( item.getName(), - [sortBy.storageClass]: (item: PersistentVolume) => item.spec.storageClassName, - [sortBy.capacity]: (item: PersistentVolume) => item.getCapacity(true), - [sortBy.status]: (item: PersistentVolume) => item.getStatus(), - [sortBy.age]: (item: PersistentVolume) => item.metadata.creationTimestamp, + [columnId.name]: (item: PersistentVolume) => item.getName(), + [columnId.storageClass]: (item: PersistentVolume) => item.spec.storageClassName, + [columnId.capacity]: (item: PersistentVolume) => item.getCapacity(true), + [columnId.status]: (item: PersistentVolume) => item.getStatus(), + [columnId.age]: (item: PersistentVolume) => item.metadata.creationTimestamp, }} searchFilters={[ (item: PersistentVolume) => item.getSearchFields(), @@ -42,13 +45,13 @@ export class PersistentVolumes extends React.Component { ]} renderHeaderTitle="Persistent Volumes" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Storage Class", className: "storageClass", sortBy: sortBy.storageClass }, - { title: "Capacity", className: "capacity", sortBy: sortBy.capacity }, - { title: "Claim", className: "claim" }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Status", className: "status", sortBy: sortBy.status }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Storage Class", className: "storageClass", sortBy: columnId.storageClass, id: columnId.storageClass }, + { title: "Capacity", className: "capacity", sortBy: columnId.capacity, id: columnId.capacity }, + { title: "Claim", className: "claim", id: columnId.claim }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(volume: PersistentVolume) => { const { claimRef, storageClassName } = volume.spec; diff --git a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx index ab06126bd2..85d27243c2 100644 --- a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx +++ b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx @@ -7,7 +7,7 @@ import { Dialog, DialogProps } from "../dialog"; import { Wizard, WizardStep } from "../wizard"; import { Select, SelectOption } from "../select"; import { SubTitle } from "../layout/sub-title"; -import { IRoleBindingSubject, RoleBinding, ServiceAccount, Role } from "../../api/endpoints"; +import { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints"; import { Icon } from "../icon"; import { Input } from "../input"; import { NamespaceSelect } from "../+namespaces/namespace-select"; @@ -19,6 +19,7 @@ import { namespaceStore } from "../+namespaces/namespace.store"; import { serviceAccountsStore } from "../+user-management-service-accounts/service-accounts.store"; import { roleBindingsStore } from "./role-bindings.store"; import { showDetails } from "../kube-object"; +import { KubeObjectStore } from "../../kube-object.store"; interface BindingSelectOption extends SelectOption { value: string; // binding name @@ -73,14 +74,14 @@ export class AddRoleBindingDialog extends React.Component { }; async loadData() { - const stores = [ + const stores: KubeObjectStore[] = [ namespaceStore, rolesStore, serviceAccountsStore, ]; this.isLoading = true; - await Promise.all(stores.map(store => store.loadAll())); + await Promise.all(stores.map(store => store.reloadAll())); this.isLoading = false; } @@ -136,8 +137,7 @@ export class AddRoleBindingDialog extends React.Component { roleBinding: this.roleBinding, addSubjects: subjects, }); - } - else { + } else { const name = useRoleForBindingName ? selectedRole.getName() : bindingName; roleBinding = await roleBindingsStore.create({ name, namespace }, { @@ -265,7 +265,7 @@ export class AddRoleBindingDialog extends React.Component { ); const disableNext = this.isLoading || !selectedRole || !selectedBindings.length; - const nextLabel = isEditing ? "Update" : "Create"; + const nextLabel = isEditing ? "Update" : "Create"; return ( { api = clusterRoleBindingApi; - subscribe() { - return super.subscribe([clusterRoleBindingApi, roleBindingApi]); + getSubscribeApis() { + return [clusterRoleBindingApi, roleBindingApi]; } protected sortItems(items: RoleBinding[]) { @@ -26,15 +26,13 @@ export class RoleBindingsStore extends KubeObjectStore { return clusterRoleBindingApi.get(params); } - protected loadItems(namespaces?: string[]) { - if (namespaces) { - return Promise.all( - namespaces.map(namespace => roleBindingApi.list({ namespace })) - ).then(items => items.flat()); - } else { - return Promise.all([clusterRoleBindingApi.list(), roleBindingApi.list()]) - .then(items => items.flat()); - } + protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { + const items = await Promise.all([ + super.loadItems({ ...params, api: clusterRoleBindingApi }), + super.loadItems({ ...params, api: roleBindingApi }), + ]); + + return items.flat(); } protected async createItem(params: { name: string; namespace?: string }, data?: Partial) { diff --git a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx index 3d64562047..f55e781e0e 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx +++ b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx @@ -10,7 +10,7 @@ import { KubeObjectListLayout } from "../kube-object"; import { AddRoleBindingDialog } from "./add-role-binding-dialog"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", bindings = "bindings", @@ -25,13 +25,15 @@ export class RoleBindings extends React.Component { render() { return ( binding.getName(), - [sortBy.namespace]: (binding: RoleBinding) => binding.getNs(), - [sortBy.bindings]: (binding: RoleBinding) => binding.getSubjectNames(), - [sortBy.age]: (binding: RoleBinding) => binding.metadata.creationTimestamp, + [columnId.name]: (binding: RoleBinding) => binding.getName(), + [columnId.namespace]: (binding: RoleBinding) => binding.getNs(), + [columnId.bindings]: (binding: RoleBinding) => binding.getSubjectNames(), + [columnId.age]: (binding: RoleBinding) => binding.metadata.creationTimestamp, }} searchFilters={[ (binding: RoleBinding) => binding.getSearchFields(), @@ -39,11 +41,11 @@ export class RoleBindings extends React.Component { ]} renderHeaderTitle="Role Bindings" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Bindings", className: "bindings", sortBy: sortBy.bindings }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(binding: RoleBinding) => [ binding.getName(), diff --git a/src/renderer/components/+user-management-roles/add-role-dialog.tsx b/src/renderer/components/+user-management-roles/add-role-dialog.tsx index f421b38ba3..512d89dcd1 100644 --- a/src/renderer/components/+user-management-roles/add-role-dialog.tsx +++ b/src/renderer/components/+user-management-roles/add-role-dialog.tsx @@ -5,9 +5,11 @@ import { observable } from "mobx"; import { observer } from "mobx-react"; import { Dialog, DialogProps } from "../dialog"; import { Wizard, WizardStep } from "../wizard"; +import { SubTitle } from "../layout/sub-title"; import { Notifications } from "../notifications"; import { rolesStore } from "./roles.store"; import { Input } from "../input"; +import { NamespaceSelect } from "../+namespaces/namespace-select"; import { showDetails } from "../kube-object"; interface Props extends Partial { @@ -18,6 +20,7 @@ export class AddRoleDialog extends React.Component { @observable static isOpen = false; @observable roleName = ""; + @observable namespace = ""; static open() { AddRoleDialog.isOpen = true; @@ -33,11 +36,12 @@ export class AddRoleDialog extends React.Component { reset = () => { this.roleName = ""; + this.namespace = ""; }; createRole = async () => { try { - const role = await rolesStore.create({ name: this.roleName }); + const role = await rolesStore.create({ name: this.roleName, namespace: this.namespace }); showDetails(role.selfLink); this.reset(); @@ -64,13 +68,20 @@ export class AddRoleDialog extends React.Component { nextLabel="Create" next={this.createRole} > + this.roleName = v} /> + + this.namespace = value} + /> diff --git a/src/renderer/components/+user-management-roles/roles.store.ts b/src/renderer/components/+user-management-roles/roles.store.ts index 6af33deacb..82b0e66612 100644 --- a/src/renderer/components/+user-management-roles/roles.store.ts +++ b/src/renderer/components/+user-management-roles/roles.store.ts @@ -1,14 +1,14 @@ import { clusterRoleApi, Role, roleApi } from "../../api/endpoints"; import { autobind } from "../../utils"; -import { KubeObjectStore } from "../../kube-object.store"; +import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store"; import { apiManager } from "../../api/api-manager"; @autobind() export class RolesStore extends KubeObjectStore { api = clusterRoleApi; - subscribe() { - return super.subscribe([roleApi, clusterRoleApi]); + getSubscribeApis() { + return [roleApi, clusterRoleApi]; } protected sortItems(items: Role[]) { @@ -24,15 +24,13 @@ export class RolesStore extends KubeObjectStore { return clusterRoleApi.get(params); } - protected loadItems(namespaces?: string[]): Promise { - if (namespaces) { - return Promise.all( - namespaces.map(namespace => roleApi.list({ namespace })) - ).then(items => items.flat()); - } else { - return Promise.all([clusterRoleApi.list(), roleApi.list()]) - .then(items => items.flat()); - } + protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { + const items = await Promise.all([ + super.loadItems({ ...params, api: clusterRoleApi }), + super.loadItems({ ...params, api: roleApi }), + ]); + + return items.flat(); } protected async createItem(params: { name: string; namespace?: string }, data?: Partial) { @@ -49,4 +47,4 @@ export const rolesStore = new RolesStore(); apiManager.registerStore(rolesStore, [ roleApi, clusterRoleApi, -]); \ No newline at end of file +]); diff --git a/src/renderer/components/+user-management-roles/roles.tsx b/src/renderer/components/+user-management-roles/roles.tsx index 21ad3bdf8a..a990cbfa7e 100644 --- a/src/renderer/components/+user-management-roles/roles.tsx +++ b/src/renderer/components/+user-management-roles/roles.tsx @@ -10,7 +10,7 @@ import { KubeObjectListLayout } from "../kube-object"; import { AddRoleDialog } from "./add-role-dialog"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", age = "age", @@ -25,22 +25,24 @@ export class Roles extends React.Component { return ( <> role.getName(), - [sortBy.namespace]: (role: Role) => role.getNs(), - [sortBy.age]: (role: Role) => role.metadata.creationTimestamp, + [columnId.name]: (role: Role) => role.getName(), + [columnId.namespace]: (role: Role) => role.getNs(), + [columnId.age]: (role: Role) => role.metadata.creationTimestamp, }} searchFilters={[ (role: Role) => role.getSearchFields(), ]} renderHeaderTitle="Roles" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(role: Role) => [ role.getName(), diff --git a/src/renderer/components/+user-management-service-accounts/index.ts b/src/renderer/components/+user-management-service-accounts/index.ts index fd45e28288..bd81292bf1 100644 --- a/src/renderer/components/+user-management-service-accounts/index.ts +++ b/src/renderer/components/+user-management-service-accounts/index.ts @@ -1,3 +1,3 @@ export * from "./service-accounts"; export * from "./service-accounts-details"; -export * from "./create-service-account-dialog"; \ No newline at end of file +export * from "./create-service-account-dialog"; diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx index 37bed40ba9..4ea78904c0 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx +++ b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx @@ -15,7 +15,7 @@ import { CreateServiceAccountDialog } from "./create-service-account-dialog"; import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", age = "age", @@ -30,21 +30,23 @@ export class ServiceAccounts extends React.Component { return ( <> account.getName(), - [sortBy.namespace]: (account: ServiceAccount) => account.getNs(), - [sortBy.age]: (account: ServiceAccount) => account.metadata.creationTimestamp, + [columnId.name]: (account: ServiceAccount) => account.getName(), + [columnId.namespace]: (account: ServiceAccount) => account.getNs(), + [columnId.age]: (account: ServiceAccount) => account.metadata.creationTimestamp, }} searchFilters={[ (account: ServiceAccount) => account.getSearchFields(), ]} renderHeaderTitle="Service Accounts" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(account: ServiceAccount) => [ account.getName(), diff --git a/src/renderer/components/+user-management/index.ts b/src/renderer/components/+user-management/index.ts index 6f29869b9b..4ff825df97 100644 --- a/src/renderer/components/+user-management/index.ts +++ b/src/renderer/components/+user-management/index.ts @@ -1,2 +1,2 @@ export * from "./user-management"; -export * from "./user-management.route"; \ No newline at end of file +export * from "./user-management.route"; diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx index 6fd04ffe7e..1b473e90c1 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx @@ -20,9 +20,7 @@ interface Props extends KubeObjectDetailsProps { @observer export class CronJobDetails extends React.Component { async componentDidMount() { - if (!jobStore.isLoaded) { - jobStore.loadAll(); - } + jobStore.reloadAll(); } render() { diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index 19d35e4b3a..08b84c0671 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -18,12 +18,13 @@ import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { ConfirmDialog } from "../confirm-dialog/confirm-dialog"; import { Notifications } from "../notifications/notifications"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + schedule = "schedule", suspend = "suspend", active = "active", - lastSchedule = "schedule", + lastSchedule = "last-schedule", age = "age", } @@ -35,15 +36,17 @@ export class CronJobs extends React.Component { render() { return ( cronJob.getName(), - [sortBy.namespace]: (cronJob: CronJob) => cronJob.getNs(), - [sortBy.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(), - [sortBy.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob), - [sortBy.lastSchedule]: (cronJob: CronJob) => cronJob.getLastScheduleTime(), - [sortBy.age]: (cronJob: CronJob) => cronJob.metadata.creationTimestamp, + [columnId.name]: (cronJob: CronJob) => cronJob.getName(), + [columnId.namespace]: (cronJob: CronJob) => cronJob.getNs(), + [columnId.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(), + [columnId.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob), + [columnId.lastSchedule]: (cronJob: CronJob) => cronJob.getLastScheduleTime(), + [columnId.age]: (cronJob: CronJob) => cronJob.metadata.creationTimestamp, }} searchFilters={[ (cronJob: CronJob) => cronJob.getSearchFields(), @@ -51,14 +54,14 @@ export class CronJobs extends React.Component { ]} renderHeaderTitle="Cron Jobs" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Schedule", className: "schedule" }, - { title: "Suspend", className: "suspend", sortBy: sortBy.suspend }, - { title: "Active", className: "active", sortBy: sortBy.active }, - { title: "Last schedule", className: "last-schedule", sortBy: sortBy.lastSchedule }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Schedule", className: "schedule", id: columnId.schedule }, + { title: "Suspend", className: "suspend", sortBy: columnId.suspend, id: columnId.suspend }, + { title: "Active", className: "active", sortBy: columnId.active, id: columnId.active }, + { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(cronJob: CronJob) => [ cronJob.getName(), diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx index 44ccbaac56..329eaf3ed7 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx @@ -30,9 +30,7 @@ export class DaemonSetDetails extends React.Component { }); componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } componentWillUnmount() { diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts index ad9713e96b..b8c8dee573 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts @@ -18,7 +18,7 @@ export class DaemonSetStore extends KubeObjectStore { } getChildPods(daemonSet: DaemonSet): Pod[] { - return podsStore.getPodsByOwner(daemonSet); + return podsStore.getPodsByOwnerId(daemonSet.getId()); } getStatuses(daemonSets?: DaemonSet[]) { diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx index ff061f7877..e2d1e30e17 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx @@ -13,10 +13,11 @@ import { IDaemonSetsRouteParams } from "../+workloads"; import { Badge } from "../badge"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", pods = "pods", + labels = "labels", age = "age", } @@ -38,13 +39,15 @@ export class DaemonSets extends React.Component { render() { return ( daemonSet.getName(), - [sortBy.namespace]: (daemonSet: DaemonSet) => daemonSet.getNs(), - [sortBy.pods]: (daemonSet: DaemonSet) => this.getPodsLength(daemonSet), - [sortBy.age]: (daemonSet: DaemonSet) => daemonSet.metadata.creationTimestamp, + [columnId.name]: (daemonSet: DaemonSet) => daemonSet.getName(), + [columnId.namespace]: (daemonSet: DaemonSet) => daemonSet.getNs(), + [columnId.pods]: (daemonSet: DaemonSet) => this.getPodsLength(daemonSet), + [columnId.age]: (daemonSet: DaemonSet) => daemonSet.metadata.creationTimestamp, }} searchFilters={[ (daemonSet: DaemonSet) => daemonSet.getSearchFields(), @@ -52,12 +55,12 @@ export class DaemonSets extends React.Component { ]} renderHeaderTitle="Daemon Sets" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Pods", className: "pods", sortBy: sortBy.pods }, - { className: "warning" }, - { title: "Node Selector", className: "labels" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Pods", className: "pods", sortBy: columnId.pods, id: columnId.pods }, + { className: "warning", showWithColumn: columnId.pods }, + { title: "Node Selector", className: "labels", id: columnId.labels }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(daemonSet: DaemonSet) => [ daemonSet.getName(), diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx index 26cd4c0b23..e31f63d7d7 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx @@ -31,9 +31,7 @@ export class DeploymentDetails extends React.Component { }); componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } componentWillUnmount() { diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index b84cd7b340..0147c238b9 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -23,9 +23,10 @@ import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-obje import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { Notifications } from "../notifications"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + pods = "pods", replicas = "replicas", age = "age", condition = "condition", @@ -55,14 +56,16 @@ export class Deployments extends React.Component { render() { return ( deployment.getName(), - [sortBy.namespace]: (deployment: Deployment) => deployment.getNs(), - [sortBy.replicas]: (deployment: Deployment) => deployment.getReplicas(), - [sortBy.age]: (deployment: Deployment) => deployment.metadata.creationTimestamp, - [sortBy.condition]: (deployment: Deployment) => deployment.getConditionsText(), + [columnId.name]: (deployment: Deployment) => deployment.getName(), + [columnId.namespace]: (deployment: Deployment) => deployment.getNs(), + [columnId.replicas]: (deployment: Deployment) => deployment.getReplicas(), + [columnId.age]: (deployment: Deployment) => deployment.metadata.creationTimestamp, + [columnId.condition]: (deployment: Deployment) => deployment.getConditionsText(), }} searchFilters={[ (deployment: Deployment) => deployment.getSearchFields(), @@ -70,13 +73,13 @@ export class Deployments extends React.Component { ]} renderHeaderTitle="Deployments" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Pods", className: "pods" }, - { title: "Replicas", className: "replicas", sortBy: sortBy.replicas }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Conditions", className: "conditions", sortBy: sortBy.condition }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Pods", className: "pods", id: columnId.pods }, + { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Conditions", className: "conditions", sortBy: columnId.condition, id: columnId.condition }, ]} renderTableContents={(deployment: Deployment) => [ deployment.getName(), diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index dfa16fe760..f0665bd291 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -25,9 +25,7 @@ interface Props extends KubeObjectDetailsProps { @observer export class JobDetails extends React.Component { async componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } render() { diff --git a/src/renderer/components/+workloads-jobs/job.store.ts b/src/renderer/components/+workloads-jobs/job.store.ts index 569c9efb13..41d514df8d 100644 --- a/src/renderer/components/+workloads-jobs/job.store.ts +++ b/src/renderer/components/+workloads-jobs/job.store.ts @@ -10,7 +10,7 @@ export class JobStore extends KubeObjectStore { api = jobApi; getChildPods(job: Job): Pod[] { - return podsStore.getPodsByOwner(job); + return podsStore.getPodsByOwnerId(job.getId()); } getJobsByOwner(cronJob: CronJob) { diff --git a/src/renderer/components/+workloads-jobs/jobs.tsx b/src/renderer/components/+workloads-jobs/jobs.tsx index 00c1ee0db5..6301c287d0 100644 --- a/src/renderer/components/+workloads-jobs/jobs.tsx +++ b/src/renderer/components/+workloads-jobs/jobs.tsx @@ -12,9 +12,10 @@ import { IJobsRouteParams } from "../+workloads"; import kebabCase from "lodash/kebabCase"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + completions = "completions", conditions = "conditions", age = "age", } @@ -27,25 +28,27 @@ export class Jobs extends React.Component { render() { return ( job.getName(), - [sortBy.namespace]: (job: Job) => job.getNs(), - [sortBy.conditions]: (job: Job) => job.getCondition() != null ? job.getCondition().type : "", - [sortBy.age]: (job: Job) => job.metadata.creationTimestamp, + [columnId.name]: (job: Job) => job.getName(), + [columnId.namespace]: (job: Job) => job.getNs(), + [columnId.conditions]: (job: Job) => job.getCondition() != null ? job.getCondition().type : "", + [columnId.age]: (job: Job) => job.metadata.creationTimestamp, }} searchFilters={[ (job: Job) => job.getSearchFields(), ]} renderHeaderTitle="Jobs" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Completions", className: "completions" }, - { className: "warning" }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Conditions", className: "conditions", sortBy: sortBy.conditions }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Completions", className: "completions", id: columnId.completions }, + { className: "warning", showWithColumn: columnId.completions }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} renderTableContents={(job: Job) => { const condition = job.getCondition(); diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index 78adecb6df..bc0484dadc 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -6,10 +6,9 @@ import { OverviewWorkloadStatus } from "./overview-workload-status"; import { Link } from "react-router-dom"; import { workloadURL, workloadStores } from "../+workloads"; import { namespaceStore } from "../+namespaces/namespace.store"; -import { PageFiltersList } from "../item-object-list/page-filters-list"; -import { NamespaceSelectFilter } from "../+namespaces/namespace-select"; +import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import { isAllowedResource, KubeResource } from "../../../common/rbac"; -import { ResourceNames } from "../../../renderer/utils/rbac"; +import { ResourceNames } from "../../utils/rbac"; import { autobind } from "../../utils"; const resources: KubeResource[] = [ @@ -27,7 +26,7 @@ export class OverviewStatuses extends React.Component { @autobind() renderWorkload(resource: KubeResource): React.ReactElement { const store = workloadStores[resource]; - const items = store.getAllByNs(namespaceStore.contextNs); + const items = store.getAllByNs(namespaceStore.contextNamespaces); return (
@@ -50,7 +49,6 @@ export class OverviewStatuses extends React.Component {
Overview
-
{workloads}
diff --git a/src/renderer/components/+workloads-overview/overview.tsx b/src/renderer/components/+workloads-overview/overview.tsx index 318ad53f77..92bc569307 100644 --- a/src/renderer/components/+workloads-overview/overview.tsx +++ b/src/renderer/components/+workloads-overview/overview.tsx @@ -1,8 +1,7 @@ import "./overview.scss"; import React from "react"; -import { observable, when } from "mobx"; -import { observer } from "mobx-react"; +import { disposeOnUnmount, observer } from "mobx-react"; import { OverviewStatuses } from "./overview-statuses"; import { RouteComponentProps } from "react-router"; import { IWorkloadsOverviewRouteParams } from "../+workloads"; @@ -15,83 +14,32 @@ import { replicaSetStore } from "../+workloads-replicasets/replicasets.store"; import { jobStore } from "../+workloads-jobs/job.store"; import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import { Events } from "../+events"; -import { KubeObjectStore } from "../../kube-object.store"; import { isAllowedResource } from "../../../common/rbac"; +import { kubeWatchApi } from "../../api/kube-watch-api"; +import { clusterContext } from "../context"; interface Props extends RouteComponentProps { } @observer export class WorkloadsOverview extends React.Component { - @observable isUnmounting = false; - - async componentDidMount() { - const stores: KubeObjectStore[] = []; - - if (isAllowedResource("pods")) { - stores.push(podsStore); - } - - if (isAllowedResource("deployments")) { - stores.push(deploymentStore); - } - - if (isAllowedResource("daemonsets")) { - stores.push(daemonSetStore); - } - - if (isAllowedResource("statefulsets")) { - stores.push(statefulSetStore); - } - - if (isAllowedResource("replicasets")) { - stores.push(replicaSetStore); - } - - if (isAllowedResource("jobs")) { - stores.push(jobStore); - } - - if (isAllowedResource("cronjobs")) { - stores.push(cronJobStore); - } - - if (isAllowedResource("events")) { - stores.push(eventStore); - } - - const unsubscribeList: Array<() => void> = []; - - for (const store of stores) { - await store.loadAll(); - unsubscribeList.push(store.subscribe()); - } - - await when(() => this.isUnmounting); - unsubscribeList.forEach(dispose => dispose()); - } - - componentWillUnmount() { - this.isUnmounting = true; - } - - get contents() { - return ( - <> - - { isAllowedResource("events") && } - - ); + componentDidMount() { + disposeOnUnmount(this, [ + kubeWatchApi.subscribeStores([ + podsStore, deploymentStore, daemonSetStore, statefulSetStore, replicaSetStore, + jobStore, cronJobStore, eventStore, + ], { + preload: true, + namespaces: clusterContext.contextNamespaces, + }), + ]); } render() { return (
- {this.contents} + + {isAllowedResource("events") && }
); } diff --git a/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx b/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx new file mode 100644 index 0000000000..dbde813e5a --- /dev/null +++ b/src/renderer/components/+workloads-pods/__tests__/pod-tolerations.test.tsx @@ -0,0 +1,59 @@ +/** + * @jest-environment jsdom + */ + +import React from "react"; +import "@testing-library/jest-dom/extend-expect"; +import { fireEvent, render } from "@testing-library/react"; +import { IToleration } from "../../../api/workload-kube-object"; +import { PodTolerations } from "../pod-tolerations"; + +const tolerations: IToleration[] =[ + { + key: "CriticalAddonsOnly", + operator: "Exist", + effect: "NoExecute", + tolerationSeconds: 3600 + }, + { + key: "node.kubernetes.io/not-ready", + operator: "NoExist", + effect: "NoSchedule", + tolerationSeconds: 7200 + }, +]; + +describe("", () => { + it("renders w/o errors", () => { + const { container } = render(); + + expect(container).toBeInstanceOf(HTMLElement); + }); + + it("shows all tolerations", () => { + const { container } = render(); + const rows = container.querySelectorAll(".TableRow"); + + expect(rows[0].querySelector(".key").textContent).toBe("CriticalAddonsOnly"); + expect(rows[0].querySelector(".operator").textContent).toBe("Exist"); + expect(rows[0].querySelector(".effect").textContent).toBe("NoExecute"); + expect(rows[0].querySelector(".seconds").textContent).toBe("3600"); + + expect(rows[1].querySelector(".key").textContent).toBe("node.kubernetes.io/not-ready"); + expect(rows[1].querySelector(".operator").textContent).toBe("NoExist"); + expect(rows[1].querySelector(".effect").textContent).toBe("NoSchedule"); + expect(rows[1].querySelector(".seconds").textContent).toBe("7200"); + }); + + it("sorts table properly", () => { + const { container, getByText } = render(); + const headCell = getByText("Key"); + + fireEvent.click(headCell); + fireEvent.click(headCell); + + const rows = container.querySelectorAll(".TableRow"); + + expect(rows[0].querySelector(".key").textContent).toBe("node.kubernetes.io/not-ready"); + }); +}); diff --git a/src/renderer/components/+workloads-pods/index.ts b/src/renderer/components/+workloads-pods/index.ts index f3181cb3a2..cc7782911c 100644 --- a/src/renderer/components/+workloads-pods/index.ts +++ b/src/renderer/components/+workloads-pods/index.ts @@ -1,2 +1,2 @@ export * from "./pods"; -export * from "./pod-details"; \ No newline at end of file +export * from "./pod-details"; diff --git a/src/renderer/components/+workloads-pods/pod-details-statuses.tsx b/src/renderer/components/+workloads-pods/pod-details-statuses.tsx index 5ce8465e72..1e0f765381 100644 --- a/src/renderer/components/+workloads-pods/pod-details-statuses.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-statuses.tsx @@ -27,4 +27,4 @@ export class PodDetailsStatuses extends React.Component { ); } -} \ No newline at end of file +} diff --git a/src/renderer/components/+workloads-pods/pod-details-tolerations.scss b/src/renderer/components/+workloads-pods/pod-details-tolerations.scss index 0aa68fa1d6..1ac932cd9d 100644 --- a/src/renderer/components/+workloads-pods/pod-details-tolerations.scss +++ b/src/renderer/components/+workloads-pods/pod-details-tolerations.scss @@ -1,5 +1,23 @@ .PodDetailsTolerations { - .toleration { - margin-bottom: $margin; + grid-template-columns: auto; + + .PodTolerations { + margin-top: var(--margin); + } + + // Expanding value cell to cover 2 columns (whole Drawer width) + + > .name { + grid-row-start: 1; + grid-column-start: 1; + } + + > .value { + grid-row-start: 1; + grid-column-start: 1; + } + + .DrawerParamToggler > .params { + margin-left: var(--drawer-item-title-width); } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx index 8b67502e26..67bd5a07d0 100644 --- a/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-tolerations.tsx @@ -1,10 +1,11 @@ import "./pod-details-tolerations.scss"; import React from "react"; -import { Pod, Deployment, DaemonSet, StatefulSet, ReplicaSet, Job } from "../../api/endpoints"; import { DrawerParamToggler, DrawerItem } from "../drawer"; +import { WorkloadKubeObject } from "../../api/workload-kube-object"; +import { PodTolerations } from "./pod-tolerations"; interface Props { - workload: Pod | Deployment | DaemonSet | StatefulSet | ReplicaSet | Job; + workload: WorkloadKubeObject; } export class PodDetailsTolerations extends React.Component { @@ -17,20 +18,7 @@ export class PodDetailsTolerations extends React.Component { return ( - { - tolerations.map((toleration, index) => { - const { key, operator, effect, tolerationSeconds } = toleration; - - return ( -
- {key} - {operator && {operator}} - {effect && {effect}} - {!!tolerationSeconds && {tolerationSeconds}} -
- ); - }) - } +
); diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.scss b/src/renderer/components/+workloads-pods/pod-tolerations.scss new file mode 100644 index 0000000000..b840697685 --- /dev/null +++ b/src/renderer/components/+workloads-pods/pod-tolerations.scss @@ -0,0 +1,14 @@ +.PodTolerations { + .TableHead { + background-color: var(--drawerSubtitleBackground); + } + + .TableCell { + white-space: normal; + word-break: normal; + + &.key { + flex-grow: 3; + } + } +} \ No newline at end of file diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-tolerations.tsx new file mode 100644 index 0000000000..e8d3d7d099 --- /dev/null +++ b/src/renderer/components/+workloads-pods/pod-tolerations.tsx @@ -0,0 +1,63 @@ +import "./pod-tolerations.scss"; +import React from "react"; +import uniqueId from "lodash/uniqueId"; + +import { IToleration } from "../../api/workload-kube-object"; +import { Table, TableCell, TableHead, TableRow } from "../table"; + +interface Props { + tolerations: IToleration[]; +} + +enum sortBy { + Key = "key", + Operator = "operator", + Effect = "effect", + Seconds = "seconds", +} + +const sortingCallbacks = { + [sortBy.Key]: (toleration: IToleration) => toleration.key, + [sortBy.Operator]: (toleration: IToleration) => toleration.operator, + [sortBy.Effect]: (toleration: IToleration) => toleration.effect, + [sortBy.Seconds]: (toleration: IToleration) => toleration.tolerationSeconds, +}; + +const getTableRow = (toleration: IToleration) => { + const { key, operator, effect, tolerationSeconds } = toleration; + + return ( + + {key} + {operator} + {effect} + {tolerationSeconds} + + ); +}; + +export function PodTolerations({ tolerations }: Props) { + return ( + + + Key + Operator + Effect + Seconds + + { + tolerations.map(getTableRow) + } +
+ ); +} diff --git a/src/renderer/components/+workloads-pods/pods.store.ts b/src/renderer/components/+workloads-pods/pods.store.ts index 9cd3c3b2f9..5a535cec66 100644 --- a/src/renderer/components/+workloads-pods/pods.store.ts +++ b/src/renderer/components/+workloads-pods/pods.store.ts @@ -3,8 +3,8 @@ import { action, observable } from "mobx"; import { KubeObjectStore } from "../../kube-object.store"; import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils"; import { IPodMetrics, Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints"; -import { WorkloadKubeObject } from "../../api/workload-kube-object"; import { apiManager } from "../../api/api-manager"; +import { WorkloadKubeObject } from "../../api/workload-kube-object"; @autobind() export class PodsStore extends KubeObjectStore { @@ -44,6 +44,12 @@ export class PodsStore extends KubeObjectStore { }); } + getPodsByOwnerId(workloadId: string): Pod[] { + return this.items.filter(pod => { + return pod.getOwnerRefs().find(owner => owner.uid === workloadId); + }); + } + getPodsByNode(node: string) { if (!this.isLoaded) return []; diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index 2296b98317..a59c9d79d2 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -19,8 +19,7 @@ import { lookupApiLink } from "../../api/kube-api"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { Badge } from "../badge"; - -enum sortBy { +enum columnId { name = "name", namespace = "namespace", containers = "containers", @@ -77,15 +76,15 @@ export class Pods extends React.Component { tableId = "workloads_pods" isConfigurable sortingCallbacks={{ - [sortBy.name]: (pod: Pod) => pod.getName(), - [sortBy.namespace]: (pod: Pod) => pod.getNs(), - [sortBy.containers]: (pod: Pod) => pod.getContainers().length, - [sortBy.restarts]: (pod: Pod) => pod.getRestartsCount(), - [sortBy.owners]: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind), - [sortBy.qos]: (pod: Pod) => pod.getQosClass(), - [sortBy.node]: (pod: Pod) => pod.getNodeName(), - [sortBy.age]: (pod: Pod) => pod.metadata.creationTimestamp, - [sortBy.status]: (pod: Pod) => pod.getStatusMessage(), + [columnId.name]: (pod: Pod) => pod.getName(), + [columnId.namespace]: (pod: Pod) => pod.getNs(), + [columnId.containers]: (pod: Pod) => pod.getContainers().length, + [columnId.restarts]: (pod: Pod) => pod.getRestartsCount(), + [columnId.owners]: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind), + [columnId.qos]: (pod: Pod) => pod.getQosClass(), + [columnId.node]: (pod: Pod) => pod.getNodeName(), + [columnId.age]: (pod: Pod) => pod.metadata.creationTimestamp, + [columnId.status]: (pod: Pod) => pod.getStatusMessage(), }} searchFilters={[ (pod: Pod) => pod.getSearchFields(), @@ -95,16 +94,16 @@ export class Pods extends React.Component { ]} renderHeaderTitle="Pods" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning", showWithColumn: "name" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Containers", className: "containers", sortBy: sortBy.containers }, - { title: "Restarts", className: "restarts", sortBy: sortBy.restarts }, - { title: "Controlled By", className: "owners", sortBy: sortBy.owners }, - { title: "Node", className: "node", sortBy: sortBy.node }, - { title: "QoS", className: "qos", sortBy: sortBy.qos }, - { title: "Age", className: "age", sortBy: sortBy.age }, - { title: "Status", className: "status", sortBy: sortBy.status }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Containers", className: "containers", sortBy: columnId.containers, id: columnId.containers }, + { title: "Restarts", className: "restarts", sortBy: columnId.restarts, id: columnId.restarts }, + { title: "Controlled By", className: "owners", sortBy: columnId.owners, id: columnId.owners }, + { title: "Node", className: "node", sortBy: columnId.node, id: columnId.node }, + { title: "QoS", className: "qos", sortBy: columnId.qos, id: columnId.qos }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(pod: Pod) => [ , diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx index 8c28d81a83..0cf747a1d1 100644 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx +++ b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx @@ -29,9 +29,7 @@ export class ReplicaSetDetails extends React.Component { }); async componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } componentWillUnmount() { diff --git a/src/renderer/components/+workloads-replicasets/replicasets.store.ts b/src/renderer/components/+workloads-replicasets/replicasets.store.ts index 337f9c0ae1..ca58006930 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.store.ts +++ b/src/renderer/components/+workloads-replicasets/replicasets.store.ts @@ -18,7 +18,7 @@ export class ReplicaSetStore extends KubeObjectStore { } getChildPods(replicaSet: ReplicaSet) { - return podsStore.getPodsByOwner(replicaSet); + return podsStore.getPodsByOwnerId(replicaSet.getId()); } getStatuses(replicaSets: ReplicaSet[]) { diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx index 55f607e3c3..fa6ee5cef4 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx @@ -14,7 +14,7 @@ import { Icon } from "../icon/icon"; import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry"; import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", desired = "desired", @@ -31,27 +31,29 @@ export class ReplicaSets extends React.Component { render() { return ( replicaSet.getName(), - [sortBy.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(), - [sortBy.desired]: (replicaSet: ReplicaSet) => replicaSet.getDesired(), - [sortBy.current]: (replicaSet: ReplicaSet) => replicaSet.getCurrent(), - [sortBy.ready]: (replicaSet: ReplicaSet) => replicaSet.getReady(), - [sortBy.age]: (replicaSet: ReplicaSet) => replicaSet.metadata.creationTimestamp, + [columnId.name]: (replicaSet: ReplicaSet) => replicaSet.getName(), + [columnId.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(), + [columnId.desired]: (replicaSet: ReplicaSet) => replicaSet.getDesired(), + [columnId.current]: (replicaSet: ReplicaSet) => replicaSet.getCurrent(), + [columnId.ready]: (replicaSet: ReplicaSet) => replicaSet.getReady(), + [columnId.age]: (replicaSet: ReplicaSet) => replicaSet.metadata.creationTimestamp, }} searchFilters={[ (replicaSet: ReplicaSet) => replicaSet.getSearchFields(), ]} renderHeaderTitle="Replica Sets" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { className: "warning" }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Desired", className: "desired", sortBy: sortBy.desired }, - { title: "Current", className: "current", sortBy: sortBy.current }, - { title: "Ready", className: "ready", sortBy: sortBy.ready }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Desired", className: "desired", sortBy: columnId.desired, id: columnId.desired }, + { title: "Current", className: "current", sortBy: columnId.current, id: columnId.current }, + { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(replicaSet: ReplicaSet) => [ replicaSet.getName(), diff --git a/src/renderer/components/+workloads-statefulsets/index.ts b/src/renderer/components/+workloads-statefulsets/index.ts index 1cb72d701a..af942b604f 100644 --- a/src/renderer/components/+workloads-statefulsets/index.ts +++ b/src/renderer/components/+workloads-statefulsets/index.ts @@ -1,2 +1,2 @@ export * from "./statefulsets"; -export * from "./statefulset-details"; \ No newline at end of file +export * from "./statefulset-details"; diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx index d30633ff32..f1f86b6b5f 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx @@ -30,9 +30,7 @@ export class StatefulSetDetails extends React.Component { }); componentDidMount() { - if (!podsStore.isLoaded) { - podsStore.loadAll(); - } + podsStore.reloadAll(); } componentWillUnmount() { diff --git a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts index 12f1f663b9..6ee4bb5c28 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts +++ b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts @@ -17,7 +17,7 @@ export class StatefulSetStore extends KubeObjectStore { } getChildPods(statefulSet: StatefulSet) { - return podsStore.getPodsByOwner(statefulSet); + return podsStore.getPodsByOwnerId(statefulSet.getId()); } getStatuses(statefulSets: StatefulSet[]) { diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx index 9e6011e156..7c91c9905c 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx @@ -17,9 +17,10 @@ import { MenuItem } from "../menu/menu"; import { Icon } from "../icon/icon"; import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry"; -enum sortBy { +enum columnId { name = "name", namespace = "namespace", + pods = "pods", age = "age", replicas = "replicas", } @@ -38,25 +39,27 @@ export class StatefulSets extends React.Component { render() { return ( statefulSet.getName(), - [sortBy.namespace]: (statefulSet: StatefulSet) => statefulSet.getNs(), - [sortBy.age]: (statefulSet: StatefulSet) => statefulSet.metadata.creationTimestamp, - [sortBy.replicas]: (statefulSet: StatefulSet) => statefulSet.getReplicas(), + [columnId.name]: (statefulSet: StatefulSet) => statefulSet.getName(), + [columnId.namespace]: (statefulSet: StatefulSet) => statefulSet.getNs(), + [columnId.age]: (statefulSet: StatefulSet) => statefulSet.metadata.creationTimestamp, + [columnId.replicas]: (statefulSet: StatefulSet) => statefulSet.getReplicas(), }} searchFilters={[ (statefulSet: StatefulSet) => statefulSet.getSearchFields(), ]} renderHeaderTitle="Stateful Sets" renderTableHeader={[ - { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Namespace", className: "namespace", sortBy: sortBy.namespace }, - { title: "Pods", className: "pods" }, - { title: "Replicas", className: "replicas", sortBy: sortBy.replicas }, - { className: "warning" }, - { title: "Age", className: "age", sortBy: sortBy.age }, + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Pods", className: "pods", id: columnId.pods }, + { title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas }, + { className: "warning", showWithColumn: columnId.replicas }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} renderTableContents={(statefulSet: StatefulSet) => [ statefulSet.getName(), diff --git a/src/renderer/components/+workloads/index.ts b/src/renderer/components/+workloads/index.ts index a6e1904b84..7e40e91f98 100644 --- a/src/renderer/components/+workloads/index.ts +++ b/src/renderer/components/+workloads/index.ts @@ -1,3 +1,4 @@ export * from "./workloads.route"; export * from "./workloads"; export * from "./workloads.stores"; +export * from "./workloads.command"; diff --git a/src/renderer/components/+workloads/workloads.command.ts b/src/renderer/components/+workloads/workloads.command.ts new file mode 100644 index 0000000000..02b5e42657 --- /dev/null +++ b/src/renderer/components/+workloads/workloads.command.ts @@ -0,0 +1,45 @@ +import { navigate } from "../../navigation"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { cronJobsURL, daemonSetsURL, deploymentsURL, jobsURL, podsURL, statefulSetsURL } from "./workloads.route"; + +commandRegistry.add({ + id: "cluster.viewPods", + title: "Cluster: View Pods", + scope: "cluster", + action: () => navigate(podsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewDeployments", + title: "Cluster: View Deployments", + scope: "cluster", + action: () => navigate(deploymentsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewDaemonSets", + title: "Cluster: View DaemonSets", + scope: "cluster", + action: () => navigate(daemonSetsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewStatefulSets", + title: "Cluster: View StatefulSets", + scope: "cluster", + action: () => navigate(statefulSetsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewJobs", + title: "Cluster: View Jobs", + scope: "cluster", + action: () => navigate(jobsURL()) +}); + +commandRegistry.add({ + id: "cluster.viewCronJobs", + title: "Cluster: View CronJobs", + scope: "cluster", + action: () => navigate(cronJobsURL()) +}); diff --git a/src/renderer/components/+workspaces/add-workspace.tsx b/src/renderer/components/+workspaces/add-workspace.tsx new file mode 100644 index 0000000000..868acc0e98 --- /dev/null +++ b/src/renderer/components/+workspaces/add-workspace.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { observer } from "mobx-react"; +import { Workspace, workspaceStore } from "../../../common/workspace-store"; +import { v4 as uuid } from "uuid"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { Input, InputValidator } from "../input"; +import { navigate } from "../../navigation"; +import { CommandOverlay } from "../command-palette/command-container"; +import { landingURL } from "../+landing-page"; +import { clusterStore } from "../../../common/cluster-store"; + +const uniqueWorkspaceName: InputValidator = { + condition: ({ required }) => required, + message: () => `Workspace with this name already exists`, + validate: value => !workspaceStore.getByName(value), +}; + +@observer +export class AddWorkspace extends React.Component { + onSubmit(name: string) { + if (!name.trim()) { + return; + } + const workspace = workspaceStore.addWorkspace(new Workspace({ + id: uuid(), + name + })); + + if (!workspace) { + return; + } + + workspaceStore.setActive(workspace.id); + clusterStore.setActive(null); + navigate(landingURL()); + CommandOverlay.close(); + } + + render() { + return ( + <> + this.onSubmit(v)} + dirty={true} + showValidationLine={true} /> + + Please provide a new workspace name (Press "Enter" to confirm or "Escape" to cancel) + + + ); + } +} + +commandRegistry.add({ + id: "workspace.addWorkspace", + title: "Workspace: Add workspace ...", + scope: "global", + action: () => CommandOverlay.open() +}); diff --git a/src/renderer/components/+workspaces/edit-workspace.tsx b/src/renderer/components/+workspaces/edit-workspace.tsx new file mode 100644 index 0000000000..3ab4b44d5a --- /dev/null +++ b/src/renderer/components/+workspaces/edit-workspace.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { observer } from "mobx-react"; +import { WorkspaceStore, workspaceStore } from "../../../common/workspace-store"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { Input, InputValidator } from "../input"; +import { CommandOverlay } from "../command-palette/command-container"; + +const validateWorkspaceName: InputValidator = { + condition: ({ required }) => required, + message: () => `Workspace with this name already exists`, + validate: (value) => { + const current = workspaceStore.currentWorkspace; + + if (current.name === value.trim()) { + return true; + } + + return !workspaceStore.enabledWorkspacesList.find((workspace) => workspace.name === value); + } +}; + +interface EditWorkspaceState { + name: string; +} + +@observer +export class EditWorkspace extends React.Component<{}, EditWorkspaceState> { + + state: EditWorkspaceState = { + name: "" + }; + + componentDidMount() { + this.setState({name: workspaceStore.currentWorkspace.name}); + } + + onSubmit(name: string) { + if (name.trim() === "") { + return; + } + + workspaceStore.currentWorkspace.name = name; + CommandOverlay.close(); + } + + onChange(name: string) { + this.setState({name}); + } + + get name() { + return this.state.name; + } + + render() { + return ( + <> + this.onChange(v)} + onSubmit={(v) => this.onSubmit(v)} + dirty={true} + value={this.name} + showValidationLine={true} /> + + Please provide a new workspace name (Press "Enter" to confirm or "Escape" to cancel) + + + ); + } +} + +commandRegistry.add({ + id: "workspace.editCurrentWorkspace", + title: "Workspace: Edit current workspace ...", + scope: "global", + action: () => CommandOverlay.open(), + isActive: (context) => context.workspace?.id !== WorkspaceStore.defaultId +}); diff --git a/src/renderer/components/+workspaces/index.ts b/src/renderer/components/+workspaces/index.ts index db23faa3be..5b84fc9b00 100644 --- a/src/renderer/components/+workspaces/index.ts +++ b/src/renderer/components/+workspaces/index.ts @@ -1,2 +1 @@ -export * from "./workspaces.route"; export * from "./workspaces"; diff --git a/src/renderer/components/+workspaces/remove-workspace.tsx b/src/renderer/components/+workspaces/remove-workspace.tsx new file mode 100644 index 0000000000..9f66292447 --- /dev/null +++ b/src/renderer/components/+workspaces/remove-workspace.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { observer } from "mobx-react"; +import { computed} from "mobx"; +import { WorkspaceStore, workspaceStore } from "../../../common/workspace-store"; +import { ConfirmDialog } from "../confirm-dialog"; +import { commandRegistry } from "../../../extensions/registries/command-registry"; +import { Select } from "../select"; +import { CommandOverlay } from "../command-palette/command-container"; + +@observer +export class RemoveWorkspace extends React.Component { + @computed get options() { + return workspaceStore.enabledWorkspacesList.filter((workspace) => workspace.id !== WorkspaceStore.defaultId).map((workspace) => { + return { value: workspace.id, label: workspace.name }; + }); + } + + onChange(id: string) { + const workspace = workspaceStore.enabledWorkspacesList.find((workspace) => workspace.id === id); + + if (!workspace ) { + return; + } + + CommandOverlay.close(); + ConfirmDialog.open({ + okButtonProps: { + label: `Remove Workspace`, + primary: false, + accent: true, + }, + ok: () => { + workspaceStore.removeWorkspace(workspace); + }, + message: ( +
+

+ Are you sure you want remove workspace {workspace.name}? +

+

+ All clusters within workspace will be cleared as well +

+
+ ), + }); + } + + render() { + return ( + editingWorkspace.name = v} - onKeyPress={(e) => this.onInputKeypress(e, workspaceId)} - validators={[isRequired, existenceValidator]} - autoFocus - /> - editingWorkspace.description = v} - onKeyPress={(e) => this.onInputKeypress(e, workspaceId)} - /> - this.saveWorkspace(workspaceId)} - /> - this.clearEditing(workspaceId)} - /> - - )} - - ); - })} - -